Skip to content

Commit 7049718

Browse files
committed
Made sure that Check() is called before doing much else in IsLinear() and added a case for catching undefined objectives in Check() + added tests for all of the above
1 parent 7541505 commit 7049718

File tree

3 files changed

+120
-0
lines changed

3 files changed

+120
-0
lines changed

mpiErrors/no_objective_defined.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package mpiErrors
2+
3+
type NoObjectiveDefinedError struct{}
4+
5+
func (e NoObjectiveDefinedError) Error() string {
6+
return "No objective defined for the optimization problem; please define one with SetObjective()"
7+
}

problem/optimization_problem.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package problem
33
import (
44
"fmt"
55

6+
"github.com/MatProGo-dev/MatProInterface.go/mpiErrors"
67
"github.com/MatProGo-dev/MatProInterface.go/optim"
78
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
89
)
@@ -278,6 +279,10 @@ Description:
278279
*/
279280
func (op *OptimizationProblem) Check() error {
280281
// Check Objective
282+
if op.Objective == (Objective{}) {
283+
return mpiErrors.NoObjectiveDefinedError{}
284+
}
285+
281286
err := op.Objective.Check()
282287
if err != nil {
283288
return fmt.Errorf("the objective is not valid: %v", err)
@@ -313,6 +318,13 @@ Description:
313318
2. All constraints are linear (i.e., an affine combination of variables in an inequality or equality).
314319
*/
315320
func (op *OptimizationProblem) IsLinear() bool {
321+
// Input Processing
322+
// Verify that the problem is well-formed
323+
err := op.Check()
324+
if err != nil {
325+
panic(fmt.Errorf("the optimization problem is not well-formed: %v", err))
326+
}
327+
316328
// Check Objective
317329
if !op.Objective.IsLinear() {
318330
return false

testing/problem/optimization_problem_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"strings"
1313
"testing"
1414

15+
"github.com/MatProGo-dev/MatProInterface.go/mpiErrors"
1516
"github.com/MatProGo-dev/MatProInterface.go/optim"
1617
"github.com/MatProGo-dev/MatProInterface.go/problem"
1718
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
@@ -1021,6 +1022,74 @@ func TestOptimizationProblem_Check2(t *testing.T) {
10211022
}
10221023
}
10231024

1025+
/*
1026+
TestOptimizationProblem_Check3
1027+
Description:
1028+
1029+
Tests the Check function with a simple problem
1030+
that has one variable and no objective defined.
1031+
The mpiErrors.NoObjectiveDefinedError should be created.
1032+
*/
1033+
func TestOptimizationProblem_Check3(t *testing.T) {
1034+
// Constants
1035+
p1 := problem.NewProblem("TestOptimizationProblem_Check3")
1036+
v1 := p1.AddVariable()
1037+
1038+
// Add variable to problem
1039+
p1.Variables = append(p1.Variables, v1)
1040+
1041+
// Algorithm
1042+
err := p1.Check()
1043+
if err == nil {
1044+
t.Errorf("expected an error; received nil")
1045+
} else {
1046+
expectedError := mpiErrors.NoObjectiveDefinedError{}
1047+
if err.Error() != expectedError.Error() {
1048+
t.Errorf("unexpected error: %v", err)
1049+
}
1050+
}
1051+
}
1052+
1053+
/*
1054+
TestOptimizationProblem_Check4
1055+
Description:
1056+
1057+
Tests the Check function with a simple problem
1058+
that has:
1059+
- objective defined
1060+
- two variables (one is NOT well-defined)
1061+
- and no constraints defined.
1062+
The result should throw an error relating to the bad variable.
1063+
*/
1064+
func TestOptimizationProblem_Check4(t *testing.T) {
1065+
// Constants
1066+
p1 := problem.NewProblem("TestOptimizationProblem_Check4")
1067+
v1 := p1.AddVariable()
1068+
v2 := symbolic.Variable{}
1069+
1070+
// Add variables to problem
1071+
// p1.Variables = append(p1.Variables, v1) // Already added
1072+
p1.Variables = append(p1.Variables, v2)
1073+
1074+
// Create good objective
1075+
p1.Objective = *problem.NewObjective(
1076+
v1, problem.SenseMaximize,
1077+
)
1078+
1079+
// Algorithm
1080+
err := p1.Check()
1081+
if err == nil {
1082+
t.Errorf("expected an error; received nil")
1083+
} else {
1084+
if !strings.Contains(
1085+
err.Error(),
1086+
v2.Check().Error(),
1087+
) {
1088+
t.Errorf("unexpected error: %v", err)
1089+
}
1090+
}
1091+
}
1092+
10241093
/*
10251094
TestOptimizationProblem_IsLinear1
10261095
Description:
@@ -1112,3 +1181,35 @@ func TestOptimizationProblem_IsLinear3(t *testing.T) {
11121181
t.Errorf("expected the problem to be non-linear; received linear")
11131182
}
11141183
}
1184+
1185+
/*
1186+
TestOptimizationProblem_IsLinear4
1187+
Description:
1188+
1189+
Tests the IsLinear function with a simple problem
1190+
that has a constant objective and a single, quadratic constraint.
1191+
The problem should be non-linear.
1192+
*/
1193+
func TestOptimizationProblem_IsLinear4(t *testing.T) {
1194+
// Constants
1195+
p1 := problem.NewProblem("TestOptimizationProblem_IsLinear4")
1196+
vv1 := p1.AddVariableVector(3)
1197+
1198+
// Add constraints
1199+
p1.Constraints = append(
1200+
p1.Constraints,
1201+
vv1.Transpose().Multiply(vv1).Plus(vv1).LessEq(1.0),
1202+
)
1203+
1204+
// Create good objective
1205+
p1.Objective = *problem.NewObjective(
1206+
symbolic.K(3.14),
1207+
problem.SenseMaximize,
1208+
)
1209+
1210+
// Algorithm
1211+
isLinear := p1.IsLinear()
1212+
if isLinear {
1213+
t.Errorf("expected the problem to be non-linear; received linear")
1214+
}
1215+
}

0 commit comments

Comments
 (0)