From fe452e4822ad1a74b7a3c0eccc8fc86c498f6b53 Mon Sep 17 00:00:00 2001 From: Chris Coey Date: Fri, 27 Oct 2023 04:31:44 -0700 Subject: [PATCH] Handle constant objectives and constraints containing no variables (#13) * handle constant objectives and constraints containing no variables * format --- CHANGELOG.md | 4 +++- src/json_to_moi.jl | 11 +++++++++-- test/all_tests.jl | 3 +++ test/inputs/cons_false.json | 1 + test/inputs/cons_true.json | 1 + test/inputs/min_constant.json | 1 + test/outputs/cons_false.json | 1 + test/outputs/cons_true.json | 1 + test/outputs/min_constant.json | 1 + 9 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 test/inputs/cons_false.json create mode 100644 test/inputs/cons_true.json create mode 100644 test/inputs/min_constant.json create mode 100644 test/outputs/cons_false.json create mode 100644 test/outputs/cons_true.json create mode 100644 test/outputs/min_constant.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 1073b42..67e3fe8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.2.0] + - Improve test coverage, error messages, and others [#1](https://github.com/RelationalAI/SolverAPI.jl/pull/1), [#7](https://github.com/RelationalAI/SolverAPI.jl/pull/7), [#8](https://github.com/RelationalAI/SolverAPI.jl/pull/8), [#10](https://github.com/RelationalAI/SolverAPI.jl/pull/10) - Fix `StackOverflowError` and improve performance [#6](https://github.com/RelationalAI/SolverAPI.jl/pull/6) - Return solve time and solver version [#5](https://github.com/RelationalAI/SolverAPI.jl/pull/5) - - Add default time limit [#2](https://github.com/RelationalAI/SolverAPI.jl/pull/2) + - Add default time limit [#2](https://github.com/RelationalAI/SolverAPI.jl/pull/2) ## [0.1.0] + - Initial release diff --git a/src/json_to_moi.jl b/src/json_to_moi.jl index 220402c..09db0c6 100644 --- a/src/json_to_moi.jl +++ b/src/json_to_moi.jl @@ -4,7 +4,7 @@ function add_obj!( ::Type{T}, model::MOI.ModelLike, sense::String, - a::Union{String,JSON3.Array}, + a::Any, vars_map::Dict, ::Dict, ) where {T<:Real} @@ -16,6 +16,9 @@ function add_obj!( MOI.set(model, MOI.ObjectiveSense(), moi_sense) g = canonicalize_SNF(T, json_to_snf(a, vars_map)) + if !(g isa MOI.AbstractScalarFunction) + g = convert(MOI.ScalarAffineFunction{T}, g) + end g_type = MOI.ObjectiveFunction{typeof(g)}() if !MOI.supports(model, g_type) msg = "Objective function $(trunc_str(g)) isn't supported by this solver." @@ -139,7 +142,11 @@ function shift_terms(::Type{T}, args::Vector) where {T<:Real} @assert length(args) == 2 # This should never happen. g1 = canonicalize_SNF(T, args[1]) g2 = canonicalize_SNF(T, args[2]) - return MOI.Utilities.operate(-, T, g1, g2) + g = MOI.Utilities.operate(-, T, g1, g2) + if !(g isa MOI.AbstractScalarFunction) + g = convert(MOI.ScalarAffineFunction{T}, g) + end + return g end # Convert object to string and truncate string length if too long. diff --git a/test/all_tests.jl b/test/all_tests.jl index d353efb..9eaf7a1 100644 --- a/test/all_tests.jl +++ b/test/all_tests.jl @@ -69,6 +69,9 @@ end "tiny_infeas", "simple_lp", "n_queens", + "min_constant", + "cons_true", + "cons_false", ] # solve and check output is expected for each input json file diff --git a/test/inputs/cons_false.json b/test/inputs/cons_false.json new file mode 100644 index 0000000..30daea1 --- /dev/null +++ b/test/inputs/cons_false.json @@ -0,0 +1 @@ +{"version":"0.1","sense":"feas","variables":["x"],"constraints":[["==",1,0],["Nonneg","x"]],"objectives":[],"options":{"solver":"highs"}} diff --git a/test/inputs/cons_true.json b/test/inputs/cons_true.json new file mode 100644 index 0000000..ba8024a --- /dev/null +++ b/test/inputs/cons_true.json @@ -0,0 +1 @@ +{"version":"0.1","sense":"feas","variables":["x_1","x_2"],"constraints":[["==",0,0],["Nonneg","x_1"],["Nonneg","x_2"]],"objectives":[],"options":{"solver":"highs"}} diff --git a/test/inputs/min_constant.json b/test/inputs/min_constant.json new file mode 100644 index 0000000..a132b5b --- /dev/null +++ b/test/inputs/min_constant.json @@ -0,0 +1 @@ +{"version":"0.1","sense":"min","variables":["x"],"constraints":[["==","x",1],["Int","x"]],"objectives":[1.5],"options":{"solver":"HiGHS"}} diff --git a/test/outputs/cons_false.json b/test/outputs/cons_false.json new file mode 100644 index 0000000..41b4aec --- /dev/null +++ b/test/outputs/cons_false.json @@ -0,0 +1 @@ +{"termination_status":"INFEASIBLE","version":"0.1"} diff --git a/test/outputs/cons_true.json b/test/outputs/cons_true.json new file mode 100644 index 0000000..fa5ca76 --- /dev/null +++ b/test/outputs/cons_true.json @@ -0,0 +1 @@ +{"termination_status":"OPTIMAL","results":[{"names":["\"x_1\"","\"x_2\""],"values":[0.0,0.0],"primal_status":"FEASIBLE_POINT"}],"version":"0.1"} diff --git a/test/outputs/min_constant.json b/test/outputs/min_constant.json new file mode 100644 index 0000000..426ef33 --- /dev/null +++ b/test/outputs/min_constant.json @@ -0,0 +1 @@ +{"termination_status":"OPTIMAL","results":[{"names":["\"x\""],"values":[1.0],"primal_status":"FEASIBLE_POINT","objective_value":1.5}],"version":"0.1"}