From fed72712d8d68f0abc7978ade25d0a55784e0e78 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Wed, 7 Feb 2024 15:51:32 -0800 Subject: [PATCH] support solution_limit option targeting MOI.SolutionLimit; fails currently due to MiniZinc bug --- src/SolverAPI.jl | 15 ++++++++++++--- test/all_tests.jl | 2 ++ test/inputs/n_queens_solution_limit_1.json | 1 + test/inputs/n_queens_solution_limit_3.json | 1 + test/outputs/n_queens_solution_limit_1.json | 1 + test/outputs/n_queens_solution_limit_3.json | 1 + 6 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 test/inputs/n_queens_solution_limit_1.json create mode 100644 test/inputs/n_queens_solution_limit_3.json create mode 100644 test/outputs/n_queens_solution_limit_1.json create mode 100644 test/outputs/n_queens_solution_limit_3.json diff --git a/src/SolverAPI.jl b/src/SolverAPI.jl index 5ea45ac..1251168 100644 --- a/src/SolverAPI.jl +++ b/src/SolverAPI.jl @@ -400,7 +400,8 @@ function validate(json::Request)#::Vector{Error} end if haskey(json, :options) - for (T, k) in [(String, :print_format), (Number, :time_limit_sec)] + for (T, k) in + [(String, :print_format), (Number, :time_limit_sec), (Int, :solution_limit)] if haskey(json.options, k) && !isa(json.options[k], T) _err("Invalid `options.$(k)` field. Must be of type `$(T)`.") end @@ -440,6 +441,15 @@ function set_options!(model::MOI.ModelLike, options::Dict{Symbol,Any})#::Nothing MOI.set(model, MOI.TimeLimitSec(), Float64(get(options, :time_limit_sec, 300.0))) end + if MOI.supports(model, MOI.SolutionLimit()) && haskey(options, :solution_limit) + # Set solution limit + solution_limit = options[:solution_limit] + if solution_limit <= 0 + throw(Error(NotAllowed, "Solution limit must be positive.")) + end + MOI.set(model, MOI.SolutionLimit(), solution_limit) + end + if MOI.supports(model, MOI.RelativeGapTolerance()) && haskey(options, :relative_gap_tolerance) # Set relative gap tolerance @@ -447,7 +457,6 @@ function set_options!(model::MOI.ModelLike, options::Dict{Symbol,Any})#::Nothing if rel_gap_tol < 0 || rel_gap_tol > 1 throw(Error(NotAllowed, "Relative gap tolerance must be within [0,1].")) end - MOI.set(model, MOI.RelativeGapTolerance(), rel_gap_tol) end @@ -458,7 +467,6 @@ function set_options!(model::MOI.ModelLike, options::Dict{Symbol,Any})#::Nothing if abs_gap_tol < 0 throw(Error(NotAllowed, "Absolute gap tolerance must be non-negative.")) end - MOI.set(model, MOI.AbsoluteGapTolerance(), abs_gap_tol) end @@ -468,6 +476,7 @@ function set_options!(model::MOI.ModelLike, options::Dict{Symbol,Any})#::Nothing :print_format, :print_only, :time_limit_sec, + :solution_limit, :relative_gap_tolerance, :absolute_gap_tolerance, ] diff --git a/test/all_tests.jl b/test/all_tests.jl index c4b2116..c39a8c3 100644 --- a/test/all_tests.jl +++ b/test/all_tests.jl @@ -66,6 +66,8 @@ end "tiny_infeas", "simple_lp", "n_queens", + "n_queens_solution_limit_1", + "n_queens_solution_limit_3", "min_constant", "cons_true", "cons_false", diff --git a/test/inputs/n_queens_solution_limit_1.json b/test/inputs/n_queens_solution_limit_1.json new file mode 100644 index 0000000..20a8213 --- /dev/null +++ b/test/inputs/n_queens_solution_limit_1.json @@ -0,0 +1 @@ +{"version":"0.1","sense":"feas","variables":["q_1","q_2","q_3","q_4"],"constraints":[["range",1,4,1,"q_1"],["range",1,4,1,"q_2"],["range",1,4,1,"q_3"],["range",1,4,1,"q_4"],["and",["and",["!=","q_1","q_2"],["!=",["abs",["-","q_1","q_2"]],1]],["and",["!=","q_1","q_3"],["!=",["abs",["-","q_1","q_3"]],2]],["and",["!=","q_1","q_4"],["!=",["abs",["-","q_1","q_4"]],3]],["and",["!=","q_2","q_3"],["!=",["abs",["-","q_2","q_3"]],1]],["and",["!=","q_2","q_4"],["!=",["abs",["-","q_2","q_4"]],2]],["and",["!=","q_3","q_4"],["!=",["abs",["-","q_3","q_4"]],1]]]],"objectives":[],"options":{"solver":"MiniZinc","solution_limit":1}} diff --git a/test/inputs/n_queens_solution_limit_3.json b/test/inputs/n_queens_solution_limit_3.json new file mode 100644 index 0000000..2fc1bdf --- /dev/null +++ b/test/inputs/n_queens_solution_limit_3.json @@ -0,0 +1 @@ +{"version":"0.1","sense":"feas","variables":["q_1","q_2","q_3","q_4"],"constraints":[["range",1,4,1,"q_1"],["range",1,4,1,"q_2"],["range",1,4,1,"q_3"],["range",1,4,1,"q_4"],["and",["and",["!=","q_1","q_2"],["!=",["abs",["-","q_1","q_2"]],1]],["and",["!=","q_1","q_3"],["!=",["abs",["-","q_1","q_3"]],2]],["and",["!=","q_1","q_4"],["!=",["abs",["-","q_1","q_4"]],3]],["and",["!=","q_2","q_3"],["!=",["abs",["-","q_2","q_3"]],1]],["and",["!=","q_2","q_4"],["!=",["abs",["-","q_2","q_4"]],2]],["and",["!=","q_3","q_4"],["!=",["abs",["-","q_3","q_4"]],1]]]],"objectives":[],"options":{"solver":"MiniZinc","solution_limit":3}} diff --git a/test/outputs/n_queens_solution_limit_1.json b/test/outputs/n_queens_solution_limit_1.json new file mode 100644 index 0000000..6a1338a --- /dev/null +++ b/test/outputs/n_queens_solution_limit_1.json @@ -0,0 +1 @@ +{"termination_status":"SOLUTION_LIMIT","names":["\"q_1\"","\"q_2\"","\"q_3\"","\"q_4\""],"results":[{"primal_status":"FEASIBLE_POINT","values":[2,4,1,3]}],"version":"0.1"} diff --git a/test/outputs/n_queens_solution_limit_3.json b/test/outputs/n_queens_solution_limit_3.json new file mode 100644 index 0000000..e2c5232 --- /dev/null +++ b/test/outputs/n_queens_solution_limit_3.json @@ -0,0 +1 @@ +{"names":["\"q_1\"","\"q_2\"","\"q_3\"","\"q_4\""],"termination_status":"OPTIMAL","results":[{"values":[2,4,1,3],"primal_status":"FEASIBLE_POINT"},{"values":[3,1,4,2],"primal_status":"FEASIBLE_POINT"}],"version":"0.1"}