-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsolution_test.go
More file actions
379 lines (321 loc) · 9.86 KB
/
solution_test.go
File metadata and controls
379 lines (321 loc) · 9.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
package solution_test
import (
"strings"
"testing"
"github.com/MatProGo-dev/MatProInterface.go/problem"
"github.com/MatProGo-dev/MatProInterface.go/solution"
solution_status "github.com/MatProGo-dev/MatProInterface.go/solution/status"
"github.com/MatProGo-dev/SymbolicMath.go/symbolic"
)
/*
solution_test.go
Description:
Testing for the solution object.
(This seems like it is highly representative of the Gurobi solver; is there a reason to make it this way?)
*/
func TestSolution_ToMessage1(t *testing.T) {
// Constants
tempSol := solution.DummySolution{
Values: map[uint64]float64{
0: 2.1,
1: 3.14,
},
Objective: 2.3,
Status: solution_status.NODE_LIMIT,
}
// Test the ToMessage() Call on this solution.
msg, err := tempSol.Status.ToMessage()
if err != nil {
t.Errorf("There was an error transforming optimization status to message: %v", err)
}
if msg != "Optimization terminated because the total number of branch-and-cut nodes explored exceeded the value specified in the NodeLimit parameter." {
t.Errorf(
"Expected for the optimization status to be the NODE_LIMIT; received %v",
msg,
)
}
}
func TestSolution_ToMessage2(t *testing.T) {
// Constants
statusMax := 16
// Test
for statusIndex := 1; statusIndex < statusMax; statusIndex++ {
tempStatus := solution_status.SolutionStatus(statusIndex)
msg, err := tempStatus.ToMessage()
if err != nil {
t.Errorf(
"Provided valid optimization status to ToMessage(), but an error occurred: %v",
err,
)
}
var expectedMessages = []string{
"Model is loaded, but no solution information is available.",
"Model was solved to optimality (subject to tolerances), and an optimal solution is available.",
"Model was proven to be infeasible.",
"Model was proven to be either infeasible or unbounded. To obtain a more definitive conclusion, set the DualReductions parameter to 0 and reoptimize.",
"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.",
"Optimal objective for model was proven to be worse than the value specified in the Cutoff parameter. No solution information is available.",
"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.",
"Optimization terminated because the total number of branch-and-cut nodes explored exceeded the value specified in the NodeLimit parameter.",
"Optimization terminated because the time expended exceeded the value specified in the TimeLimit parameter.",
"Optimization terminated because the number of solutions found reached the value specified in the SolutionLimit parameter.",
"Optimization was terminated by the user.",
"Optimization was terminated due to unrecoverable numerical difficulties.",
"Unable to satisfy optimality tolerances; a sub-optimal solution is available.",
"An asynchronous optimization call was made, but the associated optimization run is not yet complete.",
"User specified an objective limit (a bound on either the best objective or the best bound), and that limit has been reached.",
"Optimization terminated because the work expended exceeded the value specified in the WorkLimit parameter.",
}
if strings.Compare(expectedMessages[statusIndex], msg) == 0 {
t.Errorf(
"Expected message \"%v\" \n for status %v, received: \"%v\"",
expectedMessages[statusIndex-1],
statusIndex,
msg,
)
}
}
}
/*
TestSolution_Value1
Description:
This function tests whether or not we can properly retrieve values from a solution object.
*/
func TestSolution_Value1(t *testing.T) {
// Constants
v1 := symbolic.NewVariable()
v2 := symbolic.NewVariable()
tempSol := solution.DummySolution{
Values: map[uint64]float64{
v1.ID: 2.1,
v2.ID: 3.14,
},
Objective: 2.3,
Status: solution_status.NODE_LIMIT,
}
// Algorithm
v1Val, err := solution.ExtractValueOfVariable(&tempSol, v1)
if err != nil {
t.Errorf("The value of the variable v1 could not be extracted; received error %v", err)
}
if v1Val != 2.1 {
t.Errorf(
"Expected v1 to have value 2.1; received %v",
v1Val,
)
}
v2Val, err := solution.ExtractValueOfVariable(&tempSol, v2)
if err != nil {
t.Errorf("The value of the variable v2 could not be extracted; received error %v", err)
}
if v2Val != 3.14 {
t.Errorf(
"Expected v2 to have value 3.14; received %v",
v2Val,
)
}
}
/*
TestSolution_FindValueOfExpression1
Description:
This function tests whether we can evaluate a simple linear expression
using the solution values.
*/
func TestSolution_FindValueOfExpression1(t *testing.T) {
// Constants
v1 := symbolic.NewVariable()
v2 := symbolic.NewVariable()
tempSol := solution.DummySolution{
Values: map[uint64]float64{
v1.ID: 2.0,
v2.ID: 3.0,
},
Objective: 2.3,
Status: solution_status.OPTIMAL,
}
// Create expression: 2*v1 + 3*v2 = 2*2.0 + 3*3.0 = 4.0 + 9.0 = 13.0
expr := v1.Multiply(symbolic.K(2.0)).Plus(v2.Multiply(symbolic.K(3.0)))
// Algorithm
result, err := solution.FindValueOfExpression(&tempSol, expr)
if err != nil {
t.Errorf("FindValueOfExpression returned an error: %v", err)
}
expected := 13.0
if result != expected {
t.Errorf(
"Expected expression value to be %v; received %v",
expected,
result,
)
}
}
/*
TestSolution_FindValueOfExpression2
Description:
This function tests whether we can evaluate a constant expression.
*/
func TestSolution_FindValueOfExpression2(t *testing.T) {
// Constants
tempSol := solution.DummySolution{
Values: map[uint64]float64{},
Objective: 2.3,
Status: solution_status.OPTIMAL,
}
// Create constant expression: 42.0
expr := symbolic.K(42.0)
// Algorithm
result, err := solution.FindValueOfExpression(&tempSol, expr)
if err != nil {
t.Errorf("FindValueOfExpression returned an error: %v", err)
}
expected := 42.0
if result != expected {
t.Errorf(
"Expected expression value to be %v; received %v",
expected,
result,
)
}
}
/*
TestSolution_FindValueOfExpression3
Description:
This function tests whether we can evaluate an expression with a single variable.
*/
func TestSolution_FindValueOfExpression3(t *testing.T) {
// Constants
v1 := symbolic.NewVariable()
tempSol := solution.DummySolution{
Values: map[uint64]float64{
v1.ID: 5.5,
},
Objective: 2.3,
Status: solution_status.OPTIMAL,
}
// Create expression: v1 + 10 = 5.5 + 10 = 15.5
expr := v1.Plus(symbolic.K(10.0))
// Algorithm
result, err := solution.FindValueOfExpression(&tempSol, expr)
if err != nil {
t.Errorf("FindValueOfExpression returned an error: %v", err)
}
expected := 15.5
if result != expected {
t.Errorf(
"Expected expression value to be %v; received %v",
expected,
result,
)
}
}
/*
TestSolution_FindValueOfExpression4
Description:
This function tests whether we get an error when a variable is missing
from the solution.
*/
func TestSolution_FindValueOfExpression4(t *testing.T) {
// Constants
v1 := symbolic.NewVariable()
v2 := symbolic.NewVariable()
tempSol := solution.DummySolution{
Values: map[uint64]float64{
v1.ID: 2.0,
// v2 is missing
},
Objective: 2.3,
Status: solution_status.OPTIMAL,
}
// Create expression: v1 + v2
expr := v1.Plus(v2)
// Algorithm
_, err := solution.FindValueOfExpression(&tempSol, expr)
if err == nil {
t.Errorf("Expected FindValueOfExpression to return an error for missing variable, but got nil")
}
}
/*
TestSolution_FindValueOfExpression5
Description:
This function tests whether we can evaluate a more complex expression
with multiple operations.
*/
func TestSolution_FindValueOfExpression5(t *testing.T) {
// Constants
v1 := symbolic.NewVariable()
v2 := symbolic.NewVariable()
v3 := symbolic.NewVariable()
tempSol := solution.DummySolution{
Values: map[uint64]float64{
v1.ID: 1.0,
v2.ID: 2.0,
v3.ID: 3.0,
},
Objective: 2.3,
Status: solution_status.OPTIMAL,
}
// Create expression: (v1 + v2) * v3 + 5 = (1.0 + 2.0) * 3.0 + 5 = 3.0 * 3.0 + 5 = 9.0 + 5 = 14.0
expr := v1.Plus(v2).Multiply(v3).Plus(symbolic.K(5.0))
// Algorithm
result, err := solution.FindValueOfExpression(&tempSol, expr)
if err != nil {
t.Errorf("FindValueOfExpression returned an error: %v", err)
}
expected := 14.0
if result != expected {
t.Errorf(
"Expected expression value to be %v; received %v",
expected,
result,
)
}
}
/*
TestSolution_GetProblem1
Description:
This function tests whether we can retrieve the problem from a solution.
*/
func TestSolution_GetProblem1(t *testing.T) {
// Constants
p := problem.NewProblem("TestProblem1")
v1 := p.AddVariable()
tempSol := solution.DummySolution{
Values: map[uint64]float64{
v1.ID: 2.1,
},
Objective: 2.3,
Status: solution_status.OPTIMAL,
Problem: p,
}
// Algorithm
retrievedProblem := tempSol.GetProblem()
// Verify the problem is the same
if retrievedProblem != p {
t.Errorf("Expected GetProblem to return the same problem pointer")
}
if retrievedProblem.Name != "TestProblem1" {
t.Errorf("Expected problem name to be 'TestProblem1'; received %v", retrievedProblem.Name)
}
}
/*
TestSolution_GetProblem2
Description:
This function tests whether GetProblem returns nil when no problem is set.
*/
func TestSolution_GetProblem2(t *testing.T) {
// Constants
tempSol := solution.DummySolution{
Values: map[uint64]float64{
0: 2.1,
},
Objective: 2.3,
Status: solution_status.OPTIMAL,
Problem: nil,
}
// Algorithm
retrievedProblem := tempSol.GetProblem()
// Verify the problem is nil
if retrievedProblem != nil {
t.Errorf("Expected GetProblem to return nil when no problem is set")
}
}