Skip to content

Commit

Permalink
Breaking changes for MOI 0.10.0 (#114)
Browse files Browse the repository at this point in the history
odow authored Sep 7, 2021
1 parent 54ce473 commit c1ace9d
Showing 5 changed files with 208 additions and 482 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
BinaryProvider = "0.5.9"
CEnum = "0.3, 0.4"
Clp_jll = "=1.17.6, ~100.1700.600"
MathOptInterface = "0.9.6"
MathOptInterface = "0.10.0"
julia = "1"

[extras]
6 changes: 3 additions & 3 deletions bench/runbench.jl
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ function generate_moi_problem(model, At, b, c;
else
for row in 1:rows
MOI.add_constraint(model, MOI.VectorAffineFunction(
[MOI.VectorAffineTerm(1,
[MOI.VectorAffineTerm(1,
MOI.ScalarAffineTerm(A_vals[i], x[A_cols[i]])
) for i in nzrange(At, row)], [-b[row]]),
MOI.Nonpositives(1))
@@ -112,7 +112,7 @@ function time_build_and_solve(to_build, to_solve, At, b, c, scalar = true)
end
@time @timeit "opt" MOI.optimize!(to_solve)
MOI.get(to_solve, MOI.ObjectiveValue())
val = MOI.get(to_solve, MOI.SolveTime())
val = MOI.get(to_solve, MOI.SolveTimeSec())
println(val)
end

@@ -145,4 +145,4 @@ function solve_clp(seed, data; time_limit_sec=Inf)

end

solve_clp(10, RandomLP(10000, 20000, 0.01); time_limit_sec=5)
solve_clp(10, RandomLP(10000, 20000, 0.01); time_limit_sec=5)
326 changes: 135 additions & 191 deletions src/MOI_wrapper/MOI_wrapper.jl
Original file line number Diff line number Diff line change
@@ -3,7 +3,30 @@ import SparseArrays

const MOI = MathOptInterface

# Supported scalar sets
MOI.Utilities.@product_of_sets(
_LPProductOfSets,
MOI.EqualTo{T},
MOI.LessThan{T},
MOI.GreaterThan{T},
MOI.Interval{T},
)

const OptimizerCache = MOI.Utilities.GenericModel{
Float64,
MOI.Utilities.ObjectiveContainer{Float64},
MOI.Utilities.VariablesContainer{Float64},
MOI.Utilities.MatrixOfConstraints{
Float64,
MOI.Utilities.MutableSparseMatrixCSC{
Float64,
Cint,
MOI.Utilities.ZeroBasedIndexing,
},
MOI.Utilities.Hyperrectangle{Float64},
_LPProductOfSets{Float64},
},
}

const SCALAR_SETS = Union{
MOI.GreaterThan{Float64},
MOI.LessThan{Float64},
@@ -25,34 +48,37 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
Create a new Optimizer object.
Set optimizer attributes using `MOI.RawParameter` or
Set optimizer attributes using `MOI.RawOptimizerAttribute` or
`JuMP.set_optimizer_atttribute`. For a list of supported parameter names,
see `Clp.SUPPORTED_PARAMETERS`.
## Example
using JuMP, Clp
model = JuMP.Model(Clp.Optimizer)
set_optimizer_attribute(model, "LogLevel", 0)
```julia
using JuMP, Clp
model = JuMP.Model(Clp.Optimizer)
set_optimizer_attribute(model, "LogLevel", 0)
```
"""
function Optimizer(; kwargs...)
model =
new(Clp_newModel(), ClpSolve_new(), Set{String}(), false, 0.0, -1.0)
if length(kwargs) > 0
@warn("""Passing optimizer attributes as keyword arguments to
Clp.Optimizer is deprecated. Use
MOI.set(model, MOI.RawParameter("key"), value)
MOI.set(model, MOI.RawOptimizerAttribute("key"), value)
or
JuMP.set_optimizer_attribute(model, "key", value)
instead.
""")
end
for (key, value) in kwargs
MOI.set(model, MOI.RawParameter(String(key)), value)
MOI.set(model, MOI.RawOptimizerAttribute(String(key)), value)
end
finalizer(model) do m
Clp_deleteModel(m)
return ClpSolve_delete(m.solver_options)
ClpSolve_delete(m.solver_options)
return
end
return model
end
@@ -75,7 +101,7 @@ end
function MOI.empty!(model::Optimizer)
# Copy parameters from old model into new model
old_options = Dict(
key => MOI.get(model, MOI.RawParameter(key)) for
key => MOI.get(model, MOI.RawOptimizerAttribute(key)) for
key in model.options_set
)
empty!(model.options_set)
@@ -84,7 +110,7 @@ function MOI.empty!(model::Optimizer)
model.optimize_called = false
model.solve_time = 0.0
for (key, value) in old_options
MOI.set(model, MOI.RawParameter(key), value)
MOI.set(model, MOI.RawOptimizerAttribute(key), value)
end
# Work-around for maximumSeconds
Clp_setMaximumSeconds(model, model.maximumSeconds)
@@ -111,11 +137,11 @@ const SUPPORTED_PARAMETERS = (
"InfeasibleReturn",
)

function MOI.supports(::Optimizer, param::MOI.RawParameter)
function MOI.supports(::Optimizer, param::MOI.RawOptimizerAttribute)
return param.name in SUPPORTED_PARAMETERS
end

function MOI.set(model::Optimizer, param::MOI.RawParameter, value)
function MOI.set(model::Optimizer, param::MOI.RawOptimizerAttribute, value)
name = String(param.name)
push!(model.options_set, name)
if name == "PrimalTolerance"
@@ -148,7 +174,7 @@ function MOI.set(model::Optimizer, param::MOI.RawParameter, value)
return
end

function MOI.get(model::Optimizer, param::MOI.RawParameter)
function MOI.get(model::Optimizer, param::MOI.RawOptimizerAttribute)
name = String(param.name)
if name == "PrimalTolerance"
return Clp_primalTolerance(model)
@@ -175,21 +201,19 @@ function MOI.get(model::Optimizer, param::MOI.RawParameter)
return ClpSolve_getSolveType(model.solver_options)
elseif name == "InfeasibleReturn"
return ClpSolve_infeasibleReturn(model.solver_options)
else
throw(MOI.UnsupportedAttribute(param))
end
return throw(MOI.UnsupportedAttribute(param))
end

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

function MOI.set(model::Optimizer, ::MOI.Silent, value::Bool)
push!(model.options_set, "LogLevel")
Clp_setLogLevel(model, value ? 0 : 1)
return
end

function MOI.get(model::Optimizer, ::MOI.Silent)
return Clp_logLevel(model) == 0
end
MOI.get(model::Optimizer, ::MOI.Silent) = Clp_logLevel(model) == 0

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

@@ -215,15 +239,7 @@ MOI.supports(::Optimizer, ::MOI.NumberOfThreads) = false

function MOI.supports_constraint(
::Optimizer,
::Type{MOI.ScalarAffineFunction{Float64}},
::Type{<:SCALAR_SETS},
)
return true
end

function MOI.supports_constraint(
::Optimizer,
::Type{MOI.SingleVariable},
::Type{<:Union{MOI.VariableIndex,MOI.ScalarAffineFunction{Float64}}},
::Type{<:SCALAR_SETS},
)
return true
@@ -242,149 +258,73 @@ end
# `copy_to` function
# =======================

function _add_bounds(::Vector{Float64}, ub, i, s::MOI.LessThan{Float64})
return ub[i] = s.upper
end
function _add_bounds(lb, ::Vector{Float64}, i, s::MOI.GreaterThan{Float64})
return lb[i] = s.lower
end
function _add_bounds(lb, ub, i, s::MOI.EqualTo{Float64})
return lb[i], ub[i] = s.value, s.value
end
function _add_bounds(lb, ub, i, s::MOI.Interval{Float64})
return lb[i], ub[i] = s.lower, s.upper
end

function _extract_bound_data(src, mapping, lb, ub, ::Type{S}) where {S}
for con_index in
MOI.get(src, MOI.ListOfConstraintIndices{MOI.SingleVariable,S}())
f = MOI.get(src, MOI.ConstraintFunction(), con_index)
s = MOI.get(src, MOI.ConstraintSet(), con_index)
column = mapping.varmap[f.variable].value
_add_bounds(lb, ub, column, s)
mapping.conmap[con_index] =
MOI.ConstraintIndex{MOI.SingleVariable,S}(column)
end
end

function _copy_to_columns(dest::Optimizer, src, mapping)
x_src = MOI.get(src, MOI.ListOfVariableIndices())
N = Cint(length(x_src))
for i in 1:N
mapping.varmap[x_src[i]] = MOI.VariableIndex(i)
end

fobj =
MOI.get(src, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}())
c = fill(0.0, N)
for term in fobj.terms
i = mapping.varmap[term.variable_index].value
c[i] += term.coefficient
function _index_map(
src::OptimizerCache,
index_map,
::Type{F},
::Type{S},
) where {F,S}
for ci in MOI.get(src, MOI.ListOfConstraintIndices{F,S}())
row = MOI.Utilities.rows(src.constraints, ci)
index_map[ci] = MOI.ConstraintIndex{F,S}(row)
end
# Clp seems to negates the objective offset
Clp_setObjectiveOffset(dest, -fobj.constant)
return N, c
end

_bounds(s::MOI.GreaterThan{Float64}) = (s.lower, Inf)
_bounds(s::MOI.LessThan{Float64}) = (-Inf, s.upper)
_bounds(s::MOI.EqualTo{Float64}) = (s.value, s.value)
_bounds(s::MOI.Interval{Float64}) = (s.lower, s.upper)

function add_sizehint!(vec, n)
len = length(vec)
return sizehint!(vec, len + n)
return
end

function _extract_row_data(src, mapping, lb, ub, I, J, V, ::Type{S}) where {S}
row = length(I) == 0 ? 1 : I[end] + 1
list = MOI.get(
src,
MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{Float64},S}(),
)
add_sizehint!(lb, length(list))
add_sizehint!(ub, length(list))
n_terms = 0
fs = Array{MOI.ScalarAffineFunction{Float64}}(undef, length(list))
for (i, c_index) in enumerate(list)
f = MOI.get(src, MOI.ConstraintFunction(), c_index)
fs[i] = f
l, u = _bounds(MOI.get(src, MOI.ConstraintSet(), c_index))
push!(lb, l - f.constant)
push!(ub, u - f.constant)
n_terms += length(f.terms)
end
add_sizehint!(I, n_terms)
add_sizehint!(J, n_terms)
add_sizehint!(V, n_terms)
for (i, c_index) in enumerate(list)
f = fs[i]#MOI.get(src, MOI.ConstraintFunction(), c_index)
for term in f.terms
push!(I, row)
push!(J, Cint(mapping.varmap[term.variable_index].value))
push!(V, term.coefficient)
end
mapping.conmap[c_index] =
MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},S}(row)
row += 1
function _index_map(
src::OptimizerCache,
index_map,
F::Type{MOI.VariableIndex},
::Type{S},
) where {S}
for ci in MOI.get(src, MOI.ListOfConstraintIndices{F,S}())
col = index_map[MOI.VariableIndex(ci.value)].value
index_map[ci] = MOI.ConstraintIndex{F,S}(col)
end
return
end

function test_data(src, dest)
for (F, S) in MOI.get(src, MOI.ListOfConstraints())
if !MOI.supports_constraint(dest, F, S)
throw(
MOI.UnsupportedConstraint{F,S}(
"Clp.Optimizer does not support constraints of type $F-in-$S.",
),
)
end
"""
_index_map(src::OptimizerCache)
Create an `IndexMap` mapping the variables and constraints in `OptimizerCache`
to their corresponding 1-based columns and rows.
"""
function _index_map(src::OptimizerCache)
index_map = MOI.IndexMap()
for (i, x) in enumerate(MOI.get(src, MOI.ListOfVariableIndices()))
index_map[x] = MOI.VariableIndex(i)
end
fobj_type = MOI.get(src, MOI.ObjectiveFunctionType())
if !MOI.supports(dest, MOI.ObjectiveFunction{fobj_type}())
throw(MOI.UnsupportedAttribute(MOI.ObjectiveFunction(fobj_type)))
for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent())
_index_map(src, index_map, F, S)
end
return index_map
end

function MOI.copy_to(
dest::Optimizer,
src::MOI.ModelLike;
copy_names::Bool = false,
)
function MOI.copy_to(dest::Optimizer, src::OptimizerCache)
@assert MOI.is_empty(dest)
test_data(src, dest)

mapping = MOI.Utilities.IndexMap()
N, c = _copy_to_columns(dest, src, mapping)
cl, cu = fill(-Inf, N), fill(Inf, N)
rl, ru, I, J, V = Float64[], Float64[], Cint[], Cint[], Float64[]

_extract_bound_data(src, mapping, cl, cu, MOI.GreaterThan{Float64})
_extract_row_data(src, mapping, rl, ru, I, J, V, MOI.GreaterThan{Float64})
_extract_bound_data(src, mapping, cl, cu, MOI.LessThan{Float64})
_extract_row_data(src, mapping, rl, ru, I, J, V, MOI.LessThan{Float64})
_extract_bound_data(src, mapping, cl, cu, MOI.EqualTo{Float64})
_extract_row_data(src, mapping, rl, ru, I, J, V, MOI.EqualTo{Float64})
_extract_bound_data(src, mapping, cl, cu, MOI.Interval{Float64})
_extract_row_data(src, mapping, rl, ru, I, J, V, MOI.Interval{Float64})

M = Cint(length(rl))
A = SparseArrays.sparse(I, J, V, M, N)
A = src.constraints.coefficients
row_bounds = src.constraints.constants
obj =
MOI.get(src, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}())
c = zeros(A.n)
for term in obj.terms
c[term.variable.value] += term.coefficient
end
Clp_setObjectiveOffset(dest, -obj.constant)
Clp_loadProblem(
dest,
A.n,
A.m,
A.colptr .- Cint(1),
A.rowval .- Cint(1),
A.colptr,
A.rowval,
A.nzval,
cl,
cu,
src.variables.lower,
src.variables.upper,
c,
rl,
ru,
row_bounds.lower,
row_bounds.upper,
)

sense = MOI.get(src, MOI.ObjectiveSense())
if sense == MOI.MIN_SENSE
Clp_setObjSense(dest, 1)
@@ -394,7 +334,28 @@ function MOI.copy_to(
@assert sense == MOI.FEASIBILITY_SENSE
Clp_setObjSense(dest, 0)
end
return mapping
return _index_map(src)
end

function MOI.copy_to(
dest::Optimizer,
src::MOI.Utilities.UniversalFallback{OptimizerCache},
)
return MOI.copy_to(dest, src.model)
end

function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike)
cache = OptimizerCache()
src_cache = MOI.copy_to(cache, src)
cache_dest = MOI.copy_to(dest, cache)
index_map = MOI.IndexMap()
for (src_x, cache_x) in src_cache.var_map
index_map[src_x] = cache_dest[cache_x]
end
for (src_ci, cache_ci) in src_cache.con_map
index_map[src_ci] = cache_dest[cache_ci]
end
return index_map
end

# ===============================
@@ -409,13 +370,9 @@ function MOI.optimize!(model::Optimizer)
return
end

function MOI.get(model::Optimizer, ::MOI.SolveTime)
return model.solve_time
end
MOI.get(model::Optimizer, ::MOI.SolveTimeSec) = model.solve_time

function MOI.get(model::Optimizer, ::MOI.NumberOfVariables)
return Clp_getNumCols(model)
end
MOI.get(model::Optimizer, ::MOI.NumberOfVariables) = Clp_getNumCols(model)

function MOI.get(model::Optimizer, attr::MOI.ObjectiveValue)
MOI.check_result_index_bounds(model, attr)
@@ -436,9 +393,8 @@ function MOI.get(model::Optimizer, ::MOI.TerminationStatus)
elseif st == 3
# No more granular information that "some limit is reached"
return MOI.OTHER_LIMIT
else
return MOI.OTHER_ERROR
end
return MOI.OTHER_ERROR
end

function MOI.get(model::Optimizer, ::MOI.RawStatusString)
@@ -454,10 +410,9 @@ function MOI.get(model::Optimizer, ::MOI.RawStatusString)
return "2 - dual infeasible"
elseif st == 3
return "3 - stopped on iterations etc"
elseif st == 4
return "4 - stopped due to errors"
else
error("Expected integer in [0, 4] but got $st")
@assert st == 4
return "4 - stopped due to errors"
end
end

@@ -475,27 +430,25 @@ function MOI.get(model::Optimizer, ::MOI.ResultCount)
end

function MOI.get(model::Optimizer, attr::MOI.PrimalStatus)
if attr.N != 1
if attr.result_index != 1
return MOI.NO_SOLUTION
elseif Clp_isProvenDualInfeasible(model) != 0
return MOI.INFEASIBILITY_CERTIFICATE
elseif Clp_primalFeasible(model) != 0
return MOI.FEASIBLE_POINT
else
return MOI.UNKNOWN_RESULT_STATUS
end
return MOI.UNKNOWN_RESULT_STATUS
end

function MOI.get(model::Optimizer, attr::MOI.DualStatus)
if attr.N != 1
if attr.result_index != 1
return MOI.NO_SOLUTION
elseif Clp_isProvenPrimalInfeasible(model) != 0
return MOI.INFEASIBILITY_CERTIFICATE
elseif Clp_dualFeasible(model) != 0
return MOI.FEASIBLE_POINT
else
return MOI.UNKNOWN_RESULT_STATUS
end
return MOI.UNKNOWN_RESULT_STATUS
end

# ===================
@@ -540,9 +493,8 @@ function MOI.get(
Clp_getNumCols(model),
x.value,
)
else
error("Primal solution not available")
end
return error("Primal solution not available")
end

function MOI.get(
@@ -569,9 +521,8 @@ function MOI.get(
Clp_getNumCols(model),
col_indices,
)
else
error("Primal solution not available")
end
return error("Primal solution not available")
end

# TODO: What happens if model is unbounded / infeasible?
@@ -593,7 +544,7 @@ end
function MOI.get(
model::Optimizer,
attr::MOI.ConstraintPrimal,
c::MOI.ConstraintIndex{MOI.SingleVariable,<:SCALAR_SETS},
c::MOI.ConstraintIndex{MOI.VariableIndex,<:SCALAR_SETS},
)
MOI.check_result_index_bounds(model, attr)
return MOI.get(model, MOI.VariablePrimal(), MOI.VariableIndex(c.value))
@@ -652,15 +603,14 @@ function MOI.get(
c.value;
own = true,
)
else
error("Dual solution not available")
end
return error("Dual solution not available")
end

function MOI.get(
model::Optimizer,
attr::MOI.ConstraintDual,
c::MOI.ConstraintIndex{MOI.SingleVariable,MOI.LessThan{Float64}},
c::MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{Float64}},
)
MOI.check_result_index_bounds(model, attr)
if MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
@@ -678,7 +628,7 @@ end
function MOI.get(
model::Optimizer,
attr::MOI.ConstraintDual,
c::MOI.ConstraintIndex{MOI.SingleVariable,MOI.GreaterThan{Float64}},
c::MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}},
)
MOI.check_result_index_bounds(model, attr)
if MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
@@ -697,7 +647,7 @@ function MOI.get(
model::Optimizer,
attr::MOI.ConstraintDual,
c::MOI.ConstraintIndex{
MOI.SingleVariable,
MOI.VariableIndex,
<:Union{MOI.Interval{Float64},MOI.EqualTo{Float64}},
},
)
@@ -752,21 +702,15 @@ function MOI.get(
status = _CLP_BASIS_STATUS[code]
if status == MOI.NONBASIC_AT_UPPER || status == MOI.NONBASIC_AT_LOWER
return _nonbasic_status(status, S)
else
return status
end
return status
end

function MOI.get(
model::Optimizer,
::MOI.ConstraintBasisStatus,
c::MOI.ConstraintIndex{MOI.SingleVariable,S},
) where {S}
code = Clp_getColumnStatus(model, c.value - 1)
status = _CLP_BASIS_STATUS[code]
if status == MOI.NONBASIC_AT_UPPER || status == MOI.NONBASIC_AT_LOWER
return _nonbasic_status(status, S)
else
return status
end
::MOI.VariableBasisStatus,
vi::MOI.VariableIndex,
)
code = Clp_getColumnStatus(model, vi.value - 1)
return _CLP_BASIS_STATUS[code]
end
345 changes: 69 additions & 276 deletions test/MOI_wrapper.jl
Original file line number Diff line number Diff line change
@@ -6,125 +6,103 @@ import Clp

const MOI = MathOptInterface

const OPTIMIZER = Clp.Optimizer()
MOI.set(OPTIMIZER, MOI.Silent(), true)

const CACHED = MOI.Utilities.CachingOptimizer(
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
OPTIMIZER,
)

const BRIDGED = MOI.Bridges.full_bridge_optimizer(CACHED, Float64)

const CONFIG = MOI.Test.TestConfig(dual_objective_value = false, basis = true)
function runtests()
for name in names(@__MODULE__; all = true)
if startswith("$(name)", "test_")
@testset "$(name)" begin
getfield(@__MODULE__, name)()
end
end
end
end

function test_SolverName()
@test MOI.get(OPTIMIZER, MOI.SolverName()) == "Clp"
@test MOI.get(Clp.Optimizer(), MOI.SolverName()) == "Clp"
return
end

function test_supports_default_copy_to()
@test !MOI.Utilities.supports_allocate_load(OPTIMIZER, false)
@test !MOI.Utilities.supports_allocate_load(OPTIMIZER, true)
@test !MOI.Utilities.supports_default_copy_to(OPTIMIZER, false)
@test !MOI.Utilities.supports_default_copy_to(OPTIMIZER, true)
@test !MOI.supports_incremental_interface(Clp.Optimizer())
return
end

function test_basicconstraint()
return MOI.Test.basic_constraint_tests(CACHED, CONFIG)
end

function test_unittest()
return MOI.Test.unittest(
BRIDGED,
CONFIG,
[
# Not supported by upstream.
"number_threads",

# Tests that require integer variables
"solve_integer_edge_cases",
"solve_zero_one_with_bounds_1",
"solve_zero_one_with_bounds_2",
"solve_zero_one_with_bounds_3",
"solve_objbound_edge_cases",

# Tests that require quadratic objective / constraints
"solve_qcp_edge_cases",
"solve_qp_edge_cases",
"delete_soc_variables",
function test_runtests()
model = MOI.Bridges.full_bridge_optimizer(
MOI.Utilities.CachingOptimizer(
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
Clp.Optimizer(),
),
Float64,
)
MOI.set(model, MOI.Silent(), true)
MOI.Test.runtests(
model,
MOI.Test.Config(
exclude = Any[MOI.DualObjectiveValue, MOI.ObjectiveBound],
),
exclude = [
# TODO(odow): bug in Clp.jl
"test_model_copy_to_UnsupportedAttribute",
"test_model_ModelFilter_AbstractConstraintAttribute",
# Unable to prove infeasibility
"test_conic_NormInfinityCone_INFEASIBLE",
"test_conic_NormOneCone_INFEASIBLE",
],
)
end

function test_contlinear()
return MOI.Test.contlineartest(BRIDGED, CONFIG, [
# MOI.VariablePrimalStart not supported.
"partial_start",
])
end

function test_nametest()
return MOI.Test.nametest(BRIDGED)
end

function test_validtest()
return MOI.Test.validtest(BRIDGED)
end

function test_emptytest()
return MOI.Test.emptytest(BRIDGED)
return
end

function test_Nonexistant_unbounded_ray()
MOI.empty!(BRIDGED)
x = MOI.add_variables(BRIDGED, 5)
model = MOI.Utilities.CachingOptimizer(
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
Clp.Optimizer(),
)
MOI.set(model, MOI.Silent(), true)
x = MOI.add_variables(model, 5)
MOI.set(
BRIDGED,
model,
MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(1.0, x), 0.0),
)
MOI.set(BRIDGED, MOI.ObjectiveSense(), MOI.MAX_SENSE)
MOI.optimize!(BRIDGED)
status = MOI.get(BRIDGED, MOI.TerminationStatus())
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
MOI.optimize!(model)
status = MOI.get(model, MOI.TerminationStatus())
@test status == MOI.DUAL_INFEASIBLE
return
end

function test_RawParameter()
function test_RawOptimizerAttribute()
model = Clp.Optimizer()
MOI.set(model, MOI.RawParameter("LogLevel"), 1)
@test MOI.get(model, MOI.RawParameter("LogLevel")) == 1
@test MOI.get(model, MOI.RawParameter(:LogLevel)) == 1
MOI.set(model, MOI.RawParameter(:LogLevel), 2)
@test MOI.get(model, MOI.RawParameter("LogLevel")) == 2
@test MOI.get(model, MOI.RawParameter(:LogLevel)) == 2
MOI.set(model, MOI.RawOptimizerAttribute("LogLevel"), 1)
@test MOI.get(model, MOI.RawOptimizerAttribute("LogLevel")) == 1
MOI.set(model, MOI.RawOptimizerAttribute("LogLevel"), 2)
@test MOI.get(model, MOI.RawOptimizerAttribute("LogLevel")) == 2

MOI.set(model, MOI.RawParameter("SolveType"), 1)
@test MOI.get(model, MOI.RawParameter("SolveType")) == 1
@test MOI.get(model, MOI.RawParameter(:SolveType)) == 1
MOI.set(model, MOI.RawParameter("SolveType"), 4)
@test MOI.get(model, MOI.RawParameter("SolveType")) == 4
@test MOI.get(model, MOI.RawParameter(:SolveType)) == 4
MOI.set(model, MOI.RawOptimizerAttribute("SolveType"), 1)
@test MOI.get(model, MOI.RawOptimizerAttribute("SolveType")) == 1
MOI.set(model, MOI.RawOptimizerAttribute("SolveType"), 4)
@test MOI.get(model, MOI.RawOptimizerAttribute("SolveType")) == 4

MOI.set(model, MOI.RawParameter("PresolveType"), 1)
@test MOI.get(model, MOI.RawParameter("PresolveType")) == 1
@test MOI.get(model, MOI.RawParameter(:PresolveType)) == 1
MOI.set(model, MOI.RawParameter("PresolveType"), 0)
@test MOI.get(model, MOI.RawParameter("PresolveType")) == 0
@test MOI.get(model, MOI.RawParameter(:PresolveType)) == 0
MOI.set(model, MOI.RawOptimizerAttribute("PresolveType"), 1)
@test MOI.get(model, MOI.RawOptimizerAttribute("PresolveType")) == 1
MOI.set(model, MOI.RawOptimizerAttribute("PresolveType"), 0)
@test MOI.get(model, MOI.RawOptimizerAttribute("PresolveType")) == 0
return
end

function test_All_parameters()
model = Clp.Optimizer()
param = MOI.RawParameter("NotAnOption")
param = MOI.RawOptimizerAttribute("NotAnOption")
@test !MOI.supports(model, param)
@test_throws MOI.UnsupportedAttribute(param) MOI.get(model, param)
@test_throws MOI.UnsupportedAttribute(param) MOI.set(model, param, false)
for key in Clp.SUPPORTED_PARAMETERS
@test MOI.supports(model, MOI.RawParameter(key))
value = MOI.get(model, MOI.RawParameter(key))
MOI.set(model, MOI.RawParameter(key), value)
@test MOI.get(model, MOI.RawParameter(key)) == value
@test MOI.supports(model, MOI.RawOptimizerAttribute(key))
value = MOI.get(model, MOI.RawOptimizerAttribute(key))
MOI.set(model, MOI.RawOptimizerAttribute(key), value)
@test MOI.get(model, MOI.RawOptimizerAttribute(key)) == value
end
return
end

function test_copy_to_bug()
@@ -140,6 +118,7 @@ function test_copy_to_bug()
clp = Clp.Optimizer()
index_map = MOI.copy_to(clp, model)
@test index_map[con[1]] != index_map[con[2]]
return
end

function test_options_after_empty!()
@@ -149,195 +128,9 @@ function test_options_after_empty!()
@test MOI.get(model, MOI.Silent()) == true
MOI.empty!(model)
@test MOI.get(model, MOI.Silent()) == true
end

function test_farkas_dual_min()
MOI.empty!(BRIDGED)
model = BRIDGED
MOI.set(model, MOI.Silent(), true)
x = MOI.add_variables(model, 2)
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
MOI.set(
model,
MOI.ObjectiveFunction{MOI.SingleVariable}(),
MOI.SingleVariable(x[1]),
)
clb =
MOI.add_constraint.(model, MOI.SingleVariable.(x), MOI.GreaterThan(0.0))
c = MOI.add_constraint(
model,
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], x), 0.0),
MOI.LessThan(-1.0),
)
MOI.optimize!(model)
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
@test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb)
c_dual = MOI.get(model, MOI.ConstraintDual(), c)
@show clb_dual, c_dual
@test clb_dual[1] > 1e-6
@test clb_dual[2] > 1e-6
@test c_dual[1] < -1e-6
@test clb_dual[1] -2 * c_dual atol = 1e-6
@test clb_dual[2] -c_dual atol = 1e-6
end

function test_farkas_dual_min_interval()
MOI.empty!(BRIDGED)
model = BRIDGED
MOI.set(model, MOI.Silent(), true)
x = MOI.add_variables(model, 2)
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
MOI.set(
model,
MOI.ObjectiveFunction{MOI.SingleVariable}(),
MOI.SingleVariable(x[1]),
)
clb =
MOI.add_constraint.(
model,
MOI.SingleVariable.(x),
MOI.Interval(0.0, 10.0),
)
c = MOI.add_constraint(
model,
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], x), 0.0),
MOI.LessThan(-1.0),
)
MOI.optimize!(model)
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
@test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb)
c_dual = MOI.get(model, MOI.ConstraintDual(), c)
@show clb_dual, c_dual
@test clb_dual[1] > 1e-6
@test clb_dual[2] > 1e-6
@test c_dual[1] < -1e-6
@test clb_dual[1] -2 * c_dual atol = 1e-6
@test clb_dual[2] -c_dual atol = 1e-6
end

function test_farkas_dual_min_equalto()
MOI.empty!(BRIDGED)
model = BRIDGED
MOI.set(model, MOI.Silent(), true)
x = MOI.add_variables(model, 2)
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
MOI.set(
model,
MOI.ObjectiveFunction{MOI.SingleVariable}(),
MOI.SingleVariable(x[1]),
)
clb = MOI.add_constraint.(model, MOI.SingleVariable.(x), MOI.EqualTo(0.0))
c = MOI.add_constraint(
model,
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], x), 0.0),
MOI.LessThan(-1.0),
)
MOI.optimize!(model)
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
@test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb)
c_dual = MOI.get(model, MOI.ConstraintDual(), c)
@show clb_dual, c_dual
@test clb_dual[1] > 1e-6
@test clb_dual[2] > 1e-6
@test c_dual[1] < -1e-6
@test clb_dual[1] -2 * c_dual atol = 1e-6
@test clb_dual[2] -c_dual atol = 1e-6
end

function test_farkas_dual_min_ii()
MOI.empty!(BRIDGED)
model = BRIDGED
MOI.set(model, MOI.Silent(), true)
x = MOI.add_variables(model, 2)
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
MOI.set(
model,
MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(-1.0, x[1])], 0.0),
)
clb = MOI.add_constraint.(model, MOI.SingleVariable.(x), MOI.LessThan(0.0))
c = MOI.add_constraint(
model,
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([-2.0, -1.0], x), 0.0),
MOI.LessThan(-1.0),
)
MOI.optimize!(model)
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
@test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb)
c_dual = MOI.get(model, MOI.ConstraintDual(), c)
@show clb_dual, c_dual
@test clb_dual[1] < -1e-6
@test clb_dual[2] < -1e-6
@test c_dual[1] < -1e-6
@test clb_dual[1] 2 * c_dual atol = 1e-6
@test clb_dual[2] c_dual atol = 1e-6
end

function test_farkas_dual_max()
MOI.empty!(BRIDGED)
model = BRIDGED
MOI.set(model, MOI.Silent(), true)
x = MOI.add_variables(model, 2)
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
MOI.set(
model,
MOI.ObjectiveFunction{MOI.SingleVariable}(),
MOI.SingleVariable(x[1]),
)
clb =
MOI.add_constraint.(model, MOI.SingleVariable.(x), MOI.GreaterThan(0.0))
c = MOI.add_constraint(
model,
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], x), 0.0),
MOI.LessThan(-1.0),
)
MOI.optimize!(model)
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
@test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb)
c_dual = MOI.get(model, MOI.ConstraintDual(), c)
@show clb_dual, c_dual
@test clb_dual[1] > 1e-6
@test clb_dual[2] > 1e-6
@test c_dual[1] < -1e-6
@test clb_dual[1] -2 * c_dual atol = 1e-6
@test clb_dual[2] -c_dual atol = 1e-6
end

function test_farkas_dual_max_ii()
MOI.empty!(BRIDGED)
model = BRIDGED
MOI.set(model, MOI.Silent(), true)
x = MOI.add_variables(model, 2)
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
MOI.set(
model,
MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(-1.0, x[1])], 0.0),
)
clb = MOI.add_constraint.(model, MOI.SingleVariable.(x), MOI.LessThan(0.0))
c = MOI.add_constraint(
model,
MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([-2.0, -1.0], x), 0.0),
MOI.LessThan(-1.0),
)
MOI.optimize!(model)
@test MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
@test MOI.get(model, MOI.DualStatus()) == MOI.INFEASIBILITY_CERTIFICATE
clb_dual = MOI.get.(model, MOI.ConstraintDual(), clb)
c_dual = MOI.get(model, MOI.ConstraintDual(), c)
@show clb_dual, c_dual
@test clb_dual[1] < -1e-6
@test clb_dual[2] < -1e-6
@test c_dual[1] < -1e-6
@test clb_dual[1] 2 * c_dual atol = 1e-6
@test clb_dual[2] c_dual atol = 1e-6
return
end

end # module TestMOIWrapper

runtests(TestMOIWrapper)
TestMOIWrapper.runtests()
11 changes: 0 additions & 11 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
using Test

function runtests(mod)
for name in names(mod; all = true)
if !startswith("$(name)", "test_")
continue
end
@testset "$(name)" begin
getfield(mod, name)()
end
end
end

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

0 comments on commit c1ace9d

Please sign in to comment.