Skip to content

Commit

Permalink
Merge pull request #5 from MatProGo-dev/kr-example-qp1
Browse files Browse the repository at this point in the history
Introduced a Simplify() and IsLinear() method to ScalarConstraint to make some of Gurobi.go's efforts easier
  • Loading branch information
kwesiRutledge committed Nov 5, 2023
2 parents b90c05f + 7aaf198 commit 3fdab38
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 22 deletions.
26 changes: 4 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,33 +133,15 @@ code. Hopefully, this is avoided using this format.
* [X] Create New AddConstr methods which work for vector constraints
* [ ] Mult
* [ ] General Function (in operators.go)
* [ ] Methods for
* [X] Scalars
* [X] Constant
* [X] Var
* [X] ScalarLinearExpression
* [X] QuadraticExpression
* [ ] Vectors
* [ ] Vector Constant
* [ ] VarVector
* [ ] VectorLinearExpression
* [ ] Plus
* [ ] General Function (in operators.go)
* [ ] Introducing Optional Input for Variable Name to Var/VarVector
* [ ] Consider renaming VarVector to VectorVar
* [ ] Decide whether or not we really need the Coeffs() method (What is it doing?)
* [ ] Create function for easily creating MatDense:
* [ ] ones matrices
* [ ] Create function for:
* [ ] IsScalar()
* [ ] IsVector()
* [X] VectorConstraint
* [X] AtVec()
* [ ] Write changes to all AtVec() methods to output both elements AND errors (so we can detect out of length calls)
* [ ] Determine whether or not to keep the Solution and Solver() interfaces in this module. It seems like they can be solver-specific.
* [ ] Introduce MatrixVar object
* [ ] Add The Following to the Expression Interface
* [ ] Comparison
* [ ] LessEq
* [ ] GreaterEq
* [ ] Eq
* [ ] Add Check() to:
* [ ] Expression
* [ ] ScalarExpression
* [ ] VectorExpression interfaces
89 changes: 89 additions & 0 deletions optim/scalar_constraint.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package optim

import "fmt"

// ScalarConstraint represnts a linear constraint of the form x <= y, x >= y, or
// x == y. ScalarConstraint uses a left and right hand side expressions along with a
// constraint sense (<=, >=, ==) to represent a generalized linear constraint
Expand All @@ -17,6 +19,93 @@ func (sc ScalarConstraint) Right() Expression {
return sc.RightHandSide
}

/*
IsLinear
Description:
Describes whether or not a given linear constraint is
linear or not.
*/
func (sc ScalarConstraint) IsLinear() (bool, error) {
// Check left and right side.
if _, tf := sc.LeftHandSide.(ScalarQuadraticExpression); tf {
return false, nil
}

// If left side has degree less than two, then this only depends
// on the right side.
if _, tf := sc.RightHandSide.(ScalarQuadraticExpression); tf {
return false, nil
}

// Otherwise return true
return true, nil
}

/*
Simplify
Description:
Moves all of the variables of the ScalarConstraint to its
left hand side.
*/
func (sc ScalarConstraint) Simplify() (ScalarConstraint, error) {
// Create LHS
newLHS := sc.LeftHandSide

// Algorithm
switch right := sc.RightHandSide.(type) {
case K:
return sc, nil
case Variable:
newLHS, err := newLHS.Plus(right.Multiply(-1.0))
if err != nil {
return sc, err
}
newLHSAsSE, _ := ToScalarExpression(newLHS)

return ScalarConstraint{
LeftHandSide: newLHSAsSE,
RightHandSide: K(0),
Sense: sc.Sense,
}, nil
case ScalarLinearExpr:
rightWithoutConstant := right
rightWithoutConstant.C = 0.0

newLHS, err := newLHS.Plus(rightWithoutConstant.Multiply(-1.0))
if err != nil {
return sc, err
}
newLHSAsSE, _ := ToScalarExpression(newLHS)

return ScalarConstraint{
LeftHandSide: newLHSAsSE,
RightHandSide: K(right.C),
Sense: sc.Sense,
}, nil
case ScalarQuadraticExpression:
rightWithoutConstant := right
rightWithoutConstant.C = 0.0

newLHS, err := newLHS.Plus(rightWithoutConstant.Multiply(-1.0))
if err != nil {
return sc, err
}
newLHSAsSE, _ := ToScalarExpression(newLHS)

return ScalarConstraint{
LeftHandSide: newLHSAsSE,
RightHandSide: K(right.C),
Sense: sc.Sense,
}, nil

default:
return sc, fmt.Errorf("unexpected type of right hand side: %T", right)
}

}

// ConstrSense represents if the constraint x <= y, x >= y, or x == y. For easy
// integration with Gurobi, the senses have been encoding using a byte in
// the same way Gurobi encodes the constraint senses.
Expand Down
106 changes: 106 additions & 0 deletions testing/optim/scalar_constraint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Description:

import (
"github.com/MatProGo-dev/MatProInterface.go/optim"
"gonum.org/v1/gonum/mat"
"testing"
)

Expand All @@ -30,5 +31,110 @@ func TestScalarConstraint_ScalarConstraint1(t *testing.T) {
sc1.Sense,
)
}
}

/*
TestScalarConstraint_IsLinear1
Description:
Detects whether a simple inequality between
a variable and a constant is a linear constraint.
*/
func TestScalarConstraint_IsLinear1(t *testing.T) {
// Constants
m := optim.NewModel("scalar-constraint-test1")
v1 := m.AddVariable()
k1 := optim.K(2.8)

// Algorithm
sc1 := optim.ScalarConstraint{
LeftHandSide: v1,
RightHandSide: k1,
Sense: optim.SenseEqual,
}

tf, err := sc1.IsLinear()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if !tf {
t.Errorf("sc1 is linear, but function claims it is not!")
}

}

/*
TestScalarConstraint_IsLinear1
Description:
Detects whether a simple inequality between
a variable and a constant is a linear constraint.
*/
func TestScalarConstraint_IsLinear2(t *testing.T) {
// Constants
m := optim.NewModel("scalar-constraint-test1")
v1 := m.AddVariable()
sqe2 := optim.ScalarQuadraticExpression{
L: optim.OnesVector(1),
Q: *mat.NewDense(1, 1, []float64{3.14}),
X: optim.VarVector{Elements: []optim.Variable{v1}},
}

k1 := optim.K(2.8)

// Algorithm
sc1 := optim.ScalarConstraint{
LeftHandSide: sqe2,
RightHandSide: k1,
Sense: optim.SenseEqual,
}

tf, err := sc1.IsLinear()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if tf {
t.Errorf("sc1 is not linear, but function claims it is!")
}

}

/*
TestScalarConstraint_Simplify1
Description:
Attempts to simplify the constraint between
a scalar linear epression and a scalar linear expression.
*/
func TestScalarConstraint_Simplify1(t *testing.T) {
// Constants
m := optim.NewModel("scalar-constraint-test1")
vv1 := m.AddVariableVector(3)
sle2 := optim.ScalarLinearExpr{
L: optim.OnesVector(vv1.Len()),
X: vv1,
C: 2.0,
}
sle3 := optim.ScalarLinearExpr{
L: *mat.NewVecDense(vv1.Len(), []float64{1.0, 2.0, 3.0}),
X: vv1,
C: 1.0,
}

// Create sles
sc1 := optim.ScalarConstraint{
LeftHandSide: sle2,
RightHandSide: sle3,
Sense: optim.SenseEqual,
}

// Attempt to simplify
sc2, err := sc1.Simplify()
if err != nil {
t.Errorf("unexpected error during simplify(): %v", err)
}

if float64(sc2.RightHandSide.(optim.K)) != 1.0 {
t.Errorf("Remainder on LHS was not contained properly")
}
}

0 comments on commit 3fdab38

Please sign in to comment.