Skip to content
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
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[compat]
CxxWrap = "0.12"
MathOptInterface = "0.10"
MathOptInterface = "1"
julia = "1.6"

[extras]
Expand Down
18 changes: 12 additions & 6 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
varmap::Vector{Tuple{Int, Int, Int}} # Variable Index vi -> blk, i, j
b::Vector{Cdouble}
problem::Union{Nothing, SDPAProblem}
optimized::Bool
solve_time::Float64
silent::Bool
options::Dict{Symbol, Any}
function Optimizer(; kwargs...)
optimizer = new(
zero(Cdouble), 1, Int[], Tuple{Int, Int, Int}[], Cdouble[],
nothing, NaN, false, Dict{Symbol, Any}())
nothing, false, NaN, false, Dict{Symbol, Any}())
if !isempty(kwargs)
@warn("""Passing optimizer attributes as keyword arguments to
SDPA.Optimizer is deprecated. Use
Expand Down Expand Up @@ -101,6 +102,7 @@ function MOI.empty!(optimizer::Optimizer)
empty!(optimizer.varmap)
empty!(optimizer.b)
optimizer.problem = nothing
optimizer.optimized = false
end

function MOI.supports(
Expand Down Expand Up @@ -292,11 +294,15 @@ function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike)
end

function MOI.optimize!(m::Optimizer)
start_time = time()
SDPA.initializeUpperTriangle(m.problem, false)
SDPA.initializeSolve(m.problem)
SDPA.solve(m.problem)
m.solve_time = time() - start_time
# SDPA segfault if `optimize!` is called twice
if !m.optimized
start_time = time()
SDPA.initializeUpperTriangle(m.problem, false)
SDPA.initializeSolve(m.problem)
SDPA.solve(m.problem)
m.solve_time = time() - start_time
m.optimized = true
end
end

function MOI.get(m::Optimizer, ::MOI.TerminationStatus)
Expand Down
164 changes: 95 additions & 69 deletions test/MOI_wrapper.jl
Original file line number Diff line number Diff line change
@@ -1,79 +1,105 @@
using Test
module TestSDPA

using Test
using MathOptInterface
const MOI = MathOptInterface
const MOIT = MOI.DeprecatedTest
const MOIU = MOI.Utilities
const MOIB = MOI.Bridges

import SDPA
const optimizer = SDPA.Optimizer()
MOI.set(optimizer, MOI.Silent(), true)

@testset "SolverName" begin
@test MOI.get(optimizer, MOI.SolverName()) == "SDPA"
end
const MOI = MathOptInterface

# UniversalFallback is needed for starting values, even if they are ignored by SDPA
const cache = MOIU.UniversalFallback(MOIU.Model{Float64}())
const cached = MOIU.CachingOptimizer(cache, optimizer)
const bridged = MOIB.full_bridge_optimizer(cached, Float64)
# test 1e-3 because of rsoc3 test, otherwise, 1e-5 is enough
const config = MOIT.Config(atol=1e-3, rtol=1e-3)
function runtests()
for name in names(@__MODULE__; all = true)
if startswith("$(name)", "test_")
@testset "$(name)" begin
getfield(@__MODULE__, name)()
end
end
end
return
end

@testset "Unit" begin
MOIT.unittest(bridged, config, [
# TODO(odow): FIX THIS
"solve_twice",
# `NumberOfThreads` not supported.
"number_threads",
# `TimeLimitSec` not supported.
"time_limit_sec",
# SingleVariable objective of bridged variables, will be solved by objective bridges
"solve_time",
"raw_status_string",
"solve_singlevariable_obj",
# Quadratic functions are not supported
"solve_qcp_edge_cases",
"solve_qp_edge_cases",
"solve_qp_zero_offdiag",
# Integer and ZeroOne sets are not supported
"solve_integer_edge_cases",
"solve_objbound_edge_cases",
"solve_zero_one_with_bounds_1",
"solve_zero_one_with_bounds_2",
"solve_zero_one_with_bounds_3",
# FarkasDual tests: SDPA doesn't like proving infeasibility for these...
# Expression: MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
# Evaluated: MathOptInterface.INFEASIBLE_OR_UNBOUNDED == MathOptInterface.INFEASIBLE
"solve_farkas_equalto_upper",
"solve_farkas_equalto_lower",
"solve_farkas_lessthan",
"solve_farkas_greaterthan",
"solve_farkas_interval_lower",
"solve_farkas_interval_upper",
"solve_farkas_variable_lessthan",
"solve_farkas_variable_lessthan_max",
"solve_start_soc", # RSOCtoPSDBridge seems to be incorrect for dimension-2 RSOC cone.
])
function test_solver_name()
@test MOI.get(SDPA.Optimizer(), MOI.SolverName()) == "SDPA"
end

@testset "Linear tests" begin
# See explanation in `MOI/test/Bridges/lazy_bridge_optimizer.jl`.
# This is to avoid `Variable.VectorizeBridge` which does not support
# `ConstraintSet` modification.
MOIB.remove_bridge(bridged, MOIB.Constraint.ScalarSlackBridge{Float64})
MOIT.contlineartest(bridged, config, [
# `MOI.UNKNOWN_RESULT_STATUS` instead of `MOI.INFEASIBILITY_CERTIFICATE`
"linear8a",
"linear12"
])
function test_options()
param = MOI.RawOptimizerAttribute("bad_option")
err = MOI.UnsupportedAttribute(param)
@test_throws err MOI.set(
SDPA.Optimizer(),
MOI.RawOptimizerAttribute("bad_option"),
0,
)
end
@testset "Conic tests" begin
MOIT.contconictest(bridged, config, [
"lin3", "soc3", "normone2", "norminf2",
# Missing bridges
"rootdets",
# Does not support power and exponential cone
"pow", "dualpow", "logdet", "exp", "dualexp", "relentr"])

function test_runtests()
model = MOI.Utilities.CachingOptimizer(
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
MOI.instantiate(SDPA.Optimizer, with_bridge_type = Float64),
)
# `Variable.ZerosBridge` makes dual needed by some tests fail.
MOI.Bridges.remove_bridge(
model.optimizer,
MathOptInterface.Bridges.Variable.ZerosBridge{Float64},
)
MOI.set(model, MOI.Silent(), true)
MOI.Test.runtests(
model,
MOI.Test.Config(
rtol = 1e-3,
atol = 1e-3,
exclude = Any[
MOI.ConstraintBasisStatus,
MOI.VariableBasisStatus,
MOI.ObjectiveBound,
MOI.SolverVersion,
],
),
exclude = String[
# Unable to bridge RotatedSecondOrderCone to PSD because the dimension is too small: got 2, expected >= 3.
"test_conic_SecondOrderCone_INFEASIBLE",
"test_constraint_PrimalStart_DualStart_SecondOrderCone",
# Expression: MOI.get(model, MOI.TerminationStatus()) == MOI.INFEASIBLE
# Evaluated: MathOptInterface.INFEASIBLE_OR_UNBOUNDED == MathOptInterface.INFEASIBLE
"test_conic_NormInfinityCone_INFEASIBLE",
"test_conic_NormOneCone_INFEASIBLE",
# Incorrect objective
# See https://github.com/jump-dev/MathOptInterface.jl/issues/1759
"test_unbounded_MIN_SENSE",
"test_unbounded_MIN_SENSE_offset",
"test_unbounded_MAX_SENSE",
"test_unbounded_MAX_SENSE_offset",
"test_infeasible_MAX_SENSE",
"test_infeasible_MAX_SENSE_offset",
"test_infeasible_MIN_SENSE",
"test_infeasible_MIN_SENSE_offset",
"test_infeasible_affine_MAX_SENSE",
"test_infeasible_affine_MAX_SENSE_offset",
"test_infeasible_affine_MIN_SENSE",
"test_infeasible_affine_MIN_SENSE_offset",
# TODO remove when PR merged
# See https://github.com/jump-dev/MathOptInterface.jl/pull/1769
"test_objective_ObjectiveFunction_blank",
# FIXME investigate
# Expression: isapprox(MOI.get(model, MOI.ObjectiveValue()), T(2), config)
# Evaluated: isapprox(5.999999984012059, 2.0, ...
"test_modification_delete_variables_in_a_batch",
# FIXME investigate
# Expression: isapprox(MOI.get(model, MOI.ObjectiveValue()), objective_value, config)
# Evaluated: isapprox(-2.1881334077988868e-7, 5.0, ...
"test_objective_qp_ObjectiveFunction_edge_case",
# FIXME investigate
# Expression: isapprox(MOI.get(model, MOI.ObjectiveValue()), objective_value, config)
# Evaluated: isapprox(-2.1881334077988868e-7, 5.0, ...
"test_objective_qp_ObjectiveFunction_zero_ofdiag",
# FIXME investigate
# Expression: isapprox(MOI.get(model, MOI.ConstraintPrimal(), index), solution_value, config)
# Evaluated: isapprox(2.5058846553349667e-8, 1.0, ...
"test_variable_solve_with_lowerbound",
],
)
return
end

end # module

TestSDPA.runtests()