Skip to content

Commit

Permalink
implement more methods to support contlineartest (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
rschwarz committed Dec 21, 2018
1 parent 2f8cd78 commit de1a030
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 1 deletion.
152 changes: 152 additions & 0 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,158 @@
using MathOptInterface
const MOI = MathOptInterface
const MOIU = MOI.Utilities

const VI = MOI.VariableIndex
const CI = MOI.ConstraintIndex

const ConsMap = Dict{Tuple{DataType, DataType}, Vector{Int}}

mutable struct Optimizer <: MOI.AbstractOptimizer
mscip::ManagedSCIP
cons::ConsMap

Optimizer() = new(ManagedSCIP(), ConsMap())
end


## convenience methods (not part of MOI)

"Returns pointer to SCIP instance"
get_scip(o::Optimizer) = get_scip(o.mscip)

"Returns pointer to SCIP variable"
get_var(o::Optimizer, v::VI) = get_var(o.mscip, v.value)

"Returns pointer to SCIP constraint"
get_cons(o::Optimizer, v::CI{F,S}) where {F,S} = get_cons(o.mscip, c.value)

"Extract bounds from sets"
bounds(set::MOI.EqualTo{Float64}) = (set.value, set.value)
bounds(set::MOI.GreaterThan{Float64}) = (set.lower, nothing)
bounds(set::MOI.LessThan{Float64}) = (nothing, set.upper)
bounds(set::MOI.Interval{Float64}) = (set.lower, set.upper)

"Register constraint in mapping"
function register!(o::Optimizer, c::CI{F,S}) where {F,S}
if haskey(o.cons, (F, S))
push!(o.cons[F,S], c.value)
else
o.cons[F,S] = [c.value]
end
return c
end

## general queries and support

MOI.get(::Optimizer, ::MOI.SolverName) = "SCIP"

# support variable bounds and linear constraints
const SF = Union{MOI.SingleVariable,
MOI.ScalarAffineFunction{Float64}}
const SS = Union{MOI.EqualTo{Float64},
MOI.GreaterThan{Float64},
MOI.LessThan{Float64},
MOI.Interval{Float64}}
MOI.supports_constraint(o::Optimizer, ::Type{<:SF}, ::Type{<:SS}) = true

MOI.supports(::Optimizer, ::MOI.ObjectiveSense) = true
MOI.supports(::Optimizer, ::MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}) = true

MOIU.supports_allocate_load(o::Optimizer, copy_names::Bool) = !copy_names


## model creation, query and modification

function MOI.is_empty(o::Optimizer)
length(o.mscip.vars) == 0 && length(o.mscip.conss) == 0
end

function MOI.empty!(o::Optimizer)
# free the underlying problem
finalize(o.mscip)
# create a new one
o.mscip = ManagedSCIP()
return nothing
end

function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike; kws...)
MOIU.automatic_copy_to(dest, src; kws...)
end

MOI.add_variable(o::Optimizer) = MOI.VariableIndex(add_variable(o.mscip))
MOI.add_variables(o::Optimizer, n) = [MOI.add_variable(o) for i=1:n]
MOI.get(o::Optimizer, ::MOI.NumberOfVariables) = length(o.mscip.vars)

function MOI.add_constraint(o::Optimizer, func::MOI.SingleVariable,
set::S) where S <: SS
var = get_var(o, func.variable)
lb, ub = bounds(set)
lb == nothing || @SC SCIPchgVarLb(get_scip(o), var, lb)
ub == nothing || @SC SCIPchgVarUb(get_scip(o), var, ub)
# use var index for cons index of this type
i = func.variable.value
return register!(o, CI{MOI.SingleVariable, S}(i))
end

function MOI.add_constraint(o::Optimizer, func::MOI.ScalarAffineFunction{Float64},
set::S) where {S <: SS}
scip = get_scip(o)

varidx = [t.variable_index.value for t in func.terms]
coefs = [t.coefficient for t in func.terms]

lhs, rhs = bounds(set)
lhs = lhs == nothing ? -SCIPinfinity(scip) : lhs - func.constant
rhs = rhs == nothing ? SCIPinfinity(scip) : rhs - func.constant

i = add_linear_constraint(o.mscip, varidx, coefs, lhs, rhs)
return register!(o, CI{MOI.ScalarAffineFunction{Float64}, S}(i))
end

function MOI.get(o::Optimizer, ::MOI.NumberOfConstraints{F,S}) where {F,S}
haskey(o.cons, (F, S)) ? length(o.cons[F, S]) : 0
end

function MOI.set(o::Optimizer,
::MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}},
obj::MOI.ScalarAffineFunction{Float64})
scip = get_scip(o)

# reset objective coefficient of all variables first
for v in o.mscip.vars
@SC SCIPchgVarObj(scip, v[], 0.0)
end

# set new objective coefficients
for t in obj.terms
@SC SCIPchgVarObj(scip, get_var(o, t.variable_index), t.coefficient)
end

@SC SCIPaddOrigObjoffset(scip, obj.constant - SCIPgetOrigObjoffset(scip))

return nothing
end

function MOI.set(o::Optimizer, ::MOI.ObjectiveSense,
sense::MOI.OptimizationSense)
if sense == MOI.MIN_SENSE
@SC SCIPsetObjsense(get_scip(o), SCIP_OBJSENSE_MINIMIZE)
elseif sense == MOI.MAX_SENSE
@SC SCIPsetObjsense(get_scip(o), SCIP_OBJSENSE_MAXIMIZE)
end
return nothing
end

function MOI.get(o::Optimizer, ::MOI.ObjectiveSense)
SCIPgetObjsense(get_scip(o)) == SCIP_OBJSENSE_MAXIMIZE ?
MOI.MAX_SENSE :
MOI.MIN_SENSE
end


## optimization and results

function MOI.optimize!(o::Optimizer)
@SC SCIPsolve(o.mscip.scip[])
return nothing
end
11 changes: 10 additions & 1 deletion src/managed_scip.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ function free_scip(mscip::ManagedSCIP)
@assert mscip.scip[] == C_NULL
end

"Returns pointer to SCIP instance"
get_scip(mscip::ManagedSCIP) = mscip.scip[]

"Returns pointer to SCIP variable"
get_var(mscip::ManagedSCIP, i::Int) = mscip.vars[i][]

"Returns pointer to SCIP constraint"
get_cons(mscip::ManagedSCIP, i::Int) = mscip.conss[i][]

"Add variable to problem (continuous, no bounds)"
function add_variable(mscip::ManagedSCIP)
var = Ref{Ptr{SCIP_VAR}}()
Expand All @@ -47,7 +56,7 @@ end
"Add (ranged) linear constraint to problem"
function add_linear_constraint(mscip::ManagedSCIP, varidx, coeffs, lhs, rhs)
@assert length(varidx) == length(coeffs)
vars = [mscip.vars[i][] for i in varidx]
vars = [get_var(mscip, i) for i in varidx]
cons = Ref{Ptr{SCIP_CONS}}()
@SC rc = SCIPcreateConsBasicLinear(
mscip.scip[], cons, "", length(vars), vars, coeffs, lhs, rhs)
Expand Down
29 changes: 29 additions & 0 deletions test/MOI_wrapper.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using MathOptInterface
const MOI = MathOptInterface
const MOIT = MOI.Test

const optimizer = SCIP.Optimizer()
const config = MOIT.TestConfig(duals=false, infeas_certificates=false)

@testset "MOI Continuous Linear" begin
excluded = [
"linear1",
"linear2",
"linear3",
"linear4",
"linear5",
"linear6",
"linear7",
"linear8a",
"linear8b",
"linear8c",
"linear9",
"linear10",
"linear11",
"linear12",
"linear13",
"linear14",
"linear15",
]
MOIT.contlineartest(optimizer, config, excluded)
end
4 changes: 4 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ end
@testset "managed memory" begin
include("managed_scip.jl")
end

@testset "MathOptInterface tests" begin
include("MOI_wrapper.jl")
end

0 comments on commit de1a030

Please sign in to comment.