Use compact variable and constraint features

master
Alinson S. Xavier 4 years ago
parent 185ed40648
commit e9fbf4a3c7

@ -11,9 +11,9 @@ uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
[[BenchmarkTools]]
deps = ["JSON", "Logging", "Printf", "Statistics", "UUIDs"]
git-tree-sha1 = "9e62e66db34540a0c919d72172cc2f642ac71260"
git-tree-sha1 = "068fda9b756e41e6c75da7b771e6f89fa8a43d15"
uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
version = "0.5.0"
version = "0.7.0"
[[Bzip2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
@ -29,9 +29,9 @@ version = "0.5.1"
[[ChainRulesCore]]
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "44e9f638aa9ed1ad58885defc568c133010140aa"
git-tree-sha1 = "e6b23566e025d3b0d9ccc397f5c7a134af552e27"
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
version = "0.9.37"
version = "0.9.42"
[[CodecBzip2]]
deps = ["Bzip2_jll", "Libdl", "TranscodingStreams"]
@ -53,9 +53,9 @@ version = "0.3.0"
[[Compat]]
deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"]
git-tree-sha1 = "ac4132ad78082518ec2037ae5770b6e796f7f956"
git-tree-sha1 = "0a817fbe51c976de090aa8c997b7b719b786118d"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.27.0"
version = "3.28.0"
[[CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
@ -63,9 +63,9 @@ uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
[[Conda]]
deps = ["JSON", "VersionParsing"]
git-tree-sha1 = "6231e40619c15148bcb80aa19d731e629877d762"
git-tree-sha1 = "299304989a5e6473d985212c28928899c74e9421"
uuid = "8f4d0f93-b110-5947-807f-2305c1781a2d"
version = "1.5.1"
version = "1.5.2"
[[DataStructures]]
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
@ -109,9 +109,9 @@ version = "0.10.18"
[[HTTP]]
deps = ["Base64", "Dates", "IniFile", "MbedTLS", "NetworkOptions", "Sockets", "URIs"]
git-tree-sha1 = "c9f380c76d8aaa1fa7ea9cf97bddbc0d5b15adc2"
git-tree-sha1 = "b855bf8247d6e946c75bb30f593bfe7fe591058d"
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
version = "0.9.5"
version = "0.9.8"
[[IniFile]]
deps = ["Test"]
@ -124,9 +124,10 @@ deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
[[JLLWrappers]]
git-tree-sha1 = "a431f5f2ca3f4feef3bd7a5e94b8b8d4f2f647a0"
deps = ["Preferences"]
git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e"
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
version = "1.2.0"
version = "1.3.0"
[[JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
@ -141,10 +142,10 @@ uuid = "7d188eb4-7ad8-530c-ae41-71a32a6d4692"
version = "0.3.3"
[[JuMP]]
deps = ["Calculus", "DataStructures", "ForwardDiff", "JSON", "LinearAlgebra", "MathOptInterface", "MutableArithmetics", "NaNMath", "Random", "SparseArrays", "SpecialFunctions", "Statistics"]
git-tree-sha1 = "e952f49e2242fa21edcf27bbd6c67041685bee5d"
deps = ["Calculus", "DataStructures", "ForwardDiff", "JSON", "LinearAlgebra", "MathOptInterface", "MutableArithmetics", "NaNMath", "Printf", "Random", "SparseArrays", "SpecialFunctions", "Statistics"]
git-tree-sha1 = "8dfc5df8aad9f2cfebc8371b69700efd02060827"
uuid = "4076af6c-e467-56ae-b986-b466b2749572"
version = "0.21.6"
version = "0.21.8"
[[LibCURL]]
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
@ -184,9 +185,9 @@ uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
[[MathOptInterface]]
deps = ["BenchmarkTools", "CodecBzip2", "CodecZlib", "JSON", "JSONSchema", "LinearAlgebra", "MutableArithmetics", "OrderedCollections", "SparseArrays", "Test", "Unicode"]
git-tree-sha1 = "606efe4246da5407d7505265a1ead72467528996"
git-tree-sha1 = "cd3057ca89a9ab83ce37ec42324523b8db0c60dc"
uuid = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
version = "0.9.20"
version = "0.9.21"
[[MbedTLS]]
deps = ["Dates", "MbedTLS_jll", "Random", "Sockets"]
@ -206,9 +207,9 @@ uuid = "14a3606d-f60d-562e-9121-12d972cd8159"
[[MutableArithmetics]]
deps = ["LinearAlgebra", "SparseArrays", "Test"]
git-tree-sha1 = "ff3aa3e4dbc837f80c2031de2f90125c8b3793f3"
git-tree-sha1 = "ad9b2bce6021631e0e20706d361972343a03e642"
uuid = "d8a4904e-b15c-11e9-3269-09a3773c0cb0"
version = "0.2.15"
version = "0.2.19"
[[NaNMath]]
git-tree-sha1 = "bfe47e760d60b82b66b61d2d44128b62e3a369fb"
@ -220,14 +221,14 @@ uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
[[OpenSpecFun_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "9db77584158d0ab52307f8c04f8e7c08ca76b5b3"
git-tree-sha1 = "b9b8b8ed236998f91143938a760c2112dceeb2b4"
uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
version = "0.5.3+4"
version = "0.5.4+0"
[[OrderedCollections]]
git-tree-sha1 = "4fa2ba51070ec13fcc7517db714445b4ab986bdf"
git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c"
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
version = "1.4.0"
version = "1.4.1"
[[Parsers]]
deps = ["Dates"]
@ -239,15 +240,21 @@ version = "1.1.0"
deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
[[Preferences]]
deps = ["TOML"]
git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
version = "1.2.2"
[[Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
[[PyCall]]
deps = ["Conda", "Dates", "Libdl", "LinearAlgebra", "MacroTools", "Serialization", "VersionParsing"]
git-tree-sha1 = "dd1a970b543bd02efce2984582e996af28cab27f"
git-tree-sha1 = "169bb8ea6b1b143c5cf57df6d34d022a7b60c6db"
uuid = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
version = "1.92.2"
version = "1.92.3"
[[REPL]]
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
@ -282,9 +289,9 @@ version = "1.3.0"
[[StaticArrays]]
deps = ["LinearAlgebra", "Random", "Statistics"]
git-tree-sha1 = "2f01a51c23eed210ff4a1be102c4cc8236b66e5b"
git-tree-sha1 = "fb46e45ef2cade8be20bb445b3ffeca3c6d6f7d3"
uuid = "90137ffa-7385-5640-81b9-e52037218182"
version = "1.1.0"
version = "1.1.3"
[[Statistics]]
deps = ["LinearAlgebra", "SparseArrays"]
@ -315,9 +322,9 @@ uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa"
version = "0.9.5"
[[URIs]]
git-tree-sha1 = "7855809b88d7b16e9b029afd17880930626f54a2"
git-tree-sha1 = "97bbe755a53fe859669cd907f2d96aee8d2c1355"
uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
version = "1.2.0"
version = "1.3.0"
[[UUIDs]]
deps = ["Random", "SHA"]

@ -13,9 +13,7 @@ PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
[compat]
Conda = "1.4"
JuMP = "0.21"
MathOptInterface = "0.9"
PyCall = "1"
TimerOutputs = "0.5"
julia = "1"

2
deps/build.jl vendored

@ -5,7 +5,7 @@ function install_miplearn()
Conda.update()
pip = joinpath(dirname(pyimport("sys").executable), "pip")
isfile(pip) || error("$pip: invalid path")
run(`$pip install miplearn==0.2.0.dev3`)
run(`$pip install miplearn==0.2.0.dev5`)
end
install_miplearn()

@ -4,46 +4,46 @@
@pydef mutable struct JuMPInstance <: miplearn.Instance
function __init__(self, model)
init_miplearn_ext(model)
features = model.ext[:miplearn][:features]
self.model = model
# Copy training data
training_data = []
for sample in self.model.ext[:miplearn][:training_samples]
pysample = miplearn.TrainingSample()
pysample.__dict__ = sample
push!(training_data, pysample)
end
self.training_data = training_data
# init_miplearn_ext(model)
# features = model.ext[:miplearn][:features]
# # Copy training data
# training_data = []
# for sample in self.model.ext[:miplearn][:training_samples]
# pysample = miplearn.TrainingSample()
# pysample.__dict__ = sample
# push!(training_data, pysample)
# end
# self.training_data = training_data
# Copy features to data classes
self.features = miplearn.Features(
instance=miplearn.InstanceFeatures(
user_features=PyCall.array2py(
features[:instance][:user_features],
),
lazy_constraint_count=0,
),
variables=Dict(
varname => miplearn.VariableFeatures(
category=vfeatures[:category],
user_features=PyCall.array2py(
vfeatures[:user_features],
),
)
for (varname, vfeatures) in features[:variables]
),
constraints=Dict(
cname => miplearn.ConstraintFeatures(
category=cfeat[:category],
user_features=PyCall.array2py(
cfeat[:user_features],
),
)
for (cname, cfeat) in features[:constraints]
),
)
# # Copy features to data classes
# self.features = miplearn.Features(
# instance=miplearn.InstanceFeatures(
# user_features=PyCall.array2py(
# features[:instance][:user_features],
# ),
# lazy_constraint_count=0,
# ),
# variables=Dict(
# varname => miplearn.VariableFeatures(
# category=vfeatures[:category],
# user_features=PyCall.array2py(
# vfeatures[:user_features],
# ),
# )
# for (varname, vfeatures) in features[:variables]
# ),
# constraints=Dict(
# cname => miplearn.ConstraintFeatures(
# category=cfeat[:category],
# user_features=PyCall.array2py(
# cfeat[:user_features],
# ),
# )
# for (cname, cfeat) in features[:constraints]
# ),
# )
end
function to_model(self)

@ -14,8 +14,10 @@ mutable struct JuMPSolverData
instance
model
bin_vars
solution::Union{Nothing, Dict{String,Float64}}
solution
cname_to_constr
reduced_costs
dual_values
end
@ -26,34 +28,27 @@ Optimizes a given JuMP model while capturing the solver log, then returns that l
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)
original_stdout = stdout
rd, wr = redirect_stdout()
task = @async begin
log = ""
while true
line = String(readavailable(rd))
isopen(rd) || break
log *= String(line)
if tee
print(original_stdout, line)
flush(original_stdout)
logname = tempname()
logfile = open(logname, "w")
redirect_stdout(logfile) do
JuMP.optimize!(model)
Base.Libc.flush_cstdio()
end
close(logfile)
log = String(read(logname))
rm(logname)
if tee
println(log)
end
return log
end
JuMP.optimize!(model)
sleep(1)
redirect_stdout(original_stdout)
close(rd)
return fetch(task)
end
function solve(
data::JuMPSolverData;
tee::Bool=false,
iteration_cb,
)::Dict
iteration_cb=nothing,
)
instance, model = data.instance, data.model
wallclock_time = 0
log = ""
@ -78,14 +73,14 @@ function solve(
lower_bound = primal_bound
upper_bound = dual_bound
end
return Dict(
"Lower bound" => lower_bound,
"Upper bound" => upper_bound,
"Sense" => sense,
"Wallclock time" => wallclock_time,
"Nodes" => 1,
"MIP log" => log,
"Warm start value" => nothing,
return miplearn.solvers.internal.MIPSolveStats(
mip_lower_bound=lower_bound,
mip_upper_bound=upper_bound,
mip_sense=sense,
mip_wallclock_time=wallclock_time,
mip_nodes=1,
mip_log=log,
mip_warm_start_value=nothing,
)
end
@ -97,24 +92,55 @@ function solve_lp(data::JuMPSolverData; tee::Bool=false)
JuMP.set_upper_bound(var, 1.0)
JuMP.set_lower_bound(var, 0.0)
end
wallclock_time = @elapsed begin
log = optimize_and_capture_output!(model, tee=tee)
end
update_solution!(data)
obj_value = JuMP.objective_value(model)
for var in bin_vars
JuMP.set_binary(var)
end
return Dict(
"LP value" => obj_value,
"LP log" => log,
return miplearn.solvers.internal.LPSolveStats(
lp_value=obj_value,
lp_log=log,
lp_wallclock_time=wallclock_time,
)
end
function update_solution!(data::JuMPSolverData)
data.solution = Dict(
JuMP.name(var) => JuMP.value(var)
for var in JuMP.all_variables(data.model)
)
vars = JuMP.all_variables(data.model)
data.solution = [JuMP.value(var) for var in vars]
# Reduced costs
if has_duals(data.model)
data.reduced_costs = []
for var in vars
rc = 0.0
if has_upper_bound(var)
rc += shadow_price(UpperBoundRef(var))
end
if has_lower_bound(var)
# FIXME: Remove negative sign
rc -= shadow_price(LowerBoundRef(var))
end
if is_fixed(var)
rc += shadow_price(FixRef(var))
end
push!(data.reduced_costs, rc)
end
data.dual_values = Dict()
for (ftype, stype) in JuMP.list_of_constraint_types(data.model)
for constr in JuMP.all_constraints(data.model, ftype, stype)
# FIXME: Remove negative sign
data.dual_values[constr] = -JuMP.dual(constr)
end
end
else
data.reduced_costs = nothing
data.dual_values = nothing
end
end
@ -195,6 +221,151 @@ function get_constraint_sense(data::JuMPSolverData, cname)
end
function get_variables(
data::JuMPSolverData;
with_static::Bool,
)
vars = JuMP.all_variables(data.model)
lb, ub, types, obj_coeffs = nothing, nothing, nothing, nothing
rc = nothing
# Variable names
names = Tuple(JuMP.name.(vars))
if with_static
# Lower bounds
lb = Tuple(
JuMP.is_binary(v) ? 0.0 :
JuMP.has_lower_bound(v) ? JuMP.lower_bound(v) :
-Inf
for v in vars
)
# Upper bounds
ub = Tuple(
JuMP.is_binary(v) ? 1.0 :
JuMP.has_upper_bound(v) ? JuMP.upper_bound(v) :
Inf
for v in vars
)
# Variable types
types = Tuple(
JuMP.is_binary(v) ? "B" :
JuMP.is_integer(v) ? "I" :
"C"
for v in vars
)
# Objective function coefficients
obj = objective_function(data.model)
obj_coeffs = Tuple(
v keys(obj.terms) ? obj.terms[v] : 0.0
for v in vars
)
end
rc = data.reduced_costs === nothing ? nothing : Tuple(data.reduced_costs)
values = data.solution === nothing ? nothing : Tuple(data.solution)
return miplearn.features.VariableFeatures(
names=names,
lower_bounds=lb,
upper_bounds=ub,
types=types,
obj_coeffs=obj_coeffs,
reduced_costs=rc,
values=values,
)
end
function get_constraints(
data::JuMPSolverData;
with_static::Bool,
)
names = []
senses, lhs, rhs = nothing, nothing, nothing
dual_values = nothing
if data.dual_values !== nothing
dual_values = []
end
if with_static
senses, lhs, rhs = [], [], []
end
for (ftype, stype) in JuMP.list_of_constraint_types(data.model)
ftype in [JuMP.AffExpr, JuMP.VariableRef] || error("Unsupported constraint type: ($ftype, $stype)")
for constr in JuMP.all_constraints(data.model, ftype, stype)
cset = MOI.get(
constr.model.moi_backend,
MOI.ConstraintSet(),
constr.index,
)
name = JuMP.name(constr)
length(name) > 0 || continue
push!(names, name)
if data.dual_values !== nothing
push!(dual_values, data.dual_values[constr])
end
if with_static
if ftype == JuMP.AffExpr
push!(
lhs,
Tuple(
(
MOI.get(
constr.model.moi_backend,
MOI.VariableName(),
term.variable_index
),
term.coefficient,
)
for term in MOI.get(
constr.model.moi_backend,
MOI.ConstraintFunction(),
constr.index,
).terms
)
)
if stype == MOI.EqualTo{Float64}
push!(senses, "=")
push!(rhs, cset.value)
elseif stype == MOI.LessThan{Float64}
push!(senses, "<")
push!(rhs, cset.upper)
elseif stype == MOI.GreaterThan{Float64}
push!(senses, ">")
push!(rhs, cset.lower)
else
error("Unsupported set: $stype")
end
else
error("Unsupported ftype: $ftype")
end
end
end
end
function to_tuple(x)
x !== nothing || return nothing
return Tuple(x)
end
return miplearn.features.ConstraintFeatures(
names=to_tuple(names),
senses=to_tuple(senses),
lhs=to_tuple(lhs),
rhs=to_tuple(rhs),
dual_values=to_tuple(dual_values),
)
end
# Constraints: ScalarAffineFunction, LessThan
# -------------------------------------------------------------------------
function get_constraint_rhs(
@ -355,6 +526,23 @@ function get_constraint_sense(
return "="
end
# Test instances
# ---------------------------------------------
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
function __init__(self; optimizer)
@ -366,6 +554,8 @@ end
nothing, # bin_vars
nothing, # solution
nothing, # cname_to_constr
nothing, # reduced_costs
nothing, # dual_values
)
end
@ -381,9 +571,9 @@ end
solve(
self;
tee=false,
iteration_cb,
lazy_cb,
user_cut_cb,
iteration_cb=nothing,
lazy_cb=nothing,
user_cut_cb=nothing,
) = solve(
self.data,
tee=tee,
@ -396,8 +586,8 @@ end
get_solution(self) =
self.data.solution
get_variables(self) =
get_variables(self.data)
get_variables(self; with_static=true) =
get_variables(self.data; with_static=with_static)
set_branching_priorities(self, priorities) =
@warn "JuMPSolver: set_branching_priorities not implemented"
@ -411,6 +601,9 @@ end
is_infeasible(self) =
is_infeasible(self.data)
get_constraints(self; with_static=true) =
get_constraints(self.data; with_static=with_static)
get_constraint_ids(self) =
get_constraint_ids(self.data)
@ -423,8 +616,45 @@ end
get_constraint_sense(self, cname) =
get_constraint_sense(self.data, cname)
build_test_instance_knapsack(self) =
build_test_instance_knapsack()
clone(self) = self
get_variable_attrs(self) = [
"names",
# "basis_status",
"categories",
"lower_bounds",
"obj_coeffs",
"reduced_costs",
# "sa_lb_down",
# "sa_lb_up",
# "sa_obj_down",
# "sa_obj_up",
# "sa_ub_down",
# "sa_ub_up",
"types",
"upper_bounds",
"user_features",
"values",
]
get_constraint_attrs(self) = [
# "basis_status",
"categories",
"dual_values",
"lazy",
"lhs",
"names",
"rhs",
# "sa_rhs_down",
# "sa_rhs_up",
"senses",
# "slacks",
"user_features",
]
add_cut(self) = error("not implemented")
extract_constraint(self) = error("not implemented")
is_constraint_satisfied(self) = error("not implemented")
@ -433,6 +663,11 @@ end
get_inequality_slacks(self) = error("not implemented")
get_dual(self) = error("not implemented")
get_sense(self) = error("not implemented")
build_test_instance_infeasible(self) = error("not implemented")
build_test_instance_redundancy(self) = error("not implemented")
get_constraints_old(self) = error("not implemented")
is_constraint_satisfied_old(self) = error("not implemented")
remove_constraint(self) = error("not implemented")
end
export JuMPSolver, solve!, fit!, add!

@ -1,24 +0,0 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
macro pycall(expr)
quote
err_msg = nothing
result = nothing
try
result = $(esc(expr))
catch err
args = err.val.args[1]
if (err isa PyCall.PyError) && (args isa String) && startswith(args, "Julia")
err_msg = replace(args, r"Stacktrace.*" => "")
else
rethrow(err)
end
end
if err_msg != nothing
error(err_msg)
end
result
end
end

@ -4,6 +4,7 @@
[deps]
Cbc = "9961bab8-2fa3-5c5a-9d89-47fab24efd76"
Gurobi = "2e9cd046-0924-5485-92f1-d5272153d98b"
Conda = "8f4d0f93-b110-5947-807f-2305c1781a2d"
JSON2 = "2535ab7d-5cd8-5a07-80ac-9b1792aadce3"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"

@ -4,18 +4,22 @@
using Test
using MIPLearn
using Cbc
using Gurobi
using PyCall
using JuMP
miplearn_tests = pyimport("miplearn.solvers.tests")
traceback = pyimport("traceback")
@testset "JuMPSolver" begin
model = MIPLearn.knapsack_model(
[23., 26., 20., 18.],
[505., 352., 458., 220.],
67.0,
)
instance = JuMPInstance(model)
solver = JuMPSolver(optimizer=Cbc.Optimizer)
miplearn_tests.test_internal_solver(solver, instance, model)
solver = JuMPSolver(optimizer=Gurobi.Optimizer)
try
miplearn_tests.run_internal_solver_tests(solver)
catch e
if isa(e, PyCall.PyError)
printstyled("Uncaught Python exception:\n", bold=true, color=:red)
traceback.print_exception(e.T, e.val, e.traceback)
end
rethrow()
end
end

@ -8,6 +8,6 @@ using MIPLearn
MIPLearn.setup_logger()
@testset "MIPLearn" begin
#include("modeling/jump_solver_test.jl")
include("modeling/learning_solver_test.jl")
include("modeling/jump_solver_test.jl")
#include("modeling/learning_solver_test.jl")
end
Loading…
Cancel
Save