Skip to content

Commit 0bac20f

Browse files
authored
Merge pull request #92 from JuliaOpt/bl/moiv0.9
Updates to MOI v0.9
2 parents dbcf2e6 + e4bca49 commit 0bac20f

12 files changed

+141
-61
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Manifest.toml

Diff for: .travis.yml

+2-4
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ os:
33
- linux
44
- osx
55
julia:
6-
- 0.6
7-
- 0.7
86
- 1.0
7+
- 1.1
98
notifications:
109
email: false
1110
after_success:
12-
- julia -e 'cd(Pkg.dir("ECOS")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())'
13-
- julia -e 'cd(Pkg.dir("ECOS")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())'
11+
- julia -e 'import Pkg; Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder()); Codecov.submit(process_folder())'

Diff for: Project.toml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name = "ECOS"
2+
uuid = "e2685f51-7e38-5353-a97d-a921fd2c8199"
3+
repo = "https://github.com/JuliaOpt/ECOS.jl.git"
4+
version = "0.10.0"
5+
6+
[deps]
7+
BinaryProvider = "b99e7846-7c00-51b0-8f62-c81ae34c0232"
8+
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"
9+
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
10+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
11+
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
12+
MathProgBase = "fdba3010-5040-5b88-9595-932c9decdf73"
13+
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
14+
15+
[compat]
16+
BinaryProvider = "≥ 0.3.0"
17+
MathOptInterface = "0.9"
18+
MathProgBase = "~0.5.0, ~0.6, ~0.7"
19+
julia = "1"
20+
21+
[extras]
22+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
23+
24+
[targets]
25+
test = ["Test"]

Diff for: REQUIRE

-5
This file was deleted.

Diff for: appveyor.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
environment:
22
matrix:
3-
- julia_version: 0.6
4-
- julia_version: 0.7
53
- julia_version: 1.0
4+
- julia_version: 1.1
65

76
platform:
87
- x86 # 32-bit

Diff for: src/ECOS.jl

+2-4
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,10 @@
77
# Contains the wrapper itself
88
#############################################################################
99

10-
__precompile__()
1110
module ECOS
1211

13-
using Compat
14-
using Compat.SparseArrays
15-
using Compat.LinearAlgebra
12+
using SparseArrays
13+
using LinearAlgebra
1614

1715
# Try to load the binary dependency
1816
if isfile(joinpath(dirname(@__FILE__),"..","deps","deps.jl"))

Diff for: src/MOI_wrapper.jl

+88-22
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ struct Solution
1111
dual_eq::Vector{Float64}
1212
dual_ineq::Vector{Float64}
1313
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
1618
end
1719
const OPTIMIZE_NOT_CALLED = -1
1820
Solution() = Solution(OPTIMIZE_NOT_CALLED, Float64[], Float64[], Float64[],
19-
Float64[], NaN, NaN)
21+
Float64[], NaN, NaN, NaN, NaN)
2022

2123
# Used to build the data with allocate-load during `copy_to`.
2224
# When `optimize!` is called, a the data is used to build `ECOSMatrix`
@@ -32,7 +34,7 @@ mutable struct ModelData
3234
JG::Vector{Int} # List of equality cols
3335
VG::Vector{Float64} # List of equality coefficients
3436
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
3638
c::Vector{Float64}
3739
end
3840

@@ -58,14 +60,33 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
5860
maxsense::Bool
5961
data::Union{Nothing, ModelData} # only non-Nothing between MOI.copy_to and MOI.optimize!
6062
sol::Solution
61-
options
63+
silent::Bool
64+
options::Dict{Symbol, Any}
6265
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
6471
end
6572
end
6673

6774
MOI.get(::Optimizer, ::MOI.SolverName) = "ECOS"
6875

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+
6990
function MOI.is_empty(instance::Optimizer)
7091
!instance.maxsense && instance.data === nothing
7192
end
@@ -97,7 +118,7 @@ function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike; kws...)
97118
return MOIU.automatic_copy_to(dest, src; kws...)
98119
end
99120

100-
using Compat.SparseArrays
121+
using SparseArrays
101122

102123
# Computes cone dimensions
103124
constroffset(cone::ConeData, ci::CI{<:MOI.AbstractFunction, MOI.Zeros}) = ci.value
@@ -152,7 +173,7 @@ expmap(i) = (1, 3, 2)[i]
152173
function orderidx(idx, s::MOI.ExponentialCone)
153174
expmap.(idx)
154175
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)
156177
A = sparse(output_index.(f.terms), variable_index_value.(f.terms), coefficient.(f.terms))
157178
# sparse combines duplicates with + but does not remove zeros created so we call dropzeros!
158179
dropzeros!(A)
@@ -214,7 +235,7 @@ function MOIU.load(instance::Optimizer, ::MOI.ObjectiveFunction,
214235
f::MOI.ScalarAffineFunction)
215236
c0 = Vector(sparsevec(variable_index_value.(f.terms), coefficient.(f.terms),
216237
instance.data.n))
217-
instance.data.objconstant = f.constant
238+
instance.data.objective_constant = f.constant
218239
instance.data.c = instance.maxsense ? -c0 : c0
219240
return nothing
220241
end
@@ -231,28 +252,61 @@ function MOI.optimize!(instance::Optimizer)
231252
b = instance.data.b
232253
G = ECOS.ECOSMatrix(sparse(instance.data.IG, instance.data.JG, instance.data.VG, m, n))
233254
h = instance.data.h
234-
objconstant = instance.data.objconstant
255+
objective_constant = instance.data.objective_constant
235256
c = instance.data.c
236257
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
237263
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...)
239265
ret_val = ECOS.solve(ecos_prob_ptr)
266+
stat = unsafe_load(unsafe_load(ecos_prob_ptr).info)
267+
solve_time = stat.tsetup + stat.tsolve
240268
ecos_prob = unsafe_wrap(Array, ecos_prob_ptr, 1)[1]
241269
primal = unsafe_wrap(Array, ecos_prob.x, n)[:]
242270
dual_eq = unsafe_wrap(Array, ecos_prob.y, cone.f)[:]
243271
dual_ineq = unsafe_wrap(Array, ecos_prob.z, m)[:]
244272
slack = unsafe_wrap(Array, ecos_prob.s, m)[:]
245273
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"
253309
end
254-
instance.sol = Solution(ret_val, primal, dual_eq, dual_ineq, slack, objval,
255-
objbnd)
256310
end
257311

258312
# Implements getter for result value and statuses
@@ -279,8 +333,20 @@ function MOI.get(instance::Optimizer, ::MOI.TerminationStatus)
279333
end
280334
end
281335

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
284350

285351
function MOI.get(instance::Optimizer, ::MOI.PrimalStatus)
286352
flag = instance.sol.ret_val

Diff for: test/MOI_wrapper.jl

+15-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using Compat
2-
using Compat.Test
1+
using Test
32

43
using MathOptInterface
54
const MOI = MathOptInterface
@@ -8,7 +7,8 @@ const MOIU = MOI.Utilities
87
const MOIB = MOI.Bridges
98

109
import ECOS
11-
const optimizer = ECOS.Optimizer(verbose=false)
10+
const optimizer = ECOS.Optimizer()
11+
MOI.set(optimizer, MOI.Silent(), true)
1212

1313
@testset "SolverName" begin
1414
@test MOI.get(optimizer, MOI.SolverName()) == "ECOS"
@@ -19,18 +19,10 @@ end
1919
@test !MOIU.supports_allocate_load(optimizer, true)
2020
end
2121

22-
MOIU.@model(ECOSModelData,
23-
(), (), # No scalar functions
24-
(MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives, MOI.SecondOrderCone,
25-
MOI.ExponentialCone),
26-
(),
27-
(), (), # No scalar sets
28-
(), (MOI.VectorAffineFunction,))
2922
# UniversalFallback is needed for starting values, even if they are ignored by ECOS
30-
const cache = MOIU.UniversalFallback(ECOSModelData{Float64}())
23+
const cache = MOIU.UniversalFallback(MOIU.Model{Float64}())
3124
const cached = MOIU.CachingOptimizer(cache, optimizer)
3225

33-
# Essential bridges that are needed for all tests
3426
const bridged = MOIB.full_bridge_optimizer(cached, Float64)
3527

3628
# SOC2 requires 1e-4
@@ -39,10 +31,16 @@ const config = MOIT.TestConfig(atol=1e-4, rtol=1e-4)
3931
@testset "Unit" begin
4032
MOIT.unittest(bridged,
4133
config,
42-
[# Need https://github.com/JuliaOpt/MathOptInterface.jl/issues/529
43-
"solve_qp_edge_cases",
44-
# Integer and ZeroOne sets are not supported
45-
"solve_integer_edge_cases", "solve_objbound_edge_cases"])
34+
[
35+
# `TimeLimitSec` not supported.
36+
"time_limit_sec",
37+
# Need https://github.com/JuliaOpt/MathOptInterface.jl/issues/529
38+
"solve_qp_edge_cases",
39+
# Integer and ZeroOne sets are not supported
40+
"solve_integer_edge_cases", "solve_objbound_edge_cases",
41+
"solve_zero_one_with_bounds_1",
42+
"solve_zero_one_with_bounds_2",
43+
"solve_zero_one_with_bounds_3"])
4644
end
4745

4846
@testset "Continuous linear problems" begin
@@ -54,6 +52,6 @@ end
5452
end
5553

5654
@testset "Continuous conic problems" begin
57-
exclude = ["sdp", "rootdet", "logdet"]
55+
exclude = ["pow", "sdp", "rootdet", "logdet"]
5856
MOIT.contconictest(bridged, config, exclude)
5957
end

Diff for: test/MPB_linear.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
# Test the MathProgBase.jl interface for the ECOS.jl solver wrapper
88
#############################################################################
99

10-
using Compat.Test
11-
using Compat.LinearAlgebra
10+
using Test
11+
using LinearAlgebra
1212
using MathProgBase
1313
using ECOS
1414

Diff for: test/direct.jl

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
# Test the direct interface for the ECOS.jl solver wrapper
88
#############################################################################
99

10-
using Compat.Test
11-
using Compat.LinearAlgebra
12-
using Compat.SparseArrays
10+
using Test
11+
using LinearAlgebra
12+
using SparseArrays
1313

1414
# The values below are copied from data.h in ECOS source code
1515
import ECOS

Diff for: test/options.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#############################################################################
99

1010
import ECOS
11-
using Compat.Test
11+
using Test
1212

1313
println(ECOS.ver())
1414

Diff for: test/stress.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Generates a random cone problem to test the MPB interface perf
2-
using Compat.SparseArrays
2+
using SparseArrays
33

44
rows = 10000
55
cols = 10000

0 commit comments

Comments
 (0)