Skip to content

Commit 9ec6257

Browse files
authored
Merge pull request #100 from SCIP-Interfaces/rs/nonlin_cons
Support nonlinear constraints.
2 parents 5507c00 + 574190f commit 9ec6257

20 files changed

+1576
-32
lines changed

.travis.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ before_install:
2121
- sudo dpkg -i SCIPOptSuite-$VERSION-Linux.deb
2222
script:
2323
- julia -e 'using Pkg; Pkg.clone(pwd()); Pkg.build("SCIP"); Pkg.test("SCIP"; coverage=true)'
24+
jobs:
25+
include:
26+
- stage: "MINLPTests"
27+
julia: 1.0
28+
os: linux
29+
script:
30+
- julia --project=test/MINLPTests -e 'using Pkg; Pkg.instantiate(); Pkg.add(PackageSpec(path=pwd()))'
31+
- julia --project=test/MINLPTests --color=yes test/MINLPTests/run_minlptests.jl
2432
after_success:
2533
# push coverage results to Codecov, Coveralls
2634
- julia -e 'using Pkg; cd(Pkg.dir("SCIP")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder()); Coveralls.submit(Coveralls.process_folder())'

gen/generate_wrapper.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ scip_headers = vcat(
88
filter(h -> startswith(h, "scip_"), all_headers),
99
"scipdefplugins.h",
1010
filter(h -> startswith(h, "cons_"), all_headers),
11+
"intervalarith.h", # for nlpi/pub_expr
1112
)
1213
lpi_headers = ["type_lpi.h"]
13-
nlpi_headers = ["type_expr.h", "type_nlpi.h"]
14+
nlpi_headers = ["type_expr.h", "type_nlpi.h", "type_exprinterpret.h", "pub_expr.h"]
1415

1516
headers = vcat(
1617
[joinpath(HEADER_BASE, "scip", h) for h in scip_headers],

src/MOI_wrapper.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,5 +156,6 @@ include(joinpath("MOI_wrapper", "quadratic_constraints.jl"))
156156
include(joinpath("MOI_wrapper", "soc_constraints.jl"))
157157
include(joinpath("MOI_wrapper", "sos_constraints.jl"))
158158
include(joinpath("MOI_wrapper", "abspower_constraints.jl"))
159+
include(joinpath("MOI_wrapper", "nonlinear_constraints.jl"))
159160
include(joinpath("MOI_wrapper", "objective.jl"))
160161
include(joinpath("MOI_wrapper", "results.jl"))
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Nonlinear constraints (objective not supported)
2+
3+
MOI.supports(::Optimizer, ::MOI.NLPBlock) = true
4+
5+
function MOI.set(o::Optimizer, ::MOI.NLPBlock, data::MOI.NLPBlockData)
6+
# We don't actually store the data (to be queried later during the solve
7+
# process). Instead, we extract the expression graphs and add the
8+
# corresponding constraints to the model directly.
9+
if data.has_objective
10+
error("Nonlinear objective not supported by SCIP.jl!")
11+
end
12+
13+
MOI.initialize(data.evaluator, [:ExprGraph])
14+
for i in 1:length(data.constraint_bounds)
15+
expr = MOI.constraint_expr(data.evaluator, i)
16+
bounds = data.constraint_bounds[i]
17+
cr = add_nonlinear_constraint(o.mscip, expr, bounds.lower, bounds.upper)
18+
# Not registering or returning constraint reference!
19+
end
20+
21+
return nothing
22+
end

src/MOI_wrapper/variable.jl

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -84,18 +84,21 @@ function MOI.delete(o::Optimizer, ci::CI{SVF,S}) where {S <: VAR_TYPES}
8484
vi = VI(ci.value)
8585
v = var(o, vi)
8686

87-
if SCIPvarGetType(v) == SCIP_VARTYPE_BINARY
88-
# Reset bounds from constraint.
89-
bounds = o.binbounds[vi]
90-
@SC SCIPchgVarLb(o, v, bounds.lower)
91-
@SC SCIPchgVarUb(o, v, bounds.upper)
92-
end
87+
# Reset bounds from constraint if this was a binary, see below.
88+
reset_bounds = SCIPvarGetType(v) == SCIP_VARTYPE_BINARY
9389

9490
# don't actually delete any SCIP constraint, just reset type
9591
infeasible = Ref{SCIP_Bool}()
9692
@SC SCIPchgVarType(o, var(o, vi), SCIP_VARTYPE_CONTINUOUS, infeasible)
9793
# TODO: warn if infeasible[] == TRUE?
9894

95+
# Can only change bounds after chaging the var type.
96+
if reset_bounds
97+
bounds = o.binbounds[vi]
98+
@SC SCIPchgVarLb(o, v, bounds.lower)
99+
@SC SCIPchgVarUb(o, v, bounds.upper)
100+
end
101+
99102
# but do delete the constraint reference
100103
delete!(o.constypes[SVF, S], ConsRef(ci.value))
101104

@@ -118,7 +121,8 @@ function MOI.add_constraint(o::Optimizer, func::SVF, set::S) where S <: BOUNDS
118121

119122
# Check for existing bounds first.
120123
oldlb, oldub = SCIPvarGetLbOriginal(v), SCIPvarGetUbOriginal(v)
121-
if (oldlb != -inf || oldub != inf)
124+
if (oldlb != -inf && newlb != -inf || oldub != inf && newub != inf)
125+
# special case: implicit bounds for binaries
122126
if SCIPvarGetType(v) == SCIP_VARTYPE_BINARY
123127
# Store new bounds
124128
o.binbounds[vi] = MOI.Interval(newlb, newub)
@@ -130,31 +134,49 @@ function MOI.add_constraint(o::Optimizer, func::SVF, set::S) where S <: BOUNDS
130134
else
131135
error("Invalid bounds [$newlb,$newub] for binary variable at $(vi.value)!")
132136
end
133-
else
137+
else # general case (non-binary variable)
134138
error("Already have bounds [$oldlb,$oldub] for variable at $(vi.value)!")
135139
end
136140
end
137141

138-
@SC SCIPchgVarLb(o, v, newlb)
139-
@SC SCIPchgVarUb(o, v, newub)
142+
if newlb != -inf
143+
@SC SCIPchgVarLb(o, v, newlb)
144+
end
145+
146+
if newub != inf
147+
@SC SCIPchgVarUb(o, v, newub)
148+
end
149+
140150
# use var index for cons index of this type
141151
i = func.variable.value
142152
return register!(o, CI{SVF, S}(i))
143153
end
144154

155+
function reset_bounds(o::Optimizer, v, lb, ub, ci::CI{SVF, S}) where S <: Union{MOI.Interval{Float64}, MOI.EqualTo{Float64}}
156+
@SC SCIPchgVarLb(o, v, lb)
157+
@SC SCIPchgVarUb(o, v, ub)
158+
end
159+
160+
function reset_bounds(o::Optimizer, v, lb, ub, ci::CI{SVF, MOI.GreaterThan{Float64}})
161+
@SC SCIPchgVarLb(o, v, lb)
162+
end
163+
164+
function reset_bounds(o::Optimizer, v, lb, ub, ci::CI{SVF, MOI.LessThan{Float64}})
165+
@SC SCIPchgVarUb(o, v, ub)
166+
end
167+
168+
145169
function MOI.delete(o::Optimizer, ci::CI{SVF,S}) where S <: BOUNDS
146170
allow_modification(o)
147171

148172
# Don't actually delete any SCIP constraint, just reset bounds
149173
vi = VI(ci.value)
150174
v = var(o, vi)
151175
if SCIPvarGetType(v) == SCIP_VARTYPE_BINARY
152-
@SC SCIPchgVarLb(o, v, 0.0)
153-
@SC SCIPchgVarUb(o, v, 1.0)
176+
reset_bounds(o, v, 0.0, 1.0, ci)
154177
else
155178
inf = SCIPinfinity(s)
156-
@SC SCIPchgVarLb(o, v, -inf)
157-
@SC SCIPchgVarUb(o, v, inf)
179+
reset_bounds(o, v, -inf, inf, ci)
158180
end
159181

160182
# but do delete the constraint reference

src/SCIP.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ include("wrapper.jl")
99
# memory management
1010
include("managed_scip.jl")
1111

12+
# constraints from nonlinear expressions
13+
include("nonlinear.jl")
14+
1215
# implementation of MOI
1316
include("MOI_wrapper.jl")
1417

src/managed_scip.jl

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ mutable struct ManagedSCIP
1717
cons_count::Int64
1818

1919
function ManagedSCIP()
20-
scip = Ref{Ptr{SCIP_}}()
20+
scip = Ref{Ptr{SCIP_}}(C_NULL)
2121
@SC SCIPcreate(scip)
2222
@assert scip[] != C_NULL
2323
@SC SCIPincludeDefaultPlugins(scip[])
@@ -49,6 +49,12 @@ function free_scip(mscip::ManagedSCIP)
4949
@assert mscip.scip[] == C_NULL
5050
end
5151

52+
"Set generic parameter."
53+
function set_parameter(mscip::ManagedSCIP, name::String, value)
54+
@SC SCIPsetParam(mscip, name, Ptr{Cvoid}(value))
55+
return nothing
56+
end
57+
5258
"Return pointer to SCIP variable."
5359
var(mscip::ManagedSCIP, vr::VarRef) = mscip.vars[vr][]
5460

@@ -73,7 +79,7 @@ end
7379

7480
"Add variable to problem (continuous, no bounds), return var ref."
7581
function add_variable(mscip::ManagedSCIP)
76-
var__ = Ref{Ptr{SCIP_VAR}}()
82+
var__ = Ref{Ptr{SCIP_VAR}}(C_NULL)
7783
@SC SCIPcreateVarBasic(mscip, var__, "", -SCIPinfinity(mscip), SCIPinfinity(mscip),
7884
0.0, SCIP_VARTYPE_CONTINUOUS)
7985
@SC SCIPaddVar(mscip, var__[])
@@ -117,7 +123,7 @@ Use `(-)SCIPinfinity(scip)` for one of the bounds if not applicable.
117123
function add_linear_constraint(mscip::ManagedSCIP, varrefs, coefs, lhs, rhs)
118124
@assert length(varrefs) == length(coefs)
119125
vars = [var(mscip, vr) for vr in varrefs]
120-
cons__ = Ref{Ptr{SCIP_CONS}}()
126+
cons__ = Ref{Ptr{SCIP_CONS}}(C_NULL)
121127
@SC SCIPcreateConsBasicLinear(
122128
mscip, cons__, "", length(vars), vars, coefs, lhs, rhs)
123129
@SC SCIPaddCons(mscip, cons__[])
@@ -148,7 +154,7 @@ function add_quadratic_constraint(mscip::ManagedSCIP, linrefs, lincoefs,
148154
quadvars1 = [var(mscip, vr) for vr in quadrefs1]
149155
quadvars2 = [var(mscip, vr) for vr in quadrefs2]
150156

151-
cons__ = Ref{Ptr{SCIP_CONS}}()
157+
cons__ = Ref{Ptr{SCIP_CONS}}(C_NULL)
152158
@SC SCIPcreateConsBasicQuadratic(
153159
mscip, cons__, "", length(linvars), linvars, lincoefs,
154160
length(quadvars1), quadvars1, quadvars2, quadcoefs, lhs, rhs)
@@ -165,7 +171,7 @@ the right-hand side.
165171
"""
166172
function add_second_order_cone_constraint(mscip::ManagedSCIP, varrefs)
167173
vars = [var(mscip, vr) for vr in varrefs]
168-
cons__ = Ref{Ptr{SCIP_CONS}}()
174+
cons__ = Ref{Ptr{SCIP_CONS}}(C_NULL)
169175
@SC SCIPcreateConsBasicSOC(mscip, cons__, "", length(vars) - 1,
170176
vars[2:end], C_NULL, C_NULL, 0.0, vars[1], 1.0, 0.0)
171177
@SC SCIPaddCons(mscip, cons__[])
@@ -182,7 +188,7 @@ Add special-ordered-set of type 1 to problem, return cons ref.
182188
function add_special_ordered_set_type1(mscip::ManagedSCIP, varrefs, weights)
183189
@assert length(varrefs) == length(weights)
184190
vars = [var(mscip, vr) for vr in varrefs]
185-
cons__ = Ref{Ptr{SCIP_CONS}}()
191+
cons__ = Ref{Ptr{SCIP_CONS}}(C_NULL)
186192
@SC SCIPcreateConsBasicSOS1(mscip, cons__, "", length(vars), vars, weights)
187193
@SC SCIPaddCons(mscip, cons__[])
188194
return store_cons!(mscip, cons__)
@@ -198,14 +204,14 @@ Add special-ordered-set of type 2 to problem, return cons ref.
198204
function add_special_ordered_set_type2(mscip::ManagedSCIP, varrefs, weights)
199205
@assert length(varrefs) == length(weights)
200206
vars = [var(mscip, vr) for vr in varrefs]
201-
cons__ = Ref{Ptr{SCIP_CONS}}()
207+
cons__ = Ref{Ptr{SCIP_CONS}}(C_NULL)
202208
@SC SCIPcreateConsBasicSOS2(mscip, cons__, "", length(vars), vars, weights)
203209
@SC SCIPaddCons(mscip, cons__[])
204210
return store_cons!(mscip, cons__)
205211
end
206212

207213
"""
208-
Add special-ordered-set of type 2 to problem, return cons ref.
214+
Add abspower constraint to problem, return cons ref.
209215
210216
lhs ≤ sign(x + a) * abs(x + a)^n + c*z ≤ rhs
211217
@@ -221,15 +227,9 @@ Add special-ordered-set of type 2 to problem, return cons ref.
221227
Use `(-)SCIPinfinity(scip)` for one of the bounds if not applicable.
222228
"""
223229
function add_abspower_constraint(mscip::ManagedSCIP, x, a, n, z, c, lhs, rhs)
224-
cons__ = Ref{Ptr{SCIP_CONS}}()
230+
cons__ = Ref{Ptr{SCIP_CONS}}(C_NULL)
225231
@SC SCIPcreateConsBasicAbspower(
226232
mscip, cons__, "", var(mscip, x), var(mscip, z), n, a, c, lhs, rhs)
227233
@SC SCIPaddCons(mscip, cons__[])
228234
return store_cons!(mscip, cons__)
229235
end
230-
231-
"Set generic parameter."
232-
function set_parameter(mscip::ManagedSCIP, name::String, value)
233-
@SC SCIPsetParam(mscip, name, Ptr{Cvoid}(value))
234-
return nothing
235-
end

0 commit comments

Comments
 (0)