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]]
|
[[BenchmarkTools]]
|
||||||
deps = ["JSON", "Logging", "Printf", "Statistics", "UUIDs"]
|
deps = ["JSON", "Logging", "Printf", "Statistics", "UUIDs"]
|
||||||
git-tree-sha1 = "9e62e66db34540a0c919d72172cc2f642ac71260"
|
git-tree-sha1 = "068fda9b756e41e6c75da7b771e6f89fa8a43d15"
|
||||||
uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
|
uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
|
||||||
version = "0.5.0"
|
version = "0.7.0"
|
||||||
|
|
||||||
[[Bzip2_jll]]
|
[[Bzip2_jll]]
|
||||||
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
|
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
|
||||||
@@ -29,9 +29,9 @@ version = "0.5.1"
|
|||||||
|
|
||||||
[[ChainRulesCore]]
|
[[ChainRulesCore]]
|
||||||
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
|
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
|
||||||
git-tree-sha1 = "44e9f638aa9ed1ad58885defc568c133010140aa"
|
git-tree-sha1 = "e6b23566e025d3b0d9ccc397f5c7a134af552e27"
|
||||||
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
|
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
|
||||||
version = "0.9.37"
|
version = "0.9.42"
|
||||||
|
|
||||||
[[CodecBzip2]]
|
[[CodecBzip2]]
|
||||||
deps = ["Bzip2_jll", "Libdl", "TranscodingStreams"]
|
deps = ["Bzip2_jll", "Libdl", "TranscodingStreams"]
|
||||||
@@ -53,9 +53,9 @@ version = "0.3.0"
|
|||||||
|
|
||||||
[[Compat]]
|
[[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"]
|
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"
|
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
|
||||||
version = "3.27.0"
|
version = "3.28.0"
|
||||||
|
|
||||||
[[CompilerSupportLibraries_jll]]
|
[[CompilerSupportLibraries_jll]]
|
||||||
deps = ["Artifacts", "Libdl"]
|
deps = ["Artifacts", "Libdl"]
|
||||||
@@ -63,9 +63,9 @@ uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
|
|||||||
|
|
||||||
[[Conda]]
|
[[Conda]]
|
||||||
deps = ["JSON", "VersionParsing"]
|
deps = ["JSON", "VersionParsing"]
|
||||||
git-tree-sha1 = "6231e40619c15148bcb80aa19d731e629877d762"
|
git-tree-sha1 = "299304989a5e6473d985212c28928899c74e9421"
|
||||||
uuid = "8f4d0f93-b110-5947-807f-2305c1781a2d"
|
uuid = "8f4d0f93-b110-5947-807f-2305c1781a2d"
|
||||||
version = "1.5.1"
|
version = "1.5.2"
|
||||||
|
|
||||||
[[DataStructures]]
|
[[DataStructures]]
|
||||||
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
|
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
|
||||||
@@ -109,9 +109,9 @@ version = "0.10.18"
|
|||||||
|
|
||||||
[[HTTP]]
|
[[HTTP]]
|
||||||
deps = ["Base64", "Dates", "IniFile", "MbedTLS", "NetworkOptions", "Sockets", "URIs"]
|
deps = ["Base64", "Dates", "IniFile", "MbedTLS", "NetworkOptions", "Sockets", "URIs"]
|
||||||
git-tree-sha1 = "c9f380c76d8aaa1fa7ea9cf97bddbc0d5b15adc2"
|
git-tree-sha1 = "b855bf8247d6e946c75bb30f593bfe7fe591058d"
|
||||||
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
|
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
|
||||||
version = "0.9.5"
|
version = "0.9.8"
|
||||||
|
|
||||||
[[IniFile]]
|
[[IniFile]]
|
||||||
deps = ["Test"]
|
deps = ["Test"]
|
||||||
@@ -124,9 +124,10 @@ deps = ["Markdown"]
|
|||||||
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
|
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
|
||||||
|
|
||||||
[[JLLWrappers]]
|
[[JLLWrappers]]
|
||||||
git-tree-sha1 = "a431f5f2ca3f4feef3bd7a5e94b8b8d4f2f647a0"
|
deps = ["Preferences"]
|
||||||
|
git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e"
|
||||||
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
|
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
|
||||||
version = "1.2.0"
|
version = "1.3.0"
|
||||||
|
|
||||||
[[JSON]]
|
[[JSON]]
|
||||||
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
|
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
|
||||||
@@ -141,10 +142,10 @@ uuid = "7d188eb4-7ad8-530c-ae41-71a32a6d4692"
|
|||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
|
||||||
[[JuMP]]
|
[[JuMP]]
|
||||||
deps = ["Calculus", "DataStructures", "ForwardDiff", "JSON", "LinearAlgebra", "MathOptInterface", "MutableArithmetics", "NaNMath", "Random", "SparseArrays", "SpecialFunctions", "Statistics"]
|
deps = ["Calculus", "DataStructures", "ForwardDiff", "JSON", "LinearAlgebra", "MathOptInterface", "MutableArithmetics", "NaNMath", "Printf", "Random", "SparseArrays", "SpecialFunctions", "Statistics"]
|
||||||
git-tree-sha1 = "e952f49e2242fa21edcf27bbd6c67041685bee5d"
|
git-tree-sha1 = "8dfc5df8aad9f2cfebc8371b69700efd02060827"
|
||||||
uuid = "4076af6c-e467-56ae-b986-b466b2749572"
|
uuid = "4076af6c-e467-56ae-b986-b466b2749572"
|
||||||
version = "0.21.6"
|
version = "0.21.8"
|
||||||
|
|
||||||
[[LibCURL]]
|
[[LibCURL]]
|
||||||
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
|
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
|
||||||
@@ -184,9 +185,9 @@ uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
|
|||||||
|
|
||||||
[[MathOptInterface]]
|
[[MathOptInterface]]
|
||||||
deps = ["BenchmarkTools", "CodecBzip2", "CodecZlib", "JSON", "JSONSchema", "LinearAlgebra", "MutableArithmetics", "OrderedCollections", "SparseArrays", "Test", "Unicode"]
|
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"
|
uuid = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
|
||||||
version = "0.9.20"
|
version = "0.9.21"
|
||||||
|
|
||||||
[[MbedTLS]]
|
[[MbedTLS]]
|
||||||
deps = ["Dates", "MbedTLS_jll", "Random", "Sockets"]
|
deps = ["Dates", "MbedTLS_jll", "Random", "Sockets"]
|
||||||
@@ -206,9 +207,9 @@ uuid = "14a3606d-f60d-562e-9121-12d972cd8159"
|
|||||||
|
|
||||||
[[MutableArithmetics]]
|
[[MutableArithmetics]]
|
||||||
deps = ["LinearAlgebra", "SparseArrays", "Test"]
|
deps = ["LinearAlgebra", "SparseArrays", "Test"]
|
||||||
git-tree-sha1 = "ff3aa3e4dbc837f80c2031de2f90125c8b3793f3"
|
git-tree-sha1 = "ad9b2bce6021631e0e20706d361972343a03e642"
|
||||||
uuid = "d8a4904e-b15c-11e9-3269-09a3773c0cb0"
|
uuid = "d8a4904e-b15c-11e9-3269-09a3773c0cb0"
|
||||||
version = "0.2.15"
|
version = "0.2.19"
|
||||||
|
|
||||||
[[NaNMath]]
|
[[NaNMath]]
|
||||||
git-tree-sha1 = "bfe47e760d60b82b66b61d2d44128b62e3a369fb"
|
git-tree-sha1 = "bfe47e760d60b82b66b61d2d44128b62e3a369fb"
|
||||||
@@ -220,14 +221,14 @@ uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
|
|||||||
|
|
||||||
[[OpenSpecFun_jll]]
|
[[OpenSpecFun_jll]]
|
||||||
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"]
|
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"]
|
||||||
git-tree-sha1 = "9db77584158d0ab52307f8c04f8e7c08ca76b5b3"
|
git-tree-sha1 = "b9b8b8ed236998f91143938a760c2112dceeb2b4"
|
||||||
uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
|
uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
|
||||||
version = "0.5.3+4"
|
version = "0.5.4+0"
|
||||||
|
|
||||||
[[OrderedCollections]]
|
[[OrderedCollections]]
|
||||||
git-tree-sha1 = "4fa2ba51070ec13fcc7517db714445b4ab986bdf"
|
git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c"
|
||||||
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
|
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
|
||||||
version = "1.4.0"
|
version = "1.4.1"
|
||||||
|
|
||||||
[[Parsers]]
|
[[Parsers]]
|
||||||
deps = ["Dates"]
|
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"]
|
deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
|
||||||
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
|
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
|
||||||
|
|
||||||
|
[[Preferences]]
|
||||||
|
deps = ["TOML"]
|
||||||
|
git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a"
|
||||||
|
uuid = "21216c6a-2e73-6563-6e65-726566657250"
|
||||||
|
version = "1.2.2"
|
||||||
|
|
||||||
[[Printf]]
|
[[Printf]]
|
||||||
deps = ["Unicode"]
|
deps = ["Unicode"]
|
||||||
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
||||||
|
|
||||||
[[PyCall]]
|
[[PyCall]]
|
||||||
deps = ["Conda", "Dates", "Libdl", "LinearAlgebra", "MacroTools", "Serialization", "VersionParsing"]
|
deps = ["Conda", "Dates", "Libdl", "LinearAlgebra", "MacroTools", "Serialization", "VersionParsing"]
|
||||||
git-tree-sha1 = "dd1a970b543bd02efce2984582e996af28cab27f"
|
git-tree-sha1 = "169bb8ea6b1b143c5cf57df6d34d022a7b60c6db"
|
||||||
uuid = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
|
uuid = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
|
||||||
version = "1.92.2"
|
version = "1.92.3"
|
||||||
|
|
||||||
[[REPL]]
|
[[REPL]]
|
||||||
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
|
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
|
||||||
@@ -282,9 +289,9 @@ version = "1.3.0"
|
|||||||
|
|
||||||
[[StaticArrays]]
|
[[StaticArrays]]
|
||||||
deps = ["LinearAlgebra", "Random", "Statistics"]
|
deps = ["LinearAlgebra", "Random", "Statistics"]
|
||||||
git-tree-sha1 = "2f01a51c23eed210ff4a1be102c4cc8236b66e5b"
|
git-tree-sha1 = "fb46e45ef2cade8be20bb445b3ffeca3c6d6f7d3"
|
||||||
uuid = "90137ffa-7385-5640-81b9-e52037218182"
|
uuid = "90137ffa-7385-5640-81b9-e52037218182"
|
||||||
version = "1.1.0"
|
version = "1.1.3"
|
||||||
|
|
||||||
[[Statistics]]
|
[[Statistics]]
|
||||||
deps = ["LinearAlgebra", "SparseArrays"]
|
deps = ["LinearAlgebra", "SparseArrays"]
|
||||||
@@ -315,9 +322,9 @@ uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa"
|
|||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
|
|
||||||
[[URIs]]
|
[[URIs]]
|
||||||
git-tree-sha1 = "7855809b88d7b16e9b029afd17880930626f54a2"
|
git-tree-sha1 = "97bbe755a53fe859669cd907f2d96aee8d2c1355"
|
||||||
uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
|
uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
|
||||||
version = "1.2.0"
|
version = "1.3.0"
|
||||||
|
|
||||||
[[UUIDs]]
|
[[UUIDs]]
|
||||||
deps = ["Random", "SHA"]
|
deps = ["Random", "SHA"]
|
||||||
|
|||||||
@@ -13,9 +13,7 @@ PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
|
|||||||
TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
|
TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
|
||||||
|
|
||||||
[compat]
|
[compat]
|
||||||
Conda = "1.4"
|
|
||||||
JuMP = "0.21"
|
JuMP = "0.21"
|
||||||
MathOptInterface = "0.9"
|
MathOptInterface = "0.9"
|
||||||
PyCall = "1"
|
|
||||||
TimerOutputs = "0.5"
|
TimerOutputs = "0.5"
|
||||||
julia = "1"
|
julia = "1"
|
||||||
|
|||||||
2
deps/build.jl
vendored
2
deps/build.jl
vendored
@@ -5,7 +5,7 @@ function install_miplearn()
|
|||||||
Conda.update()
|
Conda.update()
|
||||||
pip = joinpath(dirname(pyimport("sys").executable), "pip")
|
pip = joinpath(dirname(pyimport("sys").executable), "pip")
|
||||||
isfile(pip) || error("$pip: invalid path")
|
isfile(pip) || error("$pip: invalid path")
|
||||||
run(`$pip install miplearn==0.2.0.dev3`)
|
run(`$pip install miplearn==0.2.0.dev5`)
|
||||||
end
|
end
|
||||||
|
|
||||||
install_miplearn()
|
install_miplearn()
|
||||||
|
|||||||
@@ -4,46 +4,46 @@
|
|||||||
|
|
||||||
@pydef mutable struct JuMPInstance <: miplearn.Instance
|
@pydef mutable struct JuMPInstance <: miplearn.Instance
|
||||||
function __init__(self, model)
|
function __init__(self, model)
|
||||||
init_miplearn_ext(model)
|
|
||||||
features = model.ext[:miplearn][:features]
|
|
||||||
self.model = model
|
self.model = model
|
||||||
|
|
||||||
# Copy training data
|
# init_miplearn_ext(model)
|
||||||
training_data = []
|
# features = model.ext[:miplearn][:features]
|
||||||
for sample in self.model.ext[:miplearn][:training_samples]
|
# # Copy training data
|
||||||
pysample = miplearn.TrainingSample()
|
# training_data = []
|
||||||
pysample.__dict__ = sample
|
# for sample in self.model.ext[:miplearn][:training_samples]
|
||||||
push!(training_data, pysample)
|
# pysample = miplearn.TrainingSample()
|
||||||
end
|
# pysample.__dict__ = sample
|
||||||
self.training_data = training_data
|
# push!(training_data, pysample)
|
||||||
|
# end
|
||||||
|
# self.training_data = training_data
|
||||||
|
|
||||||
# Copy features to data classes
|
# # Copy features to data classes
|
||||||
self.features = miplearn.Features(
|
# self.features = miplearn.Features(
|
||||||
instance=miplearn.InstanceFeatures(
|
# instance=miplearn.InstanceFeatures(
|
||||||
user_features=PyCall.array2py(
|
# user_features=PyCall.array2py(
|
||||||
features[:instance][:user_features],
|
# features[:instance][:user_features],
|
||||||
),
|
# ),
|
||||||
lazy_constraint_count=0,
|
# lazy_constraint_count=0,
|
||||||
),
|
# ),
|
||||||
variables=Dict(
|
# variables=Dict(
|
||||||
varname => miplearn.VariableFeatures(
|
# varname => miplearn.VariableFeatures(
|
||||||
category=vfeatures[:category],
|
# category=vfeatures[:category],
|
||||||
user_features=PyCall.array2py(
|
# user_features=PyCall.array2py(
|
||||||
vfeatures[:user_features],
|
# vfeatures[:user_features],
|
||||||
),
|
# ),
|
||||||
)
|
# )
|
||||||
for (varname, vfeatures) in features[:variables]
|
# for (varname, vfeatures) in features[:variables]
|
||||||
),
|
# ),
|
||||||
constraints=Dict(
|
# constraints=Dict(
|
||||||
cname => miplearn.ConstraintFeatures(
|
# cname => miplearn.ConstraintFeatures(
|
||||||
category=cfeat[:category],
|
# category=cfeat[:category],
|
||||||
user_features=PyCall.array2py(
|
# user_features=PyCall.array2py(
|
||||||
cfeat[:user_features],
|
# cfeat[:user_features],
|
||||||
),
|
# ),
|
||||||
)
|
# )
|
||||||
for (cname, cfeat) in features[:constraints]
|
# for (cname, cfeat) in features[:constraints]
|
||||||
),
|
# ),
|
||||||
)
|
# )
|
||||||
end
|
end
|
||||||
|
|
||||||
function to_model(self)
|
function to_model(self)
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ mutable struct JuMPSolverData
|
|||||||
instance
|
instance
|
||||||
model
|
model
|
||||||
bin_vars
|
bin_vars
|
||||||
solution::Union{Nothing, Dict{String,Float64}}
|
solution
|
||||||
cname_to_constr
|
cname_to_constr
|
||||||
|
reduced_costs
|
||||||
|
dual_values
|
||||||
end
|
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.
|
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)
|
||||||
original_stdout = stdout
|
logname = tempname()
|
||||||
rd, wr = redirect_stdout()
|
logfile = open(logname, "w")
|
||||||
task = @async begin
|
redirect_stdout(logfile) do
|
||||||
log = ""
|
JuMP.optimize!(model)
|
||||||
while true
|
Base.Libc.flush_cstdio()
|
||||||
line = String(readavailable(rd))
|
|
||||||
isopen(rd) || break
|
|
||||||
log *= String(line)
|
|
||||||
if tee
|
|
||||||
print(original_stdout, line)
|
|
||||||
flush(original_stdout)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return log
|
|
||||||
end
|
end
|
||||||
JuMP.optimize!(model)
|
close(logfile)
|
||||||
sleep(1)
|
log = String(read(logname))
|
||||||
redirect_stdout(original_stdout)
|
rm(logname)
|
||||||
close(rd)
|
if tee
|
||||||
return fetch(task)
|
println(log)
|
||||||
|
end
|
||||||
|
return log
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function solve(
|
function solve(
|
||||||
data::JuMPSolverData;
|
data::JuMPSolverData;
|
||||||
tee::Bool=false,
|
tee::Bool=false,
|
||||||
iteration_cb,
|
iteration_cb=nothing,
|
||||||
)::Dict
|
)
|
||||||
instance, model = data.instance, data.model
|
instance, model = data.instance, data.model
|
||||||
wallclock_time = 0
|
wallclock_time = 0
|
||||||
log = ""
|
log = ""
|
||||||
@@ -78,14 +73,14 @@ function solve(
|
|||||||
lower_bound = primal_bound
|
lower_bound = primal_bound
|
||||||
upper_bound = dual_bound
|
upper_bound = dual_bound
|
||||||
end
|
end
|
||||||
return Dict(
|
return miplearn.solvers.internal.MIPSolveStats(
|
||||||
"Lower bound" => lower_bound,
|
mip_lower_bound=lower_bound,
|
||||||
"Upper bound" => upper_bound,
|
mip_upper_bound=upper_bound,
|
||||||
"Sense" => sense,
|
mip_sense=sense,
|
||||||
"Wallclock time" => wallclock_time,
|
mip_wallclock_time=wallclock_time,
|
||||||
"Nodes" => 1,
|
mip_nodes=1,
|
||||||
"MIP log" => log,
|
mip_log=log,
|
||||||
"Warm start value" => nothing,
|
mip_warm_start_value=nothing,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -97,24 +92,55 @@ function solve_lp(data::JuMPSolverData; tee::Bool=false)
|
|||||||
JuMP.set_upper_bound(var, 1.0)
|
JuMP.set_upper_bound(var, 1.0)
|
||||||
JuMP.set_lower_bound(var, 0.0)
|
JuMP.set_lower_bound(var, 0.0)
|
||||||
end
|
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)
|
update_solution!(data)
|
||||||
obj_value = JuMP.objective_value(model)
|
obj_value = JuMP.objective_value(model)
|
||||||
for var in bin_vars
|
for var in bin_vars
|
||||||
JuMP.set_binary(var)
|
JuMP.set_binary(var)
|
||||||
end
|
end
|
||||||
return Dict(
|
return miplearn.solvers.internal.LPSolveStats(
|
||||||
"LP value" => obj_value,
|
lp_value=obj_value,
|
||||||
"LP log" => log,
|
lp_log=log,
|
||||||
|
lp_wallclock_time=wallclock_time,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function update_solution!(data::JuMPSolverData)
|
function update_solution!(data::JuMPSolverData)
|
||||||
data.solution = Dict(
|
vars = JuMP.all_variables(data.model)
|
||||||
JuMP.name(var) => JuMP.value(var)
|
data.solution = [JuMP.value(var) for var in vars]
|
||||||
for var in JuMP.all_variables(data.model)
|
|
||||||
)
|
# 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
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -195,6 +221,151 @@ function get_constraint_sense(data::JuMPSolverData, cname)
|
|||||||
end
|
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
|
# Constraints: ScalarAffineFunction, LessThan
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
function get_constraint_rhs(
|
function get_constraint_rhs(
|
||||||
@@ -355,6 +526,23 @@ function get_constraint_sense(
|
|||||||
return "="
|
return "="
|
||||||
end
|
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
|
@pydef mutable struct JuMPSolver <: miplearn.solvers.internal.InternalSolver
|
||||||
function __init__(self; optimizer)
|
function __init__(self; optimizer)
|
||||||
@@ -366,6 +554,8 @@ end
|
|||||||
nothing, # bin_vars
|
nothing, # bin_vars
|
||||||
nothing, # solution
|
nothing, # solution
|
||||||
nothing, # cname_to_constr
|
nothing, # cname_to_constr
|
||||||
|
nothing, # reduced_costs
|
||||||
|
nothing, # dual_values
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -381,9 +571,9 @@ end
|
|||||||
solve(
|
solve(
|
||||||
self;
|
self;
|
||||||
tee=false,
|
tee=false,
|
||||||
iteration_cb,
|
iteration_cb=nothing,
|
||||||
lazy_cb,
|
lazy_cb=nothing,
|
||||||
user_cut_cb,
|
user_cut_cb=nothing,
|
||||||
) = solve(
|
) = solve(
|
||||||
self.data,
|
self.data,
|
||||||
tee=tee,
|
tee=tee,
|
||||||
@@ -396,8 +586,8 @@ end
|
|||||||
get_solution(self) =
|
get_solution(self) =
|
||||||
self.data.solution
|
self.data.solution
|
||||||
|
|
||||||
get_variables(self) =
|
get_variables(self; with_static=true) =
|
||||||
get_variables(self.data)
|
get_variables(self.data; with_static=with_static)
|
||||||
|
|
||||||
set_branching_priorities(self, priorities) =
|
set_branching_priorities(self, priorities) =
|
||||||
@warn "JuMPSolver: set_branching_priorities not implemented"
|
@warn "JuMPSolver: set_branching_priorities not implemented"
|
||||||
@@ -411,6 +601,9 @@ end
|
|||||||
is_infeasible(self) =
|
is_infeasible(self) =
|
||||||
is_infeasible(self.data)
|
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) =
|
||||||
get_constraint_ids(self.data)
|
get_constraint_ids(self.data)
|
||||||
|
|
||||||
@@ -423,8 +616,45 @@ end
|
|||||||
get_constraint_sense(self, cname) =
|
get_constraint_sense(self, cname) =
|
||||||
get_constraint_sense(self.data, cname)
|
get_constraint_sense(self.data, cname)
|
||||||
|
|
||||||
|
build_test_instance_knapsack(self) =
|
||||||
|
build_test_instance_knapsack()
|
||||||
|
|
||||||
clone(self) = self
|
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")
|
add_cut(self) = error("not implemented")
|
||||||
extract_constraint(self) = error("not implemented")
|
extract_constraint(self) = error("not implemented")
|
||||||
is_constraint_satisfied(self) = error("not implemented")
|
is_constraint_satisfied(self) = error("not implemented")
|
||||||
@@ -433,6 +663,11 @@ end
|
|||||||
get_inequality_slacks(self) = error("not implemented")
|
get_inequality_slacks(self) = error("not implemented")
|
||||||
get_dual(self) = error("not implemented")
|
get_dual(self) = error("not implemented")
|
||||||
get_sense(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
|
end
|
||||||
|
|
||||||
export JuMPSolver, solve!, fit!, add!
|
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]
|
[deps]
|
||||||
Cbc = "9961bab8-2fa3-5c5a-9d89-47fab24efd76"
|
Cbc = "9961bab8-2fa3-5c5a-9d89-47fab24efd76"
|
||||||
|
Gurobi = "2e9cd046-0924-5485-92f1-d5272153d98b"
|
||||||
Conda = "8f4d0f93-b110-5947-807f-2305c1781a2d"
|
Conda = "8f4d0f93-b110-5947-807f-2305c1781a2d"
|
||||||
JSON2 = "2535ab7d-5cd8-5a07-80ac-9b1792aadce3"
|
JSON2 = "2535ab7d-5cd8-5a07-80ac-9b1792aadce3"
|
||||||
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
|
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
|
||||||
|
|||||||
@@ -4,18 +4,22 @@
|
|||||||
|
|
||||||
using Test
|
using Test
|
||||||
using MIPLearn
|
using MIPLearn
|
||||||
using Cbc
|
using Gurobi
|
||||||
using PyCall
|
using PyCall
|
||||||
|
using JuMP
|
||||||
|
|
||||||
miplearn_tests = pyimport("miplearn.solvers.tests")
|
miplearn_tests = pyimport("miplearn.solvers.tests")
|
||||||
|
traceback = pyimport("traceback")
|
||||||
|
|
||||||
@testset "JuMPSolver" begin
|
@testset "JuMPSolver" begin
|
||||||
model = MIPLearn.knapsack_model(
|
solver = JuMPSolver(optimizer=Gurobi.Optimizer)
|
||||||
[23., 26., 20., 18.],
|
try
|
||||||
[505., 352., 458., 220.],
|
miplearn_tests.run_internal_solver_tests(solver)
|
||||||
67.0,
|
catch e
|
||||||
)
|
if isa(e, PyCall.PyError)
|
||||||
instance = JuMPInstance(model)
|
printstyled("Uncaught Python exception:\n", bold=true, color=:red)
|
||||||
solver = JuMPSolver(optimizer=Cbc.Optimizer)
|
traceback.print_exception(e.T, e.val, e.traceback)
|
||||||
miplearn_tests.test_internal_solver(solver, instance, model)
|
end
|
||||||
end
|
rethrow()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ using MIPLearn
|
|||||||
MIPLearn.setup_logger()
|
MIPLearn.setup_logger()
|
||||||
|
|
||||||
@testset "MIPLearn" begin
|
@testset "MIPLearn" begin
|
||||||
#include("modeling/jump_solver_test.jl")
|
include("modeling/jump_solver_test.jl")
|
||||||
include("modeling/learning_solver_test.jl")
|
#include("modeling/learning_solver_test.jl")
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user