Skip to content

Commit fbfc97e

Browse files
kwesiRutledgeKwesi Rutledge
and
Kwesi Rutledge
authored
Fix Bug in How LinearConstraint Representations handle w.r.t. variables for scalar case (#17)
* Fixed bug in how scalar constraint computes linear constraint representations (make sure that we always include the wrt variable) * Fixed a few more cases that were needed to make the bug's tests work --------- Co-authored-by: Kwesi Rutledge <[email protected]>
1 parent 5c935be commit fbfc97e

7 files changed

+157
-12
lines changed

symbolic/constant_vector.go

+9-7
Original file line numberDiff line numberDiff line change
@@ -160,19 +160,18 @@ func (kv KVector) Plus(rightIn interface{}) Expression {
160160

161161
// Add the values
162162
return kv.Plus(VecDenseToKVector(eAsVec))
163-
case K:
164-
// Return Addition
165-
return kv.Plus(float64(right))
166-
case Variable:
163+
case K, Variable, Monomial, Polynomial:
167164
// Create a new polynomial vector
168-
var pvOut PolynomialVector
165+
var out []ScalarExpression
169166
for _, element := range kv {
170-
pvOut = append(pvOut, element.Plus(right).(Polynomial))
167+
out = append(out, element.Plus(right).(ScalarExpression))
171168
}
172-
return pvOut
169+
return ConcretizeVectorExpression(out)
173170

174171
case *mat.VecDense:
175172
return kv.Plus(VecDenseToKVector(*right)) // Convert to KVector
173+
case mat.VecDense:
174+
return kv.Plus(VecDenseToKVector(right)) // Convert to KVector
176175

177176
case KVector:
178177
// Compute Addition
@@ -234,6 +233,9 @@ func (kv KVector) Minus(e interface{}) Expression {
234233
return kv.Minus(VecDenseToKVector(right)) // Convert to KVector
235234
case *mat.VecDense:
236235
return kv.Minus(VecDenseToKVector(*right)) // Convert to KVector
236+
case K, Variable, Monomial, Polynomial:
237+
rightAsSE := right.(ScalarExpression)
238+
return kv.Plus(rightAsSE.Multiply(-1.0)) // Reuse K case
237239
case KVector, VariableVector, MonomialVector, PolynomialVector:
238240
// Force the right hand side to be a VectorExpression
239241
rhsAsVE := right.(VectorExpression)

symbolic/polynomial_like_vector.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type PolynomialLikeVector interface {
3030
// Variables returns the number of variables in the expression.
3131
Variables() []Variable
3232

33-
// Coeffs returns a slice of the coefficients in the expression
33+
// LinearCoeff returns a slice of the coefficients in the expression
3434
LinearCoeff(wrt ...[]Variable) mat.Dense
3535

3636
// Constant returns the constant additive value in the expression

symbolic/scalar_constraint.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ func (sc ScalarConstraint) LinearInequalityConstraintRepresentation(wrt ...[]Var
137137
newLHS := sc.Left().(ScalarExpression)
138138
newLHS = newLHS.Minus(sc.Right()).(ScalarExpression)
139139

140-
A = newLHS.LinearCoeff()
140+
A = newLHS.LinearCoeff(wrt...)
141141

142142
if sc.Sense == SenseGreaterThanEqual {
143143
A.ScaleVec(-1, &A)
@@ -199,7 +199,7 @@ func (sc ScalarConstraint) LinearEqualityConstraintRepresentation(wrt ...[]Varia
199199
// Create C
200200
newLHS := sc.Left().(ScalarExpression)
201201
newLHS = newLHS.Minus(sc.Right()).(ScalarExpression)
202-
C = newLHS.LinearCoeff()
202+
C = newLHS.LinearCoeff(wrt...)
203203

204204
// Create d
205205
newRHS := sc.Right().(ScalarExpression).Constant() - sc.Left().(ScalarExpression).Constant()

symbolic/variable.go

+3
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ func (v Variable) Plus(rightIn interface{}) Expression {
121121
return right.Plus(v)
122122
case *mat.VecDense:
123123
return v.Plus(VecDenseToKVector(*right))
124+
case mat.VecDense:
125+
// Convert to KVector
126+
return v.Plus(VecDenseToKVector(right))
124127
case KVector, VariableVector, MonomialVector, PolynomialVector:
125128
ve, _ := ToVectorExpression(rightIn)
126129
return ve.Plus(v)

symbolic/vector_expression.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ type VectorExpression interface {
3030
// Variables returns the number of variables in the expression.
3131
Variables() []Variable
3232

33-
//// Coeffs returns a slice of the coefficients in the expression
34-
//LinearCoeff() mat.Dense
33+
// LinearCoeffs returns a slice of the coefficients in the expression
34+
LinearCoeff(wrt ...[]Variable) mat.Dense
3535

3636
// Constant returns the constant additive value in the expression
3737
Constant() mat.VecDense

testing/symbolic/scalar_constraint_test.go

+94
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,53 @@ func TestScalarConstraint_LinearInequalityConstraintRepresentation5(t *testing.T
652652
sc.(symbolic.ScalarConstraint).LinearInequalityConstraintRepresentation()
653653
}
654654

655+
/*
656+
TestScalarConstraint_LinearInequalityConstraintRepresentation6
657+
Description:
658+
659+
Tests the LinearInequalityConstraintRepresentation() method of a scalar
660+
constraint. This test verifies that the method correctly produces a vector with
661+
length 2 and a constant of value 2.1 when using a small cosntraint:
662+
x1 <= 2.1
663+
but when calculating the representation with respect to a vector of 2 variables.
664+
*/
665+
func TestScalarConstraint_LinearInequalityConstraintRepresentation6(t *testing.T) {
666+
// Constants
667+
x := symbolic.NewVariableVector(2)
668+
c2 := symbolic.K(2.1)
669+
670+
// Create constraint
671+
sc := x.AtVec(0).LessEq(c2)
672+
673+
// Verify that the constraint is linear
674+
if !sc.IsLinear() {
675+
t.Errorf(
676+
"Expected sc to be linear; received %v",
677+
sc.IsLinear(),
678+
)
679+
}
680+
681+
// Get linear representation
682+
A, b := sc.(symbolic.ScalarConstraint).LinearInequalityConstraintRepresentation(x)
683+
684+
// Verify that the vector is all ones
685+
if A.AtVec(0) != 1 {
686+
t.Errorf("Expected A[0] to be 1; received %v", A.AtVec(0))
687+
}
688+
689+
if A.AtVec(1) != 0 {
690+
t.Errorf("Expected A[1] to be 0; received %v", A.AtVec(1))
691+
}
692+
693+
// Verify that the constant is 2.5
694+
if b != 2.1 {
695+
t.Errorf(
696+
"Expected b to be 2.5; received %v",
697+
b,
698+
)
699+
}
700+
}
701+
655702
/*
656703
TestScalarConstraint_LinearEqualityConstraintRepresentation1
657704
Description:
@@ -847,3 +894,50 @@ func TestScalarConstraint_LinearEqualityConstraintRepresentation4(t *testing.T)
847894

848895
sc.(symbolic.ScalarConstraint).LinearEqualityConstraintRepresentation()
849896
}
897+
898+
/*
899+
TestScalarConstraint_LinearEqualityConstraintRepresentation5
900+
Description:
901+
902+
Tests the LinearEqualityConstraintRepresentation() method of a scalar
903+
constraint. This test verifies that the method correctly produces a vector with
904+
length 2 and a constant of value 2.1 when using a small cosntraint:
905+
x1 = 2.1
906+
but when calculating the representation with respect to a vector of 2 variables.
907+
*/
908+
func TestScalarConstraint_LinearEqualityConstraintRepresentation5(t *testing.T) {
909+
// Constants
910+
x := symbolic.NewVariableVector(2)
911+
c2 := symbolic.K(2.1)
912+
913+
// Create constraint
914+
sc := x.AtVec(0).Eq(c2)
915+
916+
// Verify that the constraint is linear
917+
if !sc.IsLinear() {
918+
t.Errorf(
919+
"Expected sc to be linear; received %v",
920+
sc.IsLinear(),
921+
)
922+
}
923+
924+
// Get linear representation
925+
A, b := sc.(symbolic.ScalarConstraint).LinearEqualityConstraintRepresentation(x)
926+
927+
// Verify that the vector is all ones
928+
if A.AtVec(0) != 1 {
929+
t.Errorf("Expected A[0] to be 1; received %v", A.AtVec(0))
930+
}
931+
932+
if A.AtVec(1) != 0 {
933+
t.Errorf("Expected A[1] to be 0; received %v", A.AtVec(1))
934+
}
935+
936+
// Verify that the constant is 2.5
937+
if b != 2.1 {
938+
t.Errorf(
939+
"Expected b to be 2.5; received %v",
940+
b,
941+
)
942+
}
943+
}

testing/symbolic/vector_constraint_test.go

+46
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,52 @@ func TestVectorConstraint_LinearInequalityConstraintRepresentation7(t *testing.T
564564
vc.LinearInequalityConstraintRepresentation()
565565
}
566566

567+
/*
568+
TestVectorConstraint_LinearInequalityConstraintRepresentation8
569+
Description:
570+
571+
This function tests that the LinearInequalityConstraintRepresentation method
572+
properly returns a matrix of shape (2, 2) and a vector of shape (2, 1)
573+
for a well-defined, lienar vector constraint. This constraint will only contain
574+
1 variable, but the w.r.t. variable will contain 2 variables.
575+
*/
576+
func TestVectorConstraint_LinearInequalityConstraintRepresentation8(t *testing.T) {
577+
// Constants
578+
N := 2
579+
x := symbolic.NewVariableVector(N)
580+
left := x.AtVec(0).Plus(symbolic.ZerosVector(N))
581+
right := mat.NewVecDense(N, []float64{1, 2})
582+
vc := left.LessEq(right).(symbolic.VectorConstraint)
583+
584+
// Test
585+
A, b := vc.LinearInequalityConstraintRepresentation(x)
586+
587+
nRowsA, nColsA := A.Dims()
588+
if nRowsA != N || nColsA != 2 {
589+
t.Errorf(
590+
"Expected vc.LinearInequalityConstraintRepresentation() to return a matrix of dimension %v; received dimension (%v, %v)",
591+
[]int{N, 2},
592+
nRowsA, nColsA,
593+
)
594+
}
595+
596+
if b.AtVec(0) != 1 {
597+
t.Errorf(
598+
"Expected vc.LinearEqualityConstraintRepresentation()'s b vector to contain a 1 at the %v-th index; received %v",
599+
0,
600+
b.AtVec(0),
601+
)
602+
}
603+
604+
if b.AtVec(1) != 2 {
605+
t.Errorf(
606+
"Expected vc.LinearEqualityConstraintRepresentation()'s b vector to contain a 2 at the %v-th index; received %v",
607+
1,
608+
b.AtVec(1),
609+
)
610+
}
611+
}
612+
567613
/*
568614
TestVectorConstraint_LinearEqualityConstraintRepresentation1
569615
Description:

0 commit comments

Comments
 (0)