Skip to content

Commit 38a6fa5

Browse files
committed
Nearly there!
1 parent 63e2744 commit 38a6fa5

File tree

7 files changed

+138
-6
lines changed

7 files changed

+138
-6
lines changed

Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ authors = ["Ed Scheinerman <ers@jhu.edu>"]
44
version = "0.0.0"
55

66
[deps]
7+
ChooseOptimizer = "858a232f-1959-5553-8cfc-91e1fd5304e2"
78
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
9+
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
810
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
911

1012
[compat]
13+
ChooseOptimizer = "0.3"
1114
DataFrames = "1"
15+
JuMP = "1"
1216
LinearAlgebra = "1"

src/LPsolve.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""
2+
lp_solve(T::Tableau)
3+
4+
Use a standard linear programming solver [HiGHS by defaut] to find the optimal solution to the
5+
LP in `T`. Returns the values of the variables.
6+
"""
7+
function lp_solve(T::Tableau)
8+
MOD = Model(get_solver())
9+
@variable(MOD, x[1:(T.n_vars)] >= 0)
10+
@objective(MOD, Max, sum(T.c[i] * x[i] for i in 1:(T.n_vars)))
11+
for i in 1:(T.n_cons)
12+
@constraint(MOD, sum(T.A[i, j] * x[j] for j in 1:(T.n_vars)) <= T.b[i])
13+
end
14+
15+
optimize!(MOD)
16+
status = Int(termination_status(MOD))
17+
18+
if status 1
19+
error("Linear program is either infeasible or unbounded. Status = $status")
20+
end
21+
22+
return value.(x)
23+
end

src/Pivoting.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ end
6565

6666
"""
6767
pivot(T::Tableau, i::Int, j::Int)
68-
pivot!(T::Tableau)
68+
pivot(T::Tableau)
6969
7070
Non-modifying version of `pivot!`
7171
"""

src/SimpleTableaux.jl

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
11
module SimpleTableaux
22

3+
using ChooseOptimizer
34
using DataFrames
5+
using JuMP
46
using LinearAlgebra
57

68
import DataFrames: DataFrame
79
import Base: show
810

911
TabEntry = Rational{BigInt}
1012

11-
export Tableau, find_pivot, find_pivot_column, find_pivot_row, pivot, pivot!, restore
13+
export Tableau,
14+
find_pivot,
15+
find_pivot_column,
16+
find_pivot_row,
17+
lp_solve,
18+
pivot,
19+
pivot!,
20+
pivot_solve,
21+
pivot_solve!,
22+
restore
1223

1324
"""
1425
Tableau(A::Matrix, b::Vector, c::Vector)
@@ -40,9 +51,11 @@ end
4051
restore(T::Tableau)
4152
4253
Create a new `Tableau` based on the original data used to create `T`.
54+
55+
Recommended usage: `T = restore(T)` to reset `T` to its original state.
4356
"""
4457
function restore(T::Tableau)
45-
return Tableau(T.A, T.b, T.C)
58+
return Tableau(T.A, T.b, T.c)
4659
end
4760

4861
include("Exact.jl")
@@ -54,5 +67,7 @@ function show(io::IO, T::Tableau)
5467
end
5568

5669
include("Pivoting.jl")
70+
include("Solver.jl")
71+
include("LPsolve.jl")
5772

5873
end # module SimpleTableaux

src/Solver.jl

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"""
2+
pivot_solve!(T::Tableau, verbose::Bool=true)
3+
4+
Same as `pivot_solve` but this version modifies `T`.
5+
"""
6+
function pivot_solve!(T::Tableau, verbose::Bool=true)
7+
count = 0
8+
while true
9+
if verbose
10+
println("Iteration $count")
11+
println(T)
12+
println()
13+
count += 1
14+
end
15+
i, j = find_pivot(T)
16+
if i == 0 || j == 0
17+
break
18+
end
19+
pivot!(T, i, j)
20+
end # end while
21+
22+
if verbose
23+
val = Exact(T.M[end,end])
24+
println("Optimum value = $val")
25+
end
26+
27+
return _get_x(T)
28+
end # end pivot_solve
29+
30+
"""
31+
pivot_solve(T::Tableau, verbose::Bool=true)
32+
33+
Solve the linear program in `T` by pivoting.
34+
With `verbose` set to `true` all steps of the solution are shown.
35+
36+
Returns the optimal solution to the linear program.
37+
"""
38+
function pivot_solve(T::Tableau, verbose::Bool=true)
39+
TT = deepcopy(T)
40+
return pivot_solve!(TT, verbose)
41+
end
42+
43+
"""
44+
_is_basis_vector(x::Vector)::Bool
45+
46+
Determine if `x` is vector with a single entry equal to 1 and the rest equal to 0.
47+
"""
48+
function _is_basis_vector(x::Vector)::Bool
49+
x = sort(x)
50+
n = length(x)
51+
y = zeros(Int, n)
52+
y[end] = 1
53+
54+
return x == y
55+
end
56+
57+
"""
58+
_get_x(T::Tableau)
59+
60+
Extract the values of the solution to `T` after finish pivoting.
61+
"""
62+
function _get_x(T::Tableau)
63+
x = zeros(TabEntry, T.n_vars)
64+
65+
for i in 1:(T.n_vars)
66+
col = T.M[1:(T.n_cons), i]
67+
if _is_basis_vector(col)
68+
j = findfirst(col .== 1)
69+
x[i] = T.M[j, end]
70+
end
71+
end
72+
return x
73+
end

test/examples.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,19 @@ function example2()
2323
c = [3; 4; 1]
2424
return Tableau(A, b, c)
2525
end
26+
27+
function example3()
28+
A = [1 1 3; 2 2 5; 4 1 2]
29+
b = [30; 24; 36]
30+
c = [3; 1; 1]
31+
return Tableau(A, b, c)
32+
end
33+
34+
function unbounded_example()
35+
A = [-1 0; 0 -1]
36+
b = [-1; -1]
37+
c = [1; 1]
38+
return Tableau(A, b, c)
39+
end
40+
41+
nothing

test/runtests.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ using SimpleTableaux
33
using LinearAlgebra
44
using DataFrames
55

6-
@testset "Dumb start" begin
6+
@testset "Simple start" begin
77
A = [1 2 3 4; 5 6 7 8]
88
b = [1; 3//2]
99
c = [5; 6; 9; 2]
1010
T = Tableau(A, b, c)
11-
pivot!(T)
12-
@test true
11+
x = pivot_solve!(T, false)
12+
13+
@test dot(c, x) == T.M[end, end]
1314
end

0 commit comments

Comments
 (0)