Skip to content

Commit 9aba8d3

Browse files
Replacing Solution struct with Solution Interface (#17)
* Moved some of the files out of the problem package for solutions * Moved solution status to its own package * Refactoring out a bunch of the prefixes to SolutionStatus * Separated dummy solution * Update solution/solution.go fixing comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update testing/solution/solution_test.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent efd3e27 commit 9aba8d3

5 files changed

Lines changed: 170 additions & 137 deletions

File tree

problem/solution.go

Lines changed: 0 additions & 117 deletions
This file was deleted.

solution/dummy_solution.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package solution
2+
3+
import solution_status "github.com/MatProGo-dev/MatProInterface.go/solution/status"
4+
5+
type DummySolution struct {
6+
Values map[uint64]float64
7+
8+
// The objective for the solution
9+
Objective float64
10+
11+
// Whether or not the solution is within the optimality threshold
12+
Status solution_status.SolutionStatus
13+
14+
// The optimality gap returned from the solver. For many solvers, this is
15+
// the gap between the best possible solution with integer relaxation and
16+
// the best integer solution found so far.
17+
// Gap float64
18+
}
19+
20+
func (ds *DummySolution) GetOptimalValue() float64 {
21+
return ds.Objective
22+
}
23+
24+
func (ds *DummySolution) GetValueMap() map[uint64]float64 {
25+
return ds.Values
26+
}
27+
28+
func (ds *DummySolution) GetStatus() solution_status.SolutionStatus {
29+
return ds.Status
30+
}

solution/solution.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package solution
2+
3+
import (
4+
"fmt"
5+
6+
solution_status "github.com/MatProGo-dev/MatProInterface.go/solution/status"
7+
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
8+
)
9+
10+
const (
11+
tinyNum float64 = 0.01
12+
)
13+
14+
// Solution stores the solution of an optimization problem and associated
15+
// metadata
16+
type Solution interface {
17+
GetOptimalValue() float64
18+
GetValueMap() map[uint64]float64
19+
20+
// GetStatus
21+
//
22+
GetStatus() solution_status.SolutionStatus
23+
}
24+
25+
func ExtractValueOfVariableWithID(s Solution, idx uint64) (float64, error) {
26+
val, ok := s.GetValueMap()[idx]
27+
if !ok {
28+
return 0.0, fmt.Errorf(
29+
"The idx \"%v\" was not in the variable map for the solution.",
30+
idx,
31+
)
32+
}
33+
return val, nil
34+
}
35+
36+
func ExtractValueOfVariable(s Solution, v symbolic.Variable) (float64, error) {
37+
idx := v.ID // Extract index of v
38+
return ExtractValueOfVariableWithID(s, idx)
39+
}

solution/status/status.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package solution_status
2+
3+
import "fmt"
4+
5+
type SolutionStatus int
6+
7+
// OptimizationStatuses
8+
const (
9+
LOADED SolutionStatus = 1
10+
OPTIMAL SolutionStatus = 2
11+
INFEASIBLE SolutionStatus = 3
12+
INF_OR_UNBD SolutionStatus = 4
13+
UNBOUNDED SolutionStatus = 5
14+
CUTOFF SolutionStatus = 6
15+
ITERATION_LIMIT SolutionStatus = 7
16+
NODE_LIMIT SolutionStatus = 8
17+
TIME_LIMIT SolutionStatus = 9
18+
SOLUTION_LIMIT SolutionStatus = 10
19+
INTERRUPTED SolutionStatus = 11
20+
NUMERIC SolutionStatus = 12
21+
SUBOPTIMAL SolutionStatus = 13
22+
INPROGRESS SolutionStatus = 14
23+
USER_OBJ_LIMIT SolutionStatus = 15
24+
WORK_LIMIT SolutionStatus = 16
25+
)
26+
27+
/*
28+
ToMessage
29+
Description:
30+
31+
Translates the code to the text meaning.
32+
This comes from the status codes documentation: https://www.gurobi.com/documentation/9.5/refman/optimization_status_codes.html#sec:StatusCodes
33+
*/
34+
func (os SolutionStatus) ToMessage() (string, error) {
35+
// Converts each of the statuses to a text message that is human readable.
36+
switch os {
37+
case LOADED:
38+
return "Model is loaded, but no solution information is available.", nil
39+
case OPTIMAL:
40+
return "Model was solved to optimality (subject to tolerances), and an optimal solution is available.", nil
41+
case INFEASIBLE:
42+
return "Model was proven to be infeasible.", nil
43+
case INF_OR_UNBD:
44+
return "Model was proven to be either infeasible or unbounded. To obtain a more definitive conclusion, set the DualReductions parameter to 0 and reoptimize.", nil
45+
case UNBOUNDED:
46+
return "Model was proven to be unbounded. Important note: an unbounded status indicates the presence of an unbounded ray that allows the objective to improve without limit. It says nothing about whether the model has a feasible solution. If you require information on feasibility, you should set the objective to zero and reoptimize.", nil
47+
case CUTOFF:
48+
return "Optimal objective for model was proven to be worse than the value specified in the Cutoff parameter. No solution information is available.", nil
49+
case ITERATION_LIMIT:
50+
return "Optimization terminated because the total number of simplex iterations performed exceeded the value specified in the IterationLimit parameter, or because the total number of barrier iterations exceeded the value specified in the BarIterLimit parameter.", nil
51+
case NODE_LIMIT:
52+
return "Optimization terminated because the total number of branch-and-cut nodes explored exceeded the value specified in the NodeLimit parameter.", nil
53+
case TIME_LIMIT:
54+
return "Optimization terminated because the time expended exceeded the value specified in the TimeLimit parameter.", nil
55+
case SOLUTION_LIMIT:
56+
return "Optimization terminated because the number of solutions found reached the value specified in the SolutionLimit parameter.", nil
57+
case INTERRUPTED:
58+
return "Optimization was terminated by the user.", nil
59+
case NUMERIC:
60+
return "Optimization was terminated due to unrecoverable numerical difficulties.", nil
61+
case SUBOPTIMAL:
62+
return "Unable to satisfy optimality tolerances; a sub-optimal solution is available.", nil
63+
case INPROGRESS:
64+
return "An asynchronous optimization call was made, but the associated optimization run is not yet complete.", nil
65+
case USER_OBJ_LIMIT:
66+
return "User specified an objective limit (a bound on either the best objective or the best bound), and that limit has been reached.", nil
67+
case WORK_LIMIT:
68+
return "Optimization terminated because the work expended exceeded the value specified in the WorkLimit parameter.", nil
69+
default:
70+
return "", fmt.Errorf("The status with value %v is unrecognized.", os)
71+
}
72+
}
Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
package problem
1+
package solution_test
22

33
import (
4-
"github.com/MatProGo-dev/MatProInterface.go/optim"
5-
"github.com/MatProGo-dev/MatProInterface.go/problem"
64
"strings"
75
"testing"
6+
7+
"github.com/MatProGo-dev/MatProInterface.go/solution"
8+
solution_status "github.com/MatProGo-dev/MatProInterface.go/solution/status"
9+
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
810
)
911

1012
/*
@@ -16,13 +18,13 @@ Description:
1618

1719
func TestSolution_ToMessage1(t *testing.T) {
1820
// Constants
19-
tempSol := problem.Solution{
21+
tempSol := solution.DummySolution{
2022
Values: map[uint64]float64{
2123
0: 2.1,
2224
1: 3.14,
2325
},
2426
Objective: 2.3,
25-
Status: problem.OptimizationStatus_NODE_LIMIT,
27+
Status: solution_status.NODE_LIMIT,
2628
}
2729

2830
// Test the ToMessage() Call on this solution.
@@ -45,7 +47,7 @@ func TestSolution_ToMessage2(t *testing.T) {
4547

4648
// Test
4749
for statusIndex := 1; statusIndex < statusMax; statusIndex++ {
48-
tempStatus := problem.OptimizationStatus(statusIndex)
50+
tempStatus := solution_status.SolutionStatus(statusIndex)
4951

5052
msg, err := tempStatus.ToMessage()
5153
if err != nil {
@@ -94,33 +96,40 @@ Description:
9496
*/
9597
func TestSolution_Value1(t *testing.T) {
9698
// Constants
97-
tempSol := problem.Solution{
99+
v1 := symbolic.NewVariable()
100+
v2 := symbolic.NewVariable()
101+
102+
tempSol := solution.DummySolution{
98103
Values: map[uint64]float64{
99-
0: 2.1,
100-
1: 3.14,
104+
v1.ID: 2.1,
105+
v2.ID: 3.14,
101106
},
102107
Objective: 2.3,
103-
Status: problem.OptimizationStatus_NODE_LIMIT,
104-
}
105-
v1 := optim.Variable{
106-
ID: 0, Lower: -optim.INFINITY, Upper: optim.INFINITY, Vtype: optim.Continuous,
107-
}
108-
v2 := optim.Variable{
109-
ID: 1, Lower: -optim.INFINITY, Upper: optim.INFINITY, Vtype: optim.Continuous,
108+
Status: solution_status.NODE_LIMIT,
110109
}
111110

112111
// Algorithm
113-
if tempSol.Value(v1) != 2.1 {
112+
v1Val, err := solution.ExtractValueOfVariable(&tempSol, v1)
113+
if err != nil {
114+
t.Errorf("The value of the variable v1 could not be extracted; received error %v", err)
115+
}
116+
117+
if v1Val != 2.1 {
114118
t.Errorf(
115119
"Expected v1 to have value 2.1; received %v",
116-
tempSol.Value(v1),
120+
v1Val,
117121
)
118122
}
119123

120-
if tempSol.Value(v2) != 3.14 {
124+
v2Val, err := solution.ExtractValueOfVariable(&tempSol, v2)
125+
if err != nil {
126+
t.Errorf("The value of the variable v2 could not be extracted; received error %v", err)
127+
}
128+
129+
if v2Val != 3.14 {
121130
t.Errorf(
122131
"Expected v2 to have value 3.14; received %v",
123-
tempSol.Value(v2),
132+
v2Val,
124133
)
125134
}
126135

0 commit comments

Comments
 (0)