Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7b7c02b
started on more extensive README.md
kwesiRutledge Sep 29, 2025
65738b4
Updated dependencies to less buggy SymbolicMath.go + MatProInterface.go
kwesiRutledge Oct 3, 2025
ec8ae5c
Attempting to fix bug in how we solve from tableau
kwesiRutledge Oct 3, 2025
5b5c414
Upgraded package dependencies + introduced simplex solution object
kwesiRutledge Oct 4, 2025
fb0ed9f
Added iterations field to SimplexSolution
kwesiRutledge Oct 4, 2025
4096194
Improved the documentation of the `SimplexSolution`
kwesiRutledge Oct 4, 2025
4ea1230
Remove commented code
kwesiRutledge Oct 4, 2025
e9b2b4d
Tried to debug the new example
kwesiRutledge Oct 4, 2025
16d1b2a
Cleaning up unused code in example
kwesiRutledge Oct 4, 2025
18ca59b
Merge branch 'kr/feature/gonum-bug1' of github.com:MatProGo-dev/simpl…
kwesiRutledge Oct 4, 2025
6df1ba7
Upgraded MatProInterface.go interface
kwesiRutledge Oct 16, 2025
b3d2996
Upgraded usage of modified methods throughout simplex
kwesiRutledge Oct 16, 2025
aca9aa1
Fixing bug in SimplexMethod Tableau method
kwesiRutledge Oct 16, 2025
941a322
Added working box example
kwesiRutledge Oct 16, 2025
e0f2023
remove references to old MatProInterface.go
kwesiRutledge Oct 16, 2025
c58b3b9
Updated README.md
kwesiRutledge Oct 16, 2025
1882ae9
Updated README.md example
kwesiRutledge Oct 16, 2025
ccc5f8a
upgraded MatProInterface
kwesiRutledge Oct 17, 2025
5919a5e
With the introduction of improved termination detection + better exit…
kwesiRutledge Oct 17, 2025
05e6179
Fixed issues with the unused exported member Objective in SimplexSolu…
kwesiRutledge Oct 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,82 @@
# simplex
A small library used to demonstrate the concepts of the Simplex algorithm in convex optimization.

# Installation

You can add this module to your package using:
```bash
go get github.com/MatProGo-dev/simplex
```

# Usage

```
package main

import (
"github.com/MatProGo-dev/MatProInterface.go/problem"
getKVector "github.com/MatProGo-dev/SymbolicMath.go/get/KVector"
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
"github.com/MatProGo-dev/simplex/simplexSolver"
)

func BuildOptimizationProblem() problem.OptimizationProblem {
// setup
varCount := 2
out := problem.NewProblem("Box LP Problem")

// Create the variables
x := out.AddVariableVector(varCount)

// Create the objective
c := getKVector.From(
[]float64{1, 2},
)
out.SetObjective(
c.Transpose().Multiply(x),
problem.SenseMinimize,
)

// Create the constraints
// - x >= 0
out.Constraints = append(
out.Constraints,
x.GreaterEq(symbolic.ZerosVector(varCount)),
)

// - x <= 1
out.Constraints = append(
out.Constraints,
x.LessEq(symbolic.OnesVector(varCount)),
)

return *out
}

func main() {
// This is just a placeholder to make the package "main" valid.
trickyProblem := BuildOptimizationProblem()

// Use solver to solve the problem
solver := simplexSolver.New("Simplex Solver Example")
solver.IterationLimit = 100

// Solve the problem
solution, err := solver.Solve(trickyProblem)
if err != nil {
panic(err)
}

// Print the solution
solutionMessage, _ := solution.Status.ToMessage()
println("Solution Status: ", solutionMessage)
println("Objective Value: ", solution.Objective)
println("Number of Iterations: ", solution.Iterations)
println("Variable Values: ")
for varName, varValue := range solution.VariableValues {
println(" ", varName, ": ", varValue)
}
}
```

See the examples directory for more example use cases for the library.
3 changes: 2 additions & 1 deletion algorithms/algorithm_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package algorithms

import (
"github.com/MatProGo-dev/MatProInterface.go/problem"
simplex_solution "github.com/MatProGo-dev/simplex/solution"
)

type AlgorithmInterface interface {
// Solves the provided optimization problem.
Solve(prob problem.OptimizationProblem) (problem.Solution, error)
Solve(prob problem.OptimizationProblem) (simplex_solution.SimplexSolution, error)
}
34 changes: 17 additions & 17 deletions algorithms/stanford/stanford.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"fmt"

"github.com/MatProGo-dev/MatProInterface.go/problem"
solution_status "github.com/MatProGo-dev/MatProInterface.go/solution/status"
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
simplex_solution "github.com/MatProGo-dev/simplex/solution"
"github.com/MatProGo-dev/simplex/utils"
"gonum.org/v1/gonum/mat"
)
Expand Down Expand Up @@ -178,11 +180,11 @@ Description:
- The values of the basic variables
- The values of the non-basic variables
*/
func (algo *StanfordAlgorithm) ComputeSolutionFromState(state StanfordAlgorithmState) (problem.Solution, error) {
func (algo *StanfordAlgorithm) ComputeSolutionFromState(state StanfordAlgorithmState) (simplex_solution.SimplexSolution, error) {
// Setup
fmt.Printf("Computing solution from state...\n")
solution := problem.Solution{
Status: problem.OptimizationStatus_OPTIMAL,
solution := simplex_solution.SimplexSolution{
Status: solution_status.OPTIMAL,
}

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

// Compute the value of the objective function
objValue, err := algo.ComputeObjectiveFunctionValueWithFeasibleBasicSolution(state, xBasic)
_, err = algo.ComputeObjectiveFunctionValueWithFeasibleBasicSolution(state, xBasic)
if err != nil {
return solution, fmt.Errorf("StanfordAlgorithm: Failed to compute objective function value (%v)", err)
}
solution.Objective = objValue

// Set the values of the basic variables
for ii, bv := range state.BasicVariables {
solution.Values[bv.ID] = xBasic.AtVec(ii)
solution.VariableValues[bv.ID] = xBasic.AtVec(ii)
}

// Set the values of the non-basic variables
for jj, nv := range state.GetNonBasicVariables() {
solution.Values[nv.ID] = state.NonBasicValues.AtVec(jj)
solution.VariableValues[nv.ID] = state.NonBasicValues.AtVec(jj)
}
Comment on lines 203 to 210
Copy link

Copilot AI Oct 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The VariableValues map is not initialized before use. This will cause a panic when trying to assign values to a nil map.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will likely delete this soon.


return solution, nil
}

func (algo *StanfordAlgorithm) Solve(initialState StanfordAlgorithmState) (problem.Solution, error) {
func (algo *StanfordAlgorithm) Solve(initialState StanfordAlgorithmState) (simplex_solution.SimplexSolution, error) {
// Setup
var stateII StanfordAlgorithmState = initialState

Expand All @@ -221,7 +222,7 @@ func (algo *StanfordAlgorithm) Solve(initialState StanfordAlgorithmState) (probl
// Test for Termination
r, err := stateII.GetReducedCostVector()
if err != nil {
return problem.Solution{}, fmt.Errorf(
return simplex_solution.SimplexSolution{}, fmt.Errorf(
"StanfordAlgorithm: Failed to get reduced cost vector (%v) at iteration #%v",
err,
iter,
Expand All @@ -248,7 +249,7 @@ func (algo *StanfordAlgorithm) Solve(initialState StanfordAlgorithmState) (probl
var ABasicInv mat.Dense
ABasic, err := stateII.ABasic()
if err != nil {
return problem.Solution{}, fmt.Errorf("StanfordAlgorithm: Failed to get ABasic matrix (%v)", err)
return simplex_solution.SimplexSolution{}, fmt.Errorf("StanfordAlgorithm: Failed to get ABasic matrix (%v)", err)
}
ABasicInv.Inverse(ABasic)
var ABasicAe mat.VecDense
Expand All @@ -263,7 +264,7 @@ func (algo *StanfordAlgorithm) Solve(initialState StanfordAlgorithmState) (probl
}
}
if objectiveIsUnboundedBelow {
return problem.Solution{}, fmt.Errorf(
return simplex_solution.SimplexSolution{}, fmt.Errorf(
"StanfordAlgorithm: Objective function is unbounded below, no solution exists at iteration %d",
iter,
)
Expand All @@ -274,13 +275,13 @@ func (algo *StanfordAlgorithm) Solve(initialState StanfordAlgorithmState) (probl
// Compute the feasible Solution of the Basic variables
xBasicII, err := algo.ComputeFeasibleBasicSolution(stateII)
if err != nil {
return problem.Solution{}, err
return simplex_solution.SimplexSolution{}, err
}

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

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

if iter == algo.IterationLimit {
return problem.Solution{
Objective: objII,
Status: problem.OptimizationStatus_ITERATION_LIMIT,
return simplex_solution.SimplexSolution{
Status: solution_status.ITERATION_LIMIT,
}, nil
}

}

return problem.Solution{}, nil
return simplex_solution.SimplexSolution{}, nil
}
18 changes: 18 additions & 0 deletions algorithms/tableau/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package tableau_algorithm1

type VariableSelectionError struct {
EnteringVarIndex int
ExitingVarIndex int
}

func (e VariableSelectionError) Error() string {
if e.ExitingVarIndex == -1 {
return "VariableSelectionError: No exiting variable found, problem can not be improved."
}

if e.EnteringVarIndex == -1 {
return "VariableSelectionError: No entering variable found, current solution is optimal."
}

return "VariableSelectionError: Unknown variable selection error."
}
12 changes: 8 additions & 4 deletions algorithms/tableau/selection/blands_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package selection

import (
"fmt"
"math"

"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
"github.com/MatProGo-dev/simplex/utils"
)

Expand Down Expand Up @@ -55,7 +57,7 @@ Description:
func (br BlandsRule) SelectExitingVariable(tableau utils.Tableau, enteringVarIdx int) int {
// Setup
minIndex := -1
minRatio := -1.0
minRatio := float64(symbolic.Infinity)

// Get the relevant matrices
A := tableau.A()
Expand All @@ -64,7 +66,7 @@ func (br BlandsRule) SelectExitingVariable(tableau utils.Tableau, enteringVarIdx
// Create the vector of ratios
ratios := make([]float64, tableau.NumberOfConstraints())
for i := 0; i < tableau.NumberOfConstraints(); i++ {
if A.At(i, enteringVarIdx) > 0 {
if math.Abs(A.At(i, enteringVarIdx)) > 1e-12 {
ratios[i] = b.AtVec(i) / A.At(i, enteringVarIdx)
} else {
ratios[i] = -1.0 // Indicate that this variable cannot be used
Expand All @@ -74,7 +76,7 @@ func (br BlandsRule) SelectExitingVariable(tableau utils.Tableau, enteringVarIdx
// Iterate through the ratios and find the smallest one
for i, ratio := range ratios {
if ratio >= 0 { // Only consider valid ratios
if minRatio < 0 || ratio < minRatio || (ratio == minRatio && tableau.BasicVariableIndicies[i] < tableau.BasicVariableIndicies[minIndex]) {
if ratio < minRatio || (ratio == minRatio && tableau.BasicVariableIndicies[i] < tableau.BasicVariableIndicies[minIndex]) {
minRatio = ratio
minIndex = i
}
Expand All @@ -98,10 +100,12 @@ func (br BlandsRule) SelectEnteringAndExitingVariables(tableau utils.Tableau) (i
return -1, -1, nil // Optimal solution found, no entering variable
}

fmt.Println("Entering variable index:", enteringVarIdx)

// Select the exiting variable
exitingVarIdx := br.SelectExitingVariable(tableau, enteringVarIdx)
if exitingVarIdx == -1 {
return enteringVarIdx, -1, fmt.Errorf("BlandsRule: No exiting variable found, problem is unbounded (?)")
return enteringVarIdx, -1, fmt.Errorf("BlandsRule: No exiting variable found, problem can not be improved.")
}

return enteringVarIdx, exitingVarIdx, nil
Expand Down
Loading