From e02e032d8d7247305e8d6f8f213c3d10d82f3dd9 Mon Sep 17 00:00:00 2001 From: Kwesi Rutledge Date: Thu, 17 Apr 2025 20:28:22 -0400 Subject: [PATCH 1/7] Upgraded SymbolicMath --- go.mod | 2 +- go.sum | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index d3ca0a0..aa93ed3 100644 --- a/go.mod +++ b/go.mod @@ -4,4 +4,4 @@ go 1.21 require gonum.org/v1/gonum v0.14.0 -require github.com/MatProGo-dev/SymbolicMath.go v0.1.8 +require github.com/MatProGo-dev/SymbolicMath.go v0.2.0 diff --git a/go.sum b/go.sum index 3dc744e..96fb862 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,9 @@ github.com/MatProGo-dev/SymbolicMath.go v0.1.8 h1:lpe+6cK/2fg29WwxOykm4hKvfJeqvFUBGturC9qh5ug= github.com/MatProGo-dev/SymbolicMath.go v0.1.8/go.mod h1:gKbGR/6sYWi2koMUEDIPWBPi6jQPELKle0ijIM+eaHU= +github.com/MatProGo-dev/SymbolicMath.go v0.1.9 h1:NMqvS9Bt2DWWLGxd+j3Qta4Ckq/x74gpMM7bt32om5g= +github.com/MatProGo-dev/SymbolicMath.go v0.1.9/go.mod h1:gKbGR/6sYWi2koMUEDIPWBPi6jQPELKle0ijIM+eaHU= +github.com/MatProGo-dev/SymbolicMath.go v0.2.0 h1:W8IREsZGeIuPAKHgJDyeCr3vLJjMr6H/O9RNFAjOVp8= +github.com/MatProGo-dev/SymbolicMath.go v0.2.0/go.mod h1:gKbGR/6sYWi2koMUEDIPWBPi6jQPELKle0ijIM+eaHU= golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= From b81eb1edd7a380b8834dddf3fcd0438d6ee1e78c Mon Sep 17 00:00:00 2001 From: Kwesi Rutledge Date: Thu, 17 Apr 2025 22:47:55 -0400 Subject: [PATCH 2/7] Bumped the version of SymbolicMath.go --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index aa93ed3..97bf669 100644 --- a/go.mod +++ b/go.mod @@ -4,4 +4,4 @@ go 1.21 require gonum.org/v1/gonum v0.14.0 -require github.com/MatProGo-dev/SymbolicMath.go v0.2.0 +require github.com/MatProGo-dev/SymbolicMath.go v0.2.1 diff --git a/go.sum b/go.sum index 96fb862..f4742d8 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/MatProGo-dev/SymbolicMath.go v0.1.9 h1:NMqvS9Bt2DWWLGxd+j3Qta4Ckq/x74 github.com/MatProGo-dev/SymbolicMath.go v0.1.9/go.mod h1:gKbGR/6sYWi2koMUEDIPWBPi6jQPELKle0ijIM+eaHU= github.com/MatProGo-dev/SymbolicMath.go v0.2.0 h1:W8IREsZGeIuPAKHgJDyeCr3vLJjMr6H/O9RNFAjOVp8= github.com/MatProGo-dev/SymbolicMath.go v0.2.0/go.mod h1:gKbGR/6sYWi2koMUEDIPWBPi6jQPELKle0ijIM+eaHU= +github.com/MatProGo-dev/SymbolicMath.go v0.2.1 h1:3qcLYNx3+9Ud/saS4QWxJzmDUbVvYZIWt9aX2bQ9iOk= +github.com/MatProGo-dev/SymbolicMath.go v0.2.1/go.mod h1:gKbGR/6sYWi2koMUEDIPWBPi6jQPELKle0ijIM+eaHU= golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= From e2f80f8667ed25e21aff18bf9440dfc066925842 Mon Sep 17 00:00:00 2001 From: Kwesi Rutledge Date: Thu, 17 Apr 2025 22:48:14 -0400 Subject: [PATCH 3/7] Introduced method for assembling linear inequality constraints into single matrices --- problem/optimization_problem.go | 99 ++++++++++ testing/problem/optimization_problem_test.go | 193 +++++++++++++++++++ 2 files changed, 292 insertions(+) diff --git a/problem/optimization_problem.go b/problem/optimization_problem.go index 33eb1e6..e292f5c 100644 --- a/problem/optimization_problem.go +++ b/problem/optimization_problem.go @@ -5,7 +5,9 @@ import ( "github.com/MatProGo-dev/MatProInterface.go/mpiErrors" "github.com/MatProGo-dev/MatProInterface.go/optim" + getKVector "github.com/MatProGo-dev/SymbolicMath.go/get/KVector" "github.com/MatProGo-dev/SymbolicMath.go/symbolic" + "gonum.org/v1/gonum/mat" ) // OptimizationProblem represents the overall constrained linear optimization model to be @@ -340,3 +342,100 @@ func (op *OptimizationProblem) IsLinear() bool { // All Checks Passed! return true } + +/* +LinearConstraintMatrices +Description: + + Returns the linear constraint matrices and vectors. + For all linear inequality constraints, we assemble them into the form: + Ax <= b + Where A is the matrix of coefficients, x is the vector of variables, and b is the vector of constants. + We return A and b. +*/ +func (op *OptimizationProblem) LinearInequalityConstraintMatrices() (symbolic.KMatrix, symbolic.KVector) { + // Setup + + // Collect the Variables of this Problem + x := op.Variables + + fmt.Printf("Variables: %v\n", x) + + // Iterate through all constraints and collect the linear constraints + // into a matrix and vector. + scalar_constraints := make([]symbolic.ScalarConstraint, 0) + vector_constraints := make([]symbolic.VectorConstraint, 0) + for _, constraint := range op.Constraints { + switch c := constraint.(type) { + case symbolic.ScalarConstraint: + scalar_constraints = append(scalar_constraints, c) + case symbolic.VectorConstraint: + vector_constraints = append(vector_constraints, c) + } + } + + // Create the matrix and vector elements from the scalar constraints + A_components_scalar := make([]mat.VecDense, len(scalar_constraints)) + b_components_scalar := make([]float64, len(scalar_constraints)) + for ii, constraint := range scalar_constraints { + A_components_scalar[ii], b_components_scalar[ii] = constraint.LinearInequalityConstraintRepresentation(x) + + fmt.Printf("A_components_scalar[%v]: %v\n", ii, A_components_scalar[ii]) + fmt.Printf("b_components_scalar[%v]: %v\n", ii, b_components_scalar[ii]) + } + + // Create the matrix and vector elements from the vector constraints + A_components_vector := make([]mat.Dense, len(vector_constraints)) + b_components_vector := make([]mat.VecDense, len(vector_constraints)) + for ii, constraint := range vector_constraints { + A_components_vector[ii], b_components_vector[ii] = constraint.LinearInequalityConstraintRepresentation(x) + } + + // Assemble the matrix and vector components + var AOut symbolic.Expression + var bOut symbolic.Expression + scalar_constraint_matrices_exist := len(A_components_scalar) > 0 + if scalar_constraint_matrices_exist { + AOut = symbolic.VecDenseToKVector(A_components_scalar[0]).Transpose() + for ii := 1; ii < len(A_components_scalar); ii++ { + AOut = symbolic.VStack( + AOut, + symbolic.VecDenseToKVector(A_components_scalar[ii]).Transpose(), + ) + } + bOut = getKVector.From(b_components_scalar) + + fmt.Printf("AOut: %v\n", AOut) + fmt.Printf("bOut: %v\n", bOut) + } + + vector_constraint_matrices_exist := len(A_components_vector) > 0 + if vector_constraint_matrices_exist { + // Create the matrix, if it doesn't already exist + if !scalar_constraint_matrices_exist { + AOut = symbolic.DenseToKMatrix(A_components_vector[0]) + bOut = symbolic.VecDenseToKVector(b_components_vector[0]) + } else { + AOut = symbolic.VStack( + AOut, + symbolic.DenseToKMatrix(A_components_vector[0]), + ) + bOut = symbolic.VStack( + bOut, + symbolic.VecDenseToKVector(b_components_vector[0]), + ) + } + for ii := 1; ii < len(A_components_vector); ii++ { + AOut = symbolic.VStack( + AOut, + symbolic.DenseToKMatrix(A_components_vector[ii]), + ) + bOut = symbolic.VStack( + bOut, + symbolic.VecDenseToKVector(b_components_vector[ii]), + ) + } + } + + return AOut.(symbolic.KMatrix), bOut.(symbolic.KVector) +} diff --git a/testing/problem/optimization_problem_test.go b/testing/problem/optimization_problem_test.go index bd0fede..6593ec9 100644 --- a/testing/problem/optimization_problem_test.go +++ b/testing/problem/optimization_problem_test.go @@ -1213,3 +1213,196 @@ func TestOptimizationProblem_IsLinear4(t *testing.T) { t.Errorf("expected the problem to be non-linear; received linear") } } + +/* +TestOptimizationProblem_LinearInequalityConstraintMatrices1 +Description: + + Tests the LinearInequalityConstraintMatrices function with a simple problem. + The problem will have: + - a constant objective + - 2 variables, + - and a single linear inequality constraint. + The result should be a matrix with 1 row and 2 columns. +*/ +func TestOptimizationProblem_LinearInequalityConstraintMatrices1(t *testing.T) { + // Constants + p1 := problem.NewProblem("TestOptimizationProblem_LinearInequalityConstraintMatrices1") + v1 := p1.AddVariable() + p1.AddVariable() + c1 := v1.LessEq(1.0) + + p1.Constraints = append(p1.Constraints, c1) + + // Create good objective + p1.Objective = *problem.NewObjective( + symbolic.K(3.14), + problem.SenseMaximize, + ) + + // Algorithm + A, b := p1.LinearInequalityConstraintMatrices() + + // Check that the number of rows is as expected. + if A.Dims()[0] != 1 { + t.Errorf("expected the number of rows to be %v; received %v", + 1, A.Dims()[0]) + } + + // Check that the number of columns is as expected. + if A.Dims()[1] != 2 { + t.Errorf("expected the number of columns to be %v; received %v", + 2, A.Dims()[1]) + } + + // Check that the number of elements in b is as expected. + if len(b) != 1 { + t.Errorf("expected the number of elements in b to be %v; received %v", + 1, len(b)) + } +} + +/* +TestOptimizationProblem_LinearInequalityConstraintMatrices2 +Description: + + Tests the LinearInequalityConstraintMatrices function with a simple problem. + The problem will have: + - a constant objective + - 2 variables, + - and two scalar linear inequality constraints. + The result should be a matrix with 2 rows and 2 columns. +*/ +func TestOptimizationProblem_LinearInequalityConstraintMatrices2(t *testing.T) { + // Constants + p1 := problem.NewProblem("TestOptimizationProblem_LinearInequalityConstraintMatrices2") + vv1 := p1.AddVariableVector(2) + c1 := vv1.AtVec(0).LessEq(1.0) + c2 := vv1.AtVec(1).LessEq(2.0) + + p1.Constraints = append(p1.Constraints, c1) + p1.Constraints = append(p1.Constraints, c2) + + // Create good objective + p1.Objective = *problem.NewObjective( + symbolic.K(3.14), + problem.SenseMaximize, + ) + + // Algorithm + A, b := p1.LinearInequalityConstraintMatrices() + + // Check that the number of rows is as expected. + if A.Dims()[0] != 2 { + t.Errorf("expected the number of rows to be %v; received %v", + 2, A.Dims()[0]) + } + + // Check that the number of columns is as expected. + if A.Dims()[1] != 2 { + t.Errorf("expected the number of columns to be %v; received %v", + 2, A.Dims()[1]) + } + + // Check that the number of elements in b is as expected. + if len(b) != 2 { + t.Errorf("expected the number of elements in b to be %v; received %v", + 2, len(b)) + } +} + +/* +TestOptimizationProblem_LinearInequalityConstraintMatrices3 +Description: + + Tests the LinearInequalityConstraintMatrices function with a simple problem. + The problem will have: + - a constant objective + - 3 variables, + - and a single vector linear inequality constraint. + The result should be a matrix with 3 rows and 3 columns. +*/ +func TestOptimizationProblem_LinearInequalityConstraintMatrices3(t *testing.T) { + // Constants + p1 := problem.NewProblem("TestOptimizationProblem_LinearInequalityConstraintMatrices3") + vv1 := p1.AddVariableVector(3) + c1 := vv1.LessEq(symbolic.OnesVector(3)) + + p1.Constraints = append(p1.Constraints, c1) + + // Create good objective + p1.Objective = *problem.NewObjective( + symbolic.K(3.14), + problem.SenseMaximize, + ) + + // Algorithm + A, b := p1.LinearInequalityConstraintMatrices() + + // Check that the number of rows is as expected. + if A.Dims()[0] != 3 { + t.Errorf("expected the number of rows to be %v; received %v", + 3, A.Dims()[0]) + } + + // Check that the number of columns is as expected. + if A.Dims()[1] != 3 { + t.Errorf("expected the number of columns to be %v; received %v", + 3, A.Dims()[1]) + } + + // Check that the number of elements in b is as expected. + if len(b) != 3 { + t.Errorf("expected the number of elements in b to be %v; received %v", + 3, len(b)) + } +} + +/* +TestOptimizationProblem_LinearInequalityConstraintMatrices4 +Description: + + Tests the LinearInequalityConstraintMatrices function with a simple problem. + The problem will have: + - a constant objective + - 3 variables, + - and two vector linear inequality constraints. + The result should be a matrix with 6 rows and 3 columns. +*/ +func TestOptimizationProblem_LinearInequalityConstraintMatrices4(t *testing.T) { + // Constants + p1 := problem.NewProblem("TestOptimizationProblem_LinearInequalityConstraintMatrices4") + vv1 := p1.AddVariableVector(3) + c1 := vv1.AtVec(0).Plus(symbolic.OnesVector(3)).LessEq(symbolic.OnesVector(3)) + c2 := vv1.AtVec(1).Plus(symbolic.OnesVector(3)).GreaterEq(symbolic.OnesVector(3)) + + p1.Constraints = append(p1.Constraints, c1) + p1.Constraints = append(p1.Constraints, c2) + + // Create good objective + p1.Objective = *problem.NewObjective( + symbolic.K(3.14), + problem.SenseMaximize, + ) + + // Algorithm + A, b := p1.LinearInequalityConstraintMatrices() + + // Check that the number of rows is as expected. + if A.Dims()[0] != 6 { + t.Errorf("expected the number of rows to be %v; received %v", + 6, A.Dims()[0]) + } + + // Check that the number of columns is as expected. + if A.Dims()[1] != 3 { + t.Errorf("expected the number of columns to be %v; received %v", + 3, A.Dims()[1]) + } + + // Check that the number of elements in b is as expected. + if len(b) != 6 { + t.Errorf("expected the number of elements in b to be %v; received %v", + 6, len(b)) + } +} From 934e0e1731553d0e3456ccfcc805d61941e92158 Mon Sep 17 00:00:00 2001 From: Kwesi Rutledge Date: Thu, 17 Apr 2025 22:54:57 -0400 Subject: [PATCH 4/7] Updated main readme to take advantage of new problem structure --- README.md | 63 +++++++++++++++++++------------------------------------ 1 file changed, 22 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 97edad0..72f5c3c 100644 --- a/README.md +++ b/README.md @@ -26,59 +26,40 @@ by your model. ## Modeling the Mathematical Program Above For example, to model the program above one would write the following code: ``` -// Constants -modelName := "mpg-qp1" -m := optim.NewModel(modelName) -x := m.AddVariableVector(2) -// Create Vector Constants -c1 := optim.KVector( - *mat.NewVecDense(2, []float64{0.0, 1.0}), +import ( + ... + "github.com/MatProGo-dev/MatProInterface.go/problem" + getKVector "github.com/MatProGo-dev/SymbolicMath.go/get/KVector" + ... ) -c2 := optim.KVector( - *mat.NewVecDense(2, []float64{2.0, 3.0}), -) +// Constants +problemName := "mpg-qp1" +p1 := problem.NewProblem(problemName) +x := p1.AddVariableVector(2) -// Use these to create constraints. +// Create Vector Constants +c1 := getKVector.From([]float64{0.0, 1.0}) +c2 := getKVector.From([]float64{2.0, 3.0}) -vc1, err := x.LessEq(c2) -if err != nil { - t.Errorf("There was an issue creating the proper vector constraint: %v", err) -} +// Use these to create constraints. +vc1 := x.LessEq(c2) +vc2 := x.GreaterEq(c1) -vc2, err := x.GreaterEq(c1) -if err != nil { - t.Errorf("There was an issue creating the proper vector constraint: %v", err) -} +p1.Constraints = append(p1.Constraints, vc1) +p1.Constraints = append(p1.Constraints, vc2) // Create objective -Q1 := optim.Identity(x.Len()) +Q1 := symbolic.Identity(x.Len()) Q1.Set(0, 1, 0.25) Q1.Set(1, 0, 0.25) Q1.Set(1, 1, 0.25) -obj := optim.ScalarQuadraticExpression{ - Q: Q1, - X: x, - L: *mat.NewVecDense(x.Len(), []float64{0, -0.97}), - C: 2.0, -} - -// Add Constraints -constraints := []optim.Constraint{vc1, vc2} -for _, constr := range constraints { - err = m.AddConstraint(constr) - if err != nil { - t.Errorf("There was an issue adding the vector constraint to the model: %v", err) - } -} - -// Add objective -err = m.SetObjective(optim.Objective{obj, optim.SenseMinimize}) -if err != nil { - t.Errorf("There was an issue setting the objective of the Gurobi solver model: %v", err) -} +p1.Objective = *problem.NewObjective( + x.Transpose().Multiply(Q).Multiply(x), + problem.SenseMinimize, +) // Solve using the solver of your choice! ``` From baa1e4facae6f4d80f7a3d272d301c618c25c5d5 Mon Sep 17 00:00:00 2001 From: Kwesi Rutledge Date: Thu, 17 Apr 2025 23:08:37 -0400 Subject: [PATCH 5/7] wip: First attempt at creating the LinearEqualityConstraintMatrices function --- problem/optimization_problem.go | 116 +++++++++++++++++++++++++++++--- 1 file changed, 106 insertions(+), 10 deletions(-) diff --git a/problem/optimization_problem.go b/problem/optimization_problem.go index e292f5c..2968c7f 100644 --- a/problem/optimization_problem.go +++ b/problem/optimization_problem.go @@ -344,10 +344,10 @@ func (op *OptimizationProblem) IsLinear() bool { } /* -LinearConstraintMatrices +LinearInequalityConstraintMatrices Description: - Returns the linear constraint matrices and vectors. + Returns the linear INEQUALITY constraint matrices and vectors. For all linear inequality constraints, we assemble them into the form: Ax <= b Where A is the matrix of coefficients, x is the vector of variables, and b is the vector of constants. @@ -359,13 +359,19 @@ func (op *OptimizationProblem) LinearInequalityConstraintMatrices() (symbolic.KM // Collect the Variables of this Problem x := op.Variables - fmt.Printf("Variables: %v\n", x) - // Iterate through all constraints and collect the linear constraints // into a matrix and vector. scalar_constraints := make([]symbolic.ScalarConstraint, 0) vector_constraints := make([]symbolic.VectorConstraint, 0) for _, constraint := range op.Constraints { + // Skip this constraint if it is not linear + if !constraint.IsLinear() { + continue + } + // Skip this constraint if it is not an inequality + if constraint.Sense == symbolic.SenseEqual { + continue + } switch c := constraint.(type) { case symbolic.ScalarConstraint: scalar_constraints = append(scalar_constraints, c) @@ -379,9 +385,6 @@ func (op *OptimizationProblem) LinearInequalityConstraintMatrices() (symbolic.KM b_components_scalar := make([]float64, len(scalar_constraints)) for ii, constraint := range scalar_constraints { A_components_scalar[ii], b_components_scalar[ii] = constraint.LinearInequalityConstraintRepresentation(x) - - fmt.Printf("A_components_scalar[%v]: %v\n", ii, A_components_scalar[ii]) - fmt.Printf("b_components_scalar[%v]: %v\n", ii, b_components_scalar[ii]) } // Create the matrix and vector elements from the vector constraints @@ -404,9 +407,6 @@ func (op *OptimizationProblem) LinearInequalityConstraintMatrices() (symbolic.KM ) } bOut = getKVector.From(b_components_scalar) - - fmt.Printf("AOut: %v\n", AOut) - fmt.Printf("bOut: %v\n", bOut) } vector_constraint_matrices_exist := len(A_components_vector) > 0 @@ -439,3 +439,99 @@ func (op *OptimizationProblem) LinearInequalityConstraintMatrices() (symbolic.KM return AOut.(symbolic.KMatrix), bOut.(symbolic.KVector) } + +/* +LinearEqualityConstraintMatrices +Description: + + Returns the linear EQUALITY constraint matrices and vectors. + For all linear equality constraints, we assemble them into the form: + Cx = d + Where C is the matrix of coefficients, x is the vector of variables, and d is the vector of constants. + We return C and d. +*/ +func (op *OptimizationProblem) LinearEqualityConstraintMatrices() (symbolic.KMatrix, symbolic.KVector) { + // Setup + + // Collect the Variables of this Problem + x := op.Variables + + // Iterate through all constraints and collect the linear constraints + // into a matrix and vector. + scalar_constraints := make([]symbolic.ScalarConstraint, 0) + vector_constraints := make([]symbolic.VectorConstraint, 0) + for _, constraint := range op.Constraints { + // Skip this constraint if it is not linear + if !constraint.IsLinear() { + continue + } + // Skip this constraint if it is not an equality + if constraint.Sense != symbolic.SenseEqual { + continue + } + switch c := constraint.(type) { + case symbolic.ScalarConstraint: + scalar_constraints = append(scalar_constraints, c) + case symbolic.VectorConstraint: + vector_constraints = append(vector_constraints, c) + } + } + + // Create the matrix and vector elements from the scalar constraints + C_components_scalar := make([]mat.VecDense, len(scalar_constraints)) + d_components_scalar := make([]float64, len(scalar_constraints)) + for ii, constraint := range scalar_constraints { + C_components_scalar[ii], d_components_scalar[ii] = constraint.LinearEqualityConstraintRepresentation(x) + } + + // Create the matrix and vector elements from the vector constraints + C_components_vector := make([]mat.Dense, len(vector_constraints)) + d_components_vector := make([]mat.VecDense, len(vector_constraints)) + for ii, constraint := range vector_constraints { + C_components_vector[ii], d_components_vector[ii] = constraint.LinearEqualityConstraintRepresentation(x) + } + + // Assemble the matrix and vector components + var COut symbolic.Expression + var dOut symbolic.Expression + scalar_constraint_matrices_exist := len(C_components_scalar) > 0 + if scalar_constraint_matrices_exist { + COut = symbolic.VecDenseToKVector(C_components_scalar[0]).Transpose() + for ii := 1; ii < len(C_components_scalar); ii++ { + COut = symbolic.VStack( + COut, + symbolic.VecDenseToKVector(C_components_scalar[ii]).Transpose(), + ) + } + dOut = getKVector.From(d_components_scalar) + } + vector_constraint_matrices_exist := len(C_components_vector) > 0 + + if vector_constraint_matrices_exist { + // Create the matrix, if it doesn't already exist + if !scalar_constraint_matrices_exist { + COut = symbolic.DenseToKMatrix(C_components_vector[0]) + dOut = symbolic.VecDenseToKVector(d_components_vector[0]) + } else { + COut = symbolic.VStack( + COut, + symbolic.DenseToKMatrix(C_components_vector[0]), + ) + dOut = symbolic.VStack( + dOut, + symbolic.VecDenseToKVector(d_components_vector[0]), + ) + } + for ii := 1; ii < len(C_components_vector); ii++ { + COut = symbolic.VStack( + COut, + symbolic.DenseToKMatrix(C_components_vector[ii]), + ) + dOut = symbolic.VStack( + dOut, + symbolic.VecDenseToKVector(d_components_vector[ii]), + ) + } + } + return COut.(symbolic.KMatrix), dOut.(symbolic.KVector) +} From 26d2c15d2da04e95710d12483a86c4452346bc79 Mon Sep 17 00:00:00 2001 From: Kwesi Rutledge Date: Sat, 19 Apr 2025 10:30:09 -0400 Subject: [PATCH 6/7] Added tests for the new Equality constraint matrices function --- problem/optimization_problem.go | 4 +- testing/problem/optimization_problem_test.go | 297 +++++++++++++++++++ 2 files changed, 299 insertions(+), 2 deletions(-) diff --git a/problem/optimization_problem.go b/problem/optimization_problem.go index 2968c7f..a2618ca 100644 --- a/problem/optimization_problem.go +++ b/problem/optimization_problem.go @@ -369,7 +369,7 @@ func (op *OptimizationProblem) LinearInequalityConstraintMatrices() (symbolic.KM continue } // Skip this constraint if it is not an inequality - if constraint.Sense == symbolic.SenseEqual { + if constraint.ConstrSense() == symbolic.SenseEqual { continue } switch c := constraint.(type) { @@ -466,7 +466,7 @@ func (op *OptimizationProblem) LinearEqualityConstraintMatrices() (symbolic.KMat continue } // Skip this constraint if it is not an equality - if constraint.Sense != symbolic.SenseEqual { + if constraint.ConstrSense() != symbolic.SenseEqual { continue } switch c := constraint.(type) { diff --git a/testing/problem/optimization_problem_test.go b/testing/problem/optimization_problem_test.go index 6593ec9..bed1360 100644 --- a/testing/problem/optimization_problem_test.go +++ b/testing/problem/optimization_problem_test.go @@ -1406,3 +1406,300 @@ func TestOptimizationProblem_LinearInequalityConstraintMatrices4(t *testing.T) { 6, len(b)) } } + +/* +TestOptimizationProblem_LinearInequalityConstraintMatrices5 +Description: + + Tests the LinearInequalityConstraintMatrices function with a simple problem + that looks like the one in TestOptimizationProblem_LinearInequalityConstraintMatrices1. + The problem will have: + - a constant objective + - 2 variables, + - a single linear inequality constraint, + - and a single linear equality constraint. + The result should be a matrix with 1 row and 2 columns. +*/ +func TestOptimizationProblem_LinearInequalityConstraintMatrices5(t *testing.T) { + // Constants + p1 := problem.NewProblem("TestOptimizationProblem_LinearInequalityConstraintMatrices5") + v1 := p1.AddVariable() + p1.AddVariable() + c1 := v1.LessEq(1.0) + c2 := v1.Eq(2.0) + + p1.Constraints = append(p1.Constraints, c1) + p1.Constraints = append(p1.Constraints, c2) + + // Create good objective + p1.Objective = *problem.NewObjective( + symbolic.K(3.14), + problem.SenseMaximize, + ) + + // Algorithm + A, b := p1.LinearInequalityConstraintMatrices() + + // Check that the number of rows is as expected. + if A.Dims()[0] != 1 { + t.Errorf("expected the number of rows to be %v; received %v", + 1, A.Dims()[0]) + } + + // Check that the number of columns is as expected. + if A.Dims()[1] != 2 { + t.Errorf("expected the number of columns to be %v; received %v", + 2, A.Dims()[1]) + } + + // Check that the number of elements in b is as expected. + if len(b) != 1 { + t.Errorf("expected the number of elements in b to be %v; received %v", + 1, len(b)) + } +} + +/* +TestOptimizationProblem_LinearEqualityConstraintMatrices1 +Description: + + Tests the LinearEqualityConstraintMatrices function with a simple problem. + The problem will have: + - a constant objective + - 2 variables, + - and a single linear equality constraint. + The result should be a matrix with 1 row and 2 columns. +*/ +func TestOptimizationProblem_LinearEqualityConstraintMatrices1(t *testing.T) { + // Constants + p1 := problem.NewProblem("TestOptimizationProblem_LinearEqualityConstraintMatrices1") + v1 := p1.AddVariable() + p1.AddVariable() + c1 := v1.Eq(1.0) + + p1.Constraints = append(p1.Constraints, c1) + + // Create good objective + p1.Objective = *problem.NewObjective( + symbolic.K(3.14), + problem.SenseMaximize, + ) + + // Algorithm + A, b := p1.LinearEqualityConstraintMatrices() + + // Check that the number of rows is as expected. + if A.Dims()[0] != 1 { + t.Errorf("expected the number of rows to be %v; received %v", + 1, A.Dims()[0]) + } + + // Check that the number of columns is as expected. + if A.Dims()[1] != 2 { + t.Errorf("expected the number of columns to be %v; received %v", + 2, A.Dims()[1]) + } + + // Check that the number of elements in b is as expected. + if len(b) != 1 { + t.Errorf("expected the number of elements in b to be %v; received %v", + 1, len(b)) + } +} + +/* +TestOptimizationProblem_LinearEqualityConstraintMatrices2 +Description: + + Tests the LinearEqualityConstraintMatrices function with a simple problem. + The problem will have: + - a constant objective + - 2 variables, + - and two scalar linear equality constraints. + The result should be a matrix with 2 rows and 2 columns. +*/ +func TestOptimizationProblem_LinearEqualityConstraintMatrices2(t *testing.T) { + // Constants + p1 := problem.NewProblem("TestOptimizationProblem_LinearEqualityConstraintMatrices2") + vv1 := p1.AddVariableVector(2) + c1 := vv1.AtVec(0).Eq(1.0) + c2 := vv1.AtVec(1).Eq(2.0) + + p1.Constraints = append(p1.Constraints, c1) + p1.Constraints = append(p1.Constraints, c2) + + // Create good objective + p1.Objective = *problem.NewObjective( + symbolic.K(3.14), + problem.SenseMaximize, + ) + + // Algorithm + A, b := p1.LinearEqualityConstraintMatrices() + + // Check that the number of rows is as expected. + if A.Dims()[0] != 2 { + t.Errorf("expected the number of rows to be %v; received %v", + 2, A.Dims()[0]) + } + + // Check that the number of columns is as expected. + if A.Dims()[1] != 2 { + t.Errorf("expected the number of columns to be %v; received %v", + 2, A.Dims()[1]) + } + + // Check that the number of elements in b is as expected. + if len(b) != 2 { + t.Errorf("expected the number of elements in b to be %v; received %v", + 2, len(b)) + } +} + +/* +TestOptimizationProblem_LinearEqualityConstraintMatrices3 +Description: + + Tests the LinearEqualityConstraintMatrices function with a simple problem. + The problem will have: + - a constant objective + - 3 variables, + - and a single vector linear equality constraint. + The result should be a matrix with 3 rows and 3 columns. +*/ +func TestOptimizationProblem_LinearEqualityConstraintMatrices3(t *testing.T) { + // Constants + p1 := problem.NewProblem("TestOptimizationProblem_LinearEqualityConstraintMatrices3") + vv1 := p1.AddVariableVector(3) + c1 := vv1.Eq(symbolic.OnesVector(3)) + + p1.Constraints = append(p1.Constraints, c1) + + // Create good objective + p1.Objective = *problem.NewObjective( + symbolic.K(3.14), + problem.SenseMaximize, + ) + + // Algorithm + A, b := p1.LinearEqualityConstraintMatrices() + + // Check that the number of rows is as expected. + if A.Dims()[0] != 3 { + t.Errorf("expected the number of rows to be %v; received %v", + 3, A.Dims()[0]) + } + + // Check that the number of columns is as expected. + if A.Dims()[1] != 3 { + t.Errorf("expected the number of columns to be %v; received %v", + 3, A.Dims()[1]) + } + + // Check that the number of elements in b is as expected. + if len(b) != 3 { + t.Errorf("expected the number of elements in b to be %v; received %v", + 3, len(b)) + } +} + +/* +TestOptimizationProblem_LinearEqualityConstraintMatrices4 +Description: + + Tests the LinearEqualityConstraintMatrices function with a simple problem. + The problem will have: + - a constant objective + - 3 variables, + - and two vector linear equality constraints. + The result should be a matrix with 6 rows and 3 columns. +*/ +func TestOptimizationProblem_LinearEqualityConstraintMatrices4(t *testing.T) { + // Constants + p1 := problem.NewProblem("TestOptimizationProblem_LinearEqualityConstraintMatrices4") + vv1 := p1.AddVariableVector(3) + c1 := vv1.AtVec(0).Plus(symbolic.OnesVector(3)).Eq(symbolic.OnesVector(3)) + c2 := vv1.AtVec(1).Plus(symbolic.OnesVector(3)).Eq(symbolic.OnesVector(3)) + + p1.Constraints = append(p1.Constraints, c1) + p1.Constraints = append(p1.Constraints, c2) + + // Create good objective + p1.Objective = *problem.NewObjective( + symbolic.K(3.14), + problem.SenseMaximize, + ) + + // Algorithm + A, b := p1.LinearEqualityConstraintMatrices() + + // Check that the number of rows is as expected. + if A.Dims()[0] != 6 { + t.Errorf("expected the number of rows to be %v; received %v", + 6, A.Dims()[0]) + } + + // Check that the number of columns is as expected. + if A.Dims()[1] != 3 { + t.Errorf("expected the number of columns to be %v; received %v", + 3, A.Dims()[1]) + } + + // Check that the number of elements in b is as expected. + if len(b) != 6 { + t.Errorf("expected the number of elements in b to be %v; received %v", + 6, len(b)) + } +} + +/* +TestOptimizationProblem_LinearEqualityConstraintMatrices5 +Description: + + Tests the LinearEqualityConstraintMatrices function with a simple problem + that looks like the one in TestOptimizationProblem_LinearEqualityConstraintMatrices1. + The problem will have: + - a constant objective + - 2 variables, + - a single linear equality constraint, + - and a single linear inequality constraint. + The result should be a matrix with 1 row and 2 columns. +*/ +func TestOptimizationProblem_LinearEqualityConstraintMatrices5(t *testing.T) { + // Constants + p1 := problem.NewProblem("TestOptimizationProblem_LinearEqualityConstraintMatrices5") + v1 := p1.AddVariable() + p1.AddVariable() + c1 := v1.Eq(1.0) + c2 := v1.LessEq(2.0) + + p1.Constraints = append(p1.Constraints, c1) + p1.Constraints = append(p1.Constraints, c2) + + // Create good objective + p1.Objective = *problem.NewObjective( + symbolic.K(3.14), + problem.SenseMaximize, + ) + + // Algorithm + A, b := p1.LinearEqualityConstraintMatrices() + + // Check that the number of rows is as expected. + if A.Dims()[0] != 1 { + t.Errorf("expected the number of rows to be %v; received %v", + 1, A.Dims()[0]) + } + + // Check that the number of columns is as expected. + if A.Dims()[1] != 2 { + t.Errorf("expected the number of columns to be %v; received %v", + 2, A.Dims()[1]) + } + + // Check that the number of elements in b is as expected. + if len(b) != 1 { + t.Errorf("expected the number of elements in b to be %v; received %v", + 1, len(b)) + } +} From 5f4c72720fee460e155a194c28c1e11c3b2fce25 Mon Sep 17 00:00:00 2001 From: Kwesi Rutledge Date: Sat, 19 Apr 2025 10:34:37 -0400 Subject: [PATCH 7/7] Added tests for coverage of the case when there are mixtures of scalar and vector constraints --- testing/problem/optimization_problem_test.go | 102 +++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/testing/problem/optimization_problem_test.go b/testing/problem/optimization_problem_test.go index bed1360..e1cc61c 100644 --- a/testing/problem/optimization_problem_test.go +++ b/testing/problem/optimization_problem_test.go @@ -1459,6 +1459,57 @@ func TestOptimizationProblem_LinearInequalityConstraintMatrices5(t *testing.T) { } } +/* +TestOptimizationProblem_LinearInequalityConstraintMatrices6 +Description: + + Tests the LinearInequalityConstraintMatrices function with a simple problem + that contains a mixture of scalar and vector inequality constraints. + The problem will have: + - a constant objective + - 3 variables, + - a single vector linear inequality constraint, + - and a single scalar linear inequality constraint. + The result should be a matrix with 4 rows and 3 columns. +*/ +func TestOptimizationProblem_LinearInequalityConstraintMatrices6(t *testing.T) { + // Constants + p1 := problem.NewProblem("TestOptimizationProblem_LinearInequalityConstraintMatrices6") + vv1 := p1.AddVariableVector(3) + c1 := vv1.LessEq(symbolic.OnesVector(3)) + c2 := vv1.AtVec(0).LessEq(1.0) + + p1.Constraints = append(p1.Constraints, c1) + p1.Constraints = append(p1.Constraints, c2) + + // Create good objective + p1.Objective = *problem.NewObjective( + symbolic.K(3.14), + problem.SenseMaximize, + ) + + // Algorithm + A, b := p1.LinearInequalityConstraintMatrices() + + // Check that the number of rows is as expected. + if A.Dims()[0] != 4 { + t.Errorf("expected the number of rows to be %v; received %v", + 4, A.Dims()[0]) + } + + // Check that the number of columns is as expected. + if A.Dims()[1] != 3 { + t.Errorf("expected the number of columns to be %v; received %v", + 3, A.Dims()[1]) + } + + // Check that the number of elements in b is as expected. + if len(b) != 4 { + t.Errorf("expected the number of elements in b to be %v; received %v", + 4, len(b)) + } +} + /* TestOptimizationProblem_LinearEqualityConstraintMatrices1 Description: @@ -1703,3 +1754,54 @@ func TestOptimizationProblem_LinearEqualityConstraintMatrices5(t *testing.T) { 1, len(b)) } } + +/* +TestOptimizationProblem_LinearEqualityConstraintMatrices6 +Description: + + Tests the LinearEqualityConstraintMatrices function with a simple problem + that contains a mixture of scalar and vector equality constraints. + The problem will have: + - a constant objective + - 3 variables, + - a single vector linear equality constraint, + - and a single scalar linear equality constraint. + The result should be a matrix with 4 rows and 3 columns. +*/ +func TestOptimizationProblem_LinearEqualityConstraintMatrices6(t *testing.T) { + // Constants + p1 := problem.NewProblem("TestOptimizationProblem_LinearEqualityConstraintMatrices6") + vv1 := p1.AddVariableVector(3) + c1 := vv1.Eq(symbolic.OnesVector(3)) + c2 := vv1.AtVec(0).Eq(1.0) + + p1.Constraints = append(p1.Constraints, c1) + p1.Constraints = append(p1.Constraints, c2) + + // Create good objective + p1.Objective = *problem.NewObjective( + symbolic.K(3.14), + problem.SenseMaximize, + ) + + // Algorithm + A, b := p1.LinearEqualityConstraintMatrices() + + // Check that the number of rows is as expected. + if A.Dims()[0] != 4 { + t.Errorf("expected the number of rows to be %v; received %v", + 4, A.Dims()[0]) + } + + // Check that the number of columns is as expected. + if A.Dims()[1] != 3 { + t.Errorf("expected the number of columns to be %v; received %v", + 3, A.Dims()[1]) + } + + // Check that the number of elements in b is as expected. + if len(b) != 4 { + t.Errorf("expected the number of elements in b to be %v; received %v", + 4, len(b)) + } +}