Skip to content

Commit 419a58e

Browse files
Merge pull request #2 from MatProGo-dev/kr/feature/gonum-bug1
Adding a bit more documentation + clarifying some interfaces
2 parents 58b26b5 + 05e6179 commit 419a58e

20 files changed

Lines changed: 572 additions & 103 deletions

File tree

‎README.md‎

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,82 @@
11
# simplex
22
A small library used to demonstrate the concepts of the Simplex algorithm in convex optimization.
3+
4+
# Installation
5+
6+
You can add this module to your package using:
7+
```bash
8+
go get github.com/MatProGo-dev/simplex
9+
```
10+
11+
# Usage
12+
13+
```
14+
package main
15+
16+
import (
17+
"github.com/MatProGo-dev/MatProInterface.go/problem"
18+
getKVector "github.com/MatProGo-dev/SymbolicMath.go/get/KVector"
19+
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
20+
"github.com/MatProGo-dev/simplex/simplexSolver"
21+
)
22+
23+
func BuildOptimizationProblem() problem.OptimizationProblem {
24+
// setup
25+
varCount := 2
26+
out := problem.NewProblem("Box LP Problem")
27+
28+
// Create the variables
29+
x := out.AddVariableVector(varCount)
30+
31+
// Create the objective
32+
c := getKVector.From(
33+
[]float64{1, 2},
34+
)
35+
out.SetObjective(
36+
c.Transpose().Multiply(x),
37+
problem.SenseMinimize,
38+
)
39+
40+
// Create the constraints
41+
// - x >= 0
42+
out.Constraints = append(
43+
out.Constraints,
44+
x.GreaterEq(symbolic.ZerosVector(varCount)),
45+
)
46+
47+
// - x <= 1
48+
out.Constraints = append(
49+
out.Constraints,
50+
x.LessEq(symbolic.OnesVector(varCount)),
51+
)
52+
53+
return *out
54+
}
55+
56+
func main() {
57+
// This is just a placeholder to make the package "main" valid.
58+
trickyProblem := BuildOptimizationProblem()
59+
60+
// Use solver to solve the problem
61+
solver := simplexSolver.New("Simplex Solver Example")
62+
solver.IterationLimit = 100
63+
64+
// Solve the problem
65+
solution, err := solver.Solve(trickyProblem)
66+
if err != nil {
67+
panic(err)
68+
}
69+
70+
// Print the solution
71+
solutionMessage, _ := solution.Status.ToMessage()
72+
println("Solution Status: ", solutionMessage)
73+
println("Objective Value: ", solution.Objective)
74+
println("Number of Iterations: ", solution.Iterations)
75+
println("Variable Values: ")
76+
for varName, varValue := range solution.VariableValues {
77+
println(" ", varName, ": ", varValue)
78+
}
79+
}
80+
```
81+
82+
See the examples directory for more example use cases for the library.

‎algorithms/algorithm_interface.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ package algorithms
22

33
import (
44
"github.com/MatProGo-dev/MatProInterface.go/problem"
5+
simplex_solution "github.com/MatProGo-dev/simplex/solution"
56
)
67

78
type AlgorithmInterface interface {
89
// Solves the provided optimization problem.
9-
Solve(prob problem.OptimizationProblem) (problem.Solution, error)
10+
Solve(prob problem.OptimizationProblem) (simplex_solution.SimplexSolution, error)
1011
}

‎algorithms/stanford/stanford.go‎

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"fmt"
55

66
"github.com/MatProGo-dev/MatProInterface.go/problem"
7+
solution_status "github.com/MatProGo-dev/MatProInterface.go/solution/status"
78
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
9+
simplex_solution "github.com/MatProGo-dev/simplex/solution"
810
"github.com/MatProGo-dev/simplex/utils"
911
"gonum.org/v1/gonum/mat"
1012
)
@@ -178,11 +180,11 @@ Description:
178180
- The values of the basic variables
179181
- The values of the non-basic variables
180182
*/
181-
func (algo *StanfordAlgorithm) ComputeSolutionFromState(state StanfordAlgorithmState) (problem.Solution, error) {
183+
func (algo *StanfordAlgorithm) ComputeSolutionFromState(state StanfordAlgorithmState) (simplex_solution.SimplexSolution, error) {
182184
// Setup
183185
fmt.Printf("Computing solution from state...\n")
184-
solution := problem.Solution{
185-
Status: problem.OptimizationStatus_OPTIMAL,
186+
solution := simplex_solution.SimplexSolution{
187+
Status: solution_status.OPTIMAL,
186188
}
187189

188190
// Compute the feasible solution of the basic variables
@@ -192,26 +194,25 @@ func (algo *StanfordAlgorithm) ComputeSolutionFromState(state StanfordAlgorithmS
192194
}
193195

194196
// Compute the value of the objective function
195-
objValue, err := algo.ComputeObjectiveFunctionValueWithFeasibleBasicSolution(state, xBasic)
197+
_, err = algo.ComputeObjectiveFunctionValueWithFeasibleBasicSolution(state, xBasic)
196198
if err != nil {
197199
return solution, fmt.Errorf("StanfordAlgorithm: Failed to compute objective function value (%v)", err)
198200
}
199-
solution.Objective = objValue
200201

201202
// Set the values of the basic variables
202203
for ii, bv := range state.BasicVariables {
203-
solution.Values[bv.ID] = xBasic.AtVec(ii)
204+
solution.VariableValues[bv.ID] = xBasic.AtVec(ii)
204205
}
205206

206207
// Set the values of the non-basic variables
207208
for jj, nv := range state.GetNonBasicVariables() {
208-
solution.Values[nv.ID] = state.NonBasicValues.AtVec(jj)
209+
solution.VariableValues[nv.ID] = state.NonBasicValues.AtVec(jj)
209210
}
210211

211212
return solution, nil
212213
}
213214

214-
func (algo *StanfordAlgorithm) Solve(initialState StanfordAlgorithmState) (problem.Solution, error) {
215+
func (algo *StanfordAlgorithm) Solve(initialState StanfordAlgorithmState) (simplex_solution.SimplexSolution, error) {
215216
// Setup
216217
var stateII StanfordAlgorithmState = initialState
217218

@@ -221,7 +222,7 @@ func (algo *StanfordAlgorithm) Solve(initialState StanfordAlgorithmState) (probl
221222
// Test for Termination
222223
r, err := stateII.GetReducedCostVector()
223224
if err != nil {
224-
return problem.Solution{}, fmt.Errorf(
225+
return simplex_solution.SimplexSolution{}, fmt.Errorf(
225226
"StanfordAlgorithm: Failed to get reduced cost vector (%v) at iteration #%v",
226227
err,
227228
iter,
@@ -248,7 +249,7 @@ func (algo *StanfordAlgorithm) Solve(initialState StanfordAlgorithmState) (probl
248249
var ABasicInv mat.Dense
249250
ABasic, err := stateII.ABasic()
250251
if err != nil {
251-
return problem.Solution{}, fmt.Errorf("StanfordAlgorithm: Failed to get ABasic matrix (%v)", err)
252+
return simplex_solution.SimplexSolution{}, fmt.Errorf("StanfordAlgorithm: Failed to get ABasic matrix (%v)", err)
252253
}
253254
ABasicInv.Inverse(ABasic)
254255
var ABasicAe mat.VecDense
@@ -263,7 +264,7 @@ func (algo *StanfordAlgorithm) Solve(initialState StanfordAlgorithmState) (probl
263264
}
264265
}
265266
if objectiveIsUnboundedBelow {
266-
return problem.Solution{}, fmt.Errorf(
267+
return simplex_solution.SimplexSolution{}, fmt.Errorf(
267268
"StanfordAlgorithm: Objective function is unbounded below, no solution exists at iteration %d",
268269
iter,
269270
)
@@ -274,13 +275,13 @@ func (algo *StanfordAlgorithm) Solve(initialState StanfordAlgorithmState) (probl
274275
// Compute the feasible Solution of the Basic variables
275276
xBasicII, err := algo.ComputeFeasibleBasicSolution(stateII)
276277
if err != nil {
277-
return problem.Solution{}, err
278+
return simplex_solution.SimplexSolution{}, err
278279
}
279280

280281
// Compute the value of the objective function
281282
objII, err := algo.ComputeObjectiveFunctionValueWithFeasibleBasicSolution(stateII, xBasicII)
282283
if err != nil {
283-
return problem.Solution{}, err
284+
return simplex_solution.SimplexSolution{}, err
284285
}
285286
fmt.Printf("Iteration %d: Basic Solution: %v, Objective Value: %f\n", iter, xBasicII, objII)
286287

@@ -290,13 +291,12 @@ func (algo *StanfordAlgorithm) Solve(initialState StanfordAlgorithmState) (probl
290291
// Check if the solution is optimal
291292

292293
if iter == algo.IterationLimit {
293-
return problem.Solution{
294-
Objective: objII,
295-
Status: problem.OptimizationStatus_ITERATION_LIMIT,
294+
return simplex_solution.SimplexSolution{
295+
Status: solution_status.ITERATION_LIMIT,
296296
}, nil
297297
}
298298

299299
}
300300

301-
return problem.Solution{}, nil
301+
return simplex_solution.SimplexSolution{}, nil
302302
}

‎algorithms/tableau/errors.go‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package tableau_algorithm1
2+
3+
type VariableSelectionError struct {
4+
EnteringVarIndex int
5+
ExitingVarIndex int
6+
}
7+
8+
func (e VariableSelectionError) Error() string {
9+
if e.ExitingVarIndex == -1 {
10+
return "VariableSelectionError: No exiting variable found, problem can not be improved."
11+
}
12+
13+
if e.EnteringVarIndex == -1 {
14+
return "VariableSelectionError: No entering variable found, current solution is optimal."
15+
}
16+
17+
return "VariableSelectionError: Unknown variable selection error."
18+
}

‎algorithms/tableau/selection/blands_rule.go‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package selection
22

33
import (
44
"fmt"
5+
"math"
56

7+
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
68
"github.com/MatProGo-dev/simplex/utils"
79
)
810

@@ -55,7 +57,7 @@ Description:
5557
func (br BlandsRule) SelectExitingVariable(tableau utils.Tableau, enteringVarIdx int) int {
5658
// Setup
5759
minIndex := -1
58-
minRatio := -1.0
60+
minRatio := float64(symbolic.Infinity)
5961

6062
// Get the relevant matrices
6163
A := tableau.A()
@@ -64,7 +66,7 @@ func (br BlandsRule) SelectExitingVariable(tableau utils.Tableau, enteringVarIdx
6466
// Create the vector of ratios
6567
ratios := make([]float64, tableau.NumberOfConstraints())
6668
for i := 0; i < tableau.NumberOfConstraints(); i++ {
67-
if A.At(i, enteringVarIdx) > 0 {
69+
if math.Abs(A.At(i, enteringVarIdx)) > 1e-12 {
6870
ratios[i] = b.AtVec(i) / A.At(i, enteringVarIdx)
6971
} else {
7072
ratios[i] = -1.0 // Indicate that this variable cannot be used
@@ -74,7 +76,7 @@ func (br BlandsRule) SelectExitingVariable(tableau utils.Tableau, enteringVarIdx
7476
// Iterate through the ratios and find the smallest one
7577
for i, ratio := range ratios {
7678
if ratio >= 0 { // Only consider valid ratios
77-
if minRatio < 0 || ratio < minRatio || (ratio == minRatio && tableau.BasicVariableIndicies[i] < tableau.BasicVariableIndicies[minIndex]) {
79+
if ratio < minRatio || (ratio == minRatio && tableau.BasicVariableIndicies[i] < tableau.BasicVariableIndicies[minIndex]) {
7880
minRatio = ratio
7981
minIndex = i
8082
}
@@ -98,10 +100,12 @@ func (br BlandsRule) SelectEnteringAndExitingVariables(tableau utils.Tableau) (i
98100
return -1, -1, nil // Optimal solution found, no entering variable
99101
}
100102

103+
fmt.Println("Entering variable index:", enteringVarIdx)
104+
101105
// Select the exiting variable
102106
exitingVarIdx := br.SelectExitingVariable(tableau, enteringVarIdx)
103107
if exitingVarIdx == -1 {
104-
return enteringVarIdx, -1, fmt.Errorf("BlandsRule: No exiting variable found, problem is unbounded (?)")
108+
return enteringVarIdx, -1, fmt.Errorf("BlandsRule: No exiting variable found, problem can not be improved.")
105109
}
106110

107111
return enteringVarIdx, exitingVarIdx, nil

0 commit comments

Comments
 (0)