Skip to content

Commit

Permalink
Improve test coverage (#299)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Nov 25, 2024
1 parent 48a023e commit 69de84d
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 35 deletions.
60 changes: 25 additions & 35 deletions src/MOI_wrapper/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -481,49 +481,39 @@ function MOI.get(optimizer::Optimizer, ::ADMMIterations)
return optimizer.sol.iterations
end

# Implements getter for result value and statuses
# SCS returns one of the following integers:
# -7 SCS_INFEASIBLE_INACCURATE
# -6 SCS_UNBOUNDED_INACCURATE
# -5 SCS_SIGINT
# -4 SCS_FAILED
# -3 SCS_INDETERMINATE
# -2 SCS_INFEASIBLE : primal infeasible, dual unbounded
# -1 SCS_UNBOUNDED : primal unbounded, dual infeasible
# 0 SCS_UNFINISHED : never returned, used as placeholder
# 1 SCS_SOLVED
# 2 SCS_SOLVED_INACCURATE
const _TerminationStatus = Dict(
# -7 SCS_INFEASIBLE_INACCURATE
-7 => MOI.ALMOST_INFEASIBLE,
# -6 SCS_UNBOUNDED_INACCURATE
-6 => MOI.ALMOST_DUAL_INFEASIBLE,
# -5 SCS_SIGINT
-5 => MOI.INTERRUPTED,
# -4 SCS_FAILED
-4 => MOI.INVALID_MODEL,
# -3 SCS_INDETERMINATE
-3 => MOI.SLOW_PROGRESS,
# -2 SCS_INFEASIBLE : primal infeasible, dual unbounded
-2 => MOI.INFEASIBLE,
# -1 SCS_UNBOUNDED : primal unbounded, dual infeasible
-1 => MOI.DUAL_INFEASIBLE,
# 0 SCS_UNFINISHED : never returned, used as placeholder
0 => MOI.OPTIMIZE_NOT_CALLED,
# 1 SCS_SOLVED
1 => MOI.OPTIMAL,
# 2 SCS_SOLVED_INACCURATE
2 => MOI.ALMOST_OPTIMAL,
)

function MOI.get(optimizer::Optimizer, ::MOI.TerminationStatus)
s = optimizer.sol.ret_val
@assert -7 <= s <= 2
if s == -7
return MOI.ALMOST_INFEASIBLE
elseif s == -6
return MOI.ALMOST_DUAL_INFEASIBLE
elseif s == 2
if optimizer.sol.ret_val == 2
if occursin("reached time_limit_secs", optimizer.sol.raw_status)
return MOI.TIME_LIMIT
elseif occursin("reached max_iters", optimizer.sol.raw_status)
return MOI.ITERATION_LIMIT
else
return MOI.ALMOST_OPTIMAL
end
elseif s == -5
return MOI.INTERRUPTED
elseif s == -4
return MOI.INVALID_MODEL
elseif s == -3
return MOI.SLOW_PROGRESS
elseif s == -2
return MOI.INFEASIBLE
elseif s == -1
return MOI.DUAL_INFEASIBLE
elseif s == 1
return MOI.OPTIMAL
else
@assert s == 0
return MOI.OPTIMIZE_NOT_CALLED
end
return _TerminationStatus[optimizer.sol.ret_val]
end

function MOI.get(optimizer::Optimizer, attr::MOI.ObjectiveValue)
Expand Down
90 changes: 90 additions & 0 deletions test/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,96 @@ function test_attribute_TimeLimitSec()
return
end

function test_SolveTimeSec()
model = MOI.Utilities.Model{Float64}()
x = MOI.add_variables(model, 3)
f = MOI.Utilities.operate(vcat, Float64, 1.0 .* x...)
MOI.add_constraint(model, f, MOI.Nonnegatives(3))
scs = SCS.Optimizer()
MOI.optimize!(scs, model)
@test MOI.get(scs, MOI.SolveTimeSec()) >= 0
return
end

function test_RawStatusString()
model = MOI.Utilities.Model{Float64}()
x = MOI.add_variables(model, 3)
f = MOI.Utilities.operate(vcat, Float64, 1.0 .* x...)
MOI.add_constraint(model, f, MOI.Nonnegatives(3))
scs = SCS.Optimizer()
MOI.optimize!(scs, model)
@test MOI.get(scs, MOI.RawStatusString()) isa String
return
end

function test_ADMMIterations()
model = MOI.Utilities.Model{Float64}()
x = MOI.add_variables(model, 3)
f = MOI.Utilities.operate(vcat, Float64, 1.0 .* x...)
MOI.add_constraint(model, f, MOI.Nonnegatives(3))
MOI.add_constraint(
model,
MOI.Utilities.operate(vcat, Float64, 1.0 * x[1] + 1.0 * x[2] - 2.0),
MOI.Nonnegatives(1),
)
scs = SCS.Optimizer()
MOI.optimize!(scs, model)
attr = SCS.ADMMIterations()
@test MOI.is_set_by_optimize(attr)
@test MOI.get(scs, attr) > 0
return
end

function test_max_iters()
model = MOI.Utilities.Model{Float64}()
x = MOI.add_variables(model, 3)
f = MOI.Utilities.operate(vcat, Float64, 1.0 .* x...)
MOI.add_constraint(model, f, MOI.Nonnegatives(3))
MOI.add_constraint(
model,
MOI.Utilities.operate(vcat, Float64, 1.0 * x[1] + 1.0 * x[2] - 2.0),
MOI.Nonnegatives(1),
)
scs = SCS.Optimizer()
MOI.set(scs, MOI.RawOptimizerAttribute("max_iters"), 1)
MOI.optimize!(scs, model)
@test MOI.get(scs, MOI.TerminationStatus()) == MOI.ITERATION_LIMIT
return
end

function test_time_limit_secs()
model = MOI.Utilities.Model{Float64}()
x = MOI.add_variables(model, 3)
f = MOI.Utilities.operate(vcat, Float64, 1.0 .* x...)
MOI.add_constraint(model, f, MOI.Nonnegatives(3))
MOI.add_constraint(
model,
MOI.Utilities.operate(vcat, Float64, 1.0 * x[1] + 1.0 * x[2] - 2.0),
MOI.Nonnegatives(1),
)
scs = SCS.Optimizer()
# The time limit has to be > 0 for SCS to respect it.
MOI.set(scs, MOI.RawOptimizerAttribute("time_limit_secs"), 1e-14)
MOI.optimize!(scs, model)
@test MOI.get(scs, MOI.TerminationStatus()) == MOI.TIME_LIMIT
return
end

function test_unsupported_objective()
model = MOI.Utilities.Model{Float64}()
x = MOI.add_variables(model, 3)
f = MOI.Utilities.operate(vcat, Float64, 1.0 .* x...)
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
attr = MOI.ObjectiveFunction{MOI.VariableIndex}()
MOI.set(model, attr, x[1])
scs = SCS.Optimizer()
@test_throws(
MOI.UnsupportedAttribute{typeof(attr)},
MOI.optimize!(scs, model),
)
return
end

end # module

TestSCS.runtests()
2 changes: 2 additions & 0 deletions test/test_gpu.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ include("test_problems.jl")

@testset "GpuIndirectSolver" begin
@test SCS.is_available(SCS.GpuIndirectSolver)
version = SCS.scs_version(SCS.GpuIndirectSolver)
@test VersionNumber(version) >= v"3.2.0"
feasible_basic_problems(SCS.GpuIndirectSolver)
end

0 comments on commit 69de84d

Please sign in to comment.