diff --git a/smErrors/equality_constraint_required.go b/smErrors/equality_constraint_required.go new file mode 100644 index 0000000..a4897f9 --- /dev/null +++ b/smErrors/equality_constraint_required.go @@ -0,0 +1,9 @@ +package smErrors + +type EqualityConstraintRequiredError struct { + Operation string +} + +func (e EqualityConstraintRequiredError) Error() string { + return "Equality constraint required for operation: " + e.Operation +} diff --git a/smErrors/inequality_constraint_required.go b/smErrors/inequality_constraint_required.go new file mode 100644 index 0000000..7bfe562 --- /dev/null +++ b/smErrors/inequality_constraint_required.go @@ -0,0 +1,9 @@ +package smErrors + +type InequalityConstraintRequiredError struct { + Operation string +} + +func (icre InequalityConstraintRequiredError) Error() string { + return "Inequality constraint required for operation: " + icre.Operation +} diff --git a/smErrors/linear_expression_required.go b/smErrors/linear_expression_required.go new file mode 100644 index 0000000..5cee736 --- /dev/null +++ b/smErrors/linear_expression_required.go @@ -0,0 +1,16 @@ +package smErrors + +import "fmt" + +type LinearExpressionRequiredError struct { + Operation string + Expression interface{} +} + +func (lere LinearExpressionRequiredError) Error() string { + return fmt.Sprintf( + "Linear expression required for operation %v; received an expression which is not linear (%T).", + lere.Operation, + lere.Expression, + ) +} diff --git a/smErrors/dimension_error.go b/smErrors/matrix_dimension_error.go similarity index 79% rename from smErrors/dimension_error.go rename to smErrors/matrix_dimension_error.go index fc69d8f..024a874 100644 --- a/smErrors/dimension_error.go +++ b/smErrors/matrix_dimension_error.go @@ -9,16 +9,19 @@ Description: */ /* -DimensionError +MatrixDimensionError Description: + + This error is thrown when two matrices do not have the appropriate dimensions + for a given operation. */ -type DimensionError struct { +type MatrixDimensionError struct { Arg1 MatrixLike Arg2 MatrixLike Operation string // Either multiply or Plus } -func (de DimensionError) Error() string { +func (de MatrixDimensionError) Error() string { dimStrings := de.ArgDimsAsStrings() return fmt.Sprintf( "dimension error: Cannot perform %v between expression of dimension %v and expression of dimension %v", @@ -28,7 +31,7 @@ func (de DimensionError) Error() string { ) } -func (de DimensionError) ArgDimsAsStrings() []string { +func (de MatrixDimensionError) ArgDimsAsStrings() []string { // Create string for arg 1 arg1DimsAsString := "(" for ii, dimValue := range de.Arg1.Dims() { @@ -60,7 +63,7 @@ func CheckDimensionsInAddition(left, right MatrixLike) error { dimsAreMatched = dimsAreMatched || IsScalarExpression(right) if !dimsAreMatched { - return DimensionError{ + return MatrixDimensionError{ Operation: "Plus", Arg1: left, Arg2: right, @@ -83,7 +86,7 @@ func CheckDimensionsInSubtraction(left, right MatrixLike) error { dimsAreMatched = dimsAreMatched || IsScalarExpression(right) if !dimsAreMatched { - return DimensionError{ + return MatrixDimensionError{ Operation: "Minus", Arg1: left, Arg2: right, @@ -116,7 +119,7 @@ func CheckDimensionsInMultiplication(left, right MatrixLike) error { // Check that the # of columns in left // matches the # of rows in right if !multiplicationIsAllowed { - return DimensionError{ + return MatrixDimensionError{ Operation: "Multiply", Arg1: left, Arg2: right, @@ -148,7 +151,7 @@ func CheckDimensionsInHStack(sliceToStack ...MatrixLike) error { // then return an error dimsAreMatched := nRowsInSlice[ii] == nRowsInSlice[ii-1] if !dimsAreMatched { - return DimensionError{ + return MatrixDimensionError{ Operation: "HStack", Arg1: sliceToStack[ii-1], Arg2: sliceToStack[ii], @@ -182,7 +185,7 @@ func CheckDimensionsInVStack(sliceToStack ...MatrixLike) error { // then return an error dimsAreMatched := nColsInSlice[ii] == nColsInSlice[ii-1] if !dimsAreMatched { - return DimensionError{ + return MatrixDimensionError{ Operation: "VStack", Arg1: sliceToStack[ii-1], Arg2: sliceToStack[ii], @@ -193,3 +196,30 @@ func CheckDimensionsInVStack(sliceToStack ...MatrixLike) error { // If dimensions match, then return nothing. return nil } + +func CheckDimensionsInComparison(arg1, arg2 MatrixLike, comparisonType string) error { + dimsAreMatched := (arg1.Dims()[0] == arg2.Dims()[0]) && (arg1.Dims()[1] == arg2.Dims()[1]) + dimsAreMatched = dimsAreMatched || IsScalarExpression(arg1) + dimsAreMatched = dimsAreMatched || IsScalarExpression(arg2) + + if !dimsAreMatched { + // Return a specific type of error based on if this was a vector comparison + // or a matrix comparison + bothAreVectors := (arg1.Dims()[1] == 1) && (arg2.Dims()[1] == 1) + if bothAreVectors { + return VectorDimensionError{ + Operation: fmt.Sprintf("Comparison (%v)", string(comparisonType)), + Arg1: arg1.(VectorLike), + Arg2: arg2.(VectorLike), + } + } else { + return MatrixDimensionError{ + Operation: string(comparisonType), + Arg1: arg1, + Arg2: arg2, + } + } + } + + return nil +} diff --git a/smErrors/vector_dimension_error.go b/smErrors/vector_dimension_error.go new file mode 100644 index 0000000..d10a498 --- /dev/null +++ b/smErrors/vector_dimension_error.go @@ -0,0 +1,25 @@ +package smErrors + +import "fmt" + +/* +VectorDimensionError +Description: + + This error is thrown when two matrices do not have the appropriate dimensions + for a given operation. +*/ +type VectorDimensionError struct { + Arg1 VectorLike + Arg2 VectorLike + Operation string +} + +func (de VectorDimensionError) Error() string { + return fmt.Sprintf( + "vector dimension error: Cannot perform %v between expression of dimension %v and expression of dimension %v", + de.Operation, + de.Arg1.Len(), + de.Arg2.Len(), + ) +} diff --git a/smErrors/vector_like.go b/smErrors/vector_like.go new file mode 100644 index 0000000..f1e0673 --- /dev/null +++ b/smErrors/vector_like.go @@ -0,0 +1,12 @@ +package smErrors + +/* +VectorLike +Description: + + An interface for all objects that can be treated as vectors. +*/ +type VectorLike interface { + Len() int + Dims() []int +} diff --git a/symbolic/constant_vector.go b/symbolic/constant_vector.go index bb7abfe..b90086b 100644 --- a/symbolic/constant_vector.go +++ b/symbolic/constant_vector.go @@ -234,6 +234,14 @@ func (kv KVector) Minus(e interface{}) Expression { return kv.Minus(VecDenseToKVector(right)) // Convert to KVector case *mat.VecDense: return kv.Minus(VecDenseToKVector(*right)) // Convert to KVector + case KVector, VariableVector, MonomialVector, PolynomialVector: + // Force the right hand side to be a VectorExpression + rhsAsVE := right.(VectorExpression) + + // Compute Subtraction using our Multiply method + return kv.Plus( + rhsAsVE.Multiply(-1.0), + ) } // Default response is a panic @@ -297,14 +305,6 @@ func (kv KVector) Comparison(rightIn interface{}, sense ConstrSense) Constraint } switch rhsConverted := rightIn.(type) { - case KVector: - // Return constraint - return VectorConstraint{ - LeftHandSide: kv, - RightHandSide: rhsConverted, - Sense: sense, - } - case mat.VecDense: // Use KVector's Comparison method return kv.Comparison(VecDenseToKVector(rhsConverted), sense) @@ -313,11 +313,14 @@ func (kv KVector) Comparison(rightIn interface{}, sense ConstrSense) Constraint // Use KVector's Comparison method return kv.Comparison(VecDenseToKVector(*rhsConverted), sense) - case VariableVector: + case KVector, VariableVector, MonomialVector, PolynomialVector: + // Pass the rhsConverted object into a container marked as a "VectorExpression" interface + rhsAsVE := rhsConverted.(VectorExpression) + // Return constraint return VectorConstraint{ LeftHandSide: kv, - RightHandSide: rhsConverted, + RightHandSide: rhsAsVE, Sense: sense, } diff --git a/symbolic/polynomial.go b/symbolic/polynomial.go index a42bb01..9bd2538 100644 --- a/symbolic/polynomial.go +++ b/symbolic/polynomial.go @@ -394,7 +394,7 @@ func (p Polynomial) Multiply(e interface{}) Expression { // Multiply each monomial of the polynomial by the polynomial var productOut Expression = K(0.0) for ii := 0; ii < len(right.Monomials); ii++ { - fmt.Println(fmt.Sprintf("pCopy.Multiply(right.Monomials[ii]): %v", pCopy.Multiply(right.Monomials[ii]))) + // fmt.Println(fmt.Sprintf("pCopy.Multiply(right.Monomials[ii]): %v", pCopy.Multiply(right.Monomials[ii]))) productOut = productOut.Plus( pCopy.Multiply(right.Monomials[ii]), ) diff --git a/symbolic/polynomial_vector.go b/symbolic/polynomial_vector.go index dc86a39..16ccdbf 100644 --- a/symbolic/polynomial_vector.go +++ b/symbolic/polynomial_vector.go @@ -203,6 +203,14 @@ func (pv PolynomialVector) Plus(e interface{}) Expression { pvCopy[ii] = sum.(Polynomial) } return pvCopy + case Variable: + pvCopy := pv + for ii, polynomial := range pv { + sum := polynomial.Plus(right) + pvCopy[ii] = sum.(Polynomial) + } + return pvCopy + case Polynomial: pvCopy := pv @@ -417,6 +425,22 @@ func (pv PolynomialVector) Comparison(e interface{}, senseIn ConstrSense) Constr // Expression: pv.Plus(right.Multiply(K(-1))), // Sense: senseIn, // } + case *mat.VecDense: + // Convert the vector to a KVector + tempKVector := VecDenseToKVector(*right) + return VectorConstraint{ + LeftHandSide: pv, + RightHandSide: tempKVector, + Sense: senseIn, + } + case mat.VecDense: + // Convert the vector to a KVector + tempKVector := VecDenseToKVector(right) + return VectorConstraint{ + LeftHandSide: pv, + RightHandSide: tempKVector, + Sense: senseIn, + } case KVector, VariableVector, MonomialVector, PolynomialVector: rightAsVE, _ := ToVectorExpression(right) return VectorConstraint{ diff --git a/symbolic/scalar_constraint.go b/symbolic/scalar_constraint.go index 2f85f4f..0f0fb6f 100644 --- a/symbolic/scalar_constraint.go +++ b/symbolic/scalar_constraint.go @@ -1,5 +1,10 @@ package symbolic +import ( + "github.com/MatProGo-dev/SymbolicMath.go/smErrors" + "gonum.org/v1/gonum/mat" +) + // 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 @@ -95,3 +100,111 @@ func (sc ScalarConstraint) Check() error { // All Checks Passed! return nil } + +/* +LinearInequalityConstraintRepresentation +Description: + + Returns the linear constraint representation of the scalar constraint. + Returns a tuple of the form (A, b) where A is a vector and b is a constant such that: + A.Dot(x) <= b +*/ +func (sc ScalarConstraint) LinearInequalityConstraintRepresentation(wrt ...[]Variable) (A mat.VecDense, b float64) { + // Check that the constraint is well formed. + err := sc.Check() + if err != nil { + panic(err) + } + + // Check that the constraint is linear. + if !sc.IsLinear() { + if !IsLinear(sc.LeftHandSide) { + panic(smErrors.LinearExpressionRequiredError{ + Operation: "LinearInequalityConstraintRepresentation", + Expression: sc.LeftHandSide, + }) + } + + if !IsLinear(sc.RightHandSide) { + panic(smErrors.LinearExpressionRequiredError{ + Operation: "LinearInequalityConstraintRepresentation", + Expression: sc.RightHandSide, + }) + } + } + + // Create A + newLHS := sc.Left().(ScalarExpression) + newLHS = newLHS.Minus(sc.Right()).(ScalarExpression) + + A = newLHS.LinearCoeff() + + if sc.Sense == SenseGreaterThanEqual { + A.ScaleVec(-1, &A) + } + + // Create b + newRHS := sc.Right().(ScalarExpression).Constant() - sc.Left().(ScalarExpression).Constant() + b = newRHS + + if sc.Sense == SenseGreaterThanEqual { + b = -b + } + + // Return the tuple + return A, b +} + +/* +LinearEqualityConstraintRepresentation +Description: + + Returns the linear constraint representation of the scalar constraint. + Returns a tuple of the form (C, d) where C is a vector and d is a constant such that: + C.Dot(x) == d +*/ +func (sc ScalarConstraint) LinearEqualityConstraintRepresentation(wrt ...[]Variable) (C mat.VecDense, d float64) { + // Check that the constraint is well formed. + err := sc.Check() + if err != nil { + panic(err) + } + + // Check that the constraint is linear. + if !sc.IsLinear() { + if !IsLinear(sc.LeftHandSide) { + panic(smErrors.LinearExpressionRequiredError{ + Operation: "LinearEqualityConstraintRepresentation", + Expression: sc.LeftHandSide, + }) + } + + if !IsLinear(sc.RightHandSide) { + panic(smErrors.LinearExpressionRequiredError{ + Operation: "LinearEqualityConstraintRepresentation", + Expression: sc.RightHandSide, + }) + } + } + + // Check that the sense is equality. + if sc.Sense != SenseEqual { + panic( + smErrors.EqualityConstraintRequiredError{ + Operation: "LinearEqualityConstraintRepresentation", + }, + ) + } + + // Create C + newLHS := sc.Left().(ScalarExpression) + newLHS = newLHS.Minus(sc.Right()).(ScalarExpression) + C = newLHS.LinearCoeff() + + // Create d + newRHS := sc.Right().(ScalarExpression).Constant() - sc.Left().(ScalarExpression).Constant() + d = newRHS + + // Return + return C, d +} diff --git a/symbolic/utils.go b/symbolic/utils.go index db42fa2..2d4c992 100644 --- a/symbolic/utils.go +++ b/symbolic/utils.go @@ -2,6 +2,7 @@ package symbolic import ( "fmt" + "github.com/MatProGo-dev/SymbolicMath.go/smErrors" ) @@ -148,7 +149,7 @@ func CheckDimensionsInComparison(left, right Expression, senseIn ConstrSense) er dimsAreMatched = dimsAreMatched || IsScalarExpression(right) if !dimsAreMatched { - return smErrors.DimensionError{ + return smErrors.MatrixDimensionError{ Operation: "Comparison (" + senseIn.String() + ")", Arg1: left, Arg2: right, diff --git a/symbolic/variable_vector.go b/symbolic/variable_vector.go index f06468d..1de2b21 100644 --- a/symbolic/variable_vector.go +++ b/symbolic/variable_vector.go @@ -155,6 +155,9 @@ func (vv VariableVector) Plus(rightIn interface{}) Expression { // Algorithm switch right := rightIn.(type) { + case K, Variable, Monomial, Polynomial: + rightAsScalar := right.(ScalarExpression) + return rightAsScalar.Plus(vv) case *mat.VecDense: // Use KVector's method return vv.Plus(VecDenseToKVector(*right)) @@ -401,6 +404,10 @@ func (vv VariableVector) Comparison(rightIn interface{}, sense ConstrSense) Cons // Algorithm switch rhsConverted := rightIn.(type) { + case *mat.VecDense: + rhsAsKVector := VecDenseToKVector(*rhsConverted) + return vv.Comparison(rhsAsKVector, sense) + case mat.VecDense: rhsAsKVector := VecDenseToKVector(rhsConverted) diff --git a/symbolic/vector_constraint.go b/symbolic/vector_constraint.go index c3a02ed..1167025 100644 --- a/symbolic/vector_constraint.go +++ b/symbolic/vector_constraint.go @@ -1,8 +1,8 @@ package symbolic import ( - "fmt" "github.com/MatProGo-dev/SymbolicMath.go/smErrors" + "gonum.org/v1/gonum/mat" ) /* @@ -89,12 +89,9 @@ func (vc VectorConstraint) Check() error { } // Check dimensions of left and right hand sides. - if vc.LeftHandSide.Len() != vc.RightHandSide.Len() { - return fmt.Errorf( - "Left hand side has dimension %v, but right hand side has dimension %v!", - vc.LeftHandSide.Len(), - vc.RightHandSide.Len(), - ) + err = smErrors.CheckDimensionsInComparison(vc.Left(), vc.Right(), vc.ConstrSense().String()) + if err != nil { + return err } // All Checks Passed! @@ -129,3 +126,134 @@ Description: func (vc VectorConstraint) IsLinear() bool { return IsLinear(vc.RightHandSide) && IsLinear(vc.LeftHandSide) } + +/* +LinearInequalityConstraintRepresentation +Description: + + Returns the linear constraint representation of the scalar constraint. + Returns a tuple of the form (A, b) where A is a vector and b is a constant such that: + A.Dot(x) <= b +*/ +func (vc VectorConstraint) LinearInequalityConstraintRepresentation(wrt ...[]Variable) (A mat.Dense, b mat.VecDense) { + // Check that the constraint is well formed. + err := vc.Check() + if err != nil { + panic(err) + } + + // Check that the constraint is linear. + if !vc.IsLinear() { + if !IsLinear(vc.LeftHandSide) { + panic(smErrors.LinearExpressionRequiredError{ + Operation: "LinearInequalityConstraintRepresentation", + Expression: vc.LeftHandSide, + }) + } + + if !IsLinear(vc.RightHandSide) { + panic(smErrors.LinearExpressionRequiredError{ + Operation: "LinearInequalityConstraintRepresentation", + Expression: vc.RightHandSide, + }) + } + } + + // Check that the sense is inequality. + if vc.Sense == SenseEqual { + panic( + smErrors.InequalityConstraintRequiredError{ + Operation: "LinearInequalityConstraintRepresentation", + }, + ) + } + + // Create A + newLHS := vc.Left().(PolynomialLikeVector) + rhsWithoutConst := vc.Right().(PolynomialLikeVector) + rhsWithoutConst = rhsWithoutConst.Minus(rhsWithoutConst.Constant()).(PolynomialLikeVector) + newLHS = newLHS.Minus(rhsWithoutConst).(PolynomialLikeVector) + + A = newLHS.LinearCoeff(wrt...) + + if vc.Sense == SenseGreaterThanEqual { + A.Scale(-1, &A) + } + + // Create b + N := vc.Left().(VectorExpression).Len() + var newRHS *mat.VecDense = mat.NewVecDense(N, make([]float64, N)) + rightConst := vc.Right().(VectorExpression).Constant() + leftConst := vc.Left().(VectorExpression).Constant() + + newRHS.SubVec(&rightConst, &leftConst) + b = *newRHS + + if vc.Sense == SenseGreaterThanEqual { + b.ScaleVec(-1, &b) + } + + // Return the tuple + return A, b +} + +/* +LinearEqualityConstraintRepresentation +Description: + + Returns the representation of the constraint as a linear equality constraint. + Returns a tuple of the form (C, d) where C is a matrix and d is a vector such that: + C*x = d +*/ +func (vc VectorConstraint) LinearEqualityConstraintRepresentation(wrt ...[]Variable) (C mat.Dense, d mat.VecDense) { + // Check that the constraint is well formed. + err := vc.Check() + if err != nil { + panic(err) + } + + // Check that the constraint is linear. + if !vc.IsLinear() { + if !IsLinear(vc.LeftHandSide) { + panic(smErrors.LinearExpressionRequiredError{ + Operation: "LinearEqualityConstraintRepresentation", + Expression: vc.LeftHandSide, + }) + } + + if !IsLinear(vc.RightHandSide) { + panic(smErrors.LinearExpressionRequiredError{ + Operation: "LinearEqualityConstraintRepresentation", + Expression: vc.RightHandSide, + }) + } + } + + // Check that the sense is equality. + if vc.Sense != SenseEqual { + panic( + smErrors.EqualityConstraintRequiredError{ + Operation: "LinearEqualityConstraintRepresentation", + }, + ) + } + + // Create C + newLHS := vc.Left().(PolynomialLikeVector) + rhsWithoutConst := vc.Right().(PolynomialLikeVector) + rhsWithoutConst = rhsWithoutConst.Minus(rhsWithoutConst.Constant()).(PolynomialLikeVector) + newLHS = newLHS.Minus(rhsWithoutConst).(PolynomialLikeVector) + + C = newLHS.LinearCoeff(wrt...) + + // Create d + N := vc.Left().(VectorExpression).Len() + var newRHS *mat.VecDense = mat.NewVecDense(N, make([]float64, N)) + rightConst := vc.Right().(VectorExpression).Constant() + leftConst := vc.Left().(VectorExpression).Constant() + newRHS.SubVec(&rightConst, &leftConst) + d = *newRHS + + // Return the tuple + return C, d +} diff --git a/testing/symbolic/constant_matrix_test.go b/testing/symbolic/constant_matrix_test.go index d5856d5..eeecf6a 100644 --- a/testing/symbolic/constant_matrix_test.go +++ b/testing/symbolic/constant_matrix_test.go @@ -8,13 +8,14 @@ Description: import ( "fmt" - getKMatrix "github.com/MatProGo-dev/SymbolicMath.go/get/KMatrix" - "github.com/MatProGo-dev/SymbolicMath.go/smErrors" - "github.com/MatProGo-dev/SymbolicMath.go/symbolic" "math" "reflect" "strings" "testing" + + getKMatrix "github.com/MatProGo-dev/SymbolicMath.go/get/KMatrix" + "github.com/MatProGo-dev/SymbolicMath.go/smErrors" + "github.com/MatProGo-dev/SymbolicMath.go/symbolic" ) /* @@ -63,7 +64,7 @@ func TestConstantMatrix_Plus1(t *testing.T) { t.Errorf("Expected recovered value to be an error; received %T", recoveredVal) } - expectedError := smErrors.DimensionError{ + expectedError := smErrors.MatrixDimensionError{ Operation: "Plus", Arg1: km1, Arg2: symbolic.DenseToKMatrix(eye2), @@ -394,7 +395,7 @@ func TestKMatrix_Multiply2(t *testing.T) { t.Errorf("Expected recovered value to be an error; received %T", recoveredVal) } - err2 := smErrors.DimensionError{ + err2 := smErrors.MatrixDimensionError{ Operation: "Multiply", Arg1: km1, Arg2: km2, @@ -851,7 +852,7 @@ func TestKMatrix_LessEq2(t *testing.T) { } var expectedSense symbolic.ConstrSense = symbolic.SenseLessThanEqual - err2 := smErrors.DimensionError{ + err2 := smErrors.MatrixDimensionError{ Operation: "Comparison (" + expectedSense.String() + ")", Arg1: km1, Arg2: symbolic.NewVariableMatrix(3, 2), diff --git a/testing/symbolic/constant_vector_test.go b/testing/symbolic/constant_vector_test.go index 2bd304e..9a77da8 100644 --- a/testing/symbolic/constant_vector_test.go +++ b/testing/symbolic/constant_vector_test.go @@ -221,7 +221,7 @@ func TestConstantVector_Plus1(t *testing.T) { ) } - if rAsError.Error() != (smErrors.DimensionError{ + if rAsError.Error() != (smErrors.MatrixDimensionError{ Operation: "Plus", Arg1: kv1, Arg2: kv2, @@ -577,7 +577,7 @@ func TestConstantVector_LessEq1(t *testing.T) { } var sense0 symbolic.ConstrSense = symbolic.SenseLessThanEqual - if rAsError.Error() != (smErrors.DimensionError{ + if rAsError.Error() != (smErrors.MatrixDimensionError{ Operation: "Comparison (" + sense0.String() + ")", Arg1: kv1, Arg2: kv2, @@ -792,7 +792,7 @@ func TestConstantVector_Multiply2(t *testing.T) { } // Check that the error is the DimensionError - expectedError := smErrors.DimensionError{ + expectedError := smErrors.MatrixDimensionError{ Operation: "Multiply", Arg1: kv1, Arg2: vv2, diff --git a/testing/symbolic/expression_test.go b/testing/symbolic/expression_test.go index 7497056..fe9b188 100644 --- a/testing/symbolic/expression_test.go +++ b/testing/symbolic/expression_test.go @@ -197,7 +197,7 @@ func TestExpression_HStack4(t *testing.T) { // Collect the expected error which should be a dimension error and // compare it with the recovered error - expectedError := smErrors.DimensionError{ + expectedError := smErrors.MatrixDimensionError{ Operation: "HStack", Arg1: km1, Arg2: vv2, @@ -399,7 +399,7 @@ func TestExpression_VStack4(t *testing.T) { // Collect the expected error which should be a dimension error and // compare it with the recovered error - expectedError := smErrors.DimensionError{ + expectedError := smErrors.MatrixDimensionError{ Operation: "VStack", Arg1: km1, Arg2: vv2, @@ -443,7 +443,7 @@ func TestExpression_VStack5(t *testing.T) { // Collect the expected error which should be a dimension error and // compare it with the recovered error - expectedError := smErrors.DimensionError{ + expectedError := smErrors.MatrixDimensionError{ Operation: "VStack", Arg1: km1, Arg2: km2, diff --git a/testing/symbolic/monomial_matrix_test.go b/testing/symbolic/monomial_matrix_test.go index bc704e6..bfd22d5 100644 --- a/testing/symbolic/monomial_matrix_test.go +++ b/testing/symbolic/monomial_matrix_test.go @@ -1,11 +1,12 @@ package symbolic_test import ( + "strings" + "testing" + "github.com/MatProGo-dev/SymbolicMath.go/smErrors" "github.com/MatProGo-dev/SymbolicMath.go/symbolic" "gonum.org/v1/gonum/mat" - "strings" - "testing" ) /* @@ -400,7 +401,7 @@ func TestMonomialMatrix_Plus3(t *testing.T) { {v1.ToMonomial(), v1.ToMonomial()}, {v1.ToMonomial(), v1.ToMonomial()}, } - expectedError := smErrors.DimensionError{ + expectedError := smErrors.MatrixDimensionError{ Arg1: mm, Arg2: mm2, Operation: "Plus", @@ -881,7 +882,7 @@ func TestMonomialMatrix_Minus3(t *testing.T) { {v1.ToMonomial(), v1.ToMonomial()}, {v1.ToMonomial(), v1.ToMonomial()}, } - expectedError := smErrors.DimensionError{ + expectedError := smErrors.MatrixDimensionError{ Arg1: mm, Arg2: mm2, Operation: "Minus", @@ -1072,7 +1073,7 @@ func TestMonomialMatrix_Multiply3(t *testing.T) { } km2 := symbolic.DenseToKMatrix(symbolic.OnesMatrix(3, 2)) - expectedError := smErrors.DimensionError{ + expectedError := smErrors.MatrixDimensionError{ Arg1: mm, Arg2: km2, Operation: "Multiply", diff --git a/testing/symbolic/monomial_vector_test.go b/testing/symbolic/monomial_vector_test.go index 9bc242f..63fa3ee 100644 --- a/testing/symbolic/monomial_vector_test.go +++ b/testing/symbolic/monomial_vector_test.go @@ -488,7 +488,7 @@ func TestMonomialVector_Plus3(t *testing.T) { if !strings.Contains( rAsE.Error(), - smErrors.DimensionError{ + smErrors.MatrixDimensionError{ Operation: "Plus", Arg1: mv, Arg2: pm, @@ -952,7 +952,7 @@ func TestMonomialVector_Minus3(t *testing.T) { if !strings.Contains( rAsE.Error(), - smErrors.DimensionError{ + smErrors.MatrixDimensionError{ Operation: "Minus", Arg1: mv, Arg2: pm, diff --git a/testing/symbolic/polynomial_matrix_test.go b/testing/symbolic/polynomial_matrix_test.go index 087d8b3..951b261 100644 --- a/testing/symbolic/polynomial_matrix_test.go +++ b/testing/symbolic/polynomial_matrix_test.go @@ -1,13 +1,14 @@ package symbolic_test import ( + "strings" + "testing" + getKMatrix "github.com/MatProGo-dev/SymbolicMath.go/get/KMatrix" getKVector "github.com/MatProGo-dev/SymbolicMath.go/get/KVector" "github.com/MatProGo-dev/SymbolicMath.go/smErrors" "github.com/MatProGo-dev/SymbolicMath.go/symbolic" "gonum.org/v1/gonum/mat" - "strings" - "testing" ) /* @@ -333,7 +334,7 @@ func TestPolynomialMatrix_Plus2(t *testing.T) { ) } - if rAsError.Error() != (smErrors.DimensionError{ + if rAsError.Error() != (smErrors.MatrixDimensionError{ Operation: "Plus", Arg1: pm1, Arg2: pm2, @@ -717,7 +718,7 @@ func TestPolynomialMatrix_Minus2(t *testing.T) { ) } - if rAsError.Error() != (smErrors.DimensionError{ + if rAsError.Error() != (smErrors.MatrixDimensionError{ Operation: "Minus", Arg1: pm1, Arg2: pm2, @@ -957,7 +958,7 @@ func TestPolynomialMatrix_Multiply3(t *testing.T) { if !strings.Contains( rAsError.Error(), - smErrors.DimensionError{ + smErrors.MatrixDimensionError{ Operation: "Multiply", Arg1: pm1, Arg2: kv1, diff --git a/testing/symbolic/polynomial_vector_test.go b/testing/symbolic/polynomial_vector_test.go index 43ddf48..9ddb461 100644 --- a/testing/symbolic/polynomial_vector_test.go +++ b/testing/symbolic/polynomial_vector_test.go @@ -1498,7 +1498,7 @@ func TestPolynomialVector_Multiply6(t *testing.T) { } // Check that the error message is correct - if rAsE.Error() != (smErrors.DimensionError{ + if rAsE.Error() != (smErrors.MatrixDimensionError{ Arg1: pv1, Arg2: pv2, Operation: "Multiply", @@ -1815,6 +1815,40 @@ func TestPolynomialVector_Comparison4(t *testing.T) { } } +/* +TestPolynomialVector_Comparison5 +Description: + + This test verifies that the comparison function returns a well-defined constraint + when a PolynomialVector is compared with a mat.VecDense object. +*/ +func TestPolynomialVector_Comparison5(t *testing.T) { + // Constants + pv1 := symbolic.PolynomialVector{} + for ii := 0; ii < 20; ii++ { + pv1 = append(pv1, symbolic.NewVariable().ToPolynomial()) + } + v2 := symbolic.OnesVector(20) + + // Test + comp := pv1.Comparison(v2, symbolic.SenseGreaterThanEqual) + vectorComparison1, tf := comp.(symbolic.VectorConstraint) + if !tf { + t.Errorf( + "Expected comp to be of type VectorConstraint; received %T", + comp, + ) + } + + // Check that the right hand side of the constraint has the length of 20. + if vectorComparison1.RightHandSide.Len() != 20 { + t.Errorf( + "Expected vectorComparison1.RightHandSide.Len() to be 20; received %v", + vectorComparison1.RightHandSide.Len(), + ) + } +} + /* TestPolynomialVector_LessEq1 Description: diff --git a/testing/symbolic/scalar_constraint_test.go b/testing/symbolic/scalar_constraint_test.go index d884ec8..7f7dc04 100644 --- a/testing/symbolic/scalar_constraint_test.go +++ b/testing/symbolic/scalar_constraint_test.go @@ -7,9 +7,11 @@ Description: */ import ( - "github.com/MatProGo-dev/SymbolicMath.go/symbolic" "strings" "testing" + + "github.com/MatProGo-dev/SymbolicMath.go/smErrors" + "github.com/MatProGo-dev/SymbolicMath.go/symbolic" ) /* @@ -410,3 +412,438 @@ func TestScalarConstraint_Check4(t *testing.T) { ) } } + +/* +TestScalarConstraint_LinearInequalityConstraintRepresentation1 +Description: + + Tests the LinearInequalityConstraintRepresentation() method of a scalar + constraint. Here, we provide a constraint with two variables that are LessThanEqual + to a constant. The expected output is a vector of all ones and a constant 2.5. +*/ +func TestScalarConstraint_LinearInequalityConstraintRepresentation1(t *testing.T) { + // Constants + x := symbolic.NewVariableVector(2) + c2 := symbolic.K(2.5) + + // Create constraint + sc := x.Transpose().Multiply(symbolic.OnesVector(2)).LessEq(c2) + + // Verify that the constraint is linear + if !sc.IsLinear() { + t.Errorf( + "Expected sc to be linear; received %v", + sc.IsLinear(), + ) + } + + // Get linear representation + A, b := sc.(symbolic.ScalarConstraint).LinearInequalityConstraintRepresentation() + + // Verify that the vector is all ones + for ii := 0; ii < A.Len(); ii++ { + if A.AtVec(ii) != 1 { + t.Errorf( + "Expected A[%v] to be 1; received %v", + ii, + A.AtVec(ii), + ) + } + } + + // Verify that the constant is 2.5 + if b != 2.5 { + t.Errorf( + "Expected b to be 2.5; received %v", + b, + ) + } +} + +/* +TestScalarConstraint_LinearInequalityConstraintRepresentation2 +Description: + + Tests the LinearInequalityConstraintRepresentation() method of a scalar + constraint. Here, we provide a constraint with two variables that are GreaterThanEqual + to a constant. The expected output is a vector of all negative ones and a constant -2.5. +*/ +func TestScalarConstraint_LinearInequalityConstraintRepresentation2(t *testing.T) { + // Constants + x := symbolic.NewVariableVector(2) + c2 := symbolic.K(2.5) + + // Create constraint + sc := x.Transpose().Multiply(symbolic.OnesVector(2)).GreaterEq(c2) + + // Verify that the constraint is linear + if !sc.IsLinear() { + t.Errorf( + "Expected sc to be linear; received %v", + sc.IsLinear(), + ) + } + + // Get linear representation + A, b := sc.(symbolic.ScalarConstraint).LinearInequalityConstraintRepresentation() + + // Verify that the vector is all negative ones + for ii := 0; ii < A.Len(); ii++ { + if A.AtVec(ii) != -1 { + t.Errorf( + "Expected A[%v] to be -1; received %v", + ii, + A.AtVec(ii), + ) + } + } + + // Verify that the constant is -2.5 + if b != -2.5 { + t.Errorf( + "Expected b to be -2.5; received %v", + b, + ) + } +} + +/* +TestScalarConstraint_LinearInequalityConstraintRepresentation3 +Description: + + Tests the LinearInequalityConstraintRepresentation() method of a scalar + constraint. Here, we provide a constraint with two variable that is LessThanEqual + to one variable added to a constant. The expected output is a vector with a + single one and a single zero and a constant 2.5. +*/ +func TestScalarConstraint_LinearInequalityConstraintRepresentation3(t *testing.T) { + // Constants + x := symbolic.NewVariableVector(2) + c2 := symbolic.K(2.5) + + // Create constraint + sc := x.Transpose().Multiply(symbolic.OnesVector(2)).LessEq(x.AtVec(1).Plus(c2)) + + // Verify that the constraint is linear + if !sc.IsLinear() { + t.Errorf( + "Expected sc to be linear; received %v", + sc.IsLinear(), + ) + } + + // Get linear representation + A, b := sc.(symbolic.ScalarConstraint).LinearInequalityConstraintRepresentation() + + // Verify that the vector is all ones + + if A.AtVec(0) != 1 { + t.Errorf("Expected A[0] to be 1; received %v", A.AtVec(0)) + } + if A.AtVec(1) != 0.0 { + t.Errorf("Expected A[1] to be 0; received %v", A.AtVec(1)) + } + + // Verify that the constant is 2.5 + if b != 2.5 { + t.Errorf("Expected b to be 2.5; received %v", b) + } +} + +/* +TestScalarConstraint_LinearInequalityConstraintRepresentation4 +Description: + + Tests the LinearInequalityConstraintRepresentation() method of a scalar + constraint. This test verifies that the method panics if the left hand side + is not a polynomial like scalar. +*/ +func TestScalarConstraint_LinearInequalityConstraintRepresentation4(t *testing.T) { + // Constants + x := symbolic.NewVariableVector(2) + c2 := symbolic.K(2.5) + + // Create constraint + sc := x.Transpose().Multiply(x).LessEq(c2) + + // Verify that the constraint is linear + if sc.IsLinear() { + t.Errorf( + "Expected sc to be nonlinear; received IsLinear() = %v", + sc.IsLinear(), + ) + } + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation() to panic; received nil", + ) + } + + rAsError := r.(error) + expectedError := smErrors.LinearExpressionRequiredError{ + Operation: "LinearInequalityConstraintRepresentation", + Expression: sc.Left(), + } + if rAsError.Error() != expectedError.Error() { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation() to panic with error \"%v\"; received \"%v\"", + expectedError.Error(), + rAsError.Error(), + ) + } + }() + + // Get linear representation + sc.(symbolic.ScalarConstraint).LinearInequalityConstraintRepresentation() +} + +/* +TestScalarConstraint_LinearInequalityConstraintRepresentation5 +Description: + + Tests the LinearInequalityConstraintRepresentation() method of a scalar + constraint. This test verifies that the method panics if the RIGHT hand side + is not a polynomial like scalar. +*/ +func TestScalarConstraint_LinearInequalityConstraintRepresentation5(t *testing.T) { + // Constants + x := symbolic.NewVariableVector(2) + c2 := symbolic.K(2.5) + + // Create constraint + sc := c2.LessEq(x.Transpose().Multiply(x)) + + // Verify that the constraint is linear + if sc.IsLinear() { + t.Errorf( + "Expected sc to be nonlinear; received IsLinear() = %v", + sc.IsLinear(), + ) + } + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation() to panic; received nil", + ) + } + + rAsError := r.(error) + expectedError := smErrors.LinearExpressionRequiredError{ + Operation: "LinearInequalityConstraintRepresentation", + Expression: sc.Right(), + } + if rAsError.Error() != expectedError.Error() { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation() to panic with error \"%v\"; received \"%v\"", + expectedError.Error(), + rAsError.Error(), + ) + } + }() + + // Get linear representation + sc.(symbolic.ScalarConstraint).LinearInequalityConstraintRepresentation() +} + +/* +TestScalarConstraint_LinearEqualityConstraintRepresentation1 +Description: + + Tests the LinearEqualityConstraintRepresentation() method of a scalar + constraint. Here, we provide a constraint with two variables that are LessThanEqual + to a constant. The expected output is a vector of all ones and a constant 2.5. +*/ +func TestScalarConstraint_LinearEqualityConstraintRepresentation1(t *testing.T) { + // Constants + x := symbolic.NewVariableVector(2) + c2 := symbolic.K(2.5) + + // Create constraint + sc := x.Transpose().Multiply(symbolic.OnesVector(2)).Eq(c2) + + // Verify that the constraint is linear + if !sc.IsLinear() { + t.Errorf( + "Expected sc to be linear; received %v", + sc.IsLinear(), + ) + } + + // Get linear representation + A, b := sc.(symbolic.ScalarConstraint).LinearEqualityConstraintRepresentation() + + // Verify that the vector is all ones + for ii := 0; ii < A.Len(); ii++ { + if A.AtVec(ii) != 1 { + t.Errorf( + "Expected A[%v] to be 1; received %v", + ii, + A.AtVec(ii), + ) + } + } + + // Verify that the constant is 2.5 + if b != 2.5 { + t.Errorf( + "Expected b to be 2.5; received %v", + b, + ) + } +} + +/* +TestScalarConstraint_LinearEqualityConstraintRepresentation2 +Description: + + Tests the LinearEqualityConstraintRepresentation() method of a scalar + constraint. Here, we check that the method properly panics when we call + the method on a constraint that is not an equality constraint. +*/ +func TestScalarConstraint_LinearEqualityConstraintRepresentation2(t *testing.T) { + // Constants + x := symbolic.NewVariableVector(2) + c2 := symbolic.K(2.5) + + // Create constraint + sc := x.Transpose().Multiply(symbolic.OnesVector(2)).LessEq(c2) + + // Verify that the constraint is linear + if !sc.IsLinear() { + t.Errorf( + "Expected sc to be linear; received %v", + sc.IsLinear(), + ) + } + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to panic; received nil", + ) + } + + rAsError := r.(error) + expectedError := smErrors.EqualityConstraintRequiredError{ + Operation: "LinearEqualityConstraintRepresentation", + } + if rAsError.Error() != expectedError.Error() { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to panic with error \"%v\"; received \"%v\"", + expectedError.Error(), + rAsError.Error(), + ) + } + }() + + sc.(symbolic.ScalarConstraint).LinearEqualityConstraintRepresentation() +} + +/* +TestScalarConstraint_LinearEqualityConstraintRepresentation3 +Description: + + Tests the LinearEqualityConstraintRepresentation() method of a scalar + constraint. This test verifies that the method panics if the left hand side + is not a polynomial like scalar (in this case, it will be nonlinear). +*/ +func TestScalarConstraint_LinearEqualityConstraintRepresentation3(t *testing.T) { + // Constants + x := symbolic.NewVariableVector(2) + c2 := symbolic.K(2.5) + + // Create constraint + sc := x.Transpose().Multiply(x).Eq(c2) + + // Verify that the constraint is linear + if sc.IsLinear() { + t.Errorf( + "Expected sc to be nonlinear; received IsLinear() = %v", + sc.IsLinear(), + ) + } + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to panic; received nil", + ) + } + + rAsError := r.(error) + expectedError := smErrors.LinearExpressionRequiredError{ + Operation: "LinearEqualityConstraintRepresentation", + Expression: sc.Left(), + } + if rAsError.Error() != expectedError.Error() { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to panic with error \"%v\"; received \"%v\"", + expectedError.Error(), + rAsError.Error(), + ) + } + }() + + sc.(symbolic.ScalarConstraint).LinearEqualityConstraintRepresentation() +} + +/* +TestScalarConstraint_LinearEqualityConstraintRepresentation4 +Description: + + Tests the LinearEqualityConstraintRepresentation() method of a scalar + constraint. This test verifies that the method panics if the RIGHT hand side + is not a polynomial like scalar (in this case, it will be nonlinear). +*/ +func TestScalarConstraint_LinearEqualityConstraintRepresentation4(t *testing.T) { + // Constants + x := symbolic.NewVariableVector(2) + c2 := symbolic.K(2.5) + + // Create constraint + sc := c2.Eq(x.Transpose().Multiply(x)) + + // Verify that the constraint is linear + if sc.IsLinear() { + t.Errorf( + "Expected sc to be nonlinear; received IsLinear() = %v", + sc.IsLinear(), + ) + } + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to panic; received nil", + ) + } + + rAsError := r.(error) + expectedError := smErrors.LinearExpressionRequiredError{ + Operation: "LinearEqualityConstraintRepresentation", + Expression: sc.Right(), + } + if rAsError.Error() != expectedError.Error() { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to panic with error \"%v\"; received \"%v\"", + expectedError.Error(), + rAsError.Error(), + ) + } + }() + + sc.(symbolic.ScalarConstraint).LinearEqualityConstraintRepresentation() +} diff --git a/testing/symbolic/variable_vector_test.go b/testing/symbolic/variable_vector_test.go index 8bdf2a1..89a5fb5 100644 --- a/testing/symbolic/variable_vector_test.go +++ b/testing/symbolic/variable_vector_test.go @@ -259,7 +259,7 @@ func TestVariableVector_Plus1(t *testing.T) { } // Check that the error message is correct - if rAsE.Error() != (smErrors.DimensionError{ + if rAsE.Error() != (smErrors.MatrixDimensionError{ Operation: "Plus", Arg1: vv1, Arg2: vv2, @@ -575,7 +575,7 @@ func TestVariableVector_Minus2(t *testing.T) { } // Check that the error message is correct - if rAsE.Error() != (smErrors.DimensionError{ + if rAsE.Error() != (smErrors.MatrixDimensionError{ Operation: "Minus", Arg1: vv1, Arg2: vv2, @@ -791,7 +791,7 @@ func TestVariableVector_Multiply3(t *testing.T) { } // Check that the error message is correct - if rAsE.Error() != (smErrors.DimensionError{ + if rAsE.Error() != (smErrors.MatrixDimensionError{ Operation: "Multiply", Arg1: vv1, Arg2: vv2, @@ -1250,7 +1250,7 @@ func TestVariableVector_Comparison3(t *testing.T) { // Check that the error message is correct var tempSense symbolic.ConstrSense = symbolic.SenseLessThanEqual - if rAsE.Error() != (smErrors.DimensionError{ + if rAsE.Error() != (smErrors.MatrixDimensionError{ Operation: "Comparison (" + tempSense.String() + ")", Arg1: vv1, Arg2: vv2, diff --git a/testing/symbolic/vector_constraint_test.go b/testing/symbolic/vector_constraint_test.go index 641d125..60f35d7 100644 --- a/testing/symbolic/vector_constraint_test.go +++ b/testing/symbolic/vector_constraint_test.go @@ -8,10 +8,12 @@ Description: import ( "fmt" - "github.com/MatProGo-dev/SymbolicMath.go/smErrors" - "github.com/MatProGo-dev/SymbolicMath.go/symbolic" "strings" "testing" + + "github.com/MatProGo-dev/SymbolicMath.go/smErrors" + "github.com/MatProGo-dev/SymbolicMath.go/symbolic" + "gonum.org/v1/gonum/mat" ) /* @@ -35,9 +37,15 @@ func TestVectorConstraint_Check1(t *testing.T) { "Expected vc.Check() to return an error; received nil", ) } else { - if err.Error() != "Left hand side has dimension 3, but right hand side has dimension 4!" { + expectedError := smErrors.VectorDimensionError{ + Arg1: left, + Arg2: right, + Operation: fmt.Sprintf("Comparison (%v)", vc.Sense), + } + if err.Error() != expectedError.Error() { t.Errorf( - "Expected vc.Check() to return error \"Left hand side has dimension 3, but right hand side has dimension 4!\"; received \"%v\"", + "Expected vc.Check() to return error \"%v\"; received \"%v\"", + expectedError.Error(), err.Error(), ) } @@ -116,15 +124,15 @@ func TestVectorConstraint_Dims2(t *testing.T) { } rAsError := r.(error) - if rAsError.Error() != (fmt.Errorf( - "Left hand side has dimension %v, but right hand side has dimension %v!", - vc.LeftHandSide.Len(), - vc.RightHandSide.Len(), - )).Error() { + expectedError := smErrors.VectorDimensionError{ + Arg1: vc.LeftHandSide, + Arg2: vc.RightHandSide, + Operation: fmt.Sprintf("Comparison (%v)", vc.Sense), + } + if rAsError.Error() != expectedError.Error() { t.Errorf( - "Expected vc.Dims() to panic with error \"Left hand side has dimension %v, but right hand side has dimension %v!\"; received \"%v\"", - vc.LeftHandSide.Len(), - vc.RightHandSide.Len(), + "Expected vc.Dims() to panic with error \"%v\"; received \"%v\"", + expectedError.Error(), rAsError.Error(), ) } @@ -234,11 +242,12 @@ func TestVectorConstraint_AtVec3(t *testing.T) { } rAsError := r.(error) - if rAsError.Error() != (fmt.Errorf( - "Left hand side has dimension %v, but right hand side has dimension %v!", - vc.LeftHandSide.Len(), - vc.RightHandSide.Len(), - )).Error() { + expectedError := smErrors.VectorDimensionError{ + Arg1: vc.LeftHandSide, + Arg2: vc.RightHandSide, + Operation: fmt.Sprintf("Comparison (%v)", vc.Sense), + } + if rAsError.Error() != expectedError.Error() { t.Errorf( "Expected vc.Dims() to panic with error \"Left hand side has dimension %v, but right hand side has dimension %v!\"; received \"%v\"", vc.LeftHandSide.Len(), @@ -250,3 +259,517 @@ func TestVectorConstraint_AtVec3(t *testing.T) { vc.AtVec(N - 1) } + +/* +TestVectorConstraint_LinearInequalityConstraintRepresentation1 +Description: + + This function tests that the LinearInequalityConstraintRepresentation method + properly panics if the input VectorConstraint is not well defined. +*/ +func TestVectorConstraint_LinearInequalityConstraintRepresentation1(t *testing.T) { + // Constants + N := 7 + left := symbolic.VecDenseToKVector(symbolic.OnesVector(N)) + right := symbolic.NewVariableVector(N + 1) + vc := symbolic.VectorConstraint{left, right, symbolic.SenseLessThanEqual} + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation() to panic; received nil", + ) + } + + rAsError := r.(error) + expectedError := smErrors.VectorDimensionError{ + Operation: fmt.Sprintf("Comparison (%v)", vc.Sense), + Arg1: vc.LeftHandSide, + Arg2: vc.RightHandSide, + } + if rAsError.Error() != expectedError.Error() { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation() to panic with error \"%v\"; received \"%v\"", + expectedError.Error(), + rAsError.Error(), + ) + } + }() + + vc.LinearInequalityConstraintRepresentation() +} + +/* +TestVectorConstraint_LinearInequalityConstraintRepresentation2 +Description: + + This function tests that the LinearInequalityConstraintRepresentation method + properly panics when the left hand side is not linear. +*/ +func TestVectorConstraint_LinearInequalityConstraintRepresentation2(t *testing.T) { + // Constants + N := 7 + left := symbolic.VecDenseToKVector(symbolic.OnesVector(N)) + x := symbolic.NewVariableVector(N) + right := x.Plus(x.Transpose().Multiply(x)) + vc := left.LessEq(right).(symbolic.VectorConstraint) + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation() to panic; received nil", + ) + } + + rAsError := r.(error) + expectedError := smErrors.LinearExpressionRequiredError{ + Operation: "LinearInequalityConstraintRepresentation", + Expression: vc.Right(), + } + if rAsError.Error() != expectedError.Error() { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation() to panic with error \"%v\"; received \"%v\"", + expectedError.Error(), + rAsError.Error(), + ) + } + }() + + vc.LinearInequalityConstraintRepresentation() +} + +/* +TestVectorConstraint_LinearInequalityConstraintRepresentation3 +Description: + + This function tests that the LinearInequalityConstraintRepresentation method + handles a well-defined, lienar vector constraint properly. + In this case, the left hand side should be a 2x2 matrix of ones and the right hand side + should be a vector containing a 1 and a 2. This will be a LessEq constraint. +*/ +func TestVectorConstraint_LinearInequalityConstraintRepresentation3(t *testing.T) { + // Constants + N := 2 + x := symbolic.NewVariableVector(N) + left := symbolic.VecDenseToKVector(symbolic.ZerosVector(N)).Plus(x.AtVec(0)).Plus(x.AtVec(1)) + right := mat.NewVecDense(N, []float64{1, 2}) + vc := left.LessEq(right).(symbolic.VectorConstraint) + fmt.Printf("vc_0: %v\n", vc) + + // Test + A, b := vc.LinearInequalityConstraintRepresentation() + + nRowsA, nColsA := A.Dims() + if nRowsA != N || nColsA != 2 { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation() to return a matrix of dimension %v; received dimension (%v, %v)", + []int{N, 2}, + nRowsA, nColsA, + ) + } + + if b.AtVec(0) != 1 { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation()'s b vector to contain a 1 at the %v-th index; received %v", + 0, + b.AtVec(0), + ) + } + + if b.AtVec(1) != 2 { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation()'s b vector to contain a 2 at the %v-th index; received %v", + 1, + b.AtVec(1), + ) + } +} + +/* +TestVectorConstraint_LinearInequalityConstraintRepresentation4 +Description: + + This function tests that the LinearInequalityConstraintRepresentation method + returns the proper matrix and vector for a well-defined, lienar vector constraint. + We will construct a vector constraint of the form: + [1, 0; + 0, 1] * x <= [1; 2] + where x is a vector of variables. +*/ +func TestVectorConstraint_LinearInequalityConstraintRepresentation4(t *testing.T) { + // Constants + N := 2 + x := symbolic.NewVariableVector(N) + left := x + right := mat.NewVecDense(N, []float64{1, 2}) + vc := left.LessEq(right).(symbolic.VectorConstraint) + + // Test + A, b := vc.LinearInequalityConstraintRepresentation() + + nRowsA, nColsA := A.Dims() + if nRowsA != N || nColsA != 2 { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to return a matrix of dimension %v; received dimension (%v, %v)", + []int{N, 1}, + nRowsA, nColsA, + ) + } + + if b.AtVec(0) != 1 { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation()'s b vector to contain a 1 at the %v-th index; received %v", + 0, + b.AtVec(0), + ) + } + + if b.AtVec(1) != 2 { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation()'s b vector to contain a 2 at the %v-th index; received %v", + 1, + b.AtVec(1), + ) + } +} + +/* +TestVectorConstraint_LinearInequalityConstraintRepresentation5 +Description: + + This function tests that the LinearInequalityConstraintRepresentation method + properly panics if the left hand side is not linear. +*/ +func TestVectorConstraint_LinearInequalityConstraintRepresentation5(t *testing.T) { + // Constants + N := 7 + right := symbolic.VecDenseToKVector(symbolic.OnesVector(N)) + x := symbolic.NewVariableVector(N) + left := x.Plus(x.Transpose().Multiply(x)) + vc := left.GreaterEq(right).(symbolic.VectorConstraint) + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation() to panic; received nil", + ) + } + + rAsError := r.(error) + expectedError := smErrors.LinearExpressionRequiredError{ + Operation: "LinearInequalityConstraintRepresentation", + Expression: vc.Left(), + } + if rAsError.Error() != expectedError.Error() { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation() to panic with error \"%v\"; received \"%v\"", + expectedError.Error(), + rAsError.Error(), + ) + } + }() + + vc.LinearInequalityConstraintRepresentation() +} + +/* +TestVectorConstraint_LinearInequalityConstraintRepresentation6 +Description: + + This function tests that the LinearInequalityConstraintRepresentation method + properly flips the sign of the matrices and vectors. + In this case, we will create the same expressions as + TestVectorConstraint_LinearInequalityConstraintRepresentation3 but with a GreaterEq constraint + sense. +*/ +func TestVectorConstraint_LinearInequalityConstraintRepresentation6(t *testing.T) { + // Constants + N := 2 + x := symbolic.NewVariableVector(N) + left := symbolic.VecDenseToKVector(symbolic.ZerosVector(N)).Plus(x.AtVec(0)).Plus(x.AtVec(1)) + right := mat.NewVecDense(N, []float64{1, 2}) + vc := left.GreaterEq(right).(symbolic.VectorConstraint) + + // Test + A, b := vc.LinearInequalityConstraintRepresentation() + + nRowsA, nColsA := A.Dims() + if nRowsA != N || nColsA != 2 { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation() to return a matrix of dimension %v; received dimension (%v, %v)", + []int{N, 2}, + nRowsA, nColsA, + ) + } + + if b.AtVec(0) != -1 { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation()'s b vector to contain a -1 at the %v-th index; received %v", + 0, + b.AtVec(0), + ) + } + + if b.AtVec(1) != -2 { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation()'s b vector to contain a -2 at the %v-th index; received %v", + 1, + b.AtVec(1), + ) + } +} + +/* +TestVectorConstraint_LinearInequalityConstraintRepresentation7 +Description: + + This function tests that the LinearInequalityConstraintRepresentation method + properly panics when called with a constraint that is not based on inequality. +*/ +func TestVectorConstraint_LinearInequalityConstraintRepresentation7(t *testing.T) { + // Constants + N := 7 + left := symbolic.VecDenseToKVector(symbolic.OnesVector(N)) + right := symbolic.NewVariableVector(N) + vc := symbolic.VectorConstraint{left, right, symbolic.SenseEqual} + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation() to panic; received nil", + ) + } + + rAsError := r.(error) + expectedError := smErrors.InequalityConstraintRequiredError{ + Operation: "LinearInequalityConstraintRepresentation", + } + if rAsError.Error() != expectedError.Error() { + t.Errorf( + "Expected vc.LinearInequalityConstraintRepresentation() to panic with error \"%v\"; received \"%v\"", + expectedError.Error(), + rAsError.Error(), + ) + } + }() + + vc.LinearInequalityConstraintRepresentation() +} + +/* +TestVectorConstraint_LinearEqualityConstraintRepresentation1 +Description: + + This function tests that the LinearEqualityConstraintRepresentation method + properly panics if the input VectorConstraint is not well defined. +*/ +func TestVectorConstraint_LinearEqualityConstraintRepresentation1(t *testing.T) { + // Constants + N := 7 + left := symbolic.VecDenseToKVector(symbolic.OnesVector(N)) + right := symbolic.NewVariableVector(N + 1) + vc := symbolic.VectorConstraint{left, right, symbolic.SenseEqual} + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to panic; received nil", + ) + } + + rAsError := r.(error) + expectedError := smErrors.VectorDimensionError{ + Operation: fmt.Sprintf("Comparison (%v)", vc.Sense), + Arg1: vc.LeftHandSide, + Arg2: vc.RightHandSide, + } + if rAsError.Error() != expectedError.Error() { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to panic with error \"%v\"; received \"%v\"", + expectedError.Error(), + rAsError.Error(), + ) + } + }() + + vc.LinearEqualityConstraintRepresentation() +} + +/* +TestVectorConstraint_LinearEqualityConstraintRepresentation2 +Description: + + This function tests that the LinearEqualityConstraintRepresentation method + properly panics when the LEFT hand side is not linear. +*/ +func TestVectorConstraint_LinearEqualityConstraintRepresentation2(t *testing.T) { + // Constants + N := 7 + right := symbolic.VecDenseToKVector(symbolic.OnesVector(N)) + x := symbolic.NewVariableVector(N) + left := x.Plus(x.Transpose().Multiply(x)) + vc := left.Eq(right).(symbolic.VectorConstraint) + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to panic; received nil", + ) + } + + rAsError := r.(error) + expectedError := smErrors.LinearExpressionRequiredError{ + Operation: "LinearEqualityConstraintRepresentation", + Expression: vc.Left(), + } + if rAsError.Error() != expectedError.Error() { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to panic with error \"%v\"; received \"%v\"", + expectedError.Error(), + rAsError.Error(), + ) + } + }() + + vc.LinearEqualityConstraintRepresentation() +} + +/* +TestVectorConstraint_LinearEqualityConstraintRepresentation3 +Description: + + This function tests that the LinearEqualityConstraintRepresentation method + properly panics when the RIGHT hand side is not linear. +*/ +func TestVectorConstraint_LinearEqualityConstraintRepresentation3(t *testing.T) { + // Constants + N := 7 + left := symbolic.VecDenseToKVector(symbolic.OnesVector(N)) + x := symbolic.NewVariableVector(N) + right := x.Plus(x.Transpose().Multiply(x)) + vc := left.Eq(right).(symbolic.VectorConstraint) + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to panic; received nil", + ) + } + + rAsError := r.(error) + expectedError := smErrors.LinearExpressionRequiredError{ + Operation: "LinearEqualityConstraintRepresentation", + Expression: vc.Right(), + } + if rAsError.Error() != expectedError.Error() { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to panic with error \"%v\"; received \"%v\"", + expectedError.Error(), + rAsError.Error(), + ) + } + }() + + vc.LinearEqualityConstraintRepresentation() +} + +/* +TestVectorConstraint_LinearEqualityConstraintRepresentation4 +Description: + + This function tests that the LinearEqualityConstraintRepresentation method + properly returns the matrix and vector for a well-defined, lienar vector constraint. + In this case, we will create a vector constraint of the form: + [1, 0; + 0, 1] * x = [1; 2] + where x is a vector of variables. +*/ +func TestVectorConstraint_LinearEqualityConstraintRepresentation4(t *testing.T) { + // Constants + N := 2 + x := symbolic.NewVariableVector(N) + left := x + right := mat.NewVecDense(N, []float64{1, 2}) + vc := left.Eq(right).(symbolic.VectorConstraint) + + // Test + C, d := vc.LinearEqualityConstraintRepresentation() + + nRowsC, nColsC := C.Dims() + if nRowsC != N || nColsC != N { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to return a matrix of dimension %v; received dimension (%v, %v)", + []int{N, N}, + nRowsC, nColsC, + ) + } + + if d.AtVec(0) != 1 { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation()'s d vector to contain a 1 at the %v-th index; received %v", + 0, + d.AtVec(0), + ) + } + + if d.AtVec(1) != 2 { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation()'s d vector to contain a 2 at the %v-th index; received %v", + 1, + d.AtVec(1), + ) + } +} + +/* +TestVectorConstraint_LinearEqualityConstraintRepresentation5 +Description: + + This function tests that the LinearEqualityConstraintRepresentation method + properly panics when called with a constraint that is not based on equality. +*/ +func TestVectorConstraint_LinearEqualityConstraintRepresentation5(t *testing.T) { + // Constants + N := 7 + left := symbolic.VecDenseToKVector(symbolic.OnesVector(N)) + right := symbolic.NewVariableVector(N) + vc := symbolic.VectorConstraint{left, right, symbolic.SenseLessThanEqual} + + // Test + defer func() { + r := recover() + if r == nil { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to panic; received nil", + ) + } + + rAsError := r.(error) + expectedError := smErrors.EqualityConstraintRequiredError{ + Operation: "LinearEqualityConstraintRepresentation", + } + if rAsError.Error() != expectedError.Error() { + t.Errorf( + "Expected vc.LinearEqualityConstraintRepresentation() to panic with error \"%v\"; received \"%v\"", + expectedError.Error(), + rAsError.Error(), + ) + } + }() + + vc.LinearEqualityConstraintRepresentation() +}