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

MOI tests: LP unit tests and contlinear tests #571

Merged
merged 7 commits into from
Aug 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
49 changes: 34 additions & 15 deletions src/MOIwrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,10 @@ end

function MOI.get(model::Coluna.Optimizer, ::MOI.ListOfVariableIndices)
indices = Vector{MathOptInterface.VariableIndex}()
for (key,value) in model.moi_varids
for (_, value) in model.moi_varids
push!(indices, value)
end
return sort!(indices)
return sort!(indices, by = x -> x.value)
end

############################################################################################
Expand Down Expand Up @@ -286,11 +286,10 @@ end
function MOI.get(
model::Coluna.Optimizer, ::MOI.ListOfConstraintIndices{F, S}
) where {F<:MOI.SingleVariable, S}
orig_form = get_original_formulation(model.inner)
indices = MOI.ConstraintIndex{F,S}[]
for (id, var) in model.vars
if S == MathProg.convert_coluna_kind_to_moi(getperenkind(orig_form, var))
push!(indices, MOI.ConstraintIndex{F,S}(id.value))
for (id, _) in model.constrs_on_single_var_to_vars
if S == typeof(MOI.get(model, MOI.ConstraintSet(), id))
push!(indices, id)
end
end
return sort!(indices, by = x -> x.value)
Expand All @@ -302,8 +301,10 @@ function MOI.get(
orig_form = get_original_formulation(model.inner)
constrid = getid(model.constrs[index])
terms = MOI.ScalarAffineTerm{Float64}[]
for (varid, coeff) in @view getcoefmatrix(orig_form)[constrid, :]
push!(terms, MOI.ScalarAffineTerm(coeff, model.moi_varids[varid]))
coefmatrix = getcoefmatrix(orig_form)
coefmatrix.fillmode && closefillmode!(coefmatrix)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For what test did you have to do that (is it a test that does set get set) ?

If you close the fill mode, then writing operations become time consuming. Do you think we should we be able to reopen the fill mode ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests "linear1" and "linear6" threw this error:

Expression: MOI.get(model, MOI.ConstraintFunction(), c1) ≈ fx
View of a row not available in fill mode (Open an issue at https://github.com/atoptima/DynamicSparseArrays.jl if you need it).

I don't know if there's a better fix to that and I don't understand fully how fill mode works yet, but being able to reopen the fill mode seems like a good option.

for (varid, coef) in @view coefmatrix[constrid, :]
push!(terms, MOI.ScalarAffineTerm(coef, model.moi_varids[varid]))
end
return MOI.ScalarAffineFunction(terms, 0.0)
end
Expand Down Expand Up @@ -619,6 +620,8 @@ function MOI.empty!(model::Coluna.Optimizer)
model.env.varids = CleverDicts.CleverDict{MOI.VariableIndex, VarId}()
model.moi_varids = Dict{VarId, MOI.VariableIndex}()
model.constrs = Dict{MOI.ConstraintIndex, Constraint}()
model.constrs_on_single_var_to_vars = Dict{MOI.ConstraintIndex, VarId}()
model.constrs_on_single_var_to_names = Dict{MOI.ConstraintIndex, String}()
if model.default_optimizer_builder !== nothing
set_default_optimizer_builder!(model.inner, model.default_optimizer_builder)
end
Expand Down Expand Up @@ -703,18 +706,21 @@ function MOI.get(optimizer::Optimizer, ::MOI.ObjectiveValue)
return getvalue(get_ip_primal_bound(optimizer.result))
end

function MOI.get(optimizer::Optimizer, ::MOI.DualObjectiveValue)
return getvalue(get_lp_dual_bound(optimizer.result))
end

function MOI.get(optimizer::Optimizer, ::MOI.RelativeGap)
return ip_gap(optimizer.result)
end

function MOI.get(optimizer::Optimizer, ::MOI.VariablePrimal, ref::MOI.VariableIndex)
function MOI.get(optimizer::Optimizer, attr::MOI.VariablePrimal, ref::MOI.VariableIndex)
id = getid(optimizer.vars[ref]) # This gets a coluna Id{Variable}
best_primal_sol = get_best_ip_primal_sol(optimizer.result)
if best_primal_sol === nothing
@warn "Coluna did not find a primal feasible solution."
return NaN
primalsols = get_ip_primal_sols(optimizer.result)
if 1 <= attr.N <= length(primalsols)
return get(primalsols[attr.N], id, 0.0)
end
return get(best_primal_sol, id, 0.0)
return error("Invalid result index.")
end

function MOI.get(optimizer::Optimizer, ::MOI.VariablePrimal, refs::Vector{MOI.VariableIndex})
Expand Down Expand Up @@ -775,7 +781,20 @@ end
MOI.get(optimizer::Optimizer, ::MOI.NodeCount) = optimizer.env.kpis.node_count
MOI.get(optimizer::Optimizer, ::MOI.SolveTime) = optimizer.env.kpis.elapsed_optimization_time

# function MOI.get(optimizer::Optimizer, ::MOI.ConstraintDual, index::MOI.ConstraintIndex)
function MOI.get(
optimizer::Optimizer, attr::MOI.ConstraintDual,
index::MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64}}
)
dualsols = get_lp_dual_sols(optimizer.result)
if 1 <= attr.N <= length(dualsols)
return get(dualsols[attr.N], getid(optimizer.constrs[index]), 0.0)
end
return error("Invalid result index.")
end

# function MOI.get(
# optimizer::Optimizer, attr::MOI.ConstraintDual, index::MOI.ConstraintIndex{F,S}
# ) where {F<:MOI.SingleVariable,S}
# return 0.0
# end

Expand Down
66 changes: 54 additions & 12 deletions test/MathOptInterface/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const OPTIMIZER = Coluna.Optimizer()
MOI.set(OPTIMIZER, MOI.RawParameter("default_optimizer"), GLPK.Optimizer)

const CONFIG = MOIT.TestConfig(atol=1e-6, rtol=1e-6)
const CONFIG = MOIT.TestConfig(atol=1e-6, rtol=1e-6, infeas_certificates = false)


@testset "SolverName" begin
Expand Down Expand Up @@ -130,8 +130,9 @@ const UNSUPPORTED_TESTS = [
"number_threads", # TODO : support of MOI.NumberOfThreads()
"silent", # TODO : support of MOI.Silent()
"time_limit_sec", # TODO : support of MOI.TimeLimitSec()
"solve_time", # TODO : support of MOI.SolveTime()
"solve_twice" # TODO : fix
"solve_unbounded_model", # default lower bound 0
"solve_duplicate_terms_obj", # TODO: support duplicate terms
"solve_duplicate_terms_scalar_affine" # TODO: support duplicate terms
]

MathOptInterface.Test.getconstraint
Expand Down Expand Up @@ -178,6 +179,41 @@ const LP_TESTS = [
"solve_affine_lessthan"
]

const CONSTRAINTDUAL_SINGLEVAR = [
"solve_with_lowerbound",
"solve_singlevariable_obj",
"solve_constant_obj",
"solve_single_variable_dual_max",
"solve_single_variable_dual_min",
"solve_duplicate_terms_obj",
"solve_blank_obj",
"solve_with_upperbound",
"linear1",
"linear2",
"linear10b",
"linear14"
]

const MODIFY_DELETE = [
# BUG
"linear1", # modify
"linear5", # modify
"linear11", # delete
"linear14" # delete
]

const UNCOVERED_TERMINATION_STATUS = [
"linear8b", # DUAL_INFEASIBLE or INFEASIBLE_OR_UNBOUNDED required
"linear8c" # DUAL_INFEASIBLE or INFEASIBLE_OR_UNBOUNDED required
]

const SET_CONSTRAINTSET = [
# BUG
"linear4",
"linear6",
"linear7"
]

@testset "Unit Basic/MIP" begin
MOI.set(OPTIMIZER, MOI.RawParameter("params"), CL.Params(solver = ClA.SolveIpForm()))
MOIT.unittest(OPTIMIZER, CONFIG, vcat(UNSUPPORTED_TESTS, LP_TESTS, MIP_TESTS))
Expand All @@ -197,13 +233,19 @@ MOI.set(BRIDGED, MOI.RawParameter("params"), CL.Params(solver = ClA.SolveIpForm(
])
end

# @testset "Unit LP" begin
# MOI.set(OPTIMIZER, MOI.RawParameter("params"), CL.Params(solver = ClA.SolveLpForm()))
# MOIT.unittest(OPTIMIZER, CONFIG, vcat(UNSUPPORTED_TESTS, MIP_TESTS, BASIC))
# end
@testset "Unit LP" begin
MOI.set(BRIDGED, MOI.RawParameter("params"), CL.Params(solver = ClA.SolveLpForm(
update_ip_primal_solution=true, get_dual_solution=true, set_dual_bound=true
)))
MOIT.unittest(BRIDGED, CONFIG, vcat(UNSUPPORTED_TESTS, MIP_TESTS, BASIC, CONSTRAINTDUAL_SINGLEVAR))
end

# @testset "Continuous Linear" begin
# MOIT.contlineartest(OPTIMIZER, CONFIG, [
# "partial_start" # VariablePrimalStart not supported
# ])
# end
@testset "Continuous Linear" begin
MOIT.contlineartest(BRIDGED, CONFIG, vcat(
CONSTRAINTDUAL_SINGLEVAR, MODIFY_DELETE, UNCOVERED_TERMINATION_STATUS, SET_CONSTRAINTSET, [
"partial_start", # VariablePrimalStart not supported
"linear1", # TODO: support duplicate terms
"linear10" # BUG: optimize twice changing sense from max to min fails
]
))
end