Make JuMPSolver pass all tests

master
Alinson S. Xavier 4 years ago
parent 60e1c1dc0c
commit 2ebf643a33

@ -83,56 +83,81 @@ function _update_solution!(data::JuMPSolverData)
end end
function are_constraints_satisfied( function add_constraints(
data::JuMPSolverData; data::JuMPSolverData;
lhs::Vector{Vector{Tuple{String, Float64}}}, lhs::Vector{Vector{Tuple{String, Float64}}},
rhs::Vector{Float64}, rhs::Vector{Float64},
senses::Vector{String}, senses::Vector{String},
tol::Float64=1e-5, names::Vector{String},
)::Vector{Bool} )::Nothing
result = []
for (i, sense) in enumerate(senses) for (i, sense) in enumerate(senses)
lhs_value = 0.0 lhs_expr = AffExpr(0.0)
for (varname, coeff) in lhs[i] for (varname, coeff) in lhs[i]
var = data.varname_to_var[varname] var = data.varname_to_var[varname]
lhs_value += data.solution[var] * coeff add_to_expression!(lhs_expr, var, coeff)
end end
if sense == "<" if sense == "<"
push!(result, lhs_value <= rhs[i] + tol) constr = @constraint(data.model, lhs_expr <= rhs[i])
elseif sense == ">" elseif sense == ">"
push!(result, lhs_value >= rhs[i] - tol) constr = @constraint(data.model, lhs_expr >= rhs[i])
else else
push!(result, abs(lhs_value - rhs[i]) <= tol) constr = @constraint(data.model, lhs_expr == rhs[i])
end end
set_name(constr, names[i])
data.cname_to_constr[names[i]] = constr
end end
return result return
end end
function add_constraints( function are_constraints_satisfied(
data::JuMPSolverData; data::JuMPSolverData;
lhs::Vector{Vector{Tuple{String, Float64}}}, lhs::Vector{Vector{Tuple{String, Float64}}},
rhs::Vector{Float64}, rhs::Vector{Float64},
senses::Vector{String}, senses::Vector{String},
names::Vector{String}, tol::Float64=1e-5,
)::Nothing )::Vector{Bool}
result = []
for (i, sense) in enumerate(senses) for (i, sense) in enumerate(senses)
lhs_expr = AffExpr(0.0) lhs_value = 0.0
for (varname, coeff) in lhs[i] for (varname, coeff) in lhs[i]
var = data.varname_to_var[varname] var = data.varname_to_var[varname]
add_to_expression!(lhs_expr, var, coeff) lhs_value += data.solution[var] * coeff
end end
if sense == "<" if sense == "<"
constr = @constraint(data.model, lhs_expr <= rhs[i]) push!(result, lhs_value <= rhs[i] + tol)
elseif sense == ">" elseif sense == ">"
constr = @constraint(data.model, lhs_expr >= rhs[i]) push!(result, lhs_value >= rhs[i] - tol)
else else
constr = @constraint(data.model, lhs_expr == rhs[i]) push!(result, abs(lhs_value - rhs[i]) <= tol)
end end
set_name(constr, names[i])
data.cname_to_constr[names[i]] = constr
end end
return return result
end
function build_test_instance_knapsack()
weights = [23.0, 26.0, 20.0, 18.0]
prices = [505.0, 352.0, 458.0, 220.0]
capacity = 67.0
model = Model()
n = length(weights)
@variable(model, x[0:n-1], Bin)
@variable(model, z, lower_bound=0.0, upper_bound=capacity)
@objective(model, Max, sum(x[i-1] * prices[i] for i in 1:n))
@constraint(model, eq_capacity, sum(x[i-1] * weights[i] for i in 1:n) - z == 0)
return JuMPInstance(model)
end
function build_test_instance_infeasible()
model = Model()
@variable(model, x, Bin)
@objective(model, Max, x)
@constraint(model, x >= 2)
return JuMPInstance(model)
end end
@ -166,9 +191,15 @@ function solve(
break break
end end
end end
_update_solution!(data) if is_infeasible(data)
primal_bound = JuMP.objective_value(model) data.solution = Dict()
dual_bound = JuMP.objective_bound(model) primal_bound = nothing
dual_bound = nothing
else
_update_solution!(data)
primal_bound = JuMP.objective_value(model)
dual_bound = JuMP.objective_bound(model)
end
if JuMP.objective_sense(model) == MOI.MIN_SENSE if JuMP.objective_sense(model) == MOI.MIN_SENSE
sense = "min" sense = "min"
lower_bound = dual_bound lower_bound = dual_bound
@ -200,8 +231,13 @@ function solve_lp(data::JuMPSolverData; tee::Bool=false)
wallclock_time = @elapsed begin wallclock_time = @elapsed begin
log = _optimize_and_capture_output!(model, tee=tee) log = _optimize_and_capture_output!(model, tee=tee)
end end
_update_solution!(data) if is_infeasible(data)
obj_value = JuMP.objective_value(model) data.solution = Dict()
obj_value = nothing
else
_update_solution!(data)
obj_value = JuMP.objective_value(model)
end
for var in bin_vars for var in bin_vars
JuMP.set_binary(var) JuMP.set_binary(var)
end end
@ -213,17 +249,24 @@ function solve_lp(data::JuMPSolverData; tee::Bool=false)
end end
function set_instance!(data::JuMPSolverData, instance, model::JuMP.Model) function set_instance!(
data::JuMPSolverData,
instance;
model::Union{Nothing,JuMP.Model},
)::Nothing
data.instance = instance data.instance = instance
if model === nothing
model = instance.to_model()
end
data.model = model data.model = model
data.bin_vars = [ data.bin_vars = [
var var
for var in JuMP.all_variables(data.model) for var in JuMP.all_variables(model)
if JuMP.is_binary(var) if JuMP.is_binary(var)
] ]
data.varname_to_var = Dict( data.varname_to_var = Dict(
JuMP.name(var) => var JuMP.name(var) => var
for var in JuMP.all_variables(data.model) for var in JuMP.all_variables(model)
) )
JuMP.set_optimizer(model, data.optimizer_factory) JuMP.set_optimizer(model, data.optimizer_factory)
data.cname_to_constr = Dict() data.cname_to_constr = Dict()
@ -234,6 +277,7 @@ function set_instance!(data::JuMPSolverData, instance, model::JuMP.Model)
data.cname_to_constr[name] = constr data.cname_to_constr[name] = constr
end end
end end
return
end end
@ -256,7 +300,10 @@ end
function is_infeasible(data::JuMPSolverData) function is_infeasible(data::JuMPSolverData)
return JuMP.termination_status(data.model) == MOI.INFEASIBLE return JuMP.termination_status(data.model) in [
MOI.INFEASIBLE,
MOI.INFEASIBLE_OR_UNBOUNDED,
]
end end
@ -409,22 +456,6 @@ function get_constraints(
end end
function build_test_instance_knapsack()
weights = [23.0, 26.0, 20.0, 18.0]
prices = [505.0, 352.0, 458.0, 220.0]
capacity = 67.0
model = Model()
n = length(weights)
@variable(model, x[0:n-1], Bin)
@variable(model, z, lower_bound=0.0, upper_bound=capacity)
@objective(model, Max, sum(x[i-1] * prices[i] for i in 1:n))
@constraint(model, eq_capacity, sum(x[i-1] * weights[i] for i in 1:n) - z == 0)
return JuMPInstance(model)
end
@pydef mutable struct JuMPSolver <: miplearn.solvers.internal.InternalSolver @pydef mutable struct JuMPSolver <: miplearn.solvers.internal.InternalSolver
function __init__(self, optimizer_factory) function __init__(self, optimizer_factory)
self.data = JuMPSolverData( self.data = JuMPSolverData(
@ -459,19 +490,18 @@ end
)...) )...)
build_test_instance_infeasible(self) = build_test_instance_infeasible(self) =
error("not implemented") build_test_instance_infeasible()
build_test_instance_knapsack(self) = build_test_instance_knapsack(self) =
build_test_instance_knapsack() build_test_instance_knapsack()
# FIXME: Actually clone instead of returning self clone(self) = JuMPSolver(self.data.optimizer_factory)
clone(self) = self
fix(self, solution) = fix(self, solution) =
fix!(self.data, solution) fix!(self.data, solution)
get_solution(self) = get_solution(self) =
self.data.solution isempty(self.data.solution) ? nothing : self.data.solution
get_constraints( get_constraints(
self; self;
@ -529,8 +559,8 @@ end
[n for n in names], [n for n in names],
) )
set_instance(self, instance, model) = set_instance(self, instance, model=nothing) =
set_instance!(self.data, instance, model) set_instance!(self.data, instance, model=model)
set_warm_start(self, solution) = set_warm_start(self, solution) =
set_warm_start!(self.data, solution) set_warm_start!(self.data, solution)

Loading…
Cancel
Save