Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions symbolic/constant_vector.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,15 @@ func (kv KVector) Comparison(rightIn interface{}, sense ConstrSense) Constraint
}

switch rhsConverted := rightIn.(type) {
case float64:
var rhsAsVector mat.VecDense
onesVector := OnesVector(kv.Len())
rhsAsVector.ScaleVec(rhsConverted, &onesVector)
return kv.Comparison(rhsAsVector, sense)
case int:
return kv.Comparison(float64(rhsConverted), sense)
case K:
return kv.Comparison(float64(rhsConverted), sense)
case mat.VecDense:
// Use KVector's Comparison method
return kv.Comparison(VecDenseToKVector(rhsConverted), sense)
Expand Down Expand Up @@ -612,6 +621,11 @@ func (kv KVector) ToPolynomialVector() PolynomialVector {
return kv.ToMonomialVector().ToPolynomialVector()
}

/*
ToKMatrix
Description:
*/

/*
Degree
Description:
Expand Down
4 changes: 2 additions & 2 deletions symbolic/constr_sense.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ type ConstrSense byte
// Different constraint senses conforming to Gurobi's encoding.
const (
SenseEqual ConstrSense = '='
SenseLessThanEqual = '<'
SenseGreaterThanEqual = '>'
SenseLessThanEqual ConstrSense = '<'
SenseGreaterThanEqual ConstrSense = '>'
)

func (cs ConstrSense) String() string {
Expand Down
47 changes: 47 additions & 0 deletions symbolic/constraint.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package symbolic

import "fmt"

/*
constraint.go
Description:
Expand Down Expand Up @@ -27,6 +29,10 @@ type Constraint interface {
// AsSimplifiedConstraint
// Simplifies the constraint by moving all variables to the left hand side and the constants to the right.
AsSimplifiedConstraint() Constraint

// // IsPositivityConstraint
// // Returns true if the constraint defines a non-negativity constraint of the form x >= 0
// IsNonnegativityConstraint() bool
}

func IsConstraint(c interface{}) bool {
Expand Down Expand Up @@ -83,3 +89,44 @@ func VariablesInThisConstraint(c Constraint) []Variable {

return vars
}

/*
CompileConstraintsIntoScalarConstraints
Description:

This method analyzes all constraints in an OptimizationProblem and converts them all
into scalar constraints.
*/
func CompileConstraintsIntoScalarConstraints(constraints []Constraint) []ScalarConstraint {
// Setup
var out []ScalarConstraint

// Iterate through all constraints
for _, constraint := range constraints {
// Switch statement based on the type of the constraint
switch concreteConstraint := constraint.(type) {
case ScalarConstraint:
out = append(out, concreteConstraint)
case VectorConstraint:
for ii := 0; ii < concreteConstraint.Len(); ii++ {
out = append(out, concreteConstraint.AtVec(ii))
}
case MatrixConstraint:
dims := concreteConstraint.Dims()
for rowIdx := 0; rowIdx < dims[0]; rowIdx++ {
for colIdx := 0; colIdx < dims[1]; colIdx++ {
out = append(out, concreteConstraint.At(rowIdx, colIdx))
}
}
default:
panic(
fmt.Errorf(
"The received constraint type (%T) is not supported by ExtractScalarConstraints!",
constraint,
),
)
}
}

return out
}
4 changes: 4 additions & 0 deletions symbolic/monomial.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ func (m Monomial) Multiply(e interface{}) Expression {
switch right := e.(type) {
case float64:
return m.Multiply(K(right))
case int:
return m.Multiply(K(float64(right)))
case K:
rightAsFloat64 := float64(right)
monomialOut := m
Expand Down Expand Up @@ -339,6 +341,8 @@ func (m Monomial) Comparison(rhsIn interface{}, sense ConstrSense) Constraint {
switch right := rhsIn.(type) {
case float64:
return m.Comparison(K(right), sense)
case int:
return m.Comparison(K(float64(right)), sense)
case K:
return ScalarConstraint{m, right, sense}
case Variable:
Expand Down
2 changes: 2 additions & 0 deletions symbolic/polynomial.go
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,8 @@ func (p Polynomial) Comparison(rightIn interface{}, sense ConstrSense) Constrain
switch right := rightIn.(type) {
case float64:
return p.Comparison(K(right), sense)
case int:
return p.Comparison(float64(right), sense)
case K:
return ScalarConstraint{p, right, sense}
case Variable:
Expand Down
62 changes: 61 additions & 1 deletion symbolic/scalar_constraint.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ func (sc ScalarConstraint) String() string {
}

/*
Simplify
AsSimplifiedConstraint
Description:

Simplifies the constraint by moving all variables to the left hand side and the constants to the right.
Expand Down Expand Up @@ -445,3 +445,63 @@ func (sc ScalarConstraint) ImpliesThisIsAlsoSatisfied(other Constraint) bool {

return false
}

/*
IsNonnegativityConstraint
Description:

Checks to see if the constraint is of the form:
- x >= 0, or
- 0 <= x
*/
func (sc ScalarConstraint) IsNonnegativityConstraint() bool {
// Setup
err := sc.Check()
if err != nil {
panic(err)
}

simplified := sc.AsSimplifiedConstraint().(ScalarConstraint)

// Check to see if constraint contains more than 1 variable
if len(simplified.Variables()) != 1 {
return false
}

// Otherwise, the sense is SenseGreaterThanEqual, and this is a non-negativity
// constraint if:
// - LHS is Variable-Like
// - RHS is Zero

lhsIsVariableLike := false

// LHS Is Variable Like if:
// - It is a variable
// - It is a monomial with a positive coefficient
simplifiedAsPL, tf := simplified.LeftHandSide.(PolynomialLikeScalar)
if !tf {
return false // If lhs is not polynomial like, then return false.
}

lhsIsVariableLike = simplifiedAsPL.Degree() == 1

if !lhsIsVariableLike {
return false // If lhs is still not variable like, then return false.
}

// Check to see if rhs is zero
rhsAsK := simplified.RightHandSide.(K)
rhsIsZero := float64(rhsAsK) == 0

if !rhsIsZero {
return false
}

// Finally, the constraint is non-negativie if:
// - LHS has positive coefficient AND sense is GreaterThanEqual
// - LHS has negative coefficient AND sense is LessThanEqual
coeffs := simplified.LeftHandSide.LinearCoeff()

return (coeffs.AtVec(0) > 0 && simplified.Sense == SenseGreaterThanEqual) ||
(coeffs.AtVec(0) < 0 && simplified.Sense == SenseLessThanEqual)
}
3 changes: 3 additions & 0 deletions symbolic/variable.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ func (v Variable) Comparison(rhsIn interface{}, sense ConstrSense) Constraint {
case float64:
// Use version of comparison for K
return v.Comparison(K(rhs), sense)
case int:
// Use version of comparison for K
return v.Comparison(K(float64(rhs)), sense)
case K:
// Create a new constraint
return ScalarConstraint{v, rhs, sense}
Expand Down
148 changes: 148 additions & 0 deletions testing/symbolic/constant_vector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,154 @@ func TestConstantVector_Comparison1(t *testing.T) {
kv1.Comparison(input, symbolic.SenseEqual)
}

/*
TestConstantVector_Comparison2
Description:

Verifies that the Comparison method produces a proper
constraint when the left-hand side is a KVector and
the right-hand side is a float64.
The resulting compmarison should have a right hand
side which is a KVector.
*/
func TestConstantVector_Comparison2(t *testing.T) {
// Constants
kv1 := symbolic.VecDenseToKVector(symbolic.OnesVector(3))
var input float64 = 3.14

// Test
constraint := kv1.Comparison(input, symbolic.SenseEqual)

// Verify that the left hand side is of type KVector
if _, tf := constraint.Left().(symbolic.KVector); !tf {
t.Errorf(
"Expected constraint.LeftHandSide to be of type KVector; received %v",
constraint.Left(),
)
}

// Verify that the right hand side is of type KVector
if _, tf := constraint.Right().(symbolic.KVector); !tf {
t.Errorf(
"Expected constraint.RightHandSide to be of type KVector; received %v",
constraint.Right(),
)
}

// Verify that the sense of the constraint is Equal
if constraint.ConstrSense() != symbolic.SenseEqual {
t.Errorf(
"Expected constraint.Sense to be Equal; received %v",
constraint.ConstrSense(),
)
}
}

/*
TestConstantVector_Comparison3
Description:

Verifies that the Comparison method produces a proper
constraint when the left-hand side is a KVector and
the right-hand side is a int.
The resulting compmarison should have a right hand
side which is a KVector.
*/
func TestConstantVector_Comparison3(t *testing.T) {
// Constants
N := 3
kv1 := symbolic.VecDenseToKVector(symbolic.OnesVector(N))
var input int = 3

// Test
constraint := kv1.Comparison(input, symbolic.SenseEqual)

// Verify that the left hand side is of type KVector
if _, tf := constraint.Left().(symbolic.KVector); !tf {
t.Errorf(
"Expected constraint.LeftHandSide to be of type KVector; received %v",
constraint.Left(),
)
}

// Verify that the right hand side is of type KVector
kv, tf := constraint.Right().(symbolic.KVector)
if !tf {
t.Errorf(
"Expected constraint.RightHandSide to be of type KVector; received %v",
constraint.Right(),
)
}

// Verify that the length of the right hand side is N
if kv.Len() != N {
t.Errorf(
"Expected constraint.RightHandSide to have length %d; received %d",
N, kv.Len(),
)
}

// Verify that the sense of the constraint is Equal
if constraint.ConstrSense() != symbolic.SenseEqual {
t.Errorf(
"Expected constraint.Sense to be Equal; received %v",
constraint.ConstrSense(),
)
}
}

/*
TestConstantVector_Comparison4
Description:

Verifies that the Comparison method produces a proper
constraint when the left-hand side is a KVector and
the right-hand side is a symbolic.K.
The resulting compmarison should have a right hand
side which is a KVector.
*/
func TestConstantVector_Comparison4(t *testing.T) {
// Constants
N := 3
kv1 := symbolic.VecDenseToKVector(symbolic.OnesVector(N))
var input symbolic.K = symbolic.K(3.0)

// Test
constraint := kv1.Comparison(input, symbolic.SenseEqual)

// Verify that the left hand side is of type KVector
if _, tf := constraint.Left().(symbolic.KVector); !tf {
t.Errorf(
"Expected constraint.LeftHandSide to be of type KVector; received %v",
constraint.Left(),
)
}

// Verify that the right hand side is of type KVector and has length N
kv, tf := constraint.Right().(symbolic.KVector)
if !tf {
t.Errorf(
"Expected constraint.RightHandSide to be of type KVector; received %v",
constraint.Right(),
)
}

if kv.Len() != N {
t.Errorf(
"Expected constraint.RightHandSide to have length %d; received %d",
N, kv.Len(),
)
}

// Verify that the sense of the constraint is Equal
if constraint.ConstrSense() != symbolic.SenseEqual {
t.Errorf(
"Expected constraint.Sense to be Equal; received %v",
constraint.ConstrSense(),
)
}
}

/*
TestConstantVector_Multiply1
Description:
Expand Down
Loading
Loading