mirror of
https://github.com/ANL-CEEESA/MIPLearn.jl.git
synced 2025-12-06 08:28:52 -06:00
Use compact variable and constraint features
This commit is contained in:
@@ -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
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)
|
||||
end
|
||||
end
|
||||
return log
|
||||
logname = tempname()
|
||||
logfile = open(logname, "w")
|
||||
redirect_stdout(logfile) do
|
||||
JuMP.optimize!(model)
|
||||
Base.Libc.flush_cstdio()
|
||||
end
|
||||
JuMP.optimize!(model)
|
||||
sleep(1)
|
||||
redirect_stdout(original_stdout)
|
||||
close(rd)
|
||||
return fetch(task)
|
||||
close(logfile)
|
||||
log = String(read(logname))
|
||||
rm(logname)
|
||||
if tee
|
||||
println(log)
|
||||
end
|
||||
return log
|
||||
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
|
||||
log = optimize_and_capture_output!(model, tee=tee)
|
||||
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
|
||||
Reference in New Issue
Block a user