Skip to content

Commit

Permalink
Add support for MOI.TimeLimitSec and MOI.SolveTimeSec (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Sep 19, 2023
1 parent 0f3524c commit 5984159
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 2 deletions.
39 changes: 37 additions & 2 deletions src/optimize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,24 @@ mutable struct Optimizer{T} <: MOI.AbstractOptimizer
primal_objective::T
primal_solution::Dict{MOI.VariableIndex,T}
options::Dict{String,Any}
time_limit_sec::Union{Nothing,Float64}
solve_time_sec::Float64
function Optimizer{T}(solver::String) where {T}
if solver == "chuffed"
solver = Chuffed()
end
primal_solution = Dict{MOI.VariableIndex,T}()
options = Dict{String,Any}("model_filename" => "")
return new(solver, Model{T}(), "", zero(T), primal_solution, options)
return new(
solver,
Model{T}(),
"",
zero(T),
primal_solution,
options,
nothing,
NaN,
)
end
end

Expand Down Expand Up @@ -67,7 +78,12 @@ function _run_minizinc(dest::Optimizer)
output = joinpath(dir, "model.ozn")
_stdout = joinpath(dir, "_stdout.txt")
_minizinc_exe() do exe
cmd = `$(exe) --solver $(dest.solver) --output-objective -o $(output) $(filename)`
cmd = if dest.time_limit_sec !== nothing
limit = round(Int, 1_000 * dest.time_limit_sec::Float64)
`$(exe) --solver $(dest.solver) --output-objective --time-limit $limit -o $(output) $(filename)`
else
`$(exe) --solver $(dest.solver) --output-objective -o $(output) $(filename)`
end
return run(pipeline(cmd, stdout = _stdout))
end
if isfile(output)
Expand All @@ -90,6 +106,7 @@ function MOI.empty!(model::Optimizer{T}) where {T}
model.solver_status = ""
model.primal_objective = zero(T)
empty!(model.primal_solution)
model.solve_time_sec = NaN
return
end

Expand All @@ -106,6 +123,20 @@ function MOI.set(model::Optimizer, attr::MOI.RawOptimizerAttribute, value)
return
end

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

MOI.get(model::Optimizer, ::MOI.TimeLimitSec) = model.time_limit_sec

function MOI.set(model::Optimizer, ::MOI.TimeLimitSec, value::Real)
model.time_limit_sec = convert(Float64, value)
return
end

function MOI.set(model::Optimizer, ::MOI.TimeLimitSec, ::Nothing)
model.time_limit_sec = nothing
return
end

function MOI.supports_constraint(
model::Optimizer,
::Type{F},
Expand All @@ -129,6 +160,7 @@ function _parse_result(::Type{T}, s::AbstractString) where {T}
end

function MOI.optimize!(dest::Optimizer{T}, src::MOI.ModelLike) where {T}
time_start = time()
MOI.empty!(dest.inner)
empty!(dest.primal_solution)
index_map = MOI.copy_to(dest.inner, src)
Expand Down Expand Up @@ -157,6 +189,7 @@ function MOI.optimize!(dest::Optimizer{T}, src::MOI.ModelLike) where {T}
end
end
end
dest.solve_time_sec = time() - time_start
return index_map, false
end

Expand Down Expand Up @@ -209,3 +242,5 @@ function MOI.get(
MOI.throw_if_not_valid(model, x)
return model.primal_solution[x]
end

MOI.get(model::Optimizer, ::MOI.SolveTimeSec) = model.solve_time_sec
30 changes: 30 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1410,6 +1410,36 @@ function test_highs_optimization()
return
end

function test_time_limit_sec()
solver = MiniZinc.Optimizer{Int}("highs")
@test MOI.supports(solver, MOI.TimeLimitSec())
@test MOI.get(solver, MOI.TimeLimitSec()) === nothing
MOI.set(solver, MOI.TimeLimitSec(), 1)
@test MOI.get(solver, MOI.TimeLimitSec()) == 1.0
MOI.set(solver, MOI.TimeLimitSec(), nothing)
@test MOI.get(solver, MOI.TimeLimitSec()) === nothing
return
end

function test_highs_optimization_time_limit()
model = MOI.Utilities.Model{Float64}()
x, _ = MOI.add_constrained_variable(model, MOI.Integer())
MOI.add_constraint(model, x, MOI.Interval(1.0, 10.0))
MOI.set(model, MOI.ObjectiveFunction{typeof(x)}(), x)
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
solver = MiniZinc.Optimizer{Float64}("highs")
@test isnan(MOI.get(solver, MOI.SolveTimeSec()))
MOI.set(solver, MOI.TimeLimitSec(), 9e-4) # Very small limit
index_map, _ = MOI.optimize!(solver, model)
@test !isnan(MOI.get(solver, MOI.SolveTimeSec()))
@test MOI.get(solver, MOI.TerminationStatus()) === MOI.OTHER_ERROR
solver = MiniZinc.Optimizer{Float64}("highs")
MOI.set(solver, MOI.TimeLimitSec(), 100)
index_map, _ = MOI.optimize!(solver, model)
@test MOI.get(solver, MOI.TerminationStatus()) === MOI.OPTIMAL
return
end

end

TestMiniZinc.runtests()

0 comments on commit 5984159

Please sign in to comment.