Skip to content

Commit

Permalink
Solver options via params argument (#30)
Browse files Browse the repository at this point in the history
* add a params argument to pass solver options

* add test
  • Loading branch information
bachdavi authored Jan 5, 2024
1 parent 59258a2 commit e347cf8
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 11 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Make `options` field optional [#22](https://github.com/RelationalAI/SolverAPI.jl/pull/22)
- Remove `names` field in results [#20](https://github.com/RelationalAI/SolverAPI.jl/pull/20)
- Update format to use JSON vector for relational appl constraint
args [#21](https://github.com/RelationalAI/SolverAPI.jl/pull/21)
args [#21](https://github.com/RelationalAI/SolverAPI.jl/pull/21)

## [0.2.2]

Expand Down
32 changes: 22 additions & 10 deletions src/SolverAPI.jl
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,13 @@ deserialize(body::String) = deserialize(Vector{UInt8}(body))

# Internal version of `solve`. We do this to make handling keyword
# arguments easier.
function _solve(fn, json::Request, solver::MOI.AbstractOptimizer; kw...)
function _solve(
fn,
json::Request,
solver::MOI.AbstractOptimizer,
params::Dict{Symbol,Any};
kw...,
)
errors = validate(json)
isempty(errors) || return response(; errors)

Expand All @@ -191,9 +197,10 @@ function _solve(fn, json::Request, solver::MOI.AbstractOptimizer; kw...)

try
options = if haskey(json, :options)
json.options
# Copy the JSON to make it into a `Dict{Symbol,Any}`.
merge(copy(json.options), params)
else
JSON3.Object()
params
end

set_options!(model, options)
Expand Down Expand Up @@ -228,8 +235,8 @@ function _solve(fn, json::Request, solver::MOI.AbstractOptimizer; kw...)
MOI.empty!(model)
end
end
_solve(fn, request::Dict, solver::MOI.AbstractOptimizer; kw...) =
_solve(fn, JSON3.read(JSON3.write(request)), solver; kw...)
_solve(fn, request::Dict, solver::MOI.AbstractOptimizer, params::Dict{Symbol,Any}; kw...) =
_solve(fn, JSON3.read(JSON3.write(request)), solver, params; kw...)

"""
solve([fn], request::Request, solver::MOI.AbstractOptimizer; <kwargs>)::Response
Expand All @@ -244,22 +251,27 @@ gracefully and included in `Response`.
the `MOI.ModelLike` model and should return `Nothing`.
- `request`: A `SolverAPI.Request` specifying the optimization problem.
- `solver`: A `MOI.AbstractOptimizer` to use to solve the problem.
- `params = Dict{Symbol,Any}()`: Parameters to pass to the
solver. This can be used with/instead of the `options` field in
`request`.
## Keyword Args
- `use_indicator=true`: Whether to use indicator constraints.
- `numerical_type=Float64`: The numerical type to use for the solver.
"""
function solve(
fn,
fn::Function,
request,
solver;
solver,
params = Dict{Symbol,Any}();
use_indicator::Bool = true,
numerical_type::Type{<:Real} = Float64,
)
return _solve(fn, request, solver; use_indicator, numerical_type)
return _solve(fn, request, solver, params; use_indicator, numerical_type)
end
solve(request, solver; kw...) = solve(model -> nothing, request, solver; kw...)
solve(request, solver, params = Dict{Symbol,Any}(); kw...) =
solve(model -> nothing, request, solver, params; kw...)

"""
print_model(request::Request; <kwargs>)::String
Expand Down Expand Up @@ -414,7 +426,7 @@ function validate(json::Request)#::Vector{Error}
end

# Set solver options.
function set_options!(model::MOI.ModelLike, options::JSON3.Object)#::Nothing
function set_options!(model::MOI.ModelLike, options::Dict{Symbol,Any})#::Nothing
if MOI.supports(model, MOI.TimeLimitSec())
# Set time limit, defaulting to 5min.
MOI.set(model, MOI.TimeLimitSec(), Float64(get(options, :time_limit_sec, 300.0)))
Expand Down
29 changes: 29 additions & 0 deletions test/all_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,35 @@ end
end
end

@testitem "solve - params" setup = [SolverSetup] begin
import JSON3
import HiGHS

using SolverAPI

tiny_min = JSON3.read(
JSON3.write(
Dict(
"version" => "0.1",
"sense" => "min",
"variables" => ["x"],
"constraints" => [["==", "x", 1], ["Int", "x"]],
"objectives" => ["x"],
),
),
)

# We can pass options to the solver via `params` as well. The
# `solver` key will be ignored.
result = solve(
tiny_min,
HiGHS.Optimizer(),
Dict{Symbol,Any}(:time_limit_sec => 0, :presolve => "off", :solver => "highs"),
)

@test result["termination_status"] == "TIME_LIMIT"
end

@testitem "errors" setup = [SolverSetup] begin
using SolverAPI
import JSON3
Expand Down

0 comments on commit e347cf8

Please sign in to comment.