Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
138 changes: 90 additions & 48 deletions src/MathOptInterface/wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -79,29 +79,42 @@ function MOI.copy_to(
idx_map[vj] = VI(j)
end
@assert j == n

# objective function
F = MOI.get(src, MOI.ObjectiveFunctionType())
if F == VI
obj = SAF{T}(MOI.get(src, MOI.ObjectiveFunction{F}()))
elseif F == SAF{T}
obj = MOI.get(src, MOI.ObjectiveFunction{F}())
else
error("function type $F not supported")
end
(Jc, Vc) = (Int[], T[])
for t in obj.terms
push!(Jc, idx_map[t.variable].value)
push!(Vc, t.coefficient)
for attr in MOI.get(src, MOI.ListOfVariableAttributesSet())
if attr == MOI.VariableName() || attr == MOI.VariablePrimalStart()
continue # It's okay to skip these for now.
end
throw(MOI.UnsupportedAttribute(attr))
end
obj_offset = obj.constant
if MOI.get(src, MOI.ObjectiveSense()) == MOI.MAX_SENSE
Vc .*= -1
obj_offset *= -1
opt.obj_sense = MOI.MIN_SENSE
Jc, Vc = Int[], T[]
obj_offset = 0.0
for attr in MOI.get(src, MOI.ListOfModelAttributesSet())
if attr == MOI.Name()
continue # It's okay to skip this for now.
elseif attr == MOI.ObjectiveSense()
opt.obj_sense = MOI.get(src, MOI.ObjectiveSense())
elseif attr isa MOI.ObjectiveFunction
F = MOI.get(src, MOI.ObjectiveFunctionType())
obj = if F == VI
SAF{T}(MOI.get(src, MOI.ObjectiveFunction{F}()))
elseif F == SAF{T}
MOI.get(src, MOI.ObjectiveFunction{F}())
else
error("function type $F not supported")
end
for t in obj.terms
push!(Jc, idx_map[t.variable].value)
push!(Vc, t.coefficient)
end
obj_offset = obj.constant
if opt.obj_sense == MOI.MAX_SENSE
Vc .*= -1
obj_offset *= -1
end
end
throw(MOI.UnsupportedAttribute(attr))
end
opt.obj_sense = MOI.get(src, MOI.ObjectiveSense())
model_c = Vector(sparsevec(Jc, Vc, n))

# constraints
get_src_cons(F, S) = MOI.get(src, MOI.ListOfConstraintIndices{F, S}())
get_con_fun(con_idx) = MOI.get(src, MOI.ConstraintFunction(), con_idx)
Expand Down Expand Up @@ -172,7 +185,6 @@ function MOI.copy_to(
q += dim
i += 1
end

if q > 0
push!(cones, cone_from_moi(T, MOI.Nonnegatives(q)))
end
Expand All @@ -181,6 +193,20 @@ function MOI.copy_to(
opt.other_cones_start = i

for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent())
if !MOI.supports_constraint(opt, F, S)
throw(MOI.UnsupportedConstraint{F,S}())
end
for attr in MOI.get(
src,
MOI.ListOfConstraintAttributesSet{F,S}(),
)
if attr == MOI.ConstraintName() ||
attr == MOI.ConstraintPrimalStart() ||
attr == MOI.ConstraintDualStart() ||
continue # It's okay to skip these for now.
end
throw(MOI.UnsupportedAttribute(attr))
end
if S == MOI.Zeros || S == MOI.Nonnegatives
continue # already copied these constraints
end
Expand Down Expand Up @@ -230,15 +256,13 @@ function MOI.copy_to(

opt.cones_idxs = cones_idxs
opt.other_cones = other_cones

return idx_map
end

function MOI.optimize!(opt::Optimizer{T}) where {T <: Real}
# build and solve the model
model = opt.model
solver = opt.solver

Solvers.load(solver, model)
Solvers.solve(solver)

Expand All @@ -248,7 +272,7 @@ function MOI.optimize!(opt::Optimizer{T}) where {T <: Real}
opt.z = Solvers.get_z(solver)

# transform solution for MOI conventions
opt.zeros_primal = copy(model.b)
opt.zeros_primal = copy(model.b)
mul!(opt.zeros_primal, model.A, opt.x, -1, true)
i = opt.other_cones_start
for cone in opt.other_cones
Expand All @@ -260,22 +284,29 @@ function MOI.optimize!(opt::Optimizer{T}) where {T <: Real}
end
i += 1
end

return
end

MOI.supports(::Optimizer, ::MOI.Silent) = true
MOI.set(opt::Optimizer, ::MOI.Silent, value::Bool) = (opt.solver.verbose = !value)

function MOI.set(opt::Optimizer, ::MOI.Silent, value::Bool)
opt.solver.verbose = !value
return
end

MOI.get(opt::Optimizer, ::MOI.Silent) = !opt.solver.verbose

MOI.supports(::Optimizer, ::MOI.TimeLimitSec) = true
MOI.set(opt::Optimizer, ::MOI.TimeLimitSec, value::Real) =
(opt.solver.time_limit = value)
MOI.set(opt::Optimizer, ::MOI.TimeLimitSec, ::Nothing) =
(opt.solver.time_limit = Inf)

function MOI.set(opt::Optimizer, ::MOI.TimeLimitSec, value::Union{Real, Nothing})
opt.solver.time_limit = something(value, Inf)
return
end

function MOI.get(opt::Optimizer, ::MOI.TimeLimitSec)
isfinite(opt.solver.time_limit) && return opt.solver.time_limit
if isfinite(opt.solver.time_limit)
return opt.solver.time_limit
end
return
end

Expand Down Expand Up @@ -320,7 +351,10 @@ function MOI.get(opt::Optimizer, ::MOI.TerminationStatus)
end
end

function MOI.get(opt::Optimizer, ::MOI.PrimalStatus)
function MOI.get(opt::Optimizer, attr::MOI.PrimalStatus)
if attr.result_index != 1
return MOI.NO_SOLUTION
end
status = opt.solver.status
if status == Solvers.Optimal
return MOI.FEASIBLE_POINT
Expand All @@ -335,7 +369,10 @@ function MOI.get(opt::Optimizer, ::MOI.PrimalStatus)
end
end

function MOI.get(opt::Optimizer, ::MOI.DualStatus)
function MOI.get(opt::Optimizer, attr::MOI.DualStatus)
if attr.result_index != 1
return MOI.NO_SOLUTION
end
status = opt.solver.status
if status == Solvers.Optimal
return MOI.FEASIBLE_POINT
Expand All @@ -352,50 +389,55 @@ end

_sense_val(sense::MOI.OptimizationSense) = (sense == MOI.MAX_SENSE ? -1 : 1)

function MOI.get(opt::Optimizer, ::MOI.ObjectiveValue)
function MOI.get(opt::Optimizer, attr::MOI.ObjectiveValue)
MOI.check_result_index_bounds(opt, attr)
return _sense_val(opt.obj_sense) * Solvers.get_primal_obj(opt.solver)
end

function MOI.get(opt::Optimizer, ::MOI.DualObjectiveValue)
function MOI.get(opt::Optimizer, attr::MOI.DualObjectiveValue)
MOI.check_result_index_bounds(opt, attr)
return _sense_val(opt.obj_sense) * Solvers.get_dual_obj(opt.solver)
end

MOI.get(opt::Optimizer, ::MOI.ResultCount) = 1

MOI.get(opt::Optimizer, ::MOI.VariablePrimal, vi::VI) = opt.x[vi.value]
function MOI.get(opt::Optimizer, attr::MOI.VariablePrimal, vi::VI)
MOI.check_result_index_bounds(opt, attr)
return opt.x[vi.value]
end

function MOI.get(
opt::Optimizer{T},
::MOI.ConstraintDual,
opt::Optimizer{T},
attr::MOI.ConstraintDual,
ci::MOI.ConstraintIndex{<:Union{VV, VAF{T}}, MOI.Zeros},
) where T
# constraint is an equality
MOI.check_result_index_bounds(opt, attr)
return opt.y[opt.zeros_idxs[ci.value]]
end

function MOI.get(
opt::Optimizer{T},
::MOI.ConstraintDual,
opt::Optimizer{T},
attr::MOI.ConstraintDual,
ci::MOI.ConstraintIndex{<:Union{VV, VAF{T}}, <:SupportedCones{T}},
) where T
# constraint is conic
MOI.check_result_index_bounds(opt, attr)
return opt.z[opt.cones_idxs[ci.value]]
end

function MOI.get(
opt::Optimizer{T},
::MOI.ConstraintPrimal,
opt::Optimizer{T},
attr::MOI.ConstraintPrimal,
ci::MOI.ConstraintIndex{<:Union{VV, VAF{T}}, MOI.Zeros},
) where T
# constraint is an equality
MOI.check_result_index_bounds(opt, attr)
return opt.zeros_primal[opt.zeros_idxs[ci.value]]
end

function MOI.get(
opt::Optimizer{T},
::MOI.ConstraintPrimal,
opt::Optimizer{T},
attr::MOI.ConstraintPrimal,
ci::MOI.ConstraintIndex{<:Union{VV, VAF{T}}, <:SupportedCones{T}},
) where T
# constraint is conic
MOI.check_result_index_bounds(opt, attr)
return opt.s[opt.cones_idxs[ci.value]]
end
50 changes: 20 additions & 30 deletions test/runmoitests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,44 +31,34 @@ end
println("\nstarting MOI.Test tests")
# TODO test other real types
T = Float64
optimizer = Hypatia.Optimizer{T}(; default_tol_relax = 4)
MOI.set(optimizer, MOI.Silent(), true)
model = MOI.Bridges.full_bridge_optimizer(
MOI.Utilities.CachingOptimizer(
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{T}()),
optimizer,
Hypatia.Optimizer{T}(; default_tol_relax = 4),
),
T,
)

tol = 2 * sqrt(sqrt(eps(T)))
config = MOI.Test.Config(
T,
atol = tol,
rtol = tol,
exclude = Any[
MOI.ConstraintBasisStatus,
MOI.VariableBasisStatus,
MOI.ConstraintName,
MOI.VariableName,
MOI.ObjectiveBound,
MOI.set(model, MOI.Silent(), true)
MOI.Test.runtests(
model,
MOI.Test.Config(
T,
atol = 2 * sqrt(sqrt(eps(T))),
rtol = 2 * sqrt(sqrt(eps(T))),
exclude = Any[
MOI.ConstraintBasisStatus,
MOI.VariableBasisStatus,
MOI.ObjectiveBound,
MOI.SolverVersion,
],
),
exclude = String[
# TODO(odow): unexpected failure. But this is probably in the bridge
# layer, not Hypatia.
"test_model_UpperBoundAlreadySet",
"test_model_LowerBoundAlreadySet",
],
)

excludes = String[
# not implemented
"test_attribute_SolverVersion",
# TODO fix
"test_linear_INFEASIBLE_2", # slow progress
"test_solve_result_index",
"test_model_copy_to_UnsupportedAttribute",
# MathOptInterface.jl issue #1431
"test_model_LowerBoundAlreadySet",
"test_model_UpperBoundAlreadySet",
]
includes = String[]

MOI.Test.runtests(model, config, include = includes, exclude = excludes)
end

end
Expand Down