Skip to content

Commit

Permalink
Merge pull request #52 from jump-dev/jg/moi10
Browse files Browse the repository at this point in the history
MOI 0.10 (and fixes)
  • Loading branch information
joaquimg authored Mar 8, 2022
2 parents bf556e0 + c1f3324 commit 0844db1
Show file tree
Hide file tree
Showing 12 changed files with 230 additions and 108 deletions.
12 changes: 2 additions & 10 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,5 @@ version = "0.6.5"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"

[compat]
JuMP = "~0.21.1"
MathOptInterface = "~0.9.5"
julia = "1"

[extras]
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["JuMP", "Test"]
MathOptInterface = "0.10.9"
julia = "1.6"
4 changes: 4 additions & 0 deletions src/BARON.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ end
SYNTAX_ERROR = 10
LICENSING_ERROR = 11
USER_HEURISTIC_TERMINATION = 12
CALL_TO_EXEC_FAILED = 99 # TODO allow reach here
end

@enum BaronModelStatus begin
Expand Down Expand Up @@ -79,6 +80,8 @@ mutable struct BaronModel

solution_info::Union{Nothing, SolutionStatus}

print_input_file::Bool

function BaronModel(; kwargs...)
options = Dict{String, Any}(string(key) => val for (key,val) in kwargs)
model = new()
Expand All @@ -94,6 +97,7 @@ mutable struct BaronModel
model.summary_file_name = get!(options, "SumName", joinpath(temp_dir, "sum.lst"))
model.result_file_name = get!(options, "ResName", joinpath(temp_dir, "res.lst"))
model.solution_info = nothing
model.print_input_file = false
return model
end
end
Expand Down
35 changes: 27 additions & 8 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const VI = MOI.VariableIndex
const CI = MOI.ConstraintIndex

# function aliases
const SV = MOI.SingleVariable
const SAF = MOI.ScalarAffineFunction{Float64}
const SQF = MOI.ScalarQuadraticFunction{Float64}

Expand Down Expand Up @@ -42,9 +41,10 @@ function MOI.empty!(model::Optimizer)
end

# copy
MOIU.supports_default_copy_to(model::Optimizer, copy_names::Bool) = !copy_names
# MOIU.supports_default_copy_to(::Optimizer, copy_names::Bool) = !copy_names
MOI.supports_incremental_interface(::Optimizer) = true
function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike; kws...)
return MOIU.automatic_copy_to(dest, src; kws...)
return MOIU.default_copy_to(dest, src; kws...)
end

# optimize
Expand All @@ -54,17 +54,27 @@ function MOI.optimize!(model::Optimizer)
Set the environment variable `BARON_EXEC` and run `using Pkg; Pkg.build("BARON")`."""))
end
write_bar_file(model.inner)
run(`$baron_exec $(model.inner.problem_file_name)`)
if model.inner.print_input_file
println("\nBARON input file: $(model.inner.problem_file_name)\n")
println(read(model.inner.problem_file_name, String))
end
try
run(`$baron_exec $(model.inner.problem_file_name)`)
catch e
println("$e")
println(read(model.inner.problem_file_name, String))
error("failed to call BARON exec $baron_exec")
end
read_results(model.inner)
end

# RawParameter
MOI.supports(::Optimizer, ::MOI.RawParameter) = true
function MOI.set(model::Optimizer, param::MOI.RawParameter, value)
# RawOptimizerAttribute
MOI.supports(::Optimizer, ::MOI.RawOptimizerAttribute) = true
function MOI.set(model::Optimizer, param::MOI.RawOptimizerAttribute, value)
model.inner.options[param.name] = value
return
end
function MOI.get(model::Optimizer, param::MOI.RawParameter)
function MOI.get(model::Optimizer, param::MOI.RawOptimizerAttribute)
return get(model.inner.options, param.name) do
throw(ErrorException("Requested parameter $(param.name) is not set."))
end
Expand All @@ -82,6 +92,15 @@ function MOI.get(model::Optimizer, ::MOI.TimeLimitSec)
return get(model.inner.options, "MaxTime", 1000.0)
end

struct PrintInputFile <: MOI.AbstractOptimizerAttribute end
function MOI.set(model::Optimizer, ::PrintInputFile, val::Bool)
model.inner.print_input_file = val
return
end
function MOI.get(model::Optimizer, ::PrintInputFile)
model.inner.print_input_file
end

include(joinpath("moi", "util.jl"))
include(joinpath("moi", "variables.jl"))
include(joinpath("moi", "constraints.jl"))
Expand Down
64 changes: 32 additions & 32 deletions src/moi/constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,59 +16,59 @@ function set_bounds(info::Union{VariableInfo, ConstraintInfo}, set::MOI.Interval
set_upper_bound(info, set.upper)
end

MOI.supports_constraint(::Optimizer, ::Type{SV}, ::Type{<:Bounds}) = true
MOI.supports_constraint(::Optimizer, ::Type{MOI.VariableIndex}, ::Type{<:Bounds{Float64}}) = true

function MOI.add_constraint(model::Optimizer, f::SV, set::S) where {S <: Bounds}
variable_info = find_variable_info(model, f.variable)
function MOI.add_constraint(model::Optimizer, f::MOI.VariableIndex, set::S) where {S <: Bounds{Float64}}
variable_info = find_variable_info(model, f)
set_bounds(variable_info, set)
return CI{SV, S}(f.variable.value)
return CI{MOI.VariableIndex, S}(f.value)
end

MOI.supports_constraint(::Optimizer, ::Type{<:Union{SAF, SQF}}, ::Type{<:Bounds}) = true
MOI.supports_constraint(::Optimizer, ::Type{<:Union{SAF, SQF}}, ::Type{<:Bounds{Float64}}) = true

function MOI.add_constraint(model::Optimizer, f::F, set::S) where {F <: Union{SAF, SQF}, S <: Bounds}
function MOI.add_constraint(model::Optimizer, f::F, set::S) where {F <: Union{SAF, SQF}, S <: Bounds{Float64}}
ci = ConstraintInfo()
ci.expression = to_expr(f)
set_bounds(ci, set)
push!(model.inner.constraint_info, ci)
return CI{F, S}(length(model.inner.constraint_info))
end

MOI.supports(::Optimizer, ::MOI.ConstraintName, ::Type{CI}) = true

function MOI.set(model::Optimizer, attr::MOI.ConstraintName, ci::CI{SV}, value)
error("No support for naming constraints imposed on variables.")
end
function MOI.set(model::Optimizer, attr::MOI.ConstraintName, ci::CI, value)
check_constraint_indices(model, ci)
model.inner.constraint_info[ci.value].name = value
end
function MOI.get(model::Optimizer, ::MOI.ConstraintName, ci::CI)
return model.inner.constraint_info[ci.value].name
end

function MOI.get(model::Optimizer, ::Type{MathOptInterface.ConstraintIndex}, name::String)
for (i,c) in enumerate(model.inner.constraint_info)
if name == c.name
return CI(i)
end
end
error("Unrecognized constraint name $name.")
end
# see comment in: write_bar_file
# MOI.supports(::Optimizer, ::MOI.ConstraintName, ::Type{CI}) = true
# function MOI.set(model::Optimizer, attr::MOI.ConstraintName, ci::CI{MOI.VariableIndex}, value)
# error("No support for naming constraints imposed on variables.")
# end
# function MOI.set(model::Optimizer, attr::MOI.ConstraintName, ci::CI, value)
# check_constraint_indices(model, ci)
# model.inner.constraint_info[ci.value].name = value
# end
# function MOI.get(model::Optimizer, ::MOI.ConstraintName, ci::CI)
# return model.inner.constraint_info[ci.value].name
# end
# function MOI.get(model::Optimizer, ::Type{MathOptInterface.ConstraintIndex}, name::String)
# for (i,c) in enumerate(model.inner.constraint_info)
# if name == c.name
# return CI(i)
# end
# end
# error("Unrecognized constraint name $name.")
# end

MOI.supports_constraint(::Optimizer, ::Type{SV}, ::Type{MOI.ZeroOne}) = true
MOI.supports_constraint(::Optimizer, ::Type{SV}, ::Type{MOI.Integer}) = true
MOI.supports_constraint(::Optimizer, ::Type{MOI.VariableIndex}, ::Type{MOI.ZeroOne}) = true
MOI.supports_constraint(::Optimizer, ::Type{MOI.VariableIndex}, ::Type{MOI.Integer}) = true

function MOI.add_constraint(model::Optimizer, f::SV, set::S) where {S <: Union{MOI.ZeroOne, MOI.Integer}}
variable_info = find_variable_info(model, f.variable)
function MOI.add_constraint(model::Optimizer, f::MOI.VariableIndex, set::S
) where {S <: Union{MOI.ZeroOne, MOI.Integer}}
variable_info = find_variable_info(model, f)
if set isa MOI.ZeroOne
variable_info.category = :Bin
elseif set isa MOI.Integer
variable_info.category = :Int
else
error("Unsupported variable type $set.")
end
return CI{SV, S}(f.variable.value)
return CI{MOI.VariableIndex, S}(f.value)
end

# MOI.supports(::Optimizer, ::MOI.NLPBlock) = true
Expand Down
13 changes: 10 additions & 3 deletions src/moi/nlp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,19 @@ function MOI.set(model::Optimizer, ::MOI.NLPBlock, nlp_data::MOI.NLPBlockData)
MOI.initialize(nlp_eval, [:ExprGraph])

if nlp_data.has_objective
if model.inner.objective_expr !== nothing
error("Two objectives set: One linear, one nonlinear.")
end
# according to test: test_nonlinear_objective_and_moi_objective_test
# from MOI 0.10.9, linear objectives are just ignores if the noliena exists
# if model.inner.objective_expr !== nothing
# error("Two objectives set: One linear, one nonlinear.")
# end
obj = verify_support(MOI.objective_expr(nlp_eval))
walk_and_strip_variable_index!(obj)

model.inner.objective_expr = obj
# if obj == :NaN
# model.inner.objective_expr = 0.0
# else
# end
end

for i in 1:length(nlp_data.constraint_bounds)
Expand Down
2 changes: 1 addition & 1 deletion src/moi/objective.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ end
MOI.supports(::Optimizer, ::MOI.ObjectiveFunction{SAF}) = true
MOI.supports(::Optimizer, ::MOI.ObjectiveFunction{SQF}) = true

function MOI.set(model::Optimizer, ::MOI.ObjectiveFunction{F}, obj::F) where {F<:Union{SV, SAF, SQF}}
function MOI.set(model::Optimizer, ::MOI.ObjectiveFunction{F}, obj::F) where {F<:Union{VI, SAF, SQF}}
model.inner.objective_expr = to_expr(obj)
return
end
36 changes: 31 additions & 5 deletions src/moi/results.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,14 @@ function MOI.get(model::Optimizer, ::MOI.ResultCount)
return (model.inner.solution_info.feasible_point === nothing) ? 0 : 1
end

function MOI.get(model::Optimizer, ::MOI.PrimalStatus)
function MOI.get(model::Optimizer, ::MOI.DualStatus)
MOI.NO_SOLUTION
end

function MOI.get(model::Optimizer, attr::MOI.PrimalStatus)
if attr.result_index != 1
return MOI.NO_SOLUTION
end
solution_info = model.inner.solution_info
if solution_info === nothing || solution_info.feasible_point === nothing
return MOI.NO_SOLUTION
Expand All @@ -56,9 +63,22 @@ function MOI.get(model::Optimizer, ::MOI.PrimalStatus)
end
end

MOI.get(model::Optimizer, ::MOI.ObjectiveValue) = model.inner.solution_info.objective_value
function MOI.get(model::Optimizer, attr::MOI.ObjectiveValue)
MOI.check_result_index_bounds(model, attr)
# if model.inner.solution_info.model_status == UNBOUNDED
# # BARON will set the same large number
# # for both abj and variables in case of unbounded
# # this would make it retur MOI consistent values
# # but getters are no define and NL one would be messy
# return MOIU.get_fallback(model, attr)
# else
model.inner.solution_info.objective_value
# end
end

function MOI.get(model::Optimizer, ::MOI.VariablePrimal, vi::VI)
function MOI.get(model::Optimizer, attr::MOI.VariablePrimal, vi::VI)
MOI.check_result_index_bounds(model, attr)
# MOI.throw_if_not_valid(model, vi) # TODO implement is_valid
solution_info = model.inner.solution_info
if solution_info === nothing || solution_info.feasible_point === nothing
error("VariablePrimal not available.")
Expand All @@ -72,15 +92,21 @@ function MOI.get(model::Optimizer, ::MOI.ObjectiveBound)
return solution_info.dual_bound
end

function MOI.get(model::Optimizer, ::MOI.SolveTime)
function MOI.get(model::Optimizer, ::MOI.SolveTimeSec)
solution_info = model.inner.solution_info
return solution_info.wall_time
end

function MOI.get(model::Optimizer, ::MOI.RawStatusString)
info = model.inner.solution_info
return "solver: $(info.solver_status), model: $(info.model_status)"
end

# TODO: desirable?
function MOI.get(model::MOIU.CachingOptimizer{BARON.Optimizer}, attr::MOI.ConstraintPrimal, ci::MOI.ConstraintIndex)
return MOIU.get_fallback(model, attr, ci)
end

# TODO: MOI getter for solvetime
function MOI.supports(::Optimizer, ::MOI.SolverVersion)
false
end
20 changes: 10 additions & 10 deletions src/moi/util.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# to_expr
to_expr(sv::SV) = :(x[$(sv.variable.value)])
to_expr(vi::MOI.VariableIndex) = :(x[$(vi.value)])

function to_expr(f::SAF)
f = MOIU.canonical(f)
if isempty(f.terms)
return f.constant
else
linear_term_exprs = map(f.terms) do term
:($(term.coefficient) * x[$(term.variable_index.value)])
:($(term.coefficient) * x[$(term.variable.value)])
end
expr = :(+($(linear_term_exprs...)))
if !iszero(f.constant)
Expand All @@ -20,12 +20,12 @@ end
function to_expr(f::SQF)
f = MOIU.canonical(f)
linear_term_exprs = map(f.affine_terms) do term
i = term.variable_index.value
i = term.variable.value
:($(term.coefficient) * x[$i])
end
quadratic_term_exprs = map(f.quadratic_terms) do term
i = term.variable_index_1.value
j = term.variable_index_2.value
i = term.variable_1.value
j = term.variable_2.value
if i == j
:($(term.coefficient / 2) * x[$i] * x[$j])
else
Expand All @@ -46,17 +46,17 @@ end

function check_variable_indices(model::Optimizer, f::SAF)
for term in f.terms
check_variable_indices(model, term.variable_index)
check_variable_indices(model, term.variable)
end
end

function check_variable_indices(model::Optimizer, f::SQF)
for term in f.affine_terms
check_variable_indices(model, term.variable_index)
check_variable_indices(model, term.variable)
end
for term in f.quadratic_terms
check_variable_indices(model, term.variable_index_1)
check_variable_indices(model, term.variable_index_2)
check_variable_indices(model, term.variable_1)
check_variable_indices(model, term.variable_2)
end
end

Expand All @@ -65,7 +65,7 @@ function find_variable_info(model::Optimizer, vi::VI)
model.inner.variable_info[vi.value]
end

function check_constraint_indices(model::Optimizer, index::CI{SV})
function check_constraint_indices(model::Optimizer, index::CI{MOI.VariableIndex})
@assert 1 <= index.value <= length(model.inner.variable_info)
end

Expand Down
Loading

0 comments on commit 0844db1

Please sign in to comment.