From c1650df3d783e18bd5a4fd9e5bbe8767005680b0 Mon Sep 17 00:00:00 2001 From: Kwesi Rutledge Date: Thu, 2 Oct 2025 22:57:24 -0400 Subject: [PATCH 1/5] Update SymbolicMath.go version --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9998af4..fa164af 100644 --- a/go.mod +++ b/go.mod @@ -6,4 +6,4 @@ toolchain go1.23.9 require gonum.org/v1/gonum v0.16.0 -require github.com/MatProGo-dev/SymbolicMath.go v0.2.4-1 +require github.com/MatProGo-dev/SymbolicMath.go v0.2.5 diff --git a/go.sum b/go.sum index ba3f84f..42d8405 100644 --- a/go.sum +++ b/go.sum @@ -2,5 +2,7 @@ github.com/MatProGo-dev/SymbolicMath.go v0.2.4-1 h1:SIj6oFJgavWtArs8toeHCPfxOefG github.com/MatProGo-dev/SymbolicMath.go v0.2.4-1/go.mod h1:tW8thj4pkaTV9lFNU3OCKmwQ3mZ2Eim6S4JpHRDfRvU= github.com/MatProGo-dev/SymbolicMath.go v0.2.4 h1:SxvgOJBpx9H6ZHISyF3A79gOd1pHJd8Nywrqf4sJZTs= github.com/MatProGo-dev/SymbolicMath.go v0.2.4/go.mod h1:tW8thj4pkaTV9lFNU3OCKmwQ3mZ2Eim6S4JpHRDfRvU= +github.com/MatProGo-dev/SymbolicMath.go v0.2.5 h1:fGpwtywb2hUXvGKk1Te6PQEfHeVar5w05KTbc3wHj6A= +github.com/MatProGo-dev/SymbolicMath.go v0.2.5/go.mod h1:tW8thj4pkaTV9lFNU3OCKmwQ3mZ2Eim6S4JpHRDfRvU= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= From dc646b5ccc16f9d9abf5e0e7c470dc67d97c08c8 Mon Sep 17 00:00:00 2001 From: Kwesi Rutledge Date: Thu, 2 Oct 2025 23:18:44 -0400 Subject: [PATCH 2/5] Changed how ObjSense is represented to make it easier to print --- problem/objective_sense.go | 12 ++++---- testing/problem/objective_sense_test.go | 40 ++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/problem/objective_sense.go b/problem/objective_sense.go index bc4a825..2bb025e 100644 --- a/problem/objective_sense.go +++ b/problem/objective_sense.go @@ -1,16 +1,18 @@ package problem -import "github.com/MatProGo-dev/MatProInterface.go/optim" +import ( + "github.com/MatProGo-dev/MatProInterface.go/optim" +) // ObjSense represents whether an optimization objective is to be maximized or // minimized. This implementation conforms to the Gurobi encoding -type ObjSense int +type ObjSense string // Objective senses (minimize and maximize) encoding using Gurobi's standard const ( - SenseMinimize ObjSense = 1 - SenseMaximize = -1 - SenseFind ObjSense = 0 + SenseMinimize ObjSense = "Minimize" + SenseMaximize = "Maximize" + SenseFind ObjSense = "Find" ) /* diff --git a/testing/problem/objective_sense_test.go b/testing/problem/objective_sense_test.go index ff04bef..da5c360 100644 --- a/testing/problem/objective_sense_test.go +++ b/testing/problem/objective_sense_test.go @@ -1,9 +1,11 @@ package problem_test import ( + "fmt" + "testing" + "github.com/MatProGo-dev/MatProInterface.go/optim" "github.com/MatProGo-dev/MatProInterface.go/problem" - "testing" ) /* @@ -52,3 +54,39 @@ func TestObjectiveSense_ToObjSense2(t *testing.T) { problem.SenseMaximize, objSense) } } + +/* +TestObjectiveSense_String1 +Description: + + Tests that we can extract strings from the three normal ObjSense values + (Minimize, Maximize, Find). +*/ +func TestObjectiveSense_String1(t *testing.T) { + // Test Minimize + minSense := problem.SenseMinimize + if fmt.Sprintf("%v", minSense) != "Minimize" { + t.Errorf( + "minSense's string is \"%v\"; expected \"Minimize\"", + minSense, + ) + } + + // Test Maximize + maxSense := problem.SenseMaximize + if fmt.Sprintf("%v", maxSense) != "Maximize" { + t.Errorf( + "maxSense's string is \"%v\"; expected \"Maximize\"", + maxSense, + ) + } + + // Test Find + findSense := problem.SenseFind + if fmt.Sprintf("%v", findSense) != "Find" { + t.Errorf( + "findSense's string is \"%v\"; expected \"Find\"", + findSense, + ) + } +} From 31a40a68ad887a364654685655be194580926950 Mon Sep 17 00:00:00 2001 From: Kwesi Rutledge Date: Thu, 2 Oct 2025 23:31:30 -0400 Subject: [PATCH 3/5] Created a simple String method for the problem and test it --- problem/optimization_problem.go | 40 +++++++++++++++ testing/problem/optimization_problem_test.go | 53 ++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/problem/optimization_problem.go b/problem/optimization_problem.go index 248adbc..d886343 100644 --- a/problem/optimization_problem.go +++ b/problem/optimization_problem.go @@ -994,3 +994,43 @@ func (op *OptimizationProblem) MakeNotWellDefinedError() ope.NotWellDefinedError ErrorSource: op.Check(), } } + +/* +String +Description: + + Creates a string for the problem. +*/ +func (op *OptimizationProblem) String() string { + // Create string for the objective + objString := fmt.Sprintf("\n%v\n\t%v\n", op.Objective.Sense, op.Objective.Expression) + + // Create the constraints string + constraintsString := "subject to\n" + nVectorConstraints := 0 + nMatrixConstraints := 0 + for ii, constraint := range op.Constraints { + switch constraint.(type) { + case symbolic.ScalarConstraint: + constraintsString += fmt.Sprintf( + "\tConstraint #%v. %v\n", + ii, + constraint, + ) + case symbolic.VectorConstraint: + nVectorConstraints += 1 + case symbolic.MatrixConstraint: + nMatrixConstraints += 1 + } + } + + // Add a text summary of the vector and matrix constraints + // TODO(Kwesi): Create a String() method for vector and matrix constraints. + constraintsString += fmt.Sprintf( + "\t\tin addition to %v vector constraints and %v matrix constraints.", + nVectorConstraints, + nMatrixConstraints, + ) + + return objString + constraintsString +} diff --git a/testing/problem/optimization_problem_test.go b/testing/problem/optimization_problem_test.go index 9bb6f27..ecfe41c 100644 --- a/testing/problem/optimization_problem_test.go +++ b/testing/problem/optimization_problem_test.go @@ -3149,3 +3149,56 @@ func TestOptimizationProblem_CopyVariable1(t *testing.T) { ) } } + +/* +TestOptimizationProblem_String1 +Description: + + Tests that a small optimization problem with all scalar constraints gets represented + as a string with: + - Minimize sense of objective + - The objective expression is completely contained in the string + - the string describes that there are 0 vector constraints and 0 matrix constraints +*/ +func TestOptimizationProblem_String1(t *testing.T) { + // Create Optimization Problem + p := problem.NewProblem("TestOptimizationProblem_String1") + + N := 2 + x := p.AddVariableVector(N) + c := symbolic.OnesVector(N) + objExpr := x.Transpose().Multiply(c) + p.SetObjective(objExpr, problem.SenseMinimize) + + p.Constraints = append(p.Constraints, x.AtVec(0).LessEq(1.2)) + p.Constraints = append(p.Constraints, x.AtVec(1).GreaterEq(3.14)) + + // Create String + pAsString := fmt.Sprintf("%s", p) + + // Check that the string has "Minimize" in it + if !strings.Contains(pAsString, "Minimize") { + t.Errorf( + "Problem string does not contain the string \"Minimize\".", + ) + } + + if !strings.Contains(pAsString, objExpr.String()) { + t.Errorf( + "Problem string does not contain the expression in the objective %s", + objExpr, + ) + } + + if !strings.Contains(pAsString, "0 vector constraints") { + t.Errorf( + "Problem string does not contain \"0 vector constraints\".", + ) + } + + if !strings.Contains(pAsString, "0 matrix constraints") { + t.Errorf( + "Problem string does not contain \"0 matrix constraints\".", + ) + } +} From ab8bdc3082de2963438d5211e4816462dfff58ed Mon Sep 17 00:00:00 2001 From: Kwesi Rutledge Date: Thu, 2 Oct 2025 23:36:35 -0400 Subject: [PATCH 4/5] Fixing bug in one conversion method from optim.Problem to problem.OptimizationProblem --- problem/optimization_problem.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problem/optimization_problem.go b/problem/optimization_problem.go index d886343..b87f46a 100644 --- a/problem/optimization_problem.go +++ b/problem/optimization_problem.go @@ -264,7 +264,7 @@ func From(inputModel optim.Model) (*OptimizationProblem, error) { err = newOptimProblem.SetObjective( objectiveExpr, - ObjSense(inputModel.Obj.Sense), + ToObjSense(inputModel.Obj.Sense), ) if err != nil { return nil, err From 9c648f98ec353956703dc316b677644ecfe8f922 Mon Sep 17 00:00:00 2001 From: Kwesi Rutledge Date: Thu, 2 Oct 2025 23:43:10 -0400 Subject: [PATCH 5/5] Added more coverage for the problem.OptimizationProblem.String method --- testing/problem/optimization_problem_test.go | 47 ++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/testing/problem/optimization_problem_test.go b/testing/problem/optimization_problem_test.go index ecfe41c..4207b1c 100644 --- a/testing/problem/optimization_problem_test.go +++ b/testing/problem/optimization_problem_test.go @@ -3202,3 +3202,50 @@ func TestOptimizationProblem_String1(t *testing.T) { ) } } + +/* +TestOptimizationProblem_String2 +Description: + + Tests that a small optimization problem with all scalar constraints gets represented + as a string with: + - Maximize sense of objective + - the string describes that there are 2 vector constraints and 1 matrix constraints +*/ +func TestOptimizationProblem_String2(t *testing.T) { + // Create Optimization Problem + p := problem.NewProblem("TestOptimizationProblem_String1") + + N := 2 + x := p.AddVariableVector(N) + y := p.AddVariableMatrix(N, N, 0.0, 200.0, symbolic.Continuous) + c := symbolic.OnesVector(N) + objExpr := x.Transpose().Multiply(c) + p.SetObjective(objExpr, problem.SenseMaximize) + + p.Constraints = append(p.Constraints, x.LessEq(c)) + p.Constraints = append(p.Constraints, x.GreaterEq(symbolic.ZerosVector(N))) + p.Constraints = append(p.Constraints, y.Eq(symbolic.ZerosMatrix(N, N))) + + // Create String + pAsString := fmt.Sprintf("%s", p) + + // Check that the string has "Maximize" in it + if !strings.Contains(pAsString, "Maximize") { + t.Errorf( + "Problem string does not contain the string \"Maximize\".", + ) + } + + if !strings.Contains(pAsString, "2 vector constraints") { + t.Errorf( + "Problem string does not contain \"2 vector constraints\".", + ) + } + + if !strings.Contains(pAsString, "1 matrix constraints") { + t.Errorf( + "Problem string does not contain \"1 matrix constraints\".", + ) + } +}