diff --git a/docs/index.md b/docs/index.md
index 6876509..4623a88 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -10,7 +10,7 @@ Unlike conventional MIP solvers, MIPLearn can take full advantage of very specif
```{toctree}
---
maxdepth: 1
-caption: Julia Tutorials
+caption: JuMP Tutorials
numbered: true
---
jump-tutorials/getting-started.ipynb
@@ -19,6 +19,18 @@ jump-tutorials/getting-started.ipynb
#jump-tutorials/customizing-ml.ipynb
```
+```{toctree}
+---
+maxdepth: 1
+caption: Pyomo Tutorials
+numbered: true
+---
+pyomo-tutorials/getting-started.ipynb
+#pyomo-tutorials/lazy-constraints.ipynb
+#pyomo-tutorials/user-cuts.ipynb
+#pyomo-tutorials/customizing-ml.ipynb
+```
+
```{toctree}
---
maxdepth: 1
diff --git a/docs/jump-tutorials/Manifest.toml b/docs/jump-tutorials/Manifest.toml
index 413dc0a..290a9c1 100644
--- a/docs/jump-tutorials/Manifest.toml
+++ b/docs/jump-tutorials/Manifest.toml
@@ -2,9 +2,9 @@
[[ASL_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
-git-tree-sha1 = "370cafc70604b2522f2c7cf9915ebcd17b4cd38b"
+git-tree-sha1 = "6252039f98492252f9e47c312c8ffda0e3b9e78d"
uuid = "ae81ac8f-d209-56e5-92de-9978fef736f9"
-version = "0.1.2+0"
+version = "0.1.3+0"
[[ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
@@ -16,10 +16,10 @@ uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
[[BenchmarkTools]]
-deps = ["JSON", "Logging", "Printf", "Statistics", "UUIDs"]
-git-tree-sha1 = "42ac5e523869a84eac9669eaceed9e4aa0e1587b"
+deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"]
+git-tree-sha1 = "4c10eee4af024676200bc7752e536f858c6b8f93"
uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
-version = "1.1.4"
+version = "1.3.1"
[[BinaryProvider]]
deps = ["Libdl", "Logging", "SHA"]
@@ -39,10 +39,10 @@ uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82"
version = "0.4.1"
[[CSV]]
-deps = ["Dates", "Mmap", "Parsers", "PooledArrays", "SentinelArrays", "Tables", "Unicode"]
-git-tree-sha1 = "b83aa3f513be680454437a0eee21001607e5d983"
+deps = ["CodecZlib", "Dates", "FilePathsBase", "InlineStrings", "Mmap", "Parsers", "PooledArrays", "SentinelArrays", "Tables", "Unicode", "WeakRefStrings"]
+git-tree-sha1 = "9519274b50500b8029973d241d32cfbf0b127d97"
uuid = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
-version = "0.8.5"
+version = "0.10.2"
[[Calculus]]
deps = ["LinearAlgebra"]
@@ -70,9 +70,15 @@ version = "0.6000.200+0"
[[ChainRulesCore]]
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
-git-tree-sha1 = "bdc0937269321858ab2a4f288486cb258b9a0af7"
+git-tree-sha1 = "c9a6160317d1abe9c44b3beb367fd448117679ca"
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
-version = "1.3.0"
+version = "1.13.0"
+
+[[ChangesOfVariables]]
+deps = ["ChainRulesCore", "LinearAlgebra", "Test"]
+git-tree-sha1 = "bf98fa45a0a4cee295de98d4c1462be26345b9a1"
+uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0"
+version = "0.1.2"
[[Clp]]
deps = ["BinaryProvider", "CEnum", "Clp_jll", "Libdl", "MathOptInterface", "SparseArrays"]
@@ -88,9 +94,9 @@ version = "100.1700.600+0"
[[CodeTracking]]
deps = ["InteractiveUtils", "UUIDs"]
-git-tree-sha1 = "9aa8a5ebb6b5bf469a7e0e2b5202cf6f8c291104"
+git-tree-sha1 = "759a12cefe1cd1bb49e477bc3702287521797483"
uuid = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
-version = "1.0.6"
+version = "1.0.7"
[[CodecBzip2]]
deps = ["Bzip2_jll", "Libdl", "TranscodingStreams"]
@@ -106,9 +112,9 @@ version = "0.7.0"
[[CoinUtils_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "OpenBLAS32_jll", "Pkg"]
-git-tree-sha1 = "9b4a8b1087376c56189d02c3c1a48a0bba098ec2"
+git-tree-sha1 = "44173e61256f32918c6c132fc41f772bab1fb6d1"
uuid = "be027038-0da8-5614-b30d-e42594cb92df"
-version = "2.11.4+2"
+version = "200.1100.400+0"
[[CommonSubexpressions]]
deps = ["MacroTools", "Test"]
@@ -118,41 +124,41 @@ 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 = "727e463cfebd0c7b999bbf3e9e7e16f254b94193"
+git-tree-sha1 = "44c37b4636bc54afac5c574d2d02b625349d6582"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
-version = "3.34.0"
+version = "3.41.0"
[[CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
[[Conda]]
-deps = ["JSON", "VersionParsing"]
-git-tree-sha1 = "299304989a5e6473d985212c28928899c74e9421"
+deps = ["Downloads", "JSON", "VersionParsing"]
+git-tree-sha1 = "6e47d11ea2776bc5627421d59cdcc1296c058071"
uuid = "8f4d0f93-b110-5947-807f-2305c1781a2d"
-version = "1.5.2"
+version = "1.7.0"
[[Crayons]]
-git-tree-sha1 = "3f71217b538d7aaee0b69ab47d9b7724ca8afa0d"
+git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15"
uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f"
-version = "4.0.4"
+version = "4.1.1"
[[DataAPI]]
-git-tree-sha1 = "ee400abb2298bd13bfc3df1c412ed228061a2385"
+git-tree-sha1 = "cc70b17275652eb47bc9e5f81635981f13cea5c8"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
-version = "1.7.0"
+version = "1.9.0"
[[DataFrames]]
deps = ["Compat", "DataAPI", "Future", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Reexport", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"]
-git-tree-sha1 = "d785f42445b63fc86caa08bb9a9351008be9b765"
+git-tree-sha1 = "ae02104e835f219b8930c7664b8012c93475c340"
uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
-version = "1.2.2"
+version = "1.3.2"
[[DataStructures]]
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
-git-tree-sha1 = "7d9d316f04214f7efdbb6398d545446e246eff02"
+git-tree-sha1 = "3daef5523dd2e769dad2365274f760ff5f282c7d"
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
-version = "0.18.10"
+version = "0.18.11"
[[DataValueInterfaces]]
git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6"
@@ -167,6 +173,12 @@ uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
deps = ["Mmap"]
uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab"
+[[DensityInterface]]
+deps = ["InverseFunctions", "Test"]
+git-tree-sha1 = "80c3e8639e3353e5d2912fb3a1916b8455e2494b"
+uuid = "b429d917-457f-4dbc-8f4c-0cc954292b1d"
+version = "0.4.0"
+
[[DiffResults]]
deps = ["StaticArrays"]
git-tree-sha1 = "c18e98cba888c6c25d1c3b048e4b3380ca956805"
@@ -174,50 +186,62 @@ uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5"
version = "1.0.3"
[[DiffRules]]
-deps = ["NaNMath", "Random", "SpecialFunctions"]
-git-tree-sha1 = "3ed8fa7178a10d1cd0f1ca524f249ba6937490c0"
+deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"]
+git-tree-sha1 = "dd933c4ef7b4c270aacd4eb88fa64c147492acf0"
uuid = "b552c78f-8df3-52c6-915a-8e097449b14b"
-version = "1.3.0"
+version = "1.10.0"
[[Distributed]]
deps = ["Random", "Serialization", "Sockets"]
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"
[[Distributions]]
-deps = ["ChainRulesCore", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SparseArrays", "SpecialFunctions", "Statistics", "StatsBase", "StatsFuns"]
-git-tree-sha1 = "c2dbc7e0495c3f956e4615b78d03c7aa10091d0c"
+deps = ["ChainRulesCore", "DensityInterface", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SparseArrays", "SpecialFunctions", "Statistics", "StatsBase", "StatsFuns", "Test"]
+git-tree-sha1 = "9d3c0c762d4666db9187f363a76b47f7346e673b"
uuid = "31c24e10-a181-5473-b8eb-7969acd0382f"
-version = "0.25.15"
+version = "0.25.49"
[[DocStringExtensions]]
deps = ["LibGit2"]
-git-tree-sha1 = "a32185f5428d3986f47c2ab78b1f216d5e6cc96f"
+git-tree-sha1 = "b19534d1895d702889b219c382a6e18010797f0b"
uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
-version = "0.8.5"
+version = "0.8.6"
[[Downloads]]
deps = ["ArgTools", "LibCURL", "NetworkOptions"]
uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
+[[DualNumbers]]
+deps = ["Calculus", "NaNMath", "SpecialFunctions"]
+git-tree-sha1 = "84f04fe68a3176a583b864e492578b9466d87f1e"
+uuid = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74"
+version = "0.6.6"
+
[[ExprTools]]
-git-tree-sha1 = "b7e3d17636b348f005f11040025ae8c6f645fe92"
+git-tree-sha1 = "56559bbef6ca5ea0c0818fa5c90320398a6fbf8d"
uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04"
-version = "0.1.6"
+version = "0.1.8"
[[FileIO]]
deps = ["Pkg", "Requires", "UUIDs"]
-git-tree-sha1 = "937c29268e405b6808d958a9ac41bfe1a31b08e7"
+git-tree-sha1 = "80ced645013a5dbdc52cf70329399c35ce007fae"
uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
-version = "1.11.0"
+version = "1.13.0"
+
+[[FilePathsBase]]
+deps = ["Compat", "Dates", "Mmap", "Printf", "Test", "UUIDs"]
+git-tree-sha1 = "04d13bfa8ef11720c24e4d840c0033d145537df7"
+uuid = "48062228-2e41-5def-b9a4-89aafe57970f"
+version = "0.9.17"
[[FileWatching]]
uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
[[FillArrays]]
deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"]
-git-tree-sha1 = "a3b7b041753094f3b17ffa9d2e2e07d8cace09cd"
+git-tree-sha1 = "4c7d3757f3ecbcb9055870351078552b7d1dbd2d"
uuid = "1a297f60-69ca-5386-bcde-b61e274b549b"
-version = "0.12.3"
+version = "0.13.0"
[[Formatting]]
deps = ["Printf"]
@@ -226,10 +250,10 @@ uuid = "59287772-0a20-5a39-b81b-1366585eb4c0"
version = "0.4.2"
[[ForwardDiff]]
-deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "NaNMath", "Printf", "Random", "SpecialFunctions", "StaticArrays"]
-git-tree-sha1 = "b5e930ac60b613ef3406da6d4f42c35d8dc51419"
+deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions", "StaticArrays"]
+git-tree-sha1 = "1bd6fc0c344fc0cbee1f42f8d2e7ec8253dda2d2"
uuid = "f6369f11-7733-5829-9624-2563aa707210"
-version = "0.10.19"
+version = "0.10.25"
[[Future]]
deps = ["Random"]
@@ -245,26 +269,42 @@ uuid = "c27321d9-0574-5035-807b-f59d2c89b15c"
version = "1.3.0"
[[HTTP]]
-deps = ["Base64", "Dates", "IniFile", "Logging", "MbedTLS", "NetworkOptions", "Sockets", "URIs"]
-git-tree-sha1 = "60ed5f1643927479f845b0135bb369b031b541fa"
+deps = ["Base64", "Dates", "IniFile", "MbedTLS", "Sockets"]
+git-tree-sha1 = "c7ec02c4c6a039a98a15f955462cd7aea5df4508"
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
-version = "0.9.14"
+version = "0.8.19"
+
+[[HypergeometricFunctions]]
+deps = ["DualNumbers", "LinearAlgebra", "SpecialFunctions", "Test"]
+git-tree-sha1 = "65e4589030ef3c44d3b90bdc5aac462b4bb05567"
+uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a"
+version = "0.3.8"
[[IniFile]]
-deps = ["Test"]
-git-tree-sha1 = "098e4d2c533924c921f9f9847274f2ad89e018b8"
+git-tree-sha1 = "f550e6e32074c939295eb5ea6de31849ac2c9625"
uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f"
-version = "0.5.0"
+version = "0.5.1"
+
+[[InlineStrings]]
+deps = ["Parsers"]
+git-tree-sha1 = "61feba885fac3a407465726d0c330b3055df897f"
+uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48"
+version = "1.1.2"
[[InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
-[[InvertedIndices]]
+[[InverseFunctions]]
deps = ["Test"]
-git-tree-sha1 = "15732c475062348b0165684ffe28e85ea8396afc"
+git-tree-sha1 = "a7254c0acd8e62f1ac75ad24d5db43f5f19f3c65"
+uuid = "3587e190-3f89-42d0-90ee-14403ec27112"
+version = "0.1.2"
+
+[[InvertedIndices]]
+git-tree-sha1 = "bee5f1ef5bf65df56bdd2e40447590b272a5471f"
uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f"
-version = "1.0.0"
+version = "1.1.0"
[[Ipopt_jll]]
deps = ["ASL_jll", "Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "MUMPS_seq_jll", "OpenBLAS32_jll", "Pkg"]
@@ -273,9 +313,9 @@ uuid = "9cc047cb-c261-5740-88fc-0cf96f7bdcc7"
version = "3.13.4+2"
[[IrrationalConstants]]
-git-tree-sha1 = "f76424439413893a832026ca355fe273e93bce94"
+git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151"
uuid = "92d709cd-6900-40b7-9082-c6be49f344b6"
-version = "0.1.0"
+version = "0.1.1"
[[IteratorInterfaceExtensions]]
git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856"
@@ -283,40 +323,44 @@ uuid = "82899510-4779-5014-852e-03e436cf321d"
version = "1.0.0"
[[JLD2]]
-deps = ["DataStructures", "FileIO", "MacroTools", "Mmap", "Pkg", "Printf", "Reexport", "TranscodingStreams", "UUIDs"]
-git-tree-sha1 = "59ee430ac5dc87bc3eec833cc2a37853425750b4"
+deps = ["FileIO", "MacroTools", "Mmap", "OrderedCollections", "Pkg", "Printf", "Reexport", "TranscodingStreams", "UUIDs"]
+git-tree-sha1 = "28b114b3279cdbac9a61c57b3e6548a572142b34"
uuid = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
-version = "0.4.13"
+version = "0.4.21"
[[JLLWrappers]]
deps = ["Preferences"]
-git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e"
+git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1"
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
-version = "1.3.0"
+version = "1.4.1"
[[JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
-git-tree-sha1 = "8076680b162ada2a031f707ac7b4953e30667a37"
+git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
-version = "0.21.2"
+version = "0.21.3"
[[JSONSchema]]
-deps = ["HTTP", "JSON", "URIs"]
-git-tree-sha1 = "2f49f7f86762a0fbbeef84912265a1ae61c4ef80"
+deps = ["HTTP", "JSON", "ZipFile"]
+git-tree-sha1 = "b84ab8139afde82c7c65ba2b792fe12e01dd7307"
uuid = "7d188eb4-7ad8-530c-ae41-71a32a6d4692"
-version = "0.3.4"
+version = "0.3.3"
[[JuMP]]
deps = ["Calculus", "DataStructures", "ForwardDiff", "JSON", "LinearAlgebra", "MathOptInterface", "MutableArithmetics", "NaNMath", "Printf", "Random", "SparseArrays", "SpecialFunctions", "Statistics"]
-git-tree-sha1 = "4f0a771949bbe24bf70c89e8032c107ebe03f6ba"
+git-tree-sha1 = "4358b7cbf2db36596bdbbe3becc6b9d87e4eb8f5"
uuid = "4076af6c-e467-56ae-b986-b466b2749572"
-version = "0.21.9"
+version = "0.21.10"
[[JuliaInterpreter]]
deps = ["CodeTracking", "InteractiveUtils", "Random", "UUIDs"]
-git-tree-sha1 = "e273807f38074f033d94207a201e6e827d8417db"
+git-tree-sha1 = "0a815f0060ab182f6c484b281107bfcd5bbb58dc"
uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a"
-version = "0.8.21"
+version = "0.9.7"
+
+[[LazyArtifacts]]
+deps = ["Artifacts", "Pkg"]
+uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3"
[[LibCURL]]
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
@@ -342,31 +386,29 @@ deps = ["Libdl"]
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
[[LogExpFunctions]]
-deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"]
-git-tree-sha1 = "3d682c07e6dd250ed082f883dc88aee7996bf2cc"
+deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"]
+git-tree-sha1 = "e5718a00af0ab9756305a0392832c8952c7426c1"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
-version = "0.3.0"
+version = "0.3.6"
[[Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
[[LoweredCodeUtils]]
deps = ["JuliaInterpreter"]
-git-tree-sha1 = "491a883c4fef1103077a7f648961adbf9c8dd933"
+git-tree-sha1 = "6b0440822974cab904c8b14d79743565140567f6"
uuid = "6f1432cf-f94c-5a45-995e-cdbf5db27b0b"
-version = "2.1.2"
+version = "2.2.1"
[[METIS_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
-git-tree-sha1 = "2dc1a9fc87e57e32b1fc186db78811157b30c118"
+git-tree-sha1 = "1d31872bb9c5e7ec1f618e8c4a56c8b0d9bddc7e"
uuid = "d00139f3-1899-568f-a2f0-47f597d42d70"
-version = "5.1.0+5"
+version = "5.1.1+0"
[[MIPLearn]]
deps = ["CSV", "Cbc", "Clp", "Conda", "DataFrames", "DataStructures", "Distributed", "JLD2", "JSON", "JuMP", "Logging", "MathOptInterface", "PackageCompiler", "Printf", "PyCall", "Random", "SparseArrays", "Statistics", "TimerOutputs"]
-git-tree-sha1 = "8e595dec310860a52e01c8875f51165fcccb1875"
-repo-rev = "dev"
-repo-url = "https://github.com/ANL-CEEESA/MIPLearn.jl.git"
+path = "/home/isoron/Developer/MIPLearn.jl/dev"
uuid = "2b1277c3-b477-4c49-a15e-7ba350325c68"
version = "0.2.0"
@@ -378,9 +420,9 @@ version = "5.2.1+4"
[[MacroTools]]
deps = ["Markdown", "Random"]
-git-tree-sha1 = "0fb723cd8c45858c22169b2e42269e53271a6df7"
+git-tree-sha1 = "3d3e902b31198a27340d0bf00d6ac452866021cf"
uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
-version = "0.5.7"
+version = "0.5.9"
[[Markdown]]
deps = ["Base64"]
@@ -404,9 +446,9 @@ uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
[[Missings]]
deps = ["DataAPI"]
-git-tree-sha1 = "2ca267b08821e86c5ef4376cffed98a46c2cb205"
+git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f"
uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"
-version = "1.0.1"
+version = "1.0.2"
[[Mmap]]
uuid = "a63ad114-7e13-5084-954f-fe012c677804"
@@ -416,14 +458,14 @@ uuid = "14a3606d-f60d-562e-9121-12d972cd8159"
[[MutableArithmetics]]
deps = ["LinearAlgebra", "SparseArrays", "Test"]
-git-tree-sha1 = "3927848ccebcc165952dc0d9ac9aa274a87bfe01"
+git-tree-sha1 = "8d9496b2339095901106961f44718920732616bb"
uuid = "d8a4904e-b15c-11e9-3269-09a3773c0cb0"
-version = "0.2.20"
+version = "0.2.22"
[[NaNMath]]
-git-tree-sha1 = "bfe47e760d60b82b66b61d2d44128b62e3a369fb"
+git-tree-sha1 = "b086b7ea07f8e38cf122f5016af580881ac914fe"
uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
-version = "0.3.5"
+version = "0.3.7"
[[NetworkOptions]]
uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
@@ -434,6 +476,10 @@ git-tree-sha1 = "ba4a8f683303c9082e84afba96f25af3c7fb2436"
uuid = "656ef2d0-ae68-5445-9ca0-591084a874a2"
version = "0.3.12+1"
+[[OpenLibm_jll]]
+deps = ["Artifacts", "Libdl"]
+uuid = "05823500-19ac-5b8b-9628-191a04bc5112"
+
[[OpenSpecFun_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1"
@@ -447,27 +493,27 @@ version = "1.4.1"
[[Osi_jll]]
deps = ["Artifacts", "CoinUtils_jll", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "OpenBLAS32_jll", "Pkg"]
-git-tree-sha1 = "6a9967c4394858f38b7fc49787b983ba3847e73d"
+git-tree-sha1 = "28e0ddebd069f605ab1988ab396f239a3ac9b561"
uuid = "7da25872-d9ce-5375-a4d3-7a845f58efdd"
-version = "0.108.6+2"
+version = "0.10800.600+0"
[[PDMats]]
deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"]
-git-tree-sha1 = "4dd403333bcf0909341cfe57ec115152f937d7d8"
+git-tree-sha1 = "7e2166042d1698b6072352c74cfd1fca2a968253"
uuid = "90014a1f-27ba-587c-ab20-58faa44d9150"
-version = "0.11.1"
+version = "0.11.6"
[[PackageCompiler]]
-deps = ["Libdl", "Pkg", "UUIDs"]
-git-tree-sha1 = "bb40ed7cb3aac2b4cdf42f898c26a58ab797ac62"
+deps = ["Artifacts", "LazyArtifacts", "Libdl", "Pkg", "Printf", "RelocatableFolders", "UUIDs"]
+git-tree-sha1 = "4ad92047603f8e955503f92767577b32508c39af"
uuid = "9b87118b-4619-50d2-8e1e-99f35a4d4d9d"
-version = "1.3.0"
+version = "2.0.5"
[[Parsers]]
deps = ["Dates"]
-git-tree-sha1 = "bfd7d8c7fd87f04543810d9cbd3995972236ba1b"
+git-tree-sha1 = "13468f237353112a01b2d6b32f3d0f80219944aa"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
-version = "1.1.2"
+version = "2.2.2"
[[Pkg]]
deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
@@ -475,26 +521,30 @@ uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
[[PooledArrays]]
deps = ["DataAPI", "Future"]
-git-tree-sha1 = "a193d6ad9c45ada72c14b731a318bedd3c2f00cf"
+git-tree-sha1 = "db3a23166af8aebf4db5ef87ac5b00d36eb771e2"
uuid = "2dfb63ee-cc39-5dd5-95bd-886bf059d720"
-version = "1.3.0"
+version = "1.4.0"
[[Preferences]]
deps = ["TOML"]
-git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a"
+git-tree-sha1 = "de893592a221142f3db370f48290e3a2ef39998f"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
-version = "1.2.2"
+version = "1.2.4"
[[PrettyTables]]
deps = ["Crayons", "Formatting", "Markdown", "Reexport", "Tables"]
-git-tree-sha1 = "0d1245a357cc61c8cd61934c07447aa569ff22e6"
+git-tree-sha1 = "dfb54c4e414caa595a1f2ed759b160f5a3ddcba5"
uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
-version = "1.1.0"
+version = "1.3.1"
[[Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
+[[Profile]]
+deps = ["Printf"]
+uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79"
+
[[ProgressBars]]
deps = ["Printf"]
git-tree-sha1 = "938525cc66a4058f6ed75b84acd13a00fbecea11"
@@ -503,15 +553,15 @@ version = "1.4.0"
[[PyCall]]
deps = ["Conda", "Dates", "Libdl", "LinearAlgebra", "MacroTools", "Serialization", "VersionParsing"]
-git-tree-sha1 = "169bb8ea6b1b143c5cf57df6d34d022a7b60c6db"
+git-tree-sha1 = "71fd4022ecd0c6d20180e23ff1b3e05a143959c2"
uuid = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
-version = "1.92.3"
+version = "1.93.0"
[[QuadGK]]
deps = ["DataStructures", "LinearAlgebra"]
-git-tree-sha1 = "12fbe86da16df6679be7521dfb39fbc861e1dc7b"
+git-tree-sha1 = "78aadffb3efd2155af139781b8a8df1ef279ea39"
uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
-version = "2.4.1"
+version = "2.4.2"
[[REPL]]
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
@@ -526,17 +576,23 @@ git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.2.2"
+[[RelocatableFolders]]
+deps = ["SHA", "Scratch"]
+git-tree-sha1 = "cdbd3b1338c72ce29d9584fdbe9e9b70eeb5adca"
+uuid = "05181044-ff0b-4ac5-8273-598c1e38db00"
+version = "0.1.3"
+
[[Requires]]
deps = ["UUIDs"]
-git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621"
+git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7"
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
-version = "1.1.3"
+version = "1.3.0"
[[Revise]]
deps = ["CodeTracking", "Distributed", "FileWatching", "JuliaInterpreter", "LibGit2", "LoweredCodeUtils", "OrderedCollections", "Pkg", "REPL", "Requires", "UUIDs", "Unicode"]
-git-tree-sha1 = "1947d2d75463bd86d87eaba7265b0721598dd803"
+git-tree-sha1 = "606ddc4d3d098447a09c9337864c73d017476424"
uuid = "295af30f-e4ad-537b-8983-00126c2a3abe"
-version = "3.1.19"
+version = "3.3.2"
[[Rmath]]
deps = ["Random", "Rmath_jll"]
@@ -567,11 +623,17 @@ version = "0.1.3+0"
[[SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
+[[Scratch]]
+deps = ["Dates"]
+git-tree-sha1 = "0b4b7f1393cff97c33891da2a0bf69c6ed241fda"
+uuid = "6c6a2e73-6563-6170-7368-637461726353"
+version = "1.1.0"
+
[[SentinelArrays]]
deps = ["Dates", "Random"]
-git-tree-sha1 = "54f37736d8934a12a200edea2f9206b03bdf3159"
+git-tree-sha1 = "6a2f7d70512d205ca8c7ee31bfa9f142fe74310c"
uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c"
-version = "1.3.7"
+version = "1.3.12"
[[Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
@@ -594,37 +656,38 @@ deps = ["LinearAlgebra", "Random"]
uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
[[SpecialFunctions]]
-deps = ["ChainRulesCore", "LogExpFunctions", "OpenSpecFun_jll"]
-git-tree-sha1 = "a322a9493e49c5f3a10b50df3aedaf1cdb3244b7"
+deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"]
+git-tree-sha1 = "cbf21db885f478e4bd73b286af6e67d1beeebe4c"
uuid = "276daf66-3868-5448-9aa4-cd146d93841b"
-version = "1.6.1"
+version = "1.8.4"
[[StaticArrays]]
deps = ["LinearAlgebra", "Random", "Statistics"]
-git-tree-sha1 = "3240808c6d463ac46f1c1cd7638375cd22abbccb"
+git-tree-sha1 = "6354dfaf95d398a1a70e0b28238321d5d17b2530"
uuid = "90137ffa-7385-5640-81b9-e52037218182"
-version = "1.2.12"
+version = "1.4.0"
[[Statistics]]
deps = ["LinearAlgebra", "SparseArrays"]
uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
[[StatsAPI]]
-git-tree-sha1 = "1958272568dc176a1d881acb797beb909c785510"
+deps = ["LinearAlgebra"]
+git-tree-sha1 = "c3d8ba7f3fa0625b062b82853a7d5229cb728b6b"
uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
-version = "1.0.0"
+version = "1.2.1"
[[StatsBase]]
-deps = ["DataAPI", "DataStructures", "LinearAlgebra", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"]
-git-tree-sha1 = "8cbbc098554648c84f79a463c9ff0fd277144b6c"
+deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"]
+git-tree-sha1 = "8977b17906b0a1cc74ab2e3a05faa16cf08a8291"
uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
-version = "0.33.10"
+version = "0.33.16"
[[StatsFuns]]
-deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"]
-git-tree-sha1 = "46d7ccc7104860c38b11966dd1f72ff042f382e4"
+deps = ["ChainRulesCore", "HypergeometricFunctions", "InverseFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"]
+git-tree-sha1 = "25405d7016a47cf2bd6cd91e66f4de437fd54a07"
uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c"
-version = "0.9.10"
+version = "0.9.16"
[[SuiteSparse]]
deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"]
@@ -642,9 +705,9 @@ version = "1.0.1"
[[Tables]]
deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "TableTraits", "Test"]
-git-tree-sha1 = "d0c690d37c73aeb5ca063056283fde5585a41710"
+git-tree-sha1 = "bb1064c9a84c52e277f1096cf41434b675cd368b"
uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
-version = "1.5.0"
+version = "1.6.1"
[[Tar]]
deps = ["ArgTools", "SHA"]
@@ -656,9 +719,9 @@ uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[[TimerOutputs]]
deps = ["ExprTools", "Printf"]
-git-tree-sha1 = "209a8326c4f955e2442c07b56029e88bb48299c7"
+git-tree-sha1 = "97e999be94a7147d0609d0b9fc9feca4bf24d76b"
uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
-version = "0.5.12"
+version = "0.5.15"
[[TranscodingStreams]]
deps = ["Random", "Test"]
@@ -666,11 +729,6 @@ git-tree-sha1 = "216b95ea110b5972db65aa90f88d8d89dcb8851c"
uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa"
version = "0.9.6"
-[[URIs]]
-git-tree-sha1 = "97bbe755a53fe859669cd907f2d96aee8d2c1355"
-uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
-version = "1.3.0"
-
[[UUIDs]]
deps = ["Random", "SHA"]
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
@@ -679,9 +737,21 @@ uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
[[VersionParsing]]
-git-tree-sha1 = "80229be1f670524750d905f8fc8148e5a8c4537f"
+git-tree-sha1 = "58d6e80b4ee071f5efd07fda82cb9fbe17200868"
uuid = "81def892-9a0e-5fdd-b105-ffc91e053289"
-version = "1.2.0"
+version = "1.3.0"
+
+[[WeakRefStrings]]
+deps = ["DataAPI", "InlineStrings", "Parsers"]
+git-tree-sha1 = "c69f9da3ff2f4f02e811c3323c22e5dfcb584cfa"
+uuid = "ea10d353-3f73-51f8-a26c-33c1cb351aa5"
+version = "1.4.1"
+
+[[ZipFile]]
+deps = ["Libdl", "Printf", "Zlib_jll"]
+git-tree-sha1 = "3593e69e469d2111389a9bd06bac1f3d730ac6de"
+uuid = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea"
+version = "0.9.4"
[[Zlib_jll]]
deps = ["Libdl"]
@@ -689,9 +759,9 @@ uuid = "83775a58-1f1d-513f-b197-d71354ab007a"
[[bliss_jll]]
deps = ["Artifacts", "GMP_jll", "JLLWrappers", "Libdl", "Pkg"]
-git-tree-sha1 = "efa0ae50a40cdf404e18ce375dfb764001f38b92"
+git-tree-sha1 = "f8b75e896a326a162a4f6e998990521d8302c810"
uuid = "508c9074-7a14-5c94-9582-3d4bc1871065"
-version = "0.73.0+1"
+version = "0.77.0+1"
[[nghttp2_jll]]
deps = ["Artifacts", "Libdl"]
diff --git a/docs/jump-tutorials/getting-started.ipynb b/docs/jump-tutorials/getting-started.ipynb
index 9f3e0e7..f6b6eae 100644
--- a/docs/jump-tutorials/getting-started.ipynb
+++ b/docs/jump-tutorials/getting-started.ipynb
@@ -2,10 +2,10 @@
"cells": [
{
"cell_type": "markdown",
- "id": "d5d451ac",
+ "id": "9b0c4eed",
"metadata": {},
"source": [
- "# Getting started with MIPLearn\n",
+ "# Getting started\n",
"\n",
"## Introduction\n",
"\n",
@@ -14,13 +14,7 @@
"1. Install the Julia/JuMP version of MIPLearn\n",
"2. Model a simple optimization problem using JuMP\n",
"3. Generate training data and train the ML models\n",
- "4. Use the ML models together with SCIP to solve new instances\n",
- "\n",
- "
\n",
- "Note\n",
- " \n",
- "In this tutorial, we use SCIP because it is more widely available than commercial MIP solvers. However, all the steps below should work for Gurobi, CPLEX or XPRESS, as long as you have a license for these solvers. The performance impact of MIPLearn may also change for different solvers.\n",
- "
\n",
+ "4. Use the ML models together with Gurobi to solve new instances\n",
"\n",
"\n",
"Warning\n",
@@ -32,7 +26,7 @@
},
{
"cell_type": "markdown",
- "id": "193ba5db",
+ "id": "f0d159b8",
"metadata": {},
"source": [
"## Installation\n",
@@ -48,56 +42,50 @@
{
"cell_type": "code",
"execution_count": 1,
- "id": "03519dc6",
+ "id": "b16685be",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
- "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m git-repo `https://github.com/ANL-CEEESA/MIPLearn.jl.git`\n",
- "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m registry at `~/.julia/registries/General`\n",
- "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m git-repo `https://github.com/JuliaRegistries/General.git`\n",
+ "Path `/home/axavier/Packages/MIPLearn.jl/dev` exists and looks like the correct package. Using existing path.\n",
"\u001b[32m\u001b[1m Resolving\u001b[22m\u001b[39m package versions...\n",
- "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m `~/Packages/MIPLearn/dev/docs/jump-tutorials/Project.toml`\n",
- " \u001b[90m [2b1277c3] \u001b[39m\u001b[93m~ MIPLearn v0.2.0 `https://github.com/ANL-CEEESA/MIPLearn.jl.git#dev` ⇒ v0.2.0 `https://github.com/ANL-CEEESA/MIPLearn.jl.git#dev`\u001b[39m\n",
- "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m `~/Packages/MIPLearn/dev/docs/jump-tutorials/Manifest.toml`\n",
- " \u001b[90m [2b1277c3] \u001b[39m\u001b[93m~ MIPLearn v0.2.0 `https://github.com/ANL-CEEESA/MIPLearn.jl.git#dev` ⇒ v0.2.0 `https://github.com/ANL-CEEESA/MIPLearn.jl.git#dev`\u001b[39m\n",
- "\u001b[32m\u001b[1m Building\u001b[22m\u001b[39m MIPLearn → `~/.julia/scratchspaces/44cfe95a-1eb2-52ea-b672-e2afdf69b78f/8e595dec310860a52e01c8875f51165fcccb1875/build.log`\n",
- "\u001b[32m\u001b[1mPrecompiling\u001b[22m\u001b[39m project...\n",
- "\u001b[32m ✓ \u001b[39mMIPLearn\n",
- "1 dependency successfully precompiled in 10 seconds (96 already precompiled)\n"
+ "\u001b[32m\u001b[1m No Changes\u001b[22m\u001b[39m to `~/Packages/MIPLearn/dev/docs/jump-tutorials/Project.toml`\n",
+ "\u001b[32m\u001b[1m No Changes\u001b[22m\u001b[39m to `~/Packages/MIPLearn/dev/docs/jump-tutorials/Manifest.toml`\n"
]
}
],
"source": [
"using Pkg\n",
- "Pkg.add(PackageSpec(url=\"https://github.com/ANL-CEEESA/MIPLearn.jl.git\"))"
+ "#Pkg.add(PackageSpec(url=\"https://github.com/ANL-CEEESA/MIPLearn.jl.git\"))\n",
+ "Pkg.develop(PackageSpec(path=\"/home/axavier/Packages/MIPLearn.jl/dev\"))"
]
},
{
"cell_type": "markdown",
- "id": "85c136a1",
+ "id": "e5ed7716",
"metadata": {},
"source": [
"In addition to MIPLearn itself, we will also install a few other packages that are required for this tutorial:\n",
"\n",
- "- [**SCIP**](https://www.scipopt.org/), one of the fastest non-commercial MIP solvers currently available\n",
+ "- [**Gurobi**](https://www.gurobi.com/), a state-of-the-art MIP solver\n",
"- [**JuMP**](https://jump.dev/), an open source modeling language for Julia\n",
- "- [**Distributions.jl**](https://github.com/JuliaStats/Distributions.jl), a statistics package that we will use to generate random inputs\n",
- "- [**Glob.jl**](https://github.com/vtjnash/Glob.jl), a package that retrieves all files in a directory matching a certain pattern"
+ "- [**Distributions.jl**](https://github.com/JuliaStats/Distributions.jl), a statistics package that we will use to generate random inputs"
]
},
{
"cell_type": "code",
"execution_count": 2,
- "id": "36ae5ca6",
+ "id": "f88155c5",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
+ "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m registry at `~/.julia/registries/General`\n",
+ "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m git-repo `https://github.com/JuliaRegistries/General.git`\n",
"\u001b[32m\u001b[1m Resolving\u001b[22m\u001b[39m package versions...\n",
"\u001b[32m\u001b[1m No Changes\u001b[22m\u001b[39m to `~/Packages/MIPLearn/dev/docs/jump-tutorials/Project.toml`\n",
"\u001b[32m\u001b[1m No Changes\u001b[22m\u001b[39m to `~/Packages/MIPLearn/dev/docs/jump-tutorials/Manifest.toml`\n"
@@ -107,7 +95,7 @@
"source": [
"using Pkg\n",
"Pkg.add([\n",
- " PackageSpec(url=\"https://github.com/scipopt/SCIP.jl.git\", rev=\"7aa79aaa\"),\n",
+ " PackageSpec(name=\"Gurobi\", version=\"0.9.14\"),\n",
" PackageSpec(name=\"JuMP\", version=\"0.21\"),\n",
" PackageSpec(name=\"Distributions\", version=\"0.25\"),\n",
" PackageSpec(name=\"Glob\", version=\"1\"),\n",
@@ -116,7 +104,7 @@
},
{
"cell_type": "markdown",
- "id": "735da4c6",
+ "id": "a0e1dda5",
"metadata": {},
"source": [
"
\n",
@@ -130,7 +118,7 @@
},
{
"cell_type": "markdown",
- "id": "b61a4780",
+ "id": "378b6a97",
"metadata": {},
"source": [
"## Modeling a simple optimization problem\n",
@@ -166,7 +154,7 @@
{
"cell_type": "code",
"execution_count": 3,
- "id": "b8a47a58",
+ "id": "798b2f6c",
"metadata": {},
"outputs": [],
"source": [
@@ -181,7 +169,7 @@
},
{
"cell_type": "markdown",
- "id": "7acf8818",
+ "id": "104b709a",
"metadata": {},
"source": [
"Next, we create a function that converts this data structure into a concrete JuMP model. For more details on the JuMP syntax, see [the official JuMP documentation](https://jump.dev/JuMP.jl/stable/)."
@@ -190,7 +178,7 @@
{
"cell_type": "code",
"execution_count": 4,
- "id": "1b8245e2",
+ "id": "a7c048e4",
"metadata": {},
"outputs": [],
"source": [
@@ -219,7 +207,7 @@
},
{
"cell_type": "markdown",
- "id": "d4b9bfa3",
+ "id": "5f10142e",
"metadata": {},
"source": [
"At this point, we can already use JuMP and any mixed-integer linear programming solver to find optimal solutions to any instance of this problem. To illustrate this, let us solve a small instance with three generators, using SCIP:"
@@ -228,7 +216,7 @@
{
"cell_type": "code",
"execution_count": 5,
- "id": "a74e5709",
+ "id": "bc2022a4",
"metadata": {},
"outputs": [
{
@@ -242,7 +230,7 @@
}
],
"source": [
- "using SCIP\n",
+ "using Gurobi\n",
"\n",
"model = build_uc_model(\n",
" UnitCommitmentData(\n",
@@ -254,8 +242,8 @@
" )\n",
")\n",
"\n",
- "scip = optimizer_with_attributes(SCIP.Optimizer, \"limits/gap\" => 1e-4)\n",
- "set_optimizer(model, scip)\n",
+ "gurobi = optimizer_with_attributes(Gurobi.Optimizer, \"Threads\" => 1, \"Seed\" => 42)\n",
+ "set_optimizer(model, gurobi)\n",
"set_silent(model)\n",
"optimize!(model)\n",
"\n",
@@ -266,7 +254,7 @@
},
{
"cell_type": "markdown",
- "id": "033821cd",
+ "id": "9ee6958b",
"metadata": {},
"source": [
"Running the code above, we found that the optimal solution for our small problem instance costs \\$1320. It is achieve by keeping generators 2 and 3 online and producing, respectively, 60 MW and 40 MW of power."
@@ -274,7 +262,7 @@
},
{
"cell_type": "markdown",
- "id": "fdc17cc8",
+ "id": "f34e3d44",
"metadata": {},
"source": [
"## Generating training data\n",
@@ -289,7 +277,7 @@
{
"cell_type": "code",
"execution_count": 6,
- "id": "adec6906",
+ "id": "a498e1e1",
"metadata": {},
"outputs": [],
"source": [
@@ -317,433 +305,377 @@
},
{
"cell_type": "markdown",
- "id": "b1f4bdbd",
+ "id": "e33bb12c",
"metadata": {},
"source": [
- "In this example, for simplicity, only the demands change from one instance to the next. We could also have made the prices and the production limits random. The more randomization we have in the training data, however, the more challenging it is for the machine learning models to learn solution patterns.\n",
+ "In this example, for simplicity, only the demands change from one instance to the next. We could also have randomized the costs, production limits or even the number of units. The more randomization we have in the training data, however, the more challenging it is for the machine learning models to learn solution patterns.\n",
"\n",
- "Now we generate 100 instances of this problem, each one with 1,000 generators. We will use the first 90 instances for training, and the remaining 10 instances to evaluate SCIP's performance."
+ "Now we generate 500 instances of this problem, each one with 50 generators, and we use 450 of these instances for training. After generating the instances, we write them to individual files. MIPLearn uses files during the training process because, for large-scale optimization problems, it is often impractical to hold in memory the entire training data, as well as the concrete JuMP models. Files also make it much easier to solve multiple instances simultaneously, potentially even on multiple machines. We will cover parallel and distributed computing in a future tutorial. The code below generates the files `uc/train/00001.jld2`, `uc/train/00002.jld2`, etc., which contain the input data in [JLD2 format](https://github.com/JuliaIO/JLD2.jl)."
]
},
{
"cell_type": "code",
"execution_count": 7,
- "id": "ffe2f033",
+ "id": "5358a046",
"metadata": {},
"outputs": [],
"source": [
- "data = random_uc_data(samples=100, n=1000);\n",
- "train_data = data[1:90]\n",
- "test_data = data[91:100];"
+ "using MIPLearn\n",
+ "data = random_uc_data(samples=500, n=50);\n",
+ "train_files = MIPLearn.save(data[1:450], \"uc/train/\")\n",
+ "test_files = MIPLearn.save(data[451:500], \"uc/test/\");"
]
},
{
"cell_type": "markdown",
- "id": "0785952f",
+ "id": "38a27d1c",
"metadata": {},
"source": [
- "Next, we write these data structures to individual files. MIPLearn uses files during the training process because, for large-scale optimization problems, it is often impractical to hold the entire training data, as well as the concrete JuMP models, in memory. Files also make it much easier to solve multiple instances simultaneously, potentially even on multiple machines. We will cover parallel and distributed computing in a future tutorial.\n",
- "\n",
- "The code below generates the files `uc/train/000001.jld2`, `uc/train/000002.jld2`, etc., which contain the input data in [JLD2 format](https://github.com/JuliaIO/JLD2.jl)."
+ "Finally, we use `LearningSolver` to solve all the training instances. `LearningSolver` is the main component provided by MIPLearn, which integrates MIP solvers and ML. The optimal solutions, along with other useful training data, are stored in HDF5 files `uc/train/00001.h5`, `uc/train/00002.h5`, etc."
]
},
{
"cell_type": "code",
"execution_count": 8,
- "id": "e5ad203b",
+ "id": "c341b12d",
"metadata": {},
"outputs": [],
"source": [
- "using MIPLearn\n",
- "MIPLearn.save(data[1:90], \"uc/train/\")\n",
- "MIPLearn.save(data[91:100], \"uc/test/\")\n",
- "\n",
- "using Glob\n",
- "train_files = glob(\"uc/train/*.jld2\")\n",
- "test_files = glob(\"uc/test/*.jld2\");"
+ "solver = LearningSolver(gurobi)\n",
+ "solve!(solver, train_files, build_uc_model);"
]
},
{
"cell_type": "markdown",
- "id": "a758917d",
- "metadata": {},
- "source": [
- "Finally, we use `MIPLearn.LearningSolver` and `MIPLearn.solve!` to solve all the training instances. `LearningSolver` is the main component provided by MIPLearn, which integrates MIP solvers and ML. The `solve!` function can be used to solve either one or multiple instances, and requires: (i) the list of files containing the training data; and (ii) the function that converts the data structure into a concrete JuMP model:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "id": "930b59cd",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "105.491488 seconds (94.05 M allocations: 3.632 GiB, 1.17% gc time, 0.56% compilation time)\n"
- ]
- },
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "WARNING: Dual bound 1.98665e+07 is larger than the objective of the primal solution 1.98665e+07. The solution might not be optimal.\n"
- ]
- }
- ],
- "source": [
- "using Glob\n",
- "solver = LearningSolver(scip)\n",
- "@time solve!(solver, train_files, build_uc_model);"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "2e0c3908",
- "metadata": {},
- "source": [
- "The macro `@time` shows us how long did the code take to run. We can see that SCIP was able to solve all training instances in about 2 minutes. The solutions, and other useful training data, are stored by MIPLearn in `.h5` files, stored side-by-side with the original `.jld2` files."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "67b5cc7c",
+ "id": "189b4f60",
"metadata": {},
"source": [
"## Solving new instances\n",
"\n",
- "With training data in hand, we can now fit the ML models using `MIPLearn.fit!`, then solve the test instances with `MIPLearn.solve!`, as shown below:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "id": "ae19716a",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- " 5.876161 seconds (9.35 M allocations: 335.718 MiB, 1.57% gc time)\n"
- ]
- }
- ],
- "source": [
- "solver_ml = LearningSolver(scip)\n",
- "fit!(solver_ml, train_files, build_uc_model)\n",
- "@time solve!(solver_ml, test_files, build_uc_model);"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "59c709bf",
- "metadata": {},
- "source": [
- "The trained MIP solver was able to solve all test instances in about 6 seconds. To see that ML is being helpful here, let us repeat the code above, but remove the `fit!` line:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "id": "cab08714",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- " 10.308870 seconds (8.19 M allocations: 279.036 MiB, 0.45% gc time)\n"
- ]
- }
- ],
- "source": [
- "solver_baseline = LearningSolver(scip)\n",
- "@time solve!(solver_baseline, test_files, build_uc_model);"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "fb9fbfa7",
- "metadata": {},
- "source": [
- "Without the help of the ML models, SCIP took around 10 seconds to solve the same test instances.\n",
- "\n",
- "
\n",
- "Note\n",
- " \n",
- "Note that is is not necessary to specify what ML models to use. MIPLearn, by default, will try a number of classical ML models and will choose the one that performs the best, based on k-fold cross validation. MIPLearn is also able to automatically collect features based on the MIP formulation of the problem and the solution to the LP relaxation, among other things, so it does not require handcrafted features. If you do want to customize the models and features, however, that is also possible, as we will see in a later tutorial.\n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "dc1bc410",
- "metadata": {},
- "source": [
- "## Understanding the acceleration\n",
- "\n",
- "Let us go a bit deeper and try to understand how exactly did MIPLearn accelerate SCIP's performance. First, we are going to solve one of the test instances again, using the trained solver, but this time using the `tee=true` parameter, so that we can see SCIP's log:"
+ "With training data in hand, we can now fit the ML models using `MIPLearn.fit!`, then solve the test instances with `MIPLearn.solve!`, as shown below. The `tee=true` parameter asks MIPLearn to print the solver log to the screen."
]
},
{
"cell_type": "code",
- "execution_count": 12,
- "id": "cd429f82",
+ "execution_count": 9,
+ "id": "1cf11450",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "presolving:\n",
- "(round 1, fast) 861 del vars, 861 del conss, 0 add conss, 2000 chg bounds, 0 chg sides, 0 chg coeffs, 0 upgd conss, 0 impls, 0 clqs\n",
- "(round 2, fast) 861 del vars, 1722 del conss, 0 add conss, 2000 chg bounds, 0 chg sides, 0 chg coeffs, 0 upgd conss, 0 impls, 0 clqs\n",
- "(round 3, fast) 862 del vars, 1722 del conss, 0 add conss, 2000 chg bounds, 0 chg sides, 0 chg coeffs, 0 upgd conss, 0 impls, 0 clqs\n",
- "presolving (4 rounds: 4 fast, 1 medium, 1 exhaustive):\n",
- " 862 deleted vars, 1722 deleted constraints, 0 added constraints, 2000 tightened bounds, 0 added holes, 0 changed sides, 0 changed coefficients\n",
- " 0 implications, 0 cliques\n",
- "presolved problem has 1138 variables (0 bin, 0 int, 0 impl, 1138 cont) and 279 constraints\n",
- " 279 constraints of type
\n",
- "Presolving Time: 0.03\n",
+ "Gurobi Optimizer version 9.1.1 build v9.1.1rc0 (linux64)\n",
+ "Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
+ "Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
+ "Model fingerprint: 0xfb382c05\n",
+ "Coefficient statistics:\n",
+ " Matrix range [1e+00, 1e+03]\n",
+ " Objective range [1e+00, 6e+04]\n",
+ " Bounds range [1e+00, 1e+00]\n",
+ " RHS range [2e+04, 2e+04]\n",
+ "Presolve removed 100 rows and 50 columns\n",
+ "Presolve time: 0.00s\n",
+ "Presolved: 1 rows, 50 columns, 50 nonzeros\n",
+ "\n",
+ "Iteration Objective Primal Inf. Dual Inf. Time\n",
+ " 0 7.0629410e+05 6.782322e+02 0.000000e+00 0s\n",
+ " 1 8.0678161e+05 0.000000e+00 0.000000e+00 0s\n",
+ "\n",
+ "Solved in 1 iterations and 0.00 seconds\n",
+ "Optimal objective 8.067816095e+05\n",
+ "\n",
+ "User-callback calls 33, time in user-callback 0.00 sec\n",
+ "\n",
+ "Gurobi Optimizer version 9.1.1 build v9.1.1rc0 (linux64)\n",
+ "Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
+ "Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
+ "Model fingerprint: 0x7bb6bbd6\n",
+ "Variable types: 50 continuous, 50 integer (50 binary)\n",
+ "Coefficient statistics:\n",
+ " Matrix range [1e+00, 1e+03]\n",
+ " Objective range [1e+00, 6e+04]\n",
+ " Bounds range [1e+00, 1e+00]\n",
+ " RHS range [2e+04, 2e+04]\n",
"\n",
- " time | node | left |LP iter|LP it/n|mem/heur|mdpt |vars |cons |rows |cuts |sepa|confs|strbr| dualbound | primalbound | gap | compl. \n",
- "* 0.0s| 1 | 0 | 203 | - | LP | 0 |1138 | 279 | 279 | 0 | 0 | 0 | 0 | 1.705035e+07 | 1.705035e+07 | 0.00%| unknown\n",
- " 0.0s| 1 | 0 | 203 | - | 8950k | 0 |1138 | 279 | 279 | 0 | 0 | 0 | 0 | 1.705035e+07 | 1.705035e+07 | 0.00%| unknown\n",
+ "User MIP start produced solution with objective 822175 (0.00s)\n",
+ "User MIP start produced solution with objective 812767 (0.00s)\n",
+ "User MIP start produced solution with objective 811628 (0.00s)\n",
+ "User MIP start produced solution with objective 809648 (0.01s)\n",
+ "User MIP start produced solution with objective 808536 (0.01s)\n",
+ "Loaded user MIP start with objective 808536\n",
"\n",
- "SCIP Status : problem is solved [optimal solution found]\n",
- "Solving Time (sec) : 0.04\n",
- "Solving Nodes : 1\n",
- "Primal Bound : +1.70503465600131e+07 (1 solutions)\n",
- "Dual Bound : +1.70503465600131e+07\n",
- "Gap : 0.00 %\n",
+ "Presolve time: 0.00s\n",
+ "Presolved: 101 rows, 100 columns, 250 nonzeros\n",
+ "Variable types: 50 continuous, 50 integer (50 binary)\n",
"\n",
- "violation: integrality condition of variable <> = 0.338047247943162\n",
- "all 1 solutions given by solution candidate storage are infeasible\n",
+ "Root relaxation: objective 8.067816e+05, 55 iterations, 0.00 seconds\n",
"\n",
- "feasible solution found by completesol heuristic after 0.1 seconds, objective value 1.705169e+07\n",
- "presolving:\n",
- "(round 1, fast) 0 del vars, 0 del conss, 0 add conss, 3000 chg bounds, 0 chg sides, 0 chg coeffs, 0 upgd conss, 0 impls, 0 clqs\n",
- "(round 2, exhaustive) 0 del vars, 0 del conss, 0 add conss, 3000 chg bounds, 0 chg sides, 0 chg coeffs, 1000 upgd conss, 0 impls, 0 clqs\n",
- "(round 3, exhaustive) 0 del vars, 0 del conss, 0 add conss, 3000 chg bounds, 0 chg sides, 0 chg coeffs, 2000 upgd conss, 1000 impls, 0 clqs\n",
- " (0.1s) probing: 51/1000 (5.1%) - 0 fixings, 0 aggregations, 0 implications, 0 bound changes\n",
- " (0.1s) probing aborted: 50/50 successive totally useless probings\n",
- " (0.1s) symmetry computation started: requiring (bin +, int -, cont +), (fixed: bin -, int +, cont -)\n",
- " (0.1s) no symmetry present\n",
- "presolving (4 rounds: 4 fast, 3 medium, 3 exhaustive):\n",
- " 0 deleted vars, 0 deleted constraints, 0 added constraints, 3000 tightened bounds, 0 added holes, 0 changed sides, 0 changed coefficients\n",
- " 2000 implications, 0 cliques\n",
- "presolved problem has 2000 variables (1000 bin, 0 int, 0 impl, 1000 cont) and 2001 constraints\n",
- " 2000 constraints of type \n",
- " 1 constraints of type \n",
- "Presolving Time: 0.11\n",
- "transformed 1/1 original solutions to the transformed problem space\n",
+ " Nodes | Current Node | Objective Bounds | Work\n",
+ " Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
- " time | node | left |LP iter|LP it/n|mem/heur|mdpt |vars |cons |rows |cuts |sepa|confs|strbr| dualbound | primalbound | gap | compl. \n",
- " 0.2s| 1 | 0 | 1201 | - | 20M | 0 |2000 |2001 |2001 | 0 | 0 | 0 | 0 | 1.705035e+07 | 1.705169e+07 | 0.01%| unknown\n",
+ " 0 0 806781.610 0 1 808536.496 806781.610 0.22% - 0s\n",
+ "H 0 0 808091.02482 806781.610 0.16% - 0s\n",
+ " 0 0 807198.955 0 2 808091.025 807198.955 0.11% - 0s\n",
+ " 0 0 807198.955 0 1 808091.025 807198.955 0.11% - 0s\n",
+ " 0 0 807198.955 0 2 808091.025 807198.955 0.11% - 0s\n",
+ " 0 0 807226.059 0 3 808091.025 807226.059 0.11% - 0s\n",
+ " 0 0 807240.578 0 5 808091.025 807240.578 0.11% - 0s\n",
+ " 0 0 807240.663 0 5 808091.025 807240.663 0.11% - 0s\n",
+ " 0 0 807259.825 0 4 808091.025 807259.825 0.10% - 0s\n",
+ " 0 0 807275.314 0 5 808091.025 807275.314 0.10% - 0s\n",
+ " 0 0 807279.037 0 6 808091.025 807279.037 0.10% - 0s\n",
+ " 0 0 807291.881 0 8 808091.025 807291.881 0.10% - 0s\n",
+ " 0 0 807325.323 0 6 808091.025 807325.323 0.09% - 0s\n",
+ " 0 0 807326.015 0 7 808091.025 807326.015 0.09% - 0s\n",
+ " 0 0 807326.798 0 7 808091.025 807326.798 0.09% - 0s\n",
+ " 0 0 807328.550 0 8 808091.025 807328.550 0.09% - 0s\n",
+ " 0 0 807331.193 0 9 808091.025 807331.193 0.09% - 0s\n",
+ " 0 0 807332.143 0 7 808091.025 807332.143 0.09% - 0s\n",
+ " 0 0 807335.410 0 8 808091.025 807335.410 0.09% - 0s\n",
+ " 0 0 807335.452 0 8 808091.025 807335.452 0.09% - 0s\n",
+ " 0 0 807337.253 0 9 808091.025 807337.253 0.09% - 0s\n",
+ " 0 0 807337.409 0 9 808091.025 807337.409 0.09% - 0s\n",
+ " 0 0 807347.720 0 8 808091.025 807347.720 0.09% - 0s\n",
+ " 0 0 807352.765 0 7 808091.025 807352.765 0.09% - 0s\n",
+ " 0 0 807366.618 0 9 808091.025 807366.618 0.09% - 0s\n",
+ " 0 0 807368.345 0 10 808091.025 807368.345 0.09% - 0s\n",
+ " 0 0 807369.195 0 10 808091.025 807369.195 0.09% - 0s\n",
+ " 0 0 807392.319 0 8 808091.025 807392.319 0.09% - 0s\n",
+ " 0 0 807401.436 0 9 808091.025 807401.436 0.09% - 0s\n",
+ " 0 0 807405.685 0 8 808091.025 807405.685 0.08% - 0s\n",
+ " 0 0 807411.994 0 8 808091.025 807411.994 0.08% - 0s\n",
+ " 0 0 807424.710 0 9 808091.025 807424.710 0.08% - 0s\n",
+ " 0 0 807424.867 0 11 808091.025 807424.867 0.08% - 0s\n",
+ " 0 0 807427.428 0 12 808091.025 807427.428 0.08% - 0s\n",
+ " 0 0 807433.211 0 10 808091.025 807433.211 0.08% - 0s\n",
+ " 0 0 807439.215 0 10 808091.025 807439.215 0.08% - 0s\n",
+ " 0 0 807439.303 0 11 808091.025 807439.303 0.08% - 0s\n",
+ " 0 0 807443.312 0 11 808091.025 807443.312 0.08% - 0s\n",
+ " 0 0 807444.488 0 12 808091.025 807444.488 0.08% - 0s\n",
+ " 0 0 807444.499 0 13 808091.025 807444.499 0.08% - 0s\n",
+ " 0 0 807444.499 0 13 808091.025 807444.499 0.08% - 0s\n",
+ " 0 2 807445.982 0 13 808091.025 807445.982 0.08% - 0s\n",
"\n",
- "SCIP Status : solving was interrupted [gap limit reached]\n",
- "Solving Time (sec) : 0.21\n",
- "Solving Nodes : 1\n",
- "Primal Bound : +1.70516871251443e+07 (1 solutions)\n",
- "Dual Bound : +1.70503465600130e+07\n",
- "Gap : 0.01 %\n",
+ "Cutting planes:\n",
+ " Cover: 3\n",
+ " MIR: 18\n",
+ " StrongCG: 1\n",
+ " Flow cover: 3\n",
+ "\n",
+ "Explored 39 nodes (333 simplex iterations) in 0.03 seconds\n",
+ "Thread count was 1 (of 32 available processors)\n",
+ "\n",
+ "Solution count 6: 808091 808536 809648 ... 822175\n",
+ "\n",
+ "Optimal solution found (tolerance 1.00e-04)\n",
+ "Best objective 8.080910248225e+05, best bound 8.080640878016e+05, gap 0.0033%\n",
+ "\n",
+ "User-callback calls 341, time in user-callback 0.00 sec\n",
"\n"
]
}
],
"source": [
+ "solver_ml = LearningSolver(gurobi)\n",
+ "fit!(solver_ml, train_files, build_uc_model)\n",
"solve!(solver_ml, test_files[1], build_uc_model, tee=true);"
]
},
{
"cell_type": "markdown",
- "id": "8b14440d",
+ "id": "872211e7",
"metadata": {},
"source": [
- "The log above is quite complicated if you have never seen it before, but the important line is the one starting with `feasible solution found [...] objective value 1.705169e+07`. This line indicates that MIPLearn was able to construct a warm start with value `1.705169e+07`. Using this warm start, SCIP then used the branch-and-cut method to either prove its optimality or to find an even better solution. Very quickly, however, SCIP proved that the solution produced by MIPLearn was indeed optimal. It was able to do this without generating a single cutting plane or running any other heuristics; it could tell the optimality by the root LP relaxation alone, which was very fast. \n",
- "\n",
- "Let us now repeat the process, but using the untrained solver this time:"
+ "By examining the solve log above, specifically the line `Loaded user MIP start with objective...`, we can see that MIPLearn was able to construct an initial solution which turned out to be near optimal for the problem. Now let us repeat the code above, but using an untrained solver. Note that the `fit` line is omitted."
]
},
{
"cell_type": "code",
- "execution_count": 13,
- "id": "3a96934a",
+ "execution_count": 10,
+ "id": "fc1e3629",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "presolving:\n",
- "(round 1, fast) 861 del vars, 861 del conss, 0 add conss, 2000 chg bounds, 0 chg sides, 0 chg coeffs, 0 upgd conss, 0 impls, 0 clqs\n",
- "(round 2, fast) 861 del vars, 1722 del conss, 0 add conss, 2000 chg bounds, 0 chg sides, 0 chg coeffs, 0 upgd conss, 0 impls, 0 clqs\n",
- "(round 3, fast) 862 del vars, 1722 del conss, 0 add conss, 2000 chg bounds, 0 chg sides, 0 chg coeffs, 0 upgd conss, 0 impls, 0 clqs\n",
- "presolving (4 rounds: 4 fast, 1 medium, 1 exhaustive):\n",
- " 862 deleted vars, 1722 deleted constraints, 0 added constraints, 2000 tightened bounds, 0 added holes, 0 changed sides, 0 changed coefficients\n",
- " 0 implications, 0 cliques\n",
- "presolved problem has 1138 variables (0 bin, 0 int, 0 impl, 1138 cont) and 279 constraints\n",
- " 279 constraints of type \n",
- "Presolving Time: 0.03\n",
+ "Gurobi Optimizer version 9.1.1 build v9.1.1rc0 (linux64)\n",
+ "Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
+ "Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
+ "Model fingerprint: 0xfb382c05\n",
+ "Coefficient statistics:\n",
+ " Matrix range [1e+00, 1e+03]\n",
+ " Objective range [1e+00, 6e+04]\n",
+ " Bounds range [1e+00, 1e+00]\n",
+ " RHS range [2e+04, 2e+04]\n",
+ "Presolve removed 100 rows and 50 columns\n",
+ "Presolve time: 0.00s\n",
+ "Presolved: 1 rows, 50 columns, 50 nonzeros\n",
+ "\n",
+ "Iteration Objective Primal Inf. Dual Inf. Time\n",
+ " 0 7.0629410e+05 6.782322e+02 0.000000e+00 0s\n",
+ " 1 8.0678161e+05 0.000000e+00 0.000000e+00 0s\n",
+ "\n",
+ "Solved in 1 iterations and 0.00 seconds\n",
+ "Optimal objective 8.067816095e+05\n",
"\n",
- " time | node | left |LP iter|LP it/n|mem/heur|mdpt |vars |cons |rows |cuts |sepa|confs|strbr| dualbound | primalbound | gap | compl. \n",
- "* 0.0s| 1 | 0 | 203 | - | LP | 0 |1138 | 279 | 279 | 0 | 0 | 0 | 0 | 1.705035e+07 | 1.705035e+07 | 0.00%| unknown\n",
- " 0.0s| 1 | 0 | 203 | - | 8950k | 0 |1138 | 279 | 279 | 0 | 0 | 0 | 0 | 1.705035e+07 | 1.705035e+07 | 0.00%| unknown\n",
+ "User-callback calls 33, time in user-callback 0.00 sec\n",
"\n",
- "SCIP Status : problem is solved [optimal solution found]\n",
- "Solving Time (sec) : 0.04\n",
- "Solving Nodes : 1\n",
- "Primal Bound : +1.70503465600131e+07 (1 solutions)\n",
- "Dual Bound : +1.70503465600131e+07\n",
- "Gap : 0.00 %\n",
+ "Gurobi Optimizer version 9.1.1 build v9.1.1rc0 (linux64)\n",
+ "Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
+ "Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
+ "Model fingerprint: 0x899aac3d\n",
+ "Variable types: 50 continuous, 50 integer (50 binary)\n",
+ "Coefficient statistics:\n",
+ " Matrix range [1e+00, 1e+03]\n",
+ " Objective range [1e+00, 6e+04]\n",
+ " Bounds range [1e+00, 1e+00]\n",
+ " RHS range [2e+04, 2e+04]\n",
+ "Found heuristic solution: objective 893073.33620\n",
+ "Presolve time: 0.00s\n",
+ "Presolved: 101 rows, 100 columns, 250 nonzeros\n",
+ "Variable types: 50 continuous, 50 integer (50 binary)\n",
"\n",
- "violation: integrality condition of variable <> = 0.338047247943162\n",
- "all 1 solutions given by solution candidate storage are infeasible\n",
+ "Root relaxation: objective 8.067816e+05, 55 iterations, 0.00 seconds\n",
"\n",
- "presolving:\n",
- "(round 1, fast) 0 del vars, 0 del conss, 0 add conss, 2000 chg bounds, 0 chg sides, 0 chg coeffs, 0 upgd conss, 0 impls, 0 clqs\n",
- "(round 2, exhaustive) 0 del vars, 0 del conss, 0 add conss, 2000 chg bounds, 0 chg sides, 0 chg coeffs, 1000 upgd conss, 0 impls, 0 clqs\n",
- "(round 3, exhaustive) 0 del vars, 0 del conss, 0 add conss, 2000 chg bounds, 0 chg sides, 0 chg coeffs, 2000 upgd conss, 1000 impls, 0 clqs\n",
- " (0.0s) probing: 51/1000 (5.1%) - 0 fixings, 0 aggregations, 0 implications, 0 bound changes\n",
- " (0.0s) probing aborted: 50/50 successive totally useless probings\n",
- " (0.0s) symmetry computation started: requiring (bin +, int -, cont +), (fixed: bin -, int +, cont -)\n",
- " (0.0s) no symmetry present\n",
- "presolving (4 rounds: 4 fast, 3 medium, 3 exhaustive):\n",
- " 0 deleted vars, 0 deleted constraints, 0 added constraints, 2000 tightened bounds, 0 added holes, 0 changed sides, 0 changed coefficients\n",
- " 2000 implications, 0 cliques\n",
- "presolved problem has 2000 variables (1000 bin, 0 int, 0 impl, 1000 cont) and 2001 constraints\n",
- " 2000 constraints of type \n",
- " 1 constraints of type \n",
- "Presolving Time: 0.03\n",
+ " Nodes | Current Node | Objective Bounds | Work\n",
+ " Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
- " time | node | left |LP iter|LP it/n|mem/heur|mdpt |vars |cons |rows |cuts |sepa|confs|strbr| dualbound | primalbound | gap | compl. \n",
- "p 0.0s| 1 | 0 | 1 | - | locks| 0 |2000 |2001 |2001 | 0 | 0 | 0 | 0 | 0.000000e+00 | 2.335200e+07 | Inf | unknown\n",
- "p 0.0s| 1 | 0 | 2 | - | vbounds| 0 |2000 |2001 |2001 | 0 | 0 | 0 | 0 | 0.000000e+00 | 1.839873e+07 | Inf | unknown\n",
- " 0.1s| 1 | 0 | 1204 | - | 20M | 0 |2000 |2001 |2001 | 0 | 0 | 0 | 0 | 1.705035e+07 | 1.839873e+07 | 7.91%| unknown\n",
- " 0.1s| 1 | 0 | 1207 | - | 22M | 0 |2000 |2001 |2002 | 1 | 1 | 0 | 0 | 1.705036e+07 | 1.839873e+07 | 7.91%| unknown\n",
- "r 0.1s| 1 | 0 | 1207 | - |shifting| 0 |2000 |2001 |2002 | 1 | 1 | 0 | 0 | 1.705036e+07 | 1.711399e+07 | 0.37%| unknown\n",
- " 0.1s| 1 | 0 | 1209 | - | 22M | 0 |2000 |2001 |2003 | 2 | 2 | 0 | 0 | 1.705037e+07 | 1.711399e+07 | 0.37%| unknown\n",
- "r 0.1s| 1 | 0 | 1209 | - |shifting| 0 |2000 |2001 |2003 | 2 | 2 | 0 | 0 | 1.705037e+07 | 1.706492e+07 | 0.09%| unknown\n",
- " 0.1s| 1 | 0 | 1210 | - | 22M | 0 |2000 |2001 |2004 | 3 | 3 | 0 | 0 | 1.705037e+07 | 1.706492e+07 | 0.09%| unknown\n",
- " 0.1s| 1 | 0 | 1211 | - | 23M | 0 |2000 |2001 |2005 | 4 | 4 | 0 | 0 | 1.705037e+07 | 1.706492e+07 | 0.09%| unknown\n",
- " 0.1s| 1 | 0 | 1212 | - | 23M | 0 |2000 |2001 |2006 | 5 | 5 | 0 | 0 | 1.705037e+07 | 1.706492e+07 | 0.09%| unknown\n",
- "r 0.1s| 1 | 0 | 1212 | - |shifting| 0 |2000 |2001 |2006 | 5 | 5 | 0 | 0 | 1.705037e+07 | 1.706228e+07 | 0.07%| unknown\n",
- " 0.1s| 1 | 0 | 1214 | - | 24M | 0 |2000 |2001 |2007 | 6 | 7 | 0 | 0 | 1.705037e+07 | 1.706228e+07 | 0.07%| unknown\n",
- " 0.2s| 1 | 0 | 1216 | - | 24M | 0 |2000 |2001 |2009 | 8 | 8 | 0 | 0 | 1.705037e+07 | 1.706228e+07 | 0.07%| unknown\n",
- " 0.2s| 1 | 0 | 1220 | - | 25M | 0 |2000 |2001 |2011 | 10 | 9 | 0 | 0 | 1.705037e+07 | 1.706228e+07 | 0.07%| unknown\n",
- " 0.2s| 1 | 0 | 1223 | - | 25M | 0 |2000 |2001 |2014 | 13 | 10 | 0 | 0 | 1.705037e+07 | 1.706228e+07 | 0.07%| unknown\n",
- " time | node | left |LP iter|LP it/n|mem/heur|mdpt |vars |cons |rows |cuts |sepa|confs|strbr| dualbound | primalbound | gap | compl. \n",
- " 0.2s| 1 | 0 | 1229 | - | 26M | 0 |2000 |2001 |2015 | 14 | 11 | 0 | 0 | 1.705038e+07 | 1.706228e+07 | 0.07%| unknown\n",
- "r 0.2s| 1 | 0 | 1403 | - |intshift| 0 |2000 |2001 |2015 | 14 | 11 | 0 | 0 | 1.705038e+07 | 1.705687e+07 | 0.04%| unknown\n",
- "L 0.7s| 1 | 0 | 1707 | - | rens| 0 |2000 |2001 |2015 | 14 | 11 | 0 | 0 | 1.705038e+07 | 1.705332e+07 | 0.02%| unknown\n",
- "L 0.7s| 1 | 0 | 1707 | - | alns| 0 |2000 |2001 |2015 | 14 | 11 | 0 | 0 | 1.705038e+07 | 1.705178e+07 | 0.01%| unknown\n",
+ " 0 0 806781.610 0 1 893073.336 806781.610 9.66% - 0s\n",
+ "H 0 0 842766.25007 806781.610 4.27% - 0s\n",
+ "H 0 0 818273.05208 806781.610 1.40% - 0s\n",
+ " 0 0 807198.955 0 2 818273.052 807198.955 1.35% - 0s\n",
+ "H 0 0 813499.43980 807198.955 0.77% - 0s\n",
+ " 0 0 807246.085 0 3 813499.440 807246.085 0.77% - 0s\n",
+ " 0 0 807272.377 0 4 813499.440 807272.377 0.77% - 0s\n",
+ " 0 0 807284.557 0 1 813499.440 807284.557 0.76% - 0s\n",
+ " 0 0 807298.666 0 2 813499.440 807298.666 0.76% - 0s\n",
+ " 0 0 807305.559 0 6 813499.440 807305.559 0.76% - 0s\n",
+ "H 0 0 812223.58825 807305.559 0.61% - 0s\n",
+ " 0 0 807309.503 0 4 812223.588 807309.503 0.61% - 0s\n",
+ " 0 0 807339.469 0 4 812223.588 807339.469 0.60% - 0s\n",
+ " 0 0 807344.135 0 6 812223.588 807344.135 0.60% - 0s\n",
+ " 0 0 807359.565 0 7 812223.588 807359.565 0.60% - 0s\n",
+ " 0 0 807371.997 0 8 812223.588 807371.997 0.60% - 0s\n",
+ " 0 0 807372.245 0 8 812223.588 807372.245 0.60% - 0s\n",
+ " 0 0 807378.545 0 9 812223.588 807378.545 0.60% - 0s\n",
+ " 0 0 807378.545 0 9 812223.588 807378.545 0.60% - 0s\n",
+ "H 0 0 811628.30751 807378.545 0.52% - 0s\n",
+ "H 0 0 810280.45754 807378.545 0.36% - 0s\n",
+ " 0 0 807378.545 0 1 810280.458 807378.545 0.36% - 0s\n",
+ "H 0 0 810123.10116 807378.545 0.34% - 0s\n",
+ " 0 0 807378.545 0 1 810123.101 807378.545 0.34% - 0s\n",
+ " 0 0 807378.545 0 3 810123.101 807378.545 0.34% - 0s\n",
+ " 0 0 807378.545 0 7 810123.101 807378.545 0.34% - 0s\n",
+ " 0 0 807379.672 0 8 810123.101 807379.672 0.34% - 0s\n",
+ " 0 0 807379.905 0 9 810123.101 807379.905 0.34% - 0s\n",
+ " 0 0 807380.615 0 10 810123.101 807380.615 0.34% - 0s\n",
+ " 0 0 807402.384 0 10 810123.101 807402.384 0.34% - 0s\n",
+ " 0 0 807407.299 0 12 810123.101 807407.299 0.34% - 0s\n",
+ " 0 0 807407.299 0 12 810123.101 807407.299 0.34% - 0s\n",
+ " 0 2 807408.320 0 12 810123.101 807408.320 0.34% - 0s\n",
+ "H 3 3 809647.65837 807476.463 0.27% 3.0 0s\n",
+ "H 84 35 808870.26352 807568.065 0.16% 2.7 0s\n",
+ "H 99 29 808536.49552 807588.561 0.12% 2.7 0s\n",
+ "* 310 1 5 808091.02482 808069.217 0.00% 3.3 0s\n",
"\n",
- "SCIP Status : solving was interrupted [gap limit reached]\n",
- "Solving Time (sec) : 0.69\n",
- "Solving Nodes : 1\n",
- "Primal Bound : +1.70517823853380e+07 (13 solutions)\n",
- "Dual Bound : +1.70503798271962e+07\n",
- "Gap : 0.01 %\n",
+ "Cutting planes:\n",
+ " Gomory: 3\n",
+ " Cover: 7\n",
+ " MIR: 9\n",
+ " Flow cover: 3\n",
+ "\n",
+ "Explored 311 nodes (1175 simplex iterations) in 0.06 seconds\n",
+ "Thread count was 1 (of 32 available processors)\n",
+ "\n",
+ "Solution count 10: 808091 808536 808870 ... 818273\n",
+ "\n",
+ "Optimal solution found (tolerance 1.00e-04)\n",
+ "Best objective 8.080910248225e+05, best bound 8.080692169045e+05, gap 0.0027%\n",
+ "\n",
+ "User-callback calls 832, time in user-callback 0.00 sec\n",
"\n"
]
}
],
"source": [
+ "solver_baseline = LearningSolver(gurobi)\n",
"solve!(solver_baseline, test_files[1], build_uc_model, tee=true);"
]
},
{
"cell_type": "markdown",
- "id": "92d23037",
+ "id": "7b5ce528",
"metadata": {},
"source": [
- "In this log file, notice how the previous line about warm starts is missing. Since no warm starts were provided, SCIP had to find an initial solution using its own internal heuristics, which are not specifically tailored for this problem. The initial solution found by SCIP's heuristics has value `2.335200e+07`, which is significantly worse than the one constructed by MIPLearn. SCIP then proceeded to improve this solution, by generating cutting planes and repeatedly running additional primal heuristics. In the end, it was able to find the optimal solution, as expected, but it took longer.\n",
+ "In the log above, the `MIP start` line is missing, and Gurobi had to start with a significantly inferior initial solution. The solver was still able to find the optimal solution at the end, but it required using its own internal heuristic procedures. In this example, because we solve very small optimization problems, there was almost no difference in terms of running time. For larger problems, however, the difference can be significant. See benchmarks for more details.\n",
+ "\n",
+ "\n",
+ "Note\n",
+ " \n",
+ "In addition to partial initial solutions, MIPLearn is also able to predict lazy constraints, cutting planes and branching priorities. See the next tutorials for more details.\n",
+ "
\n",
"\n",
- "In summary, MIPLearn accelerated the solution process by constructing a high-quality initial solution. In the following tutorials, we will see other strategies that MIPLearn can use to accelerate MIP performance, besides warm starts."
+ "\n",
+ "Note\n",
+ " \n",
+ "It is not necessary to specify what ML models to use. MIPLearn, by default, will try a number of classical ML models and will choose the one that performs the best, based on k-fold cross validation. MIPLearn is also able to automatically collect features based on the MIP formulation of the problem and the solution to the LP relaxation, among other things, so it does not require handcrafted features. If you do want to customize the models and features, however, that is also possible, as we will see in a later tutorial.\n",
+ "
"
]
},
{
"cell_type": "markdown",
- "id": "94f869d7",
+ "id": "46da094b",
"metadata": {},
"source": [
"## Accessing the solution\n",
"\n",
- "In the example above, we used `MIPLearn.solve!` together with data files to solve both the training and the test instances. The solutions were saved to a `.h5` files in the train/test folders, and could be retrieved by reading theses files, but that is not very convenient. In this section we will use an easier method.\n",
- "\n",
- "We can use the function `MIPLearn.load!` to obtain a regular JuMP model:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "id": "948d3056",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "A JuMP Model\n",
- "Minimization problem with:\n",
- "Variables: 2000\n",
- "Objective function type: AffExpr\n",
- "`AffExpr`-in-`MathOptInterface.EqualTo{Float64}`: 1 constraint\n",
- "`AffExpr`-in-`MathOptInterface.GreaterThan{Float64}`: 1000 constraints\n",
- "`AffExpr`-in-`MathOptInterface.LessThan{Float64}`: 1000 constraints\n",
- "`VariableRef`-in-`MathOptInterface.GreaterThan{Float64}`: 1000 constraints\n",
- "`VariableRef`-in-`MathOptInterface.ZeroOne`: 1000 constraints\n",
- "Model mode: AUTOMATIC\n",
- "CachingOptimizer state: NO_OPTIMIZER\n",
- "Solver name: No optimizer attached.\n",
- "Names registered in the model: eq_demand, eq_max_power, eq_min_power, x, y"
- ]
- },
- "execution_count": 14,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "model = MIPLearn.load(\"uc/test/000001.jld2\", build_uc_model)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "0b689ac5",
- "metadata": {},
- "source": [
- "We can then solve this model as before, with `MIPLearn.solve!`:"
+ "In the example above, we used `MIPLearn.solve` together with data files to solve both the training and the test instances. The optimal solutions were saved to HDF5 files in the train/test folders, and could be retrieved by reading theses files, but that is not very convenient. In the following example, we show how to build and solve a JuMP model entirely in-memory, using our trained solver."
]
},
{
"cell_type": "code",
- "execution_count": 15,
- "id": "696a33ae",
+ "execution_count": 11,
+ "id": "986f0c18",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "obj = 1.7051217395548128e7\n",
- " x = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0]\n",
- " y = [767.11, 646.61, 230.28, 365.46, 1150.99, 1103.36, 0.0, 0.0, 0.0, 0.0]\n"
+ "obj = 809710.340270503\n",
+ " x = [1.0, -0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0]\n",
+ " y = [696.38, 0.0, 249.05, 0.0, 1183.75, 0.0, 504.91, 387.32, 1178.0, 765.25]\n"
]
}
],
"source": [
+ "# Construct model using previously defined functions\n",
+ "data = random_uc_data(samples=1, n=50)[1]\n",
+ "model = build_uc_model(data)\n",
+ "\n",
+ "# Solve model\n",
"solve!(solver_ml, model)\n",
+ "\n",
+ "# Print part of the optimal solution\n",
"println(\"obj = \", objective_value(model))\n",
"println(\" x = \", round.(value.(model[:x][1:10])))\n",
"println(\" y = \", round.(value.(model[:y][1:10]), digits=2))"
]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f43ed281",
+ "metadata": {},
+ "outputs": [],
+ "source": []
}
],
"metadata": {
"kernelspec": {
- "display_name": "Julia 1.6.0",
+ "display_name": "Julia 1.6.2",
"language": "julia",
"name": "julia-1.6"
},
@@ -751,7 +683,7 @@
"file_extension": ".jl",
"mimetype": "application/julia",
"name": "julia",
- "version": "1.6.0"
+ "version": "1.6.2"
}
},
"nbformat": 4,
diff --git a/docs/pyomo-tutorials/getting-started.ipynb b/docs/pyomo-tutorials/getting-started.ipynb
index 189b148..46d7c2c 100644
--- a/docs/pyomo-tutorials/getting-started.ipynb
+++ b/docs/pyomo-tutorials/getting-started.ipynb
@@ -3,9 +3,11 @@
{
"cell_type": "markdown",
"id": "6b8983b1",
- "metadata": {},
+ "metadata": {
+ "tags": []
+ },
"source": [
- "# Getting started with MIPLearn\n",
+ "# Getting started\n",
"\n",
"## Introduction\n",
"\n",
@@ -50,64 +52,9 @@
"execution_count": 1,
"id": "cd8a69c1",
"metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Defaulting to user installation because normal site-packages is not writeable\n",
- "Requirement already satisfied: MIPLearn==0.2.0.dev13 in /home/axavier/.local/lib/python3.7/site-packages (0.2.0.dev13)\n",
- "Requirement already satisfied: p-tqdm<2,>=1 in /home/axavier/.local/lib/python3.7/site-packages (from MIPLearn==0.2.0.dev13) (1.3.3)\n",
- "Requirement already satisfied: overrides<4,>=3 in /home/axavier/.local/lib/python3.7/site-packages (from MIPLearn==0.2.0.dev13) (3.1.0)\n",
- "Requirement already satisfied: scikit-learn<0.25,>=0.24 in /opt/anaconda3/lib/python3.7/site-packages (from MIPLearn==0.2.0.dev13) (0.24.1)\n",
- "Requirement already satisfied: h5py<4,>=3 in /home/axavier/.local/lib/python3.7/site-packages (from MIPLearn==0.2.0.dev13) (3.5.0)\n",
- "Requirement already satisfied: matplotlib<4,>=3 in /home/axavier/.local/lib/python3.7/site-packages (from MIPLearn==0.2.0.dev13) (3.4.1)\n",
- "Requirement already satisfied: seaborn<0.12,>=0.11 in /opt/anaconda3/lib/python3.7/site-packages (from MIPLearn==0.2.0.dev13) (0.11.1)\n",
- "Requirement already satisfied: python-markdown-math<0.9,>=0.8 in /home/axavier/.local/lib/python3.7/site-packages (from MIPLearn==0.2.0.dev13) (0.8)\n",
- "Requirement already satisfied: decorator<5,>=4 in /home/axavier/.local/lib/python3.7/site-packages (from MIPLearn==0.2.0.dev13) (4.4.2)\n",
- "Requirement already satisfied: mypy==0.790 in /home/axavier/.local/lib/python3.7/site-packages (from MIPLearn==0.2.0.dev13) (0.790)\n",
- "Requirement already satisfied: pandas<2,>=1 in /opt/anaconda3/lib/python3.7/site-packages (from MIPLearn==0.2.0.dev13) (1.2.3)\n",
- "Requirement already satisfied: pytest<7,>=6 in /opt/anaconda3/lib/python3.7/site-packages (from MIPLearn==0.2.0.dev13) (6.2.3)\n",
- "Requirement already satisfied: numpy<1.21,>=1 in /home/axavier/.local/lib/python3.7/site-packages (from MIPLearn==0.2.0.dev13) (1.19.5)\n",
- "Requirement already satisfied: tqdm<5,>=4 in /home/axavier/.local/lib/python3.7/site-packages (from MIPLearn==0.2.0.dev13) (4.60.0)\n",
- "Requirement already satisfied: pyomo<6,>=5 in /home/axavier/.local/lib/python3.7/site-packages (from MIPLearn==0.2.0.dev13) (5.7.3)\n",
- "Requirement already satisfied: networkx<3,>=2 in /home/axavier/.local/lib/python3.7/site-packages (from MIPLearn==0.2.0.dev13) (2.5.1)\n",
- "Requirement already satisfied: typed-ast<1.5.0,>=1.4.0 in /opt/anaconda3/lib/python3.7/site-packages (from mypy==0.790->MIPLearn==0.2.0.dev13) (1.4.2)\n",
- "Requirement already satisfied: typing-extensions>=3.7.4 in /opt/anaconda3/lib/python3.7/site-packages (from mypy==0.790->MIPLearn==0.2.0.dev13) (3.7.4.3)\n",
- "Requirement already satisfied: mypy-extensions<0.5.0,>=0.4.3 in /home/axavier/.local/lib/python3.7/site-packages (from mypy==0.790->MIPLearn==0.2.0.dev13) (0.4.3)\n",
- "Requirement already satisfied: cached-property in /home/axavier/.local/lib/python3.7/site-packages (from h5py<4,>=3->MIPLearn==0.2.0.dev13) (1.5.2)\n",
- "Requirement already satisfied: pyparsing>=2.2.1 in /opt/anaconda3/lib/python3.7/site-packages (from matplotlib<4,>=3->MIPLearn==0.2.0.dev13) (2.4.7)\n",
- "Requirement already satisfied: pillow>=6.2.0 in /opt/anaconda3/lib/python3.7/site-packages (from matplotlib<4,>=3->MIPLearn==0.2.0.dev13) (8.2.0)\n",
- "Requirement already satisfied: python-dateutil>=2.7 in /opt/anaconda3/lib/python3.7/site-packages (from matplotlib<4,>=3->MIPLearn==0.2.0.dev13) (2.8.1)\n",
- "Requirement already satisfied: cycler>=0.10 in /opt/anaconda3/lib/python3.7/site-packages (from matplotlib<4,>=3->MIPLearn==0.2.0.dev13) (0.10.0)\n",
- "Requirement already satisfied: kiwisolver>=1.0.1 in /opt/anaconda3/lib/python3.7/site-packages (from matplotlib<4,>=3->MIPLearn==0.2.0.dev13) (1.3.1)\n",
- "Requirement already satisfied: six in /opt/anaconda3/lib/python3.7/site-packages (from cycler>=0.10->matplotlib<4,>=3->MIPLearn==0.2.0.dev13) (1.15.0)\n",
- "Requirement already satisfied: pathos in /home/axavier/.local/lib/python3.7/site-packages (from p-tqdm<2,>=1->MIPLearn==0.2.0.dev13) (0.2.7)\n",
- "Requirement already satisfied: pytz>=2017.3 in /opt/anaconda3/lib/python3.7/site-packages (from pandas<2,>=1->MIPLearn==0.2.0.dev13) (2021.1)\n",
- "Requirement already satisfied: PyUtilib>=6.0.0 in /home/axavier/.local/lib/python3.7/site-packages (from pyomo<6,>=5->MIPLearn==0.2.0.dev13) (6.0.0)\n",
- "Requirement already satisfied: ply in /opt/anaconda3/lib/python3.7/site-packages (from pyomo<6,>=5->MIPLearn==0.2.0.dev13) (3.11)\n",
- "Requirement already satisfied: attrs>=19.2.0 in /opt/anaconda3/lib/python3.7/site-packages (from pytest<7,>=6->MIPLearn==0.2.0.dev13) (20.3.0)\n",
- "Requirement already satisfied: iniconfig in /opt/anaconda3/lib/python3.7/site-packages (from pytest<7,>=6->MIPLearn==0.2.0.dev13) (1.1.1)\n",
- "Requirement already satisfied: packaging in /opt/anaconda3/lib/python3.7/site-packages (from pytest<7,>=6->MIPLearn==0.2.0.dev13) (20.9)\n",
- "Requirement already satisfied: pluggy<1.0.0a1,>=0.12 in /opt/anaconda3/lib/python3.7/site-packages (from pytest<7,>=6->MIPLearn==0.2.0.dev13) (0.13.1)\n",
- "Requirement already satisfied: py>=1.8.2 in /opt/anaconda3/lib/python3.7/site-packages (from pytest<7,>=6->MIPLearn==0.2.0.dev13) (1.10.0)\n",
- "Requirement already satisfied: toml in /opt/anaconda3/lib/python3.7/site-packages (from pytest<7,>=6->MIPLearn==0.2.0.dev13) (0.10.2)\n",
- "Requirement already satisfied: importlib-metadata>=0.12 in /opt/anaconda3/lib/python3.7/site-packages (from pytest<7,>=6->MIPLearn==0.2.0.dev13) (3.10.0)\n",
- "Requirement already satisfied: zipp>=0.5 in /opt/anaconda3/lib/python3.7/site-packages (from importlib-metadata>=0.12->pytest<7,>=6->MIPLearn==0.2.0.dev13) (3.4.1)\n",
- "Requirement already satisfied: Markdown>=3.0 in /opt/anaconda3/lib/python3.7/site-packages (from python-markdown-math<0.9,>=0.8->MIPLearn==0.2.0.dev13) (3.3.4)\n",
- "Requirement already satisfied: nose in /opt/anaconda3/lib/python3.7/site-packages (from PyUtilib>=6.0.0->pyomo<6,>=5->MIPLearn==0.2.0.dev13) (1.3.7)\n",
- "Requirement already satisfied: scipy>=0.19.1 in /opt/anaconda3/lib/python3.7/site-packages (from scikit-learn<0.25,>=0.24->MIPLearn==0.2.0.dev13) (1.6.2)\n",
- "Requirement already satisfied: threadpoolctl>=2.0.0 in /opt/anaconda3/lib/python3.7/site-packages (from scikit-learn<0.25,>=0.24->MIPLearn==0.2.0.dev13) (2.1.0)\n",
- "Requirement already satisfied: joblib>=0.11 in /opt/anaconda3/lib/python3.7/site-packages (from scikit-learn<0.25,>=0.24->MIPLearn==0.2.0.dev13) (1.0.1)\n",
- "Requirement already satisfied: multiprocess>=0.70.11 in /home/axavier/.local/lib/python3.7/site-packages (from pathos->p-tqdm<2,>=1->MIPLearn==0.2.0.dev13) (0.70.11.1)\n",
- "Requirement already satisfied: ppft>=1.6.6.3 in /home/axavier/.local/lib/python3.7/site-packages (from pathos->p-tqdm<2,>=1->MIPLearn==0.2.0.dev13) (1.6.6.3)\n",
- "Requirement already satisfied: dill>=0.3.3 in /home/axavier/.local/lib/python3.7/site-packages (from pathos->p-tqdm<2,>=1->MIPLearn==0.2.0.dev13) (0.3.3)\n",
- "Requirement already satisfied: pox>=0.2.9 in /home/axavier/.local/lib/python3.7/site-packages (from pathos->p-tqdm<2,>=1->MIPLearn==0.2.0.dev13) (0.2.9)\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
- "!pip install MIPLearn==0.2.0.dev13"
+ "# !pip install MIPLearn==0.2.0.dev13"
]
},
{
@@ -115,7 +62,7 @@
"id": "e8274543",
"metadata": {},
"source": [
- "In addition to MIPLearn itself, we will also install Gurobi 9.1, a state-of-the-art commercial MILP solver. To succesfully complete this step, you will need a valid Gurobi license."
+ "In addition to MIPLearn itself, we will also install Gurobi 9.5, a state-of-the-art commercial MILP solver. This step also install a demo license for Gurobi, which should able to solve the small optimization problems in this tutorial. A paid license is required for solving large-scale problems."
]
},
{
@@ -128,14 +75,13 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "Defaulting to user installation because normal site-packages is not writeable\n",
"Looking in indexes: https://pypi.gurobi.com\n",
- "Requirement already satisfied: gurobipy==9.1.2 in /home/axavier/.local/lib/python3.7/site-packages (9.1.2)\n"
+ "Requirement already satisfied: gurobipy<9.6,>=9.5 in /opt/anaconda3/envs/miplearn/lib/python3.8/site-packages (9.5.1)\n"
]
}
],
"source": [
- "!pip install --upgrade -i https://pypi.gurobi.com gurobipy==9.1.2"
+ "!pip install --upgrade -i https://pypi.gurobi.com 'gurobipy>=9.5,<9.6'"
]
},
{
@@ -184,57 +130,68 @@
" \n",
"\n",
"\n",
- "Next, let us convert this abstract mathematical formulation into a concrete optimization model, using Python and Pyomo. We start by defining a class `UnitCommitmentInstance`, which holds all the input data. The class also contains a method `to_model` which constructs a concrete Pyomo model object:"
+ "Next, let us convert this abstract mathematical formulation into a concrete optimization model, using Python and Pyomo. We start by defining a data class `UnitCommitmentData`, which holds all the input data."
]
},
{
"cell_type": "code",
- "execution_count": 12,
- "id": "b5bd2c58",
+ "execution_count": 3,
+ "id": "22a67170-10b4-43d3-8708-014d91141e73",
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "from dataclasses import dataclass\n",
+ "import numpy as np\n",
+ "\n",
+ "@dataclass\n",
+ "class UnitCommitmentData:\n",
+ " demand: float\n",
+ " pmin: np.ndarray\n",
+ " pmax: np.ndarray\n",
+ " cfix: np.ndarray\n",
+ " cvar: np.ndarray"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "29f55efa-0751-465a-9b0a-a821d46a3d40",
+ "metadata": {},
+ "source": [
+ "Next, we write a `build_uc_model` function, which converts the input data into a concrete Pyomo model."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "2f67032f-0d74-4317-b45c-19da0ec859e9",
"metadata": {},
"outputs": [],
"source": [
"import pyomo.environ as pe\n",
- "import pyomo.opt as pyo\n",
- "from miplearn import Instance\n",
"\n",
- "class UnitCommitmentInstance(Instance):\n",
- " def __init__(\n",
- " self,\n",
- " demand,\n",
- " pmin,\n",
- " pmax,\n",
- " cfix,\n",
- " cvar,\n",
- " ):\n",
- " super().__init__()\n",
- " self.demand = demand\n",
- " self.pmin = pmin\n",
- " self.pmax = pmax\n",
- " self.cfix = cfix\n",
- " self.cvar = cvar\n",
- " \n",
- " def to_model(self):\n",
- " n = len(self.pmin)\n",
- " model = pe.ConcreteModel()\n",
- " model.x = pe.Var(range(n), domain=pe.Binary)\n",
- " model.y = pe.Var(range(n), bounds=(0, float(\"inf\")))\n",
- " model.obj = pe.Objective(\n",
- " expr=sum(\n",
- " self.cfix[i] * model.x[i] +\n",
- " self.cvar[i] * model.y[i]\n",
- " for i in range(n)\n",
- " )\n",
+ "def build_uc_model(data: UnitCommitmentData) -> pe.ConcreteModel:\n",
+ " model = pe.ConcreteModel()\n",
+ " n = len(data.pmin)\n",
+ " model.x = pe.Var(range(n), domain=pe.Binary)\n",
+ " model.y = pe.Var(range(n), domain=pe.NonNegativeReals)\n",
+ " model.obj = pe.Objective(\n",
+ " expr=sum(\n",
+ " data.cfix[i] * model.x[i] +\n",
+ " data.cvar[i] * model.y[i]\n",
+ " for i in range(n)\n",
" )\n",
- " model.eq_max_power = pe.ConstraintList()\n",
- " model.eq_min_power = pe.ConstraintList()\n",
- " for i in range(n):\n",
- " model.eq_max_power.add(model.y[i] <= self.pmax[i] * model.x[i])\n",
- " model.eq_min_power.add(model.y[i] >= self.pmin[i] * model.x[i])\n",
- " model.eq_demand = pe.Constraint(\n",
- " expr=sum(model.y[i] for i in range(n)) == self.demand,\n",
- " )\n",
- " return model"
+ " )\n",
+ " model.eq_max_power = pe.ConstraintList()\n",
+ " model.eq_min_power = pe.ConstraintList()\n",
+ " for i in range(n):\n",
+ " model.eq_max_power.add(model.y[i] <= data.pmax[i] * model.x[i])\n",
+ " model.eq_min_power.add(model.y[i] >= data.pmin[i] * model.x[i])\n",
+ " model.eq_demand = pe.Constraint(\n",
+ " expr=sum(model.y[i] for i in range(n)) == data.demand,\n",
+ " )\n",
+ " return model"
]
},
{
@@ -247,7 +204,7 @@
},
{
"cell_type": "code",
- "execution_count": 14,
+ "execution_count": 5,
"id": "2a896f47",
"metadata": {},
"outputs": [
@@ -255,27 +212,32 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "obj = 1320.0\n",
- " x = [0.0, 1.0, 1.0]\n",
- " y = [0.0, 60.0, 40.0]\n"
+ "Set parameter Threads to value 1\n",
+ "Set parameter Seed to value 42\n",
+ "Restricted license - for non-production use only - expires 2023-10-25\n",
+ "obj = 1320.0\n",
+ "x = [-0.0, 1.0, 1.0]\n",
+ "y = [0.0, 60.0, 40.0]\n"
]
}
],
"source": [
- "instance = UnitCommitmentInstance(\n",
- " demand = 100.0,\n",
- " pmin = [10, 20, 30],\n",
- " pmax = [50, 60, 70],\n",
- " cfix = [700, 600, 500],\n",
- " cvar = [1.5, 2.0, 2.5],\n",
+ "model = build_uc_model(\n",
+ " UnitCommitmentData(\n",
+ " demand = 100.0,\n",
+ " pmin = [10, 20, 30],\n",
+ " pmax = [50, 60, 70],\n",
+ " cfix = [700, 600, 500],\n",
+ " cvar = [1.5, 2.0, 2.5],\n",
+ " )\n",
")\n",
- "model = instance.to_model()\n",
- "opt = pyo.SolverFactory(\"gurobi\")\n",
- "opt.solve(model)\n",
"\n",
- "print(\"obj = \", pe.value(model.obj))\n",
- "print(\" x = \", [pe.value(model.x[i]) for i in range(3)])\n",
- "print(\" y = \", [pe.value(model.y[i]) for i in range(3)])\n"
+ "solver = pe.SolverFactory(\"gurobi_persistent\")\n",
+ "solver.set_instance(model)\n",
+ "solver.solve()\n",
+ "print(\"obj =\", model.obj())\n",
+ "print(\"x =\", [model.x[i].value for i in range(3)])\n",
+ "print(\"y =\", [model.y[i].value for i in range(3)])"
]
},
{
@@ -295,29 +257,29 @@
"\n",
"Although Gurobi could solve the small example above in a fraction of a second, it gets slower for larger and more complex versions of the problem. If this is a problem that needs to be solved frequently, as it is often the case in practice, it could make sense to spend some time upfront generating a **trained** version of Gurobi, which can solve new instances (similar to the ones it was trained on) faster.\n",
"\n",
- "In the following, we will use MIPLearn to train machine learning models that can be used to accelerate Gurobi's performance on a particular set of instances. More specifically, MIPLearn will train a model that is able to predict the optimal solution for instances that follow a given probability distribution, then it will provide this predicted solution to Gurobi as a warm start.\n",
- "\n",
- "Before we can train the model, we need to collect training data by solving a large number of instances. In real-world situations, we may construct these training instances based on historical data. In this tutorial, we will construct them using a random instance generator:"
+ "In the following, we will use MIPLearn to train machine learning models that is able to predict the optimal solution for instances that follow a given probability distribution, then it will provide this predicted solution to Gurobi as a warm start. Before we can train the model, we need to collect training data by solving a large number of instances. In real-world situations, we may construct these training instances based on historical data. In this tutorial, we will construct them using a random instance generator:"
]
},
{
"cell_type": "code",
- "execution_count": 42,
+ "execution_count": 6,
"id": "5eb09fab",
"metadata": {},
"outputs": [],
"source": [
"from scipy.stats import uniform\n",
+ "from typing import List\n",
"import random\n",
"\n",
- "def random_uc_instances(samples, n, seed=42):\n",
+ "def random_uc_data(samples: int, n: int, seed: int = 42) -> List[UnitCommitmentData]:\n",
" random.seed(seed)\n",
+ " np.random.seed(seed)\n",
" pmin = uniform(loc=100_000.0, scale=400_000.0).rvs(n)\n",
" pmax = pmin * uniform(loc=2.0, scale=2.5).rvs(n)\n",
" cfix = pmin * uniform(loc=100.0, scale=25.0).rvs(n)\n",
" cvar = uniform(loc=1.25, scale=0.25).rvs(n)\n",
" return [\n",
- " UnitCommitmentInstance(\n",
+ " UnitCommitmentData(\n",
" demand = pmax.sum() * uniform(loc=0.5, scale=0.25).rvs(),\n",
" pmin = pmin,\n",
" pmax = pmax,\n",
@@ -333,116 +295,22 @@
"id": "3a03a7ac",
"metadata": {},
"source": [
- "In this example, for simplicity, only the demands change from one instance to the next. We could also have made the prices and the production limits random. The more randomization we have in the training data, however, the more challenging it is for the machine learning models to learn solution patterns.\n",
+ "In this example, for simplicity, only the demands change from one instance to the next. We could also have randomized the costs, production limits or even the number of units. The more randomization we have in the training data, however, the more challenging it is for the machine learning models to learn solution patterns.\n",
"\n",
- "Now we generate 100 instances of this problem, each one with 1,000 generators. We will use the first 90 instances for training, and the remaining 10 instances to evaluate performance."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 43,
- "id": "6156752c",
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Using license file /home/axavier/gurobi.lic\n",
- "Using gurobi.env file\n",
- "Set parameter Threads to value 1\n",
- "Read LP format model from file /tmp/tmpl37dgdq_.pyomo.lp\n",
- "Reading time = 0.01 seconds\n",
- "x2001: 2002 rows, 2001 columns, 5001 nonzeros\n",
- "Gurobi Optimizer version 9.1.1 build v9.1.1rc0 (linux64)\n",
- "Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
- "Optimize a model with 2002 rows, 2001 columns and 5001 nonzeros\n",
- "Model fingerprint: 0x637c283e\n",
- "Variable types: 1001 continuous, 1000 integer (1000 binary)\n",
- "Coefficient statistics:\n",
- " Matrix range [1e+00, 2e+06]\n",
- " Objective range [1e+00, 6e+07]\n",
- " Bounds range [1e+00, 1e+00]\n",
- " RHS range [1e+00, 7e+08]\n",
- "Presolve removed 1 rows and 1 columns\n",
- "Presolve time: 0.00s\n",
- "Presolved: 2001 rows, 2000 columns, 5000 nonzeros\n",
- "Variable types: 1000 continuous, 1000 integer (1000 binary)\n",
- "\n",
- "Root relaxation: objective 2.143251e+10, 1010 iterations, 0.00 seconds\n",
- "\n",
- " Nodes | Current Node | Objective Bounds | Work\n",
- " Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
- "\n",
- " 0 0 2.1433e+10 0 1 - 2.1433e+10 - - 0s\n",
- "H 0 0 2.143648e+10 2.1433e+10 0.02% - 0s\n",
- " 0 0 2.1433e+10 0 3 2.1436e+10 2.1433e+10 0.02% - 0s\n",
- " 0 0 2.1433e+10 0 1 2.1436e+10 2.1433e+10 0.02% - 0s\n",
- " 0 0 2.1433e+10 0 3 2.1436e+10 2.1433e+10 0.02% - 0s\n",
- "H 0 0 2.143631e+10 2.1433e+10 0.02% - 0s\n",
- " 0 0 2.1433e+10 0 2 2.1436e+10 2.1433e+10 0.02% - 0s\n",
- " 0 0 2.1433e+10 0 2 2.1436e+10 2.1433e+10 0.02% - 0s\n",
- " 0 2 2.1433e+10 0 2 2.1436e+10 2.1433e+10 0.02% - 0s\n",
- "H 224 189 2.143590e+10 2.1433e+10 0.02% 1.0 0s\n",
- "H 259 218 2.143566e+10 2.1433e+10 0.01% 1.0 0s\n",
- "H 516 430 2.143497e+10 2.1433e+10 0.01% 1.1 0s\n",
- "H 631 467 2.143400e+10 2.1433e+10 0.01% 1.7 0s\n",
- "\n",
- "Cutting planes:\n",
- " Gomory: 1\n",
- "\n",
- "Explored 632 nodes (2451 simplex iterations) in 0.34 seconds\n",
- "Thread count was 1 (of 32 available processors)\n",
- "\n",
- "Solution count 6: 2.1434e+10 2.1435e+10 2.14357e+10 ... 2.14365e+10\n",
- "\n",
- "Optimal solution found (tolerance 1.00e-04)\n",
- "Best objective 2.143399941747e+10, best bound 2.143258805394e+10, gap 0.0066%\n"
- ]
- }
- ],
- "source": [
- "instances = random_uc_instances(samples=100, n=1000);\n",
- "train_instances = instances[1:90]\n",
- "test_instances = instances[91:100];\n",
- "\n",
- "model = instances[0].to_model()\n",
- "opt = pyo.SolverFactory(\"gurobi\")\n",
- "opt.solve(model, tee=True);"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "b795a0b1",
- "metadata": {},
- "source": [
- "Next, we write these objects to individual files. MIPLearn uses files during the training process because, for large-scale optimization problems, it is often impractical to hold the entire training data, as well as the concrete Pyomo models, in memory. Files also make it much easier to solve multiple instances simultaneously, potentially even on multiple machines. We will cover parallel and distributed computing in a future tutorial.\n",
- "\n",
- "The code below generates the files `uc/train/00001.h5`, `uc/train/00002.h5`, etc., which contain the input data in HDF5 format."
+ "Now we generate 500 instances of this problem, each one with 50 generators, and we use 450 of these instances for training. After generating the instances, we write them to individual files. MIPLearn uses files during the training process because, for large-scale optimization problems, it is often impractical to hold in memory the entire training data, as well as the concrete Pyomo models. Files also make it much easier to solve multiple instances simultaneously, potentially even on multiple machines. We will cover parallel and distributed computing in a future tutorial. The code below generates the files `uc/train/00000.pkl.gz`, `uc/train/00001.pkl.gz`, etc., which contain the input data in compressed (gzipped) pickle format."
]
},
{
"cell_type": "code",
"execution_count": 7,
- "id": "e21d9aa0",
+ "id": "6156752c",
"metadata": {},
"outputs": [],
"source": [
- "from miplearn.instance.file import FileInstance\n",
- "from glob import glob\n",
- "import os\n",
- "\n",
- "os.makedirs(\"uc/train\", exist_ok=True)\n",
- "os.makedirs(\"uc/test\", exist_ok=True)\n",
- "\n",
- "for (i, instance) in enumerate(train_instances):\n",
- " FileInstance.save(instance, f\"uc/train/{i:05d}.h5\")\n",
- " \n",
- "for (i, instance) in enumerate(test_instances):\n",
- " FileInstance.save(instance, f\"uc/test/{i:05d}.h5\")\n",
- "\n",
- "train_instances = [FileInstance(f) for f in glob(\"uc/train/*.h5\")]\n",
- "test_instances = [FileInstance(f) for f in glob(\"uc/test/*.h5\")]"
+ "from miplearn import save\n",
+ "data = random_uc_data(samples=500, n=50)\n",
+ "train_files = save(data[0:450], \"uc/train/\")\n",
+ "test_files = save(data[450:500], \"uc/test/\")"
]
},
{
@@ -450,50 +318,19 @@
"id": "b17af877",
"metadata": {},
"source": [
- "Finally, we use `LearningSolver` to solve all the training instances. `LearningSolver` is the main component provided by MIPLearn, which integrates MIP solvers and ML. The solutions, and other useful training data, are stored in the same HDF5 files."
+ "Finally, we use `LearningSolver` to solve all the training instances. `LearningSolver` is the main component provided by MIPLearn, which integrates MIP solvers and ML. The optimal solutions, along with other useful training data, are stored in HDF5 files `uc/train/00000.h5`, `uc/train/00001.h5`, etc."
]
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": 12,
"id": "7623f002",
"metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- " 12%|█▏ | 11/89 [00:50<05:56, 4.57s/it]\n"
- ]
- },
- {
- "ename": "KeyboardInterrupt",
- "evalue": "",
- "output_type": "error",
- "traceback": [
- "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
- "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
- "\u001b[0;32m
\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0msolver\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mLearningSolver\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0minstance\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtqdm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtrain_instances\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m \u001b[0msolver\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msolve\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minstance\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
- "\u001b[0;32m~/Packages/MIPLearn/dev/miplearn/solvers/learning.py\u001b[0m in \u001b[0;36msolve\u001b[0;34m(self, instance, model, discard_output, tee)\u001b[0m\n\u001b[1;32m 354\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0minstance\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 355\u001b[0m \u001b[0minstance\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minstance\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 356\u001b[0;31m return self._solve(\n\u001b[0m\u001b[1;32m 357\u001b[0m \u001b[0minstance\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0minstance\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 358\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m~/Packages/MIPLearn/dev/miplearn/solvers/learning.py\u001b[0m in \u001b[0;36m_solve\u001b[0;34m(self, instance, model, discard_output, tee)\u001b[0m\n\u001b[1;32m 169\u001b[0m \u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Extracting features (after-load)...\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 170\u001b[0m \u001b[0minitial_time\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 171\u001b[0;31m self.extractor.extract_after_load_features(\n\u001b[0m\u001b[1;32m 172\u001b[0m \u001b[0minstance\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minternal_solver\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msample\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 173\u001b[0m )\n",
- "\u001b[0;32m~/Packages/MIPLearn/dev/miplearn/features/extractor.py\u001b[0m in \u001b[0;36mextract_after_load_features\u001b[0;34m(self, instance, solver, sample)\u001b[0m\n\u001b[1;32m 35\u001b[0m ) -> None:\n\u001b[1;32m 36\u001b[0m \u001b[0mvariables\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msolver\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_variables\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mwith_static\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 37\u001b[0;31m \u001b[0mconstraints\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msolver\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_constraints\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mwith_static\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwith_lhs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwith_lhs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 38\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0mconstraints\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnames\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 39\u001b[0m \u001b[0msample\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mput_array\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"static_var_lower_bounds\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvariables\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlower_bounds\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m~/Packages/MIPLearn/dev/miplearn/solvers/pyomo/base.py\u001b[0m in \u001b[0;36mget_constraints\u001b[0;34m(self, with_static, with_sa, with_lhs)\u001b[0m\n\u001b[1;32m 237\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0midx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mconstr\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 238\u001b[0m \u001b[0mnames\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconstr\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0midx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 239\u001b[0;31m \u001b[0m_parse_constraint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconstr\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0midx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcurr_row\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 240\u001b[0m \u001b[0mcurr_row\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 241\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m~/Packages/MIPLearn/dev/miplearn/solvers/pyomo/base.py\u001b[0m in \u001b[0;36m_parse_constraint\u001b[0;34m(c, row)\u001b[0m\n\u001b[1;32m 204\u001b[0m \u001b[0mlhs_row\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrow\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 205\u001b[0m lhs_col.append(\n\u001b[0;32m--> 206\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_varname_to_idx\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mterm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_args_\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 207\u001b[0m )\n\u001b[1;32m 208\u001b[0m \u001b[0mlhs_data\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mterm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_args_\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m~/.conda/envs/miplearn/lib/python3.8/site-packages/Pyomo-5.7.3-py3.8-linux-x86_64.egg/pyomo/core/base/component.py\u001b[0m in \u001b[0;36mname\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 285\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 286\u001b[0m \u001b[0;34m\"\"\"Get the fully qualifed component name.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 287\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetname\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfully_qualified\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 288\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 289\u001b[0m \u001b[0;31m# Adding a setter here to help users adapt to the new\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m~/.conda/envs/miplearn/lib/python3.8/site-packages/Pyomo-5.7.3-py3.8-linux-x86_64.egg/pyomo/core/base/component.py\u001b[0m in \u001b[0;36mgetname\u001b[0;34m(self, fully_qualified, name_buffer, relative_to)\u001b[0m\n\u001b[1;32m 910\u001b[0m \u001b[0;31m# more expensive than if a buffer is provided.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 911\u001b[0m \u001b[0;31m#\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 912\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0midx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mobj\u001b[0m \u001b[0;32min\u001b[0m \u001b[0miteritems\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 913\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mobj\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 914\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mbase\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0m_name_index_generator\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0midx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m~/.conda/envs/miplearn/lib/python3.8/site-packages/Pyomo-5.7.3-py3.8-linux-x86_64.egg/pyomo/core/base/indexed_component.py\u001b[0m in \u001b[0;36miteritems\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 370\u001b[0m \u001b[0;34m\"\"\"Return an iterator of (index,data) tuples from the dictionary\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 371\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mkey\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 372\u001b[0;31m \u001b[0;32myield\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 373\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 374\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__getitem__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindex\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;32m~/.conda/envs/miplearn/lib/python3.8/site-packages/Pyomo-5.7.3-py3.8-linux-x86_64.egg/pyomo/core/base/indexed_component.py\u001b[0m in \u001b[0;36m__getitem__\u001b[0;34m(self, index)\u001b[0m\n\u001b[1;32m 380\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 381\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 382\u001b[0;31m \u001b[0mobj\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_data\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_NotFound\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 383\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 384\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
- "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
- ]
- }
- ],
+ "outputs": [],
"source": [
"from miplearn import LearningSolver\n",
- "from tqdm import tqdm\n",
- "\n",
"solver = LearningSolver()\n",
- "for instance in tqdm(train_instances):\n",
- " solver.solve(instance)"
+ "solver.solve(train_files, build_uc_model);"
]
},
{
@@ -501,157 +338,274 @@
"id": "2f24ee83",
"metadata": {},
"source": [
- "## Solving new instances\n",
+ "## Solving test instances\n",
"\n",
- "With training data in hand, we can now fit the ML models, using the `LearningSolver.fit` method, then solve the test instances with `MIPLearn.solve!`, as shown below:"
+ "With training data in hand, we can now fit the ML models, using the `LearningSolver.fit` method, then solve the test instances with `LearningSolver.solve`, as shown below. The `tee=True` parameter asks MIPLearn to print the solver log to the screen."
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 9,
"id": "c8385030",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Set parameter LogFile to value \"/tmp/tmpvbaqbyty.log\"\n",
+ "Set parameter QCPDual to value 1\n",
+ "Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)\n",
+ "Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
+ "Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
+ "Model fingerprint: 0x8de73876\n",
+ "Coefficient statistics:\n",
+ " Matrix range [1e+00, 2e+06]\n",
+ " Objective range [1e+00, 6e+07]\n",
+ " Bounds range [1e+00, 1e+00]\n",
+ " RHS range [2e+07, 2e+07]\n",
+ "Presolve removed 100 rows and 50 columns\n",
+ "Presolve time: 0.00s\n",
+ "Presolved: 1 rows, 50 columns, 50 nonzeros\n",
+ "\n",
+ "Iteration Objective Primal Inf. Dual Inf. Time\n",
+ " 0 5.7349081e+08 1.044003e+04 0.000000e+00 0s\n",
+ " 1 6.8268465e+08 0.000000e+00 0.000000e+00 0s\n",
+ "\n",
+ "Solved in 1 iterations and 0.00 seconds (0.00 work units)\n",
+ "Optimal objective 6.826846503e+08\n",
+ "Set parameter LogFile to value \"\"\n",
+ "Set parameter LogFile to value \"/tmp/tmp48j6n35b.log\"\n",
+ "Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)\n",
+ "Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
+ "Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
+ "Model fingerprint: 0x200d64ba\n",
+ "Variable types: 50 continuous, 50 integer (50 binary)\n",
+ "Coefficient statistics:\n",
+ " Matrix range [1e+00, 2e+06]\n",
+ " Objective range [1e+00, 6e+07]\n",
+ " Bounds range [1e+00, 1e+00]\n",
+ " RHS range [2e+07, 2e+07]\n",
+ "\n",
+ "User MIP start produced solution with objective 6.84841e+08 (0.00s)\n",
+ "Loaded user MIP start with objective 6.84841e+08\n",
+ "\n",
+ "Presolve time: 0.00s\n",
+ "Presolved: 101 rows, 100 columns, 250 nonzeros\n",
+ "Variable types: 50 continuous, 50 integer (50 binary)\n",
+ "\n",
+ "Root relaxation: objective 6.826847e+08, 56 iterations, 0.00 seconds (0.00 work units)\n",
+ "\n",
+ " Nodes | Current Node | Objective Bounds | Work\n",
+ " Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
+ "\n",
+ " 0 0 6.8268e+08 0 1 6.8484e+08 6.8268e+08 0.31% - 0s\n",
+ " 0 0 6.8315e+08 0 3 6.8484e+08 6.8315e+08 0.25% - 0s\n",
+ " 0 0 6.8315e+08 0 1 6.8484e+08 6.8315e+08 0.25% - 0s\n",
+ " 0 0 6.8315e+08 0 3 6.8484e+08 6.8315e+08 0.25% - 0s\n",
+ " 0 0 6.8315e+08 0 4 6.8484e+08 6.8315e+08 0.25% - 0s\n",
+ " 0 0 6.8315e+08 0 4 6.8484e+08 6.8315e+08 0.25% - 0s\n",
+ " 0 2 6.8327e+08 0 4 6.8484e+08 6.8327e+08 0.23% - 0s\n",
+ "\n",
+ "Cutting planes:\n",
+ " Flow cover: 3\n",
+ "\n",
+ "Explored 32 nodes (155 simplex iterations) in 0.02 seconds (0.00 work units)\n",
+ "Thread count was 1 (of 32 available processors)\n",
+ "\n",
+ "Solution count 1: 6.84841e+08 \n",
+ "\n",
+ "Optimal solution found (tolerance 1.00e-04)\n",
+ "Best objective 6.848411655488e+08, best bound 6.848411655488e+08, gap 0.0000%\n",
+ "Set parameter LogFile to value \"\"\n",
+ "WARNING: Cannot get reduced costs for MIP.\n",
+ "WARNING: Cannot get duals for MIP.\n"
+ ]
+ }
+ ],
"source": [
"solver_ml = LearningSolver()\n",
- "solver_ml.fit(train_instances)\n",
- "for instance in tqdm(test_instances):\n",
- " solver_ml.solve(instance)"
+ "solver_ml.fit(train_files, build_uc_model)\n",
+ "solver_ml.solve(test_files[0:1], build_uc_model, tee=True);"
]
},
{
"cell_type": "markdown",
- "id": "8ae11854",
+ "id": "61da6dad-7f56-4edb-aa26-c00eb5f946c0",
"metadata": {},
"source": [
- "The trained MIP solver was able to solve all test instances in about 6 seconds. To see that ML is being helpful here, let us repeat the code above, but remove the `fit!` line:"
+ "By examining the solve log above, specifically the line `Loaded user MIP start with objective...`, we can see that MIPLearn was able to construct an initial solution which turned out to be the optimal solution to the problem. Now let us repeat the code above, but using an untrained solver. Note that the `fit` line is omitted."
]
},
{
"cell_type": "code",
- "execution_count": null,
- "id": "71c58e4a",
+ "execution_count": 10,
+ "id": "33d15d6c-6db4-477f-bd4b-fe8e84e5f023",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Set parameter LogFile to value \"/tmp/tmp3uhhdurw.log\"\n",
+ "Set parameter QCPDual to value 1\n",
+ "Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)\n",
+ "Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
+ "Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
+ "Model fingerprint: 0x8de73876\n",
+ "Coefficient statistics:\n",
+ " Matrix range [1e+00, 2e+06]\n",
+ " Objective range [1e+00, 6e+07]\n",
+ " Bounds range [1e+00, 1e+00]\n",
+ " RHS range [2e+07, 2e+07]\n",
+ "Presolve removed 100 rows and 50 columns\n",
+ "Presolve time: 0.00s\n",
+ "Presolved: 1 rows, 50 columns, 50 nonzeros\n",
+ "\n",
+ "Iteration Objective Primal Inf. Dual Inf. Time\n",
+ " 0 5.7349081e+08 1.044003e+04 0.000000e+00 0s\n",
+ " 1 6.8268465e+08 0.000000e+00 0.000000e+00 0s\n",
+ "\n",
+ "Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
+ "Optimal objective 6.826846503e+08\n",
+ "Set parameter LogFile to value \"\"\n",
+ "Set parameter LogFile to value \"/tmp/tmp18aqg2ic.log\"\n",
+ "Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)\n",
+ "Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
+ "Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
+ "Model fingerprint: 0xb90d1075\n",
+ "Variable types: 50 continuous, 50 integer (50 binary)\n",
+ "Coefficient statistics:\n",
+ " Matrix range [1e+00, 2e+06]\n",
+ " Objective range [1e+00, 6e+07]\n",
+ " Bounds range [1e+00, 1e+00]\n",
+ " RHS range [2e+07, 2e+07]\n",
+ "Found heuristic solution: objective 8.056576e+08\n",
+ "Presolve time: 0.00s\n",
+ "Presolved: 101 rows, 100 columns, 250 nonzeros\n",
+ "Variable types: 50 continuous, 50 integer (50 binary)\n",
+ "\n",
+ "Root relaxation: objective 6.826847e+08, 56 iterations, 0.00 seconds (0.00 work units)\n",
+ "\n",
+ " Nodes | Current Node | Objective Bounds | Work\n",
+ " Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
+ "\n",
+ " 0 0 6.8268e+08 0 1 8.0566e+08 6.8268e+08 15.3% - 0s\n",
+ "H 0 0 7.099498e+08 6.8268e+08 3.84% - 0s\n",
+ " 0 0 6.8315e+08 0 3 7.0995e+08 6.8315e+08 3.78% - 0s\n",
+ "H 0 0 6.883227e+08 6.8315e+08 0.75% - 0s\n",
+ " 0 0 6.8352e+08 0 4 6.8832e+08 6.8352e+08 0.70% - 0s\n",
+ " 0 0 6.8352e+08 0 4 6.8832e+08 6.8352e+08 0.70% - 0s\n",
+ " 0 0 6.8352e+08 0 1 6.8832e+08 6.8352e+08 0.70% - 0s\n",
+ "H 0 0 6.862582e+08 6.8352e+08 0.40% - 0s\n",
+ " 0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s\n",
+ " 0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s\n",
+ " 0 0 6.8352e+08 0 1 6.8626e+08 6.8352e+08 0.40% - 0s\n",
+ " 0 0 6.8352e+08 0 3 6.8626e+08 6.8352e+08 0.40% - 0s\n",
+ " 0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s\n",
+ " 0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s\n",
+ " 0 2 6.8354e+08 0 4 6.8626e+08 6.8354e+08 0.40% - 0s\n",
+ "* 18 5 6 6.849018e+08 6.8413e+08 0.11% 3.1 0s\n",
+ "H 24 1 6.848412e+08 6.8426e+08 0.09% 3.2 0s\n",
+ "\n",
+ "Cutting planes:\n",
+ " Gomory: 1\n",
+ " Flow cover: 2\n",
+ "\n",
+ "Explored 30 nodes (217 simplex iterations) in 0.02 seconds (0.00 work units)\n",
+ "Thread count was 1 (of 32 available processors)\n",
+ "\n",
+ "Solution count 6: 6.84841e+08 6.84902e+08 6.86258e+08 ... 8.05658e+08\n",
+ "\n",
+ "Optimal solution found (tolerance 1.00e-04)\n",
+ "Best objective 6.848411655488e+08, best bound 6.848411655488e+08, gap 0.0000%\n",
+ "Set parameter LogFile to value \"\"\n",
+ "WARNING: Cannot get reduced costs for MIP.\n",
+ "WARNING: Cannot get duals for MIP.\n"
+ ]
+ }
+ ],
"source": [
"solver_baseline = LearningSolver()\n",
- "for instance in tqdm(test_instances):\n",
- " solver_baseline.solve(instance)"
+ "solver_baseline.solve(test_files[0:1], build_uc_model, tee=True);"
]
},
{
"cell_type": "markdown",
- "id": "117adfb7",
+ "id": "b6d37b88-9fcc-43ee-ac1e-2a7b1e51a266",
"metadata": {},
"source": [
- "Without the help of the ML models, SCIP took around 10 seconds to solve the same test instances.\n",
+ "In the log above, the `MIP start` line is missing, and Gurobi had to start with a significantly inferior initial solution. The solver was still able to find the optimal solution at the end, but it required using its own internal heuristic procedures. In this example, because we solve very small optimization problems, there was almost no difference in terms of running time. For larger problems, however, the difference can be significant. See benchmarks for more details.\n",
"\n",
"\n",
"Note\n",
" \n",
- "Note that is is not necessary to specify what ML models to use. MIPLearn, by default, will try a number of classical ML models and will choose the one that performs the best, based on k-fold cross validation. MIPLearn is also able to automatically collect features based on the MIP formulation of the problem and the solution to the LP relaxation, among other things, so it does not require handcrafted features. If you do want to customize the models and features, however, that is also possible, as we will see in a later tutorial.\n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "dec6acd7",
- "metadata": {},
- "source": [
- "## Understanding the acceleration\n",
- "\n",
- "Let us go a bit deeper and try to understand how exactly did MIPLearn accelerate Gurobi's performance. First, we are going to solve one of the test instances again, using the trained solver, but this time using the `tee=True` parameter, so that we can see Gurobi's log:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "0d4df336",
- "metadata": {},
- "outputs": [],
- "source": [
- "solver_ml.solve(test_instances[0], tee=True);"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "df5108b9",
- "metadata": {},
- "source": [
- "The log above is quite complicated if you have never seen it before, but the important line is the one starting with `feasible solution found [...] objective value 1.705169e+07`. This line indicates that MIPLearn was able to construct a warm start with value `1.705169e+07`. Using this warm start, SCIP then used the branch-and-cut method to either prove its optimality or to find an even better solution. Very quickly, however, SCIP proved that the solution produced by MIPLearn was indeed optimal. It was able to do this without generating a single cutting plane or running any other heuristics; it could tell the optimality by the root LP relaxation alone, which was very fast. \n",
- "\n",
- "Let us now repeat the process, but using the untrained solver this time:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "id": "51864ed9",
- "metadata": {},
- "outputs": [],
- "source": [
- "solver_baseline.solve(test_instances[0], tee=True);"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "5a78550e",
- "metadata": {},
- "source": [
- "In this log file, notice how the previous line about warm starts is missing. Since no warm starts were provided, SCIP had to find an initial solution using its own internal heuristics, which are not specifically tailored for this problem. The initial solution found by SCIP's heuristics has value `2.335200e+07`, which is significantly worse than the one constructed by MIPLearn. SCIP then proceeded to improve this solution, by generating cutting planes and repeatedly running additional primal heuristics. In the end, it was able to find the optimal solution, as expected, but it took longer.\n",
+ "In addition to partial initial solutions, MIPLearn is also able to predict lazy constraints, cutting planes and branching priorities. See the next tutorials for more details.\n",
+ " \n",
"\n",
- "In summary, MIPLearn accelerated the solution process by constructing a high-quality initial solution. In the following tutorials, we will see other strategies that MIPLearn can use to accelerate MIP performance, besides warm starts."
+ "\n",
+ "Note\n",
+ " \n",
+ "It is not necessary to specify what ML models to use. MIPLearn, by default, will try a number of classical ML models and will choose the one that performs the best, based on k-fold cross validation. MIPLearn is also able to automatically collect features based on the MIP formulation of the problem and the solution to the LP relaxation, among other things, so it does not require handcrafted features. If you do want to customize the models and features, however, that is also possible, as we will see in a later tutorial.\n",
+ "
"
]
},
{
"cell_type": "markdown",
"id": "eec97f06",
- "metadata": {},
+ "metadata": {
+ "tags": []
+ },
"source": [
"## Accessing the solution\n",
"\n",
- "In the example above, we used `MIPLearn.solve!` together with data files to solve both the training and the test instances. The solutions were saved to a `.h5` files in the train/test folders, and could be retrieved by reading theses files, but that is not very convenient. In this section we will use an easier method.\n",
- "\n",
- "We can use the function `MIPLearn.load!` to obtain a regular JuMP model:"
+ "In the example above, we used `LearningSolver.solve` together with data files to solve both the training and the test instances. The optimal solutions were saved to HDF5 files in the train/test folders, and could be retrieved by reading theses files, but that is not very convenient. In the following example, we show how to build and solve a Pyomo model entirely in-memory, using our trained solver."
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 11,
"id": "67a6cd18",
"metadata": {},
- "outputs": [],
- "source": [
- "model = MIPLearn.load(\"uc/test/000001.jld2\", build_uc_model)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "b7a5629c",
- "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "obj = 903865807.3536932\n",
+ " x = [1.0, 1.0, 1.0, 1.0, 1.0]\n",
+ " y = [1105176.593734543, 1891284.5155055337, 1708177.4224033852, 1438329.610189608, 535496.3347187206]\n"
+ ]
+ }
+ ],
"source": [
- "We can then solve this model as before, with `MIPLearn.solve!`:"
+ "# Construct model using previously defined functions\n",
+ "data = random_uc_data(samples=1, n=50)[0]\n",
+ "model = build_uc_model(data)\n",
+ "\n",
+ "# Solve model using ML + Gurobi\n",
+ "solver_ml.solve(model)\n",
+ "\n",
+ "# Print part of the optimal solution\n",
+ "print(\"obj =\", model.obj())\n",
+ "print(\" x =\", [model.x[i].value for i in range(5)])\n",
+ "print(\" y =\", [model.y[i].value for i in range(5)])"
]
},
{
"cell_type": "code",
"execution_count": null,
- "id": "6d5fa7e9",
+ "id": "5593d23a-83bd-4e16-8253-6300f5e3f63b",
"metadata": {},
"outputs": [],
- "source": [
- "solve!(solver_ml, model)\n",
- "println(\"obj = \", objective_value(model))\n",
- "println(\" x = \", round.(value.(model[:x][1:10])))\n",
- "println(\" y = \", round.(value.(model[:y][1:10]), digits=2))"
- ]
+ "source": []
}
],
"metadata": {
"kernelspec": {
- "display_name": "miplearn",
+ "display_name": "Python 3",
"language": "python",
- "name": "miplearn"
+ "name": "python3"
},
"language_info": {
"codemirror_mode": {
diff --git a/docs/pyomo-tutorials/gurobi.env b/docs/pyomo-tutorials/gurobi.env
index 70e8481..705c8fe 100644
--- a/docs/pyomo-tutorials/gurobi.env
+++ b/docs/pyomo-tutorials/gurobi.env
@@ -1,2 +1,3 @@
OutputFlag 1
-Threads 1
\ No newline at end of file
+Threads 1
+Seed 42
\ No newline at end of file
diff --git a/miplearn/solvers/pyomo/cplex.py b/miplearn/solvers/pyomo/cplex.py
index 2cf8ddd..021a430 100644
--- a/miplearn/solvers/pyomo/cplex.py
+++ b/miplearn/solvers/pyomo/cplex.py
@@ -28,7 +28,6 @@ class CplexPyomoSolver(BasePyomoSolver):
) -> None:
if params is None:
params = {}
- params["randomseed"] = randint(low=0, high=1000).rvs()
if "mip_display" not in params.keys():
params["mip_display"] = 4
super().__init__(
diff --git a/miplearn/solvers/pyomo/gurobi.py b/miplearn/solvers/pyomo/gurobi.py
index 162c668..f350405 100644
--- a/miplearn/solvers/pyomo/gurobi.py
+++ b/miplearn/solvers/pyomo/gurobi.py
@@ -32,7 +32,6 @@ class GurobiPyomoSolver(BasePyomoSolver):
) -> None:
if params is None:
params = {}
- params["seed"] = randint(low=0, high=1000).rvs()
super().__init__(
solver_factory=pe.SolverFactory("gurobi_persistent"),
params=params,
diff --git a/miplearn/solvers/pyomo/xpress.py b/miplearn/solvers/pyomo/xpress.py
index d49db8c..b0cf763 100644
--- a/miplearn/solvers/pyomo/xpress.py
+++ b/miplearn/solvers/pyomo/xpress.py
@@ -32,7 +32,6 @@ class XpressPyomoSolver(BasePyomoSolver):
) -> None:
if params is None:
params = {}
- params["randomseed"] = randint(low=0, high=1000).rvs()
super().__init__(
solver_factory=pe.SolverFactory("xpress_persistent"),
params=params,