Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support nonlinear constraints. #100

Merged
merged 39 commits into from
Mar 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
3795064
also wrap pub_expr.h
rschwarz Feb 14, 2019
64628fd
exclude newly added MOI test (unsupported feature)
rschwarz Feb 16, 2019
7993117
fix typo
rschwarz Feb 16, 2019
fba2f6c
mv function set_parameter
rschwarz Feb 16, 2019
533f0c8
add methods for SCIPexprCall
rschwarz Feb 17, 2019
e423203
add add_nonlinear_constraint(::ManagedSCIP, ...)
rschwarz Feb 17, 2019
4996418
add minimal test for add_nonlinear_constraint (memory mgmt)
rschwarz Feb 17, 2019
7f00cdb
fix signature of SCIPexprCreate for Ptr{Ptr{SCIP_EXPR}}
rschwarz Feb 22, 2019
94e80d6
fix reference to children in variadic expressions
rschwarz Feb 22, 2019
34a4ba5
add MOI support for NLPBlock (constraints only)
rschwarz Feb 22, 2019
abe4a07
add first MOI-level test for nonlinear constraints
rschwarz Feb 22, 2019
674afee
fix typo (undefined var)
rschwarz Feb 22, 2019
eda856e
don't reuse SCIPblkmem(mscip)
rschwarz Feb 22, 2019
f17720e
managed_scip: fix storage of power exponent
rschwarz Feb 22, 2019
abd43b6
off-topic: fix deletion of variable type constraint
rschwarz Feb 22, 2019
3e38e0a
MOI wrapper: special handling of power expressions
rschwarz Feb 22, 2019
152e2f9
add test with various expression types
rschwarz Feb 22, 2019
8f9e160
fix handling of unary minus
rschwarz Feb 22, 2019
b910f25
initialize some pointers (to no avail?)
rschwarz Feb 23, 2019
311e2a3
more memory safety measures
rschwarz Feb 24, 2019
370198e
nonlinear objective: turn warning into error.
rschwarz Feb 25, 2019
1f04d77
refactoring to remove intermediate representation
rschwarz Feb 25, 2019
609c34a
initialize all Ref{Ptr{T}} instances
rschwarz Feb 25, 2019
7ab08ab
add paranoid consistency check to help with debugging
rschwarz Feb 25, 2019
b536f02
fix ccall signature for variadiac SCIPexprCreate
rschwarz Feb 26, 2019
3615d7e
add project test/MINLPTests
rschwarz Mar 1, 2019
f0fd55c
more detail in error message (unsupported operator)
rschwarz Mar 1, 2019
c98664b
update nonlinear tests to reflect what JuMP passes to MOI
rschwarz Mar 1, 2019
ee3b0b8
nonlinear: handle (skip) comparison operators
rschwarz Mar 1, 2019
e719c19
nonlinear: handle MOI.VariableIndex for :ref
rschwarz Mar 1, 2019
2e1ccfa
Really fix variable handling in nonlinear expressions.
rschwarz Mar 1, 2019
23ffc96
add test/MINLPTests/run_minlptests.jl
rschwarz Mar 1, 2019
cb227a0
travis: add stage "MINLPTests"
rschwarz Mar 1, 2019
b56e97a
fix typo in run_minlptests, rm unsupported test
rschwarz Mar 2, 2019
0a61458
relax handling of variable bound constraints
rschwarz Mar 2, 2019
3f3b306
weaken assertion about nchildren of sum/product
rschwarz Mar 2, 2019
672eeef
run_minlptests: add more cvx_nlp tests, weaken tolerances
rschwarz Mar 2, 2019
675474f
run_minlptests: fix typo (test name)
rschwarz Mar 2, 2019
574190f
run_minlptests: add some nlp-mi tests
rschwarz Mar 2, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ before_install:
- sudo dpkg -i SCIPOptSuite-$VERSION-Linux.deb
script:
- julia -e 'using Pkg; Pkg.clone(pwd()); Pkg.build("SCIP"); Pkg.test("SCIP"; coverage=true)'
jobs:
include:
- stage: "MINLPTests"
julia: 1.0
os: linux
script:
- julia --project=test/MINLPTests -e 'using Pkg; Pkg.instantiate(); Pkg.add(PackageSpec(path=pwd()))'
- julia --project=test/MINLPTests --color=yes test/MINLPTests/run_minlptests.jl
after_success:
# push coverage results to Codecov, Coveralls
- julia -e 'using Pkg; cd(Pkg.dir("SCIP")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder()); Coveralls.submit(Coveralls.process_folder())'
3 changes: 2 additions & 1 deletion gen/generate_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ scip_headers = vcat(
filter(h -> startswith(h, "scip_"), all_headers),
"scipdefplugins.h",
filter(h -> startswith(h, "cons_"), all_headers),
"intervalarith.h", # for nlpi/pub_expr
)
lpi_headers = ["type_lpi.h"]
nlpi_headers = ["type_expr.h", "type_nlpi.h"]
nlpi_headers = ["type_expr.h", "type_nlpi.h", "type_exprinterpret.h", "pub_expr.h"]

headers = vcat(
[joinpath(HEADER_BASE, "scip", h) for h in scip_headers],
Expand Down
1 change: 1 addition & 0 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -156,5 +156,6 @@ include(joinpath("MOI_wrapper", "quadratic_constraints.jl"))
include(joinpath("MOI_wrapper", "soc_constraints.jl"))
include(joinpath("MOI_wrapper", "sos_constraints.jl"))
include(joinpath("MOI_wrapper", "abspower_constraints.jl"))
include(joinpath("MOI_wrapper", "nonlinear_constraints.jl"))
include(joinpath("MOI_wrapper", "objective.jl"))
include(joinpath("MOI_wrapper", "results.jl"))
22 changes: 22 additions & 0 deletions src/MOI_wrapper/nonlinear_constraints.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Nonlinear constraints (objective not supported)

MOI.supports(::Optimizer, ::MOI.NLPBlock) = true

function MOI.set(o::Optimizer, ::MOI.NLPBlock, data::MOI.NLPBlockData)
# We don't actually store the data (to be queried later during the solve
# process). Instead, we extract the expression graphs and add the
# corresponding constraints to the model directly.
if data.has_objective
error("Nonlinear objective not supported by SCIP.jl!")
end

MOI.initialize(data.evaluator, [:ExprGraph])
for i in 1:length(data.constraint_bounds)
expr = MOI.constraint_expr(data.evaluator, i)
bounds = data.constraint_bounds[i]
cr = add_nonlinear_constraint(o.mscip, expr, bounds.lower, bounds.upper)
# Not registering or returning constraint reference!
end

return nothing
end
50 changes: 36 additions & 14 deletions src/MOI_wrapper/variable.jl
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,21 @@ function MOI.delete(o::Optimizer, ci::CI{SVF,S}) where {S <: VAR_TYPES}
vi = VI(ci.value)
v = var(o, vi)

if SCIPvarGetType(v) == SCIP_VARTYPE_BINARY
# Reset bounds from constraint.
bounds = o.binbounds[vi]
@SC SCIPchgVarLb(o, v, bounds.lower)
@SC SCIPchgVarUb(o, v, bounds.upper)
end
# Reset bounds from constraint if this was a binary, see below.
reset_bounds = SCIPvarGetType(v) == SCIP_VARTYPE_BINARY

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

# Can only change bounds after chaging the var type.
if reset_bounds
bounds = o.binbounds[vi]
@SC SCIPchgVarLb(o, v, bounds.lower)
@SC SCIPchgVarUb(o, v, bounds.upper)
end

# but do delete the constraint reference
delete!(o.constypes[SVF, S], ConsRef(ci.value))

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

# Check for existing bounds first.
oldlb, oldub = SCIPvarGetLbOriginal(v), SCIPvarGetUbOriginal(v)
if (oldlb != -inf || oldub != inf)
if (oldlb != -inf && newlb != -inf || oldub != inf && newub != inf)
# special case: implicit bounds for binaries
if SCIPvarGetType(v) == SCIP_VARTYPE_BINARY
# Store new bounds
o.binbounds[vi] = MOI.Interval(newlb, newub)
Expand All @@ -130,31 +134,49 @@ function MOI.add_constraint(o::Optimizer, func::SVF, set::S) where S <: BOUNDS
else
error("Invalid bounds [$newlb,$newub] for binary variable at $(vi.value)!")
end
else
else # general case (non-binary variable)
error("Already have bounds [$oldlb,$oldub] for variable at $(vi.value)!")
end
end

@SC SCIPchgVarLb(o, v, newlb)
@SC SCIPchgVarUb(o, v, newub)
if newlb != -inf
@SC SCIPchgVarLb(o, v, newlb)
end

if newub != inf
@SC SCIPchgVarUb(o, v, newub)
end

# use var index for cons index of this type
i = func.variable.value
return register!(o, CI{SVF, S}(i))
end

function reset_bounds(o::Optimizer, v, lb, ub, ci::CI{SVF, S}) where S <: Union{MOI.Interval{Float64}, MOI.EqualTo{Float64}}
@SC SCIPchgVarLb(o, v, lb)
@SC SCIPchgVarUb(o, v, ub)
end

function reset_bounds(o::Optimizer, v, lb, ub, ci::CI{SVF, MOI.GreaterThan{Float64}})
@SC SCIPchgVarLb(o, v, lb)
end

function reset_bounds(o::Optimizer, v, lb, ub, ci::CI{SVF, MOI.LessThan{Float64}})
@SC SCIPchgVarUb(o, v, ub)
end


function MOI.delete(o::Optimizer, ci::CI{SVF,S}) where S <: BOUNDS
allow_modification(o)

# Don't actually delete any SCIP constraint, just reset bounds
vi = VI(ci.value)
v = var(o, vi)
if SCIPvarGetType(v) == SCIP_VARTYPE_BINARY
@SC SCIPchgVarLb(o, v, 0.0)
@SC SCIPchgVarUb(o, v, 1.0)
reset_bounds(o, v, 0.0, 1.0, ci)
else
inf = SCIPinfinity(s)
@SC SCIPchgVarLb(o, v, -inf)
@SC SCIPchgVarUb(o, v, inf)
reset_bounds(o, v, -inf, inf, ci)
end

# but do delete the constraint reference
Expand Down
3 changes: 3 additions & 0 deletions src/SCIP.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ include("wrapper.jl")
# memory management
include("managed_scip.jl")

# constraints from nonlinear expressions
include("nonlinear.jl")

# implementation of MOI
include("MOI_wrapper.jl")

Expand Down
30 changes: 15 additions & 15 deletions src/managed_scip.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ mutable struct ManagedSCIP
cons_count::Int64

function ManagedSCIP()
scip = Ref{Ptr{SCIP_}}()
scip = Ref{Ptr{SCIP_}}(C_NULL)
@SC SCIPcreate(scip)
@assert scip[] != C_NULL
@SC SCIPincludeDefaultPlugins(scip[])
Expand Down Expand Up @@ -49,6 +49,12 @@ function free_scip(mscip::ManagedSCIP)
@assert mscip.scip[] == C_NULL
end

"Set generic parameter."
function set_parameter(mscip::ManagedSCIP, name::String, value)
@SC SCIPsetParam(mscip, name, Ptr{Cvoid}(value))
return nothing
end

"Return pointer to SCIP variable."
var(mscip::ManagedSCIP, vr::VarRef) = mscip.vars[vr][]

Expand All @@ -73,7 +79,7 @@ end

"Add variable to problem (continuous, no bounds), return var ref."
function add_variable(mscip::ManagedSCIP)
var__ = Ref{Ptr{SCIP_VAR}}()
var__ = Ref{Ptr{SCIP_VAR}}(C_NULL)
@SC SCIPcreateVarBasic(mscip, var__, "", -SCIPinfinity(mscip), SCIPinfinity(mscip),
0.0, SCIP_VARTYPE_CONTINUOUS)
@SC SCIPaddVar(mscip, var__[])
Expand Down Expand Up @@ -117,7 +123,7 @@ Use `(-)SCIPinfinity(scip)` for one of the bounds if not applicable.
function add_linear_constraint(mscip::ManagedSCIP, varrefs, coefs, lhs, rhs)
@assert length(varrefs) == length(coefs)
vars = [var(mscip, vr) for vr in varrefs]
cons__ = Ref{Ptr{SCIP_CONS}}()
cons__ = Ref{Ptr{SCIP_CONS}}(C_NULL)
@SC SCIPcreateConsBasicLinear(
mscip, cons__, "", length(vars), vars, coefs, lhs, rhs)
@SC SCIPaddCons(mscip, cons__[])
Expand Down Expand Up @@ -148,7 +154,7 @@ function add_quadratic_constraint(mscip::ManagedSCIP, linrefs, lincoefs,
quadvars1 = [var(mscip, vr) for vr in quadrefs1]
quadvars2 = [var(mscip, vr) for vr in quadrefs2]

cons__ = Ref{Ptr{SCIP_CONS}}()
cons__ = Ref{Ptr{SCIP_CONS}}(C_NULL)
@SC SCIPcreateConsBasicQuadratic(
mscip, cons__, "", length(linvars), linvars, lincoefs,
length(quadvars1), quadvars1, quadvars2, quadcoefs, lhs, rhs)
Expand All @@ -165,7 +171,7 @@ the right-hand side.
"""
function add_second_order_cone_constraint(mscip::ManagedSCIP, varrefs)
vars = [var(mscip, vr) for vr in varrefs]
cons__ = Ref{Ptr{SCIP_CONS}}()
cons__ = Ref{Ptr{SCIP_CONS}}(C_NULL)
@SC SCIPcreateConsBasicSOC(mscip, cons__, "", length(vars) - 1,
vars[2:end], C_NULL, C_NULL, 0.0, vars[1], 1.0, 0.0)
@SC SCIPaddCons(mscip, cons__[])
Expand All @@ -182,7 +188,7 @@ Add special-ordered-set of type 1 to problem, return cons ref.
function add_special_ordered_set_type1(mscip::ManagedSCIP, varrefs, weights)
@assert length(varrefs) == length(weights)
vars = [var(mscip, vr) for vr in varrefs]
cons__ = Ref{Ptr{SCIP_CONS}}()
cons__ = Ref{Ptr{SCIP_CONS}}(C_NULL)
@SC SCIPcreateConsBasicSOS1(mscip, cons__, "", length(vars), vars, weights)
@SC SCIPaddCons(mscip, cons__[])
return store_cons!(mscip, cons__)
Expand All @@ -198,14 +204,14 @@ Add special-ordered-set of type 2 to problem, return cons ref.
function add_special_ordered_set_type2(mscip::ManagedSCIP, varrefs, weights)
@assert length(varrefs) == length(weights)
vars = [var(mscip, vr) for vr in varrefs]
cons__ = Ref{Ptr{SCIP_CONS}}()
cons__ = Ref{Ptr{SCIP_CONS}}(C_NULL)
@SC SCIPcreateConsBasicSOS2(mscip, cons__, "", length(vars), vars, weights)
@SC SCIPaddCons(mscip, cons__[])
return store_cons!(mscip, cons__)
end

"""
Add special-ordered-set of type 2 to problem, return cons ref.
Add abspower constraint to problem, return cons ref.

lhs ≤ sign(x + a) * abs(x + a)^n + c*z ≤ rhs

Expand All @@ -221,15 +227,9 @@ Add special-ordered-set of type 2 to problem, return cons ref.
Use `(-)SCIPinfinity(scip)` for one of the bounds if not applicable.
"""
function add_abspower_constraint(mscip::ManagedSCIP, x, a, n, z, c, lhs, rhs)
cons__ = Ref{Ptr{SCIP_CONS}}()
cons__ = Ref{Ptr{SCIP_CONS}}(C_NULL)
@SC SCIPcreateConsBasicAbspower(
mscip, cons__, "", var(mscip, x), var(mscip, z), n, a, c, lhs, rhs)
@SC SCIPaddCons(mscip, cons__[])
return store_cons!(mscip, cons__)
end

"Set generic parameter."
function set_parameter(mscip::ManagedSCIP, name::String, value)
@SC SCIPsetParam(mscip, name, Ptr{Cvoid}(value))
return nothing
end
Loading