|
|
@ -9,9 +9,8 @@ using MathOptInterface
|
|
|
|
using TimerOutputs
|
|
|
|
using TimerOutputs
|
|
|
|
const MOI = MathOptInterface
|
|
|
|
const MOI = MathOptInterface
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mutable struct JuMPSolverData
|
|
|
|
mutable struct JuMPSolverData
|
|
|
|
optimizer_factory
|
|
|
|
optimizer_factory::Any
|
|
|
|
varname_to_var::Dict{String,VariableRef}
|
|
|
|
varname_to_var::Dict{String,VariableRef}
|
|
|
|
cname_to_constr::Dict{String,JuMP.ConstraintRef}
|
|
|
|
cname_to_constr::Dict{String,JuMP.ConstraintRef}
|
|
|
|
instance::Union{Nothing,PyObject}
|
|
|
|
instance::Union{Nothing,PyObject}
|
|
|
@ -29,7 +28,7 @@ end
|
|
|
|
Optimizes a given JuMP model while capturing the solver log, then returns that log.
|
|
|
|
Optimizes a given JuMP model while capturing the solver log, then returns that log.
|
|
|
|
If tee=true, prints the solver log to the standard output as the optimization takes place.
|
|
|
|
If tee=true, prints the solver log to the standard output as the optimization takes place.
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
function _optimize_and_capture_output!(model; tee::Bool=false)
|
|
|
|
function _optimize_and_capture_output!(model; tee::Bool = false)
|
|
|
|
logname = tempname()
|
|
|
|
logname = tempname()
|
|
|
|
logfile = open(logname, "w")
|
|
|
|
logfile = open(logname, "w")
|
|
|
|
redirect_stdout(logfile) do
|
|
|
|
redirect_stdout(logfile) do
|
|
|
@ -50,10 +49,7 @@ end
|
|
|
|
|
|
|
|
|
|
|
|
function _update_solution!(data::JuMPSolverData)
|
|
|
|
function _update_solution!(data::JuMPSolverData)
|
|
|
|
vars = JuMP.all_variables(data.model)
|
|
|
|
vars = JuMP.all_variables(data.model)
|
|
|
|
data.solution = Dict(
|
|
|
|
data.solution = Dict(var => JuMP.value(var) for var in vars)
|
|
|
|
var => JuMP.value(var)
|
|
|
|
|
|
|
|
for var in vars
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Reduced costs
|
|
|
|
# Reduced costs
|
|
|
|
if has_duals(data.model)
|
|
|
|
if has_duals(data.model)
|
|
|
@ -89,7 +85,7 @@ end
|
|
|
|
|
|
|
|
|
|
|
|
function add_constraints(
|
|
|
|
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},
|
|
|
|
names::Vector{String},
|
|
|
|
names::Vector{String},
|
|
|
@ -104,8 +100,10 @@ function add_constraints(
|
|
|
|
constr = @constraint(data.model, lhs_expr <= rhs[i])
|
|
|
|
constr = @constraint(data.model, lhs_expr <= rhs[i])
|
|
|
|
elseif sense == ">"
|
|
|
|
elseif sense == ">"
|
|
|
|
constr = @constraint(data.model, lhs_expr >= rhs[i])
|
|
|
|
constr = @constraint(data.model, lhs_expr >= rhs[i])
|
|
|
|
else
|
|
|
|
elseif sense == "="
|
|
|
|
constr = @constraint(data.model, lhs_expr == rhs[i])
|
|
|
|
constr = @constraint(data.model, lhs_expr == rhs[i])
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
error("unknown sense: $(sense)")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
set_name(constr, names[i])
|
|
|
|
set_name(constr, names[i])
|
|
|
|
data.cname_to_constr[names[i]] = constr
|
|
|
|
data.cname_to_constr[names[i]] = constr
|
|
|
@ -116,10 +114,10 @@ end
|
|
|
|
|
|
|
|
|
|
|
|
function are_constraints_satisfied(
|
|
|
|
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},
|
|
|
|
tol::Float64=1e-5,
|
|
|
|
tol::Float64 = 1e-5,
|
|
|
|
)::Vector{Bool}
|
|
|
|
)::Vector{Bool}
|
|
|
|
result = []
|
|
|
|
result = []
|
|
|
|
for (i, sense) in enumerate(senses)
|
|
|
|
for (i, sense) in enumerate(senses)
|
|
|
@ -132,8 +130,10 @@ function are_constraints_satisfied(
|
|
|
|
push!(result, lhs_value <= rhs[i] + tol)
|
|
|
|
push!(result, lhs_value <= rhs[i] + tol)
|
|
|
|
elseif sense == ">"
|
|
|
|
elseif sense == ">"
|
|
|
|
push!(result, lhs_value >= rhs[i] - tol)
|
|
|
|
push!(result, lhs_value >= rhs[i] - tol)
|
|
|
|
else
|
|
|
|
elseif sense == "="
|
|
|
|
push!(result, abs(lhs_value - rhs[i]) <= tol)
|
|
|
|
push!(result, abs(lhs_value - rhs[i]) <= tol)
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
error("unknown sense: $(sense)")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return result
|
|
|
|
return result
|
|
|
@ -148,9 +148,9 @@ function build_test_instance_knapsack()
|
|
|
|
model = Model()
|
|
|
|
model = Model()
|
|
|
|
n = length(weights)
|
|
|
|
n = length(weights)
|
|
|
|
@variable(model, x[0:n-1], Bin)
|
|
|
|
@variable(model, x[0:n-1], Bin)
|
|
|
|
@variable(model, z, lower_bound=0.0, upper_bound=capacity)
|
|
|
|
@variable(model, z, lower_bound = 0.0, upper_bound = capacity)
|
|
|
|
@objective(model, Max, sum(x[i-1] * prices[i] for i in 1:n))
|
|
|
|
@objective(model, Max, sum(x[i-1] * prices[i] for i = 1:n))
|
|
|
|
@constraint(model, eq_capacity, sum(x[i-1] * weights[i] for i in 1:n) - z == 0)
|
|
|
|
@constraint(model, eq_capacity, sum(x[i-1] * weights[i] for i = 1:n) - z == 0)
|
|
|
|
|
|
|
|
|
|
|
|
return JuMPInstance(model).py
|
|
|
|
return JuMPInstance(model).py
|
|
|
|
end
|
|
|
|
end
|
|
|
@ -165,10 +165,7 @@ function build_test_instance_infeasible()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function remove_constraints(
|
|
|
|
function remove_constraints(data::JuMPSolverData, names::Vector{String})::Nothing
|
|
|
|
data::JuMPSolverData,
|
|
|
|
|
|
|
|
names::Vector{String},
|
|
|
|
|
|
|
|
)::Nothing
|
|
|
|
|
|
|
|
for name in names
|
|
|
|
for name in names
|
|
|
|
constr = data.cname_to_constr[name]
|
|
|
|
constr = data.cname_to_constr[name]
|
|
|
|
delete(data.model, constr)
|
|
|
|
delete(data.model, constr)
|
|
|
@ -178,17 +175,13 @@ function remove_constraints(
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function solve(
|
|
|
|
function solve(data::JuMPSolverData; tee::Bool = false, iteration_cb = nothing)
|
|
|
|
data::JuMPSolverData;
|
|
|
|
|
|
|
|
tee::Bool=false,
|
|
|
|
|
|
|
|
iteration_cb=nothing,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
model = data.model
|
|
|
|
model = data.model
|
|
|
|
wallclock_time = 0
|
|
|
|
wallclock_time = 0
|
|
|
|
log = ""
|
|
|
|
log = ""
|
|
|
|
while true
|
|
|
|
while true
|
|
|
|
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
|
|
|
|
if iteration_cb !== nothing
|
|
|
|
if iteration_cb !== nothing
|
|
|
|
iteration_cb() || break
|
|
|
|
iteration_cb() || break
|
|
|
@ -215,18 +208,18 @@ function solve(
|
|
|
|
upper_bound = dual_bound
|
|
|
|
upper_bound = dual_bound
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return miplearn.solvers.internal.MIPSolveStats(
|
|
|
|
return miplearn.solvers.internal.MIPSolveStats(
|
|
|
|
mip_lower_bound=lower_bound,
|
|
|
|
mip_lower_bound = lower_bound,
|
|
|
|
mip_upper_bound=upper_bound,
|
|
|
|
mip_upper_bound = upper_bound,
|
|
|
|
mip_sense=sense,
|
|
|
|
mip_sense = sense,
|
|
|
|
mip_wallclock_time=wallclock_time,
|
|
|
|
mip_wallclock_time = wallclock_time,
|
|
|
|
mip_nodes=1,
|
|
|
|
mip_nodes = 1,
|
|
|
|
mip_log=log,
|
|
|
|
mip_log = log,
|
|
|
|
mip_warm_start_value=nothing,
|
|
|
|
mip_warm_start_value = nothing,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function solve_lp(data::JuMPSolverData; tee::Bool=false)
|
|
|
|
function solve_lp(data::JuMPSolverData; tee::Bool = false)
|
|
|
|
model, bin_vars = data.model, data.bin_vars
|
|
|
|
model, bin_vars = data.model, data.bin_vars
|
|
|
|
for var in bin_vars
|
|
|
|
for var in bin_vars
|
|
|
|
~is_fixed(var) || continue
|
|
|
|
~is_fixed(var) || continue
|
|
|
@ -242,7 +235,7 @@ function solve_lp(data::JuMPSolverData; tee::Bool=false)
|
|
|
|
set_optimizer(model, Clp.Optimizer)
|
|
|
|
set_optimizer(model, Clp.Optimizer)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
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
|
|
|
|
if is_infeasible(data)
|
|
|
|
if is_infeasible(data)
|
|
|
|
data.solution = Dict()
|
|
|
|
data.solution = Dict()
|
|
|
@ -259,9 +252,9 @@ function solve_lp(data::JuMPSolverData; tee::Bool=false)
|
|
|
|
set_binary(var)
|
|
|
|
set_binary(var)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return miplearn.solvers.internal.LPSolveStats(
|
|
|
|
return miplearn.solvers.internal.LPSolveStats(
|
|
|
|
lp_value=obj_value,
|
|
|
|
lp_value = obj_value,
|
|
|
|
lp_log=log,
|
|
|
|
lp_log = log,
|
|
|
|
lp_wallclock_time=wallclock_time,
|
|
|
|
lp_wallclock_time = wallclock_time,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
@ -276,15 +269,8 @@ function set_instance!(
|
|
|
|
model = instance.to_model()
|
|
|
|
model = instance.to_model()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
data.model = model
|
|
|
|
data.model = model
|
|
|
|
data.bin_vars = [
|
|
|
|
data.bin_vars = [var for var in JuMP.all_variables(model) if JuMP.is_binary(var)]
|
|
|
|
var
|
|
|
|
data.varname_to_var = Dict(JuMP.name(var) => var for var in JuMP.all_variables(model))
|
|
|
|
for var in JuMP.all_variables(model)
|
|
|
|
|
|
|
|
if JuMP.is_binary(var)
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
data.varname_to_var = Dict(
|
|
|
|
|
|
|
|
JuMP.name(var) => var
|
|
|
|
|
|
|
|
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()
|
|
|
|
for (ftype, stype) in JuMP.list_of_constraint_types(model)
|
|
|
|
for (ftype, stype) in JuMP.list_of_constraint_types(model)
|
|
|
@ -302,7 +288,7 @@ function fix!(data::JuMPSolverData, solution)
|
|
|
|
for (varname, value) in solution
|
|
|
|
for (varname, value) in solution
|
|
|
|
value !== nothing || continue
|
|
|
|
value !== nothing || continue
|
|
|
|
var = data.varname_to_var[varname]
|
|
|
|
var = data.varname_to_var[varname]
|
|
|
|
JuMP.fix(var, value, force=true)
|
|
|
|
JuMP.fix(var, value, force = true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
@ -317,17 +303,12 @@ end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function is_infeasible(data::JuMPSolverData)
|
|
|
|
function is_infeasible(data::JuMPSolverData)
|
|
|
|
return JuMP.termination_status(data.model) in [
|
|
|
|
return JuMP.termination_status(data.model) in
|
|
|
|
MOI.INFEASIBLE,
|
|
|
|
[MOI.INFEASIBLE, MOI.INFEASIBLE_OR_UNBOUNDED]
|
|
|
|
MOI.INFEASIBLE_OR_UNBOUNDED,
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function get_variables(
|
|
|
|
function get_variables(data::JuMPSolverData; with_static::Bool)
|
|
|
|
data::JuMPSolverData;
|
|
|
|
|
|
|
|
with_static::Bool,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
vars = JuMP.all_variables(data.model)
|
|
|
|
vars = JuMP.all_variables(data.model)
|
|
|
|
lb, ub, types, obj_coeffs = nothing, nothing, nothing, nothing
|
|
|
|
lb, ub, types, obj_coeffs = nothing, nothing, nothing, nothing
|
|
|
|
values, rc = nothing, nothing
|
|
|
|
values, rc = nothing, nothing
|
|
|
@ -343,46 +324,32 @@ function get_variables(
|
|
|
|
if with_static
|
|
|
|
if with_static
|
|
|
|
# Lower bounds
|
|
|
|
# Lower bounds
|
|
|
|
lb = [
|
|
|
|
lb = [
|
|
|
|
JuMP.is_binary(v) ? 0.0 :
|
|
|
|
JuMP.is_binary(v) ? 0.0 : JuMP.has_lower_bound(v) ? JuMP.lower_bound(v) : -Inf for v in vars
|
|
|
|
JuMP.has_lower_bound(v) ? JuMP.lower_bound(v) :
|
|
|
|
|
|
|
|
-Inf
|
|
|
|
|
|
|
|
for v in vars
|
|
|
|
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
# Upper bounds
|
|
|
|
# Upper bounds
|
|
|
|
ub = [
|
|
|
|
ub = [
|
|
|
|
JuMP.is_binary(v) ? 1.0 :
|
|
|
|
JuMP.is_binary(v) ? 1.0 : JuMP.has_upper_bound(v) ? JuMP.upper_bound(v) : Inf for v in vars
|
|
|
|
JuMP.has_upper_bound(v) ? JuMP.upper_bound(v) :
|
|
|
|
|
|
|
|
Inf
|
|
|
|
|
|
|
|
for v in vars
|
|
|
|
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
# Variable types
|
|
|
|
# Variable types
|
|
|
|
types = [
|
|
|
|
types = [JuMP.is_binary(v) ? "B" : JuMP.is_integer(v) ? "I" : "C" for v in vars]
|
|
|
|
JuMP.is_binary(v) ? "B" :
|
|
|
|
|
|
|
|
JuMP.is_integer(v) ? "I" :
|
|
|
|
|
|
|
|
"C"
|
|
|
|
|
|
|
|
for v in vars
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Objective function coefficients
|
|
|
|
# Objective function coefficients
|
|
|
|
obj = objective_function(data.model)
|
|
|
|
obj = objective_function(data.model)
|
|
|
|
obj_coeffs = [
|
|
|
|
obj_coeffs = [v ∈ keys(obj.terms) ? obj.terms[v] : 0.0 for v in vars]
|
|
|
|
v ∈ keys(obj.terms) ? obj.terms[v] : 0.0
|
|
|
|
|
|
|
|
for v in vars
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
rc = isempty(data.reduced_costs) ? nothing : data.reduced_costs
|
|
|
|
rc = isempty(data.reduced_costs) ? nothing : data.reduced_costs
|
|
|
|
|
|
|
|
|
|
|
|
vf = miplearn.solvers.internal.Variables(
|
|
|
|
vf = miplearn.solvers.internal.Variables(
|
|
|
|
names=names,
|
|
|
|
names = to_str_array(names),
|
|
|
|
lower_bounds=lb,
|
|
|
|
lower_bounds = lb,
|
|
|
|
upper_bounds=ub,
|
|
|
|
upper_bounds = ub,
|
|
|
|
types=types,
|
|
|
|
types = to_str_array(types),
|
|
|
|
obj_coeffs=obj_coeffs,
|
|
|
|
obj_coeffs = obj_coeffs,
|
|
|
|
reduced_costs=rc,
|
|
|
|
reduced_costs = rc,
|
|
|
|
values=values,
|
|
|
|
values = values,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return vf
|
|
|
|
return vf
|
|
|
|
end
|
|
|
|
end
|
|
|
@ -394,26 +361,23 @@ function get_constraints(
|
|
|
|
with_sa::Bool,
|
|
|
|
with_sa::Bool,
|
|
|
|
with_lhs::Bool,
|
|
|
|
with_lhs::Bool,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
names = []
|
|
|
|
names = String[]
|
|
|
|
senses, lhs, rhs = nothing, nothing, nothing
|
|
|
|
senses, lhs, rhs = nothing, nothing, nothing
|
|
|
|
dual_values = nothing
|
|
|
|
dual_values = nothing
|
|
|
|
|
|
|
|
|
|
|
|
if !isempty(data.dual_values)
|
|
|
|
if !isempty(data.dual_values)
|
|
|
|
dual_values = []
|
|
|
|
dual_values = Float64[]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
if with_static
|
|
|
|
if with_static
|
|
|
|
senses, lhs, rhs = [], [], []
|
|
|
|
senses, lhs, rhs = String[], [], Float64[]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
for (ftype, stype) in JuMP.list_of_constraint_types(data.model)
|
|
|
|
for (ftype, stype) in JuMP.list_of_constraint_types(data.model)
|
|
|
|
ftype in [JuMP.AffExpr, JuMP.VariableRef] || error("Unsupported constraint type: ($ftype, $stype)")
|
|
|
|
ftype in [JuMP.AffExpr, JuMP.VariableRef] ||
|
|
|
|
|
|
|
|
error("Unsupported constraint type: ($ftype, $stype)")
|
|
|
|
for constr in JuMP.all_constraints(data.model, ftype, stype)
|
|
|
|
for constr in JuMP.all_constraints(data.model, ftype, stype)
|
|
|
|
cset = MOI.get(
|
|
|
|
cset = MOI.get(constr.model.moi_backend, MOI.ConstraintSet(), constr.index)
|
|
|
|
constr.model.moi_backend,
|
|
|
|
|
|
|
|
MOI.ConstraintSet(),
|
|
|
|
|
|
|
|
constr.index,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
name = JuMP.name(constr)
|
|
|
|
name = JuMP.name(constr)
|
|
|
|
length(name) > 0 || continue
|
|
|
|
length(name) > 0 || continue
|
|
|
|
push!(names, name)
|
|
|
|
push!(names, name)
|
|
|
@ -429,19 +393,21 @@ function get_constraints(
|
|
|
|
lhs,
|
|
|
|
lhs,
|
|
|
|
[
|
|
|
|
[
|
|
|
|
(
|
|
|
|
(
|
|
|
|
MOI.get(
|
|
|
|
pybytes(
|
|
|
|
constr.model.moi_backend,
|
|
|
|
MOI.get(
|
|
|
|
MOI.VariableName(),
|
|
|
|
constr.model.moi_backend,
|
|
|
|
term.variable_index
|
|
|
|
MOI.VariableName(),
|
|
|
|
|
|
|
|
term.variable_index,
|
|
|
|
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
term.coefficient,
|
|
|
|
term.coefficient,
|
|
|
|
)
|
|
|
|
) for term in
|
|
|
|
for term in MOI.get(
|
|
|
|
MOI.get(
|
|
|
|
constr.model.moi_backend,
|
|
|
|
constr.model.moi_backend,
|
|
|
|
MOI.ConstraintFunction(),
|
|
|
|
MOI.ConstraintFunction(),
|
|
|
|
constr.index,
|
|
|
|
constr.index,
|
|
|
|
).terms
|
|
|
|
).terms
|
|
|
|
]
|
|
|
|
],
|
|
|
|
)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if stype == MOI.EqualTo{Float64}
|
|
|
|
if stype == MOI.EqualTo{Float64}
|
|
|
@ -464,11 +430,11 @@ function get_constraints(
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
return miplearn.solvers.internal.Constraints(
|
|
|
|
return miplearn.solvers.internal.Constraints(
|
|
|
|
names=names,
|
|
|
|
names = to_str_array(names),
|
|
|
|
senses=senses,
|
|
|
|
senses = to_str_array(senses),
|
|
|
|
lhs=lhs,
|
|
|
|
lhs = lhs,
|
|
|
|
rhs=rhs,
|
|
|
|
rhs = rhs,
|
|
|
|
dual_values=dual_values,
|
|
|
|
dual_values = dual_values,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
@ -497,14 +463,14 @@ function __init_JuMPSolver__()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
add_constraints(
|
|
|
|
add_constraints(
|
|
|
|
self.data,
|
|
|
|
self.data,
|
|
|
|
lhs=lhs,
|
|
|
|
lhs = lhs,
|
|
|
|
rhs=cf.rhs,
|
|
|
|
rhs = cf.rhs,
|
|
|
|
senses=cf.senses,
|
|
|
|
senses = from_str_array(cf.senses),
|
|
|
|
names=cf.names,
|
|
|
|
names = from_str_array(cf.names),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
function are_constraints_satisfied(self, cf; tol=1e-5)
|
|
|
|
function are_constraints_satisfied(self, cf; tol = 1e-5)
|
|
|
|
lhs = cf.lhs
|
|
|
|
lhs = cf.lhs
|
|
|
|
if lhs isa Matrix
|
|
|
|
if lhs isa Matrix
|
|
|
|
# Undo incorrect automatic conversion performed by PyCall
|
|
|
|
# Undo incorrect automatic conversion performed by PyCall
|
|
|
@ -512,38 +478,30 @@ function __init_JuMPSolver__()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return are_constraints_satisfied(
|
|
|
|
return are_constraints_satisfied(
|
|
|
|
self.data,
|
|
|
|
self.data,
|
|
|
|
lhs=lhs,
|
|
|
|
lhs = lhs,
|
|
|
|
rhs=cf.rhs,
|
|
|
|
rhs = cf.rhs,
|
|
|
|
senses=cf.senses,
|
|
|
|
senses = from_str_array(cf.senses),
|
|
|
|
tol=tol,
|
|
|
|
tol = tol,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
build_test_instance_infeasible(self) =
|
|
|
|
build_test_instance_infeasible(self) = build_test_instance_infeasible()
|
|
|
|
build_test_instance_infeasible()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
build_test_instance_knapsack(self) =
|
|
|
|
build_test_instance_knapsack(self) = build_test_instance_knapsack()
|
|
|
|
build_test_instance_knapsack()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clone(self) = JuMPSolver(self.data.optimizer_factory)
|
|
|
|
clone(self) = JuMPSolver(self.data.optimizer_factory)
|
|
|
|
|
|
|
|
|
|
|
|
fix(self, solution) =
|
|
|
|
fix(self, solution) = fix!(self.data, solution)
|
|
|
|
fix!(self.data, solution)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
get_solution(self) =
|
|
|
|
get_solution(self) = isempty(self.data.solution) ? nothing : self.data.solution
|
|
|
|
isempty(self.data.solution) ? nothing : self.data.solution
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
get_constraints(
|
|
|
|
get_constraints(self; with_static = true, with_sa = true, with_lhs = true) =
|
|
|
|
self;
|
|
|
|
get_constraints(
|
|
|
|
with_static=true,
|
|
|
|
self.data,
|
|
|
|
with_sa=true,
|
|
|
|
with_static = with_static,
|
|
|
|
with_lhs=true,
|
|
|
|
with_sa = with_sa,
|
|
|
|
) = get_constraints(
|
|
|
|
with_lhs = with_lhs,
|
|
|
|
self.data,
|
|
|
|
)
|
|
|
|
with_static=with_static,
|
|
|
|
|
|
|
|
with_sa=with_sa,
|
|
|
|
|
|
|
|
with_lhs=with_lhs,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
get_constraint_attrs(self) = [
|
|
|
|
get_constraint_attrs(self) = [
|
|
|
|
# "basis_status",
|
|
|
|
# "basis_status",
|
|
|
@ -560,13 +518,10 @@ function __init_JuMPSolver__()
|
|
|
|
"user_features",
|
|
|
|
"user_features",
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
get_variables(
|
|
|
|
get_variables(self; with_static = true, with_sa = true) =
|
|
|
|
self;
|
|
|
|
get_variables(self.data; with_static = with_static)
|
|
|
|
with_static=true,
|
|
|
|
|
|
|
|
with_sa=true,
|
|
|
|
|
|
|
|
) = get_variables(self.data; with_static=with_static)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
get_variable_attrs(self) = [
|
|
|
|
get_variable_attrs(self) = [
|
|
|
|
"names",
|
|
|
|
"names",
|
|
|
|
# "basis_status",
|
|
|
|
# "basis_status",
|
|
|
|
"categories",
|
|
|
|
"categories",
|
|
|
@ -585,35 +540,24 @@ function __init_JuMPSolver__()
|
|
|
|
"values",
|
|
|
|
"values",
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
is_infeasible(self) =
|
|
|
|
is_infeasible(self) = is_infeasible(self.data)
|
|
|
|
is_infeasible(self.data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
remove_constraints(self, names) =
|
|
|
|
remove_constraints(self, names) = remove_constraints(self.data, [n for n in names])
|
|
|
|
remove_constraints(
|
|
|
|
|
|
|
|
self.data,
|
|
|
|
|
|
|
|
[n for n in names],
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
set_instance(self, instance, model=nothing) =
|
|
|
|
set_instance(self, instance, model = nothing) =
|
|
|
|
set_instance!(self.data, instance, model=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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
solve(
|
|
|
|
solve(
|
|
|
|
self;
|
|
|
|
self;
|
|
|
|
tee=false,
|
|
|
|
tee = false,
|
|
|
|
iteration_cb=nothing,
|
|
|
|
iteration_cb = nothing,
|
|
|
|
lazy_cb=nothing,
|
|
|
|
lazy_cb = nothing,
|
|
|
|
user_cut_cb=nothing,
|
|
|
|
user_cut_cb = nothing,
|
|
|
|
) = solve(
|
|
|
|
) = solve(self.data, tee = tee, iteration_cb = iteration_cb)
|
|
|
|
self.data,
|
|
|
|
|
|
|
|
tee=tee,
|
|
|
|
solve_lp(self; tee = false) = solve_lp(self.data, tee = tee)
|
|
|
|
iteration_cb=iteration_cb,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
solve_lp(self; tee=false) =
|
|
|
|
|
|
|
|
solve_lp(self.data, tee=tee)
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
copy!(JuMPSolver, Class)
|
|
|
|
copy!(JuMPSolver, Class)
|
|
|
|
end
|
|
|
|
end
|
|
|
|