@@ -11,12 +11,14 @@ struct Solution
11
11
dual_eq:: Vector{Float64}
12
12
dual_ineq:: Vector{Float64}
13
13
slack:: Vector{Float64}
14
- objval:: Float64
15
- objbnd:: Float64
14
+ objective_value:: Float64
15
+ dual_objective_value:: Float64
16
+ objective_constant:: Float64
17
+ solve_time:: Float64
16
18
end
17
19
const OPTIMIZE_NOT_CALLED = - 1
18
20
Solution () = Solution (OPTIMIZE_NOT_CALLED, Float64[], Float64[], Float64[],
19
- Float64[], NaN , NaN )
21
+ Float64[], NaN , NaN , NaN , NaN )
20
22
21
23
# Used to build the data with allocate-load during `copy_to`.
22
24
# When `optimize!` is called, a the data is used to build `ECOSMatrix`
@@ -32,7 +34,7 @@ mutable struct ModelData
32
34
JG:: Vector{Int} # List of equality cols
33
35
VG:: Vector{Float64} # List of equality coefficients
34
36
h:: Vector{Float64} # List of equality coefficients
35
- objconstant :: Float64 # The objective is min c'x + objconstant
37
+ objective_constant :: Float64 # The objective is min c'x + objective_constant
36
38
c:: Vector{Float64}
37
39
end
38
40
@@ -58,14 +60,33 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
58
60
maxsense:: Bool
59
61
data:: Union{Nothing, ModelData} # only non-Nothing between MOI.copy_to and MOI.optimize!
60
62
sol:: Solution
61
- options
63
+ silent:: Bool
64
+ options:: Dict{Symbol, Any}
62
65
function Optimizer (; kwargs... )
63
- new (ConeData (), false , nothing , Solution (), kwargs)
66
+ optimizer = new (ConeData (), false , nothing , Solution (), false , Dict {Symbol, Any} ())
67
+ for (key, value) in kwargs
68
+ MOI. set (optimizer, MOI. RawParameter (key), value)
69
+ end
70
+ return optimizer
64
71
end
65
72
end
66
73
67
74
MOI. get (:: Optimizer , :: MOI.SolverName ) = " ECOS"
68
75
76
+ function MOI. set (optimizer:: Optimizer , param:: MOI.RawParameter , value)
77
+ optimizer. options[param. name] = value
78
+ end
79
+ function MOI. get (optimizer:: Optimizer , param:: MOI.RawParameter )
80
+ # TODO : This gives a poor error message if the name of the parameter is invalid.
81
+ return optimizer. options[param. name]
82
+ end
83
+
84
+ MOI. supports (:: Optimizer , :: MOI.Silent ) = true
85
+ function MOI. set (optimizer:: Optimizer , :: MOI.Silent , value:: Bool )
86
+ optimizer. silent = value
87
+ end
88
+ MOI. get (optimizer:: Optimizer , :: MOI.Silent ) = optimizer. silent
89
+
69
90
function MOI. is_empty (instance:: Optimizer )
70
91
! instance. maxsense && instance. data === nothing
71
92
end
@@ -97,7 +118,7 @@ function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike; kws...)
97
118
return MOIU. automatic_copy_to (dest, src; kws... )
98
119
end
99
120
100
- using Compat . SparseArrays
121
+ using SparseArrays
101
122
102
123
# Computes cone dimensions
103
124
constroffset (cone:: ConeData , ci:: CI{<:MOI.AbstractFunction, MOI.Zeros} ) = ci. value
@@ -152,7 +173,7 @@ expmap(i) = (1, 3, 2)[i]
152
173
function orderidx (idx, s:: MOI.ExponentialCone )
153
174
expmap .(idx)
154
175
end
155
- function MOIU. load_constraint (instance:: Optimizer , ci, f:: MOI.VectorAffineFunction , s:: MOI.AbstractVectorSet )
176
+ function MOIU. load_constraint (instance:: Optimizer , ci:: MOI.ConstraintIndex , f:: MOI.VectorAffineFunction , s:: MOI.AbstractVectorSet )
156
177
A = sparse (output_index .(f. terms), variable_index_value .(f. terms), coefficient .(f. terms))
157
178
# sparse combines duplicates with + but does not remove zeros created so we call dropzeros!
158
179
dropzeros! (A)
@@ -214,7 +235,7 @@ function MOIU.load(instance::Optimizer, ::MOI.ObjectiveFunction,
214
235
f:: MOI.ScalarAffineFunction )
215
236
c0 = Vector (sparsevec (variable_index_value .(f. terms), coefficient .(f. terms),
216
237
instance. data. n))
217
- instance. data. objconstant = f. constant
238
+ instance. data. objective_constant = f. constant
218
239
instance. data. c = instance. maxsense ? - c0 : c0
219
240
return nothing
220
241
end
@@ -231,28 +252,61 @@ function MOI.optimize!(instance::Optimizer)
231
252
b = instance. data. b
232
253
G = ECOS. ECOSMatrix (sparse (instance. data. IG, instance. data. JG, instance. data. VG, m, n))
233
254
h = instance. data. h
234
- objconstant = instance. data. objconstant
255
+ objective_constant = instance. data. objective_constant
235
256
c = instance. data. c
236
257
instance. data = nothing # Allows GC to free instance.data before A is loaded to ECOS
258
+ options = instance. options
259
+ if instance. silent
260
+ options = copy (options)
261
+ options[:verbose ] = false
262
+ end
237
263
ecos_prob_ptr = ECOS. setup (n, m, cone. f, cone. l, length (cone. qa), cone. qa,
238
- cone. ep, G, A, c, h, b; instance . options... )
264
+ cone. ep, G, A, c, h, b; options... )
239
265
ret_val = ECOS. solve (ecos_prob_ptr)
266
+ stat = unsafe_load (unsafe_load (ecos_prob_ptr). info)
267
+ solve_time = stat. tsetup + stat. tsolve
240
268
ecos_prob = unsafe_wrap (Array, ecos_prob_ptr, 1 )[1 ]
241
269
primal = unsafe_wrap (Array, ecos_prob. x, n)[:]
242
270
dual_eq = unsafe_wrap (Array, ecos_prob. y, cone. f)[:]
243
271
dual_ineq = unsafe_wrap (Array, ecos_prob. z, m)[:]
244
272
slack = unsafe_wrap (Array, ecos_prob. s, m)[:]
245
273
ECOS. cleanup (ecos_prob_ptr, 0 )
246
- objval = (instance. maxsense ? - 1 : 1 ) * dot (c, primal)
247
- if ret_val != ECOS. ECOS_DINF
248
- objval += objconstant
249
- end
250
- objbnd = - (dot (b, dual_eq) + dot (h, dual_ineq))
251
- if ret_val != ECOS. ECOS_PINF
252
- objbnd += objconstant
274
+ objective_value = (instance. maxsense ? - 1 : 1 ) * stat. pcost
275
+ dual_objective_value = (instance. maxsense ? - 1 : 1 ) * stat. dcost
276
+ instance. sol = Solution (ret_val, primal, dual_eq, dual_ineq, slack, objective_value,
277
+ dual_objective_value, objective_constant, solve_time)
278
+ end
279
+
280
+ MOI. get (optimizer:: Optimizer , :: MOI.SolveTime ) = optimizer. sol. solve_time
281
+ function MOI. get (optimizer:: Optimizer , :: MOI.RawStatusString )
282
+ # Strings from https://github.com/ifa-ethz/ecos/blob/master/include/ecos.h
283
+ flag = optimizer. sol. ret_val
284
+ if flag == OPTIMIZE_NOT_CALLED
285
+ return " Optimize not called"
286
+ elseif flag == ECOS_OPTIMAL
287
+ return " Problem solved to optimality"
288
+ elseif flag == ECOS_OPTIMAL + ECOS_INACC_OFFSET
289
+ return " Problem solved to inaccurate optimality"
290
+ elseif flag == ECOS_PINF
291
+ return " Found certificate of primal infeasibility"
292
+ elseif flag == ECOS_PINF + ECOS_INACC_OFFSET
293
+ return " Found inaccurate certificate of primal infeasibility"
294
+ elseif flag == ECOS_DINF
295
+ return " Found certificate of dual infeasibility"
296
+ elseif flag == ECOS_DINF + ECOS_INACC_OFFSET
297
+ return " Found inaccurate certificate of dual infeasibility"
298
+ elseif flag == ECOS_MAXIT
299
+ return " Maximum number of iterations reached"
300
+ elseif flag == ECOS_NUMERICS
301
+ return " Search direction unreliable"
302
+ elseif flag == ECOS_OUTCONE
303
+ return " s or z got outside the cone, numerics?"
304
+ elseif flag == ECOS_SIGINT
305
+ return " solver interrupted by a signal/ctrl-c"
306
+ else
307
+ @assert flag == ECOS_FATAL
308
+ return " Unknown problem in solver"
253
309
end
254
- instance. sol = Solution (ret_val, primal, dual_eq, dual_ineq, slack, objval,
255
- objbnd)
256
310
end
257
311
258
312
# Implements getter for result value and statuses
@@ -279,8 +333,20 @@ function MOI.get(instance::Optimizer, ::MOI.TerminationStatus)
279
333
end
280
334
end
281
335
282
- MOI. get (instance:: Optimizer , :: MOI.ObjectiveValue ) = instance. sol. objval
283
- MOI. get (instance:: Optimizer , :: MOI.ObjectiveBound ) = instance. sol. objbnd
336
+ function MOI. get (optimizer:: Optimizer , :: MOI.ObjectiveValue )
337
+ value = optimizer. sol. objective_value
338
+ if ! MOIU. is_ray (MOI. get (optimizer, MOI. PrimalStatus ()))
339
+ value += optimizer. sol. objective_constant
340
+ end
341
+ return value
342
+ end
343
+ function MOI. get (optimizer:: Optimizer , :: MOI.DualObjectiveValue )
344
+ value = optimizer. sol. dual_objective_value
345
+ if ! MOIU. is_ray (MOI. get (optimizer, MOI. DualStatus ()))
346
+ value += optimizer. sol. objective_constant
347
+ end
348
+ return value
349
+ end
284
350
285
351
function MOI. get (instance:: Optimizer , :: MOI.PrimalStatus )
286
352
flag = instance. sol. ret_val
0 commit comments