13 Commits
v0.4.3 ... dev

24 changed files with 1028 additions and 421 deletions

View File

@@ -7,6 +7,10 @@ VERSION := 0.4
all: docs test
conda-create:
conda env remove -n miplearn
conda create -n miplearn python=3.12
clean:
rm -rf build/* dist/*

View File

@@ -14,7 +14,7 @@
</a>
</p>
**MIPLearn** is an extensible framework for solving discrete optimization problems using a combination of Mixed-Integer Linear Programming (MIP) and Machine Learning (ML). MIPLearn uses ML methods to automatically identify patterns in previously solved instances of the problem, then uses these patterns to accelerate the performance of conventional state-of-the-art MIP solvers such as CPLEX, Gurobi or XPRESS.
**MIPLearn** is an extensible framework for solving discrete optimization problems using a combination of Mixed-Integer Programming (MIP) and Machine Learning (ML). MIPLearn uses ML methods to automatically identify patterns in previously solved instances of the problem, then uses these patterns to accelerate the performance of conventional state-of-the-art MIP solvers such as CPLEX, Gurobi or XPRESS.
Unlike pure ML methods, MIPLearn is not only able to find high-quality solutions to discrete optimization problems, but it can also prove the optimality and feasibility of these solutions. Unlike conventional MIP solvers, MIPLearn can take full advantage of very specific observations that happen to be true in a particular family of instances (such as the observation that a particular constraint is typically redundant, or that a particular variable typically assumes a certain value). For certain classes of problems, this approach may provide significant performance benefits.

View File

@@ -118,3 +118,13 @@ table tr:last-child {
border-bottom: 0;
}
@media (min-width: 960px) {
.bd-page-width {
max-width: 100rem;
}
}
.bd-sidebar-primary .sidebar-primary-items__end {
margin-bottom: 0;
margin-top: 0;
}

View File

@@ -55,3 +55,9 @@ miplearn.problems.vertexcover
.. automodule:: miplearn.problems.vertexcover
:members:
miplearn.problems.maxcut
-----------------------------
.. automodule:: miplearn.problems.maxcut
:members:

View File

@@ -14,7 +14,7 @@
"\n",
"## HDF5 Format\n",
"\n",
"MIPLearn stores all training data in [HDF5](HDF5) (Hierarchical Data Format, Version 5) files. The HDF format was originally developed by the [National Center for Supercomputing Applications][NCSA] (NCSA) for storing and organizing large amounts of data, and supports a variety of data types, including integers, floating-point numbers, strings, and arrays. Compared to other formats, such as CSV, JSON or SQLite, the HDF5 format provides several advantages for MIPLearn, including:\n",
"MIPLearn stores all training data in [HDF5][HDF5] (Hierarchical Data Format, Version 5) files. The HDF format was originally developed by the [National Center for Supercomputing Applications][NCSA] (NCSA) for storing and organizing large amounts of data, and supports a variety of data types, including integers, floating-point numbers, strings, and arrays. Compared to other formats, such as CSV, JSON or SQLite, the HDF5 format provides several advantages for MIPLearn, including:\n",
"\n",
"- *Storage of multiple scalars, vectors and matrices in a single file* --- This allows MIPLearn to store all training data related to a given problem instance in a single file, which makes training data easier to store, organize and transfer.\n",
"- *High-performance partial I/O* --- Partial I/O allows MIPLearn to read a single element from the training data (e.g. value of the optimal solution) without loading the entire file to memory or reading it from beginning to end, which dramatically improves performance and reduces memory requirements. This is especially important when processing a large number of training data files.\n",
@@ -58,21 +58,21 @@
"x1 = 1\n",
"x2 = hello world\n",
"x3 = [1 2 3]\n",
"x4 = [[0.37454012 0.9507143 0.7319939 ]\n",
" [0.5986585 0.15601864 0.15599452]\n",
" [0.05808361 0.8661761 0.601115 ]]\n",
"x5 = (3, 2)\t0.6803075671195984\n",
" (2, 3)\t0.4504992663860321\n",
" (0, 4)\t0.013264961540699005\n",
" (2, 0)\t0.9422017335891724\n",
" (2, 4)\t0.5632882118225098\n",
" (1, 2)\t0.38541650772094727\n",
" (1, 1)\t0.015966251492500305\n",
" (0, 3)\t0.2308938205242157\n",
" (4, 4)\t0.24102546274662018\n",
" (3, 1)\t0.6832635402679443\n",
" (1, 3)\t0.6099966764450073\n",
" (3, 0)\t0.83319491147995\n"
"x4 = [[0.37454012 0.95071431 0.73199394]\n",
" [0.59865848 0.15601864 0.15599452]\n",
" [0.05808361 0.86617615 0.60111501]]\n",
"x5 = (3, 2)\t0.6803075385877797\n",
" (2, 3)\t0.450499251969543\n",
" (0, 4)\t0.013264961159866528\n",
" (2, 0)\t0.9422017556848528\n",
" (2, 4)\t0.5632882178455393\n",
" (1, 2)\t0.3854165025399161\n",
" (1, 1)\t0.015966252220214194\n",
" (0, 3)\t0.230893825622149\n",
" (4, 4)\t0.24102546602601171\n",
" (3, 1)\t0.6832635188254582\n",
" (1, 3)\t0.6099966577826209\n",
" (3, 0)\t0.8331949117361643\n"
]
}
],
@@ -108,12 +108,6 @@
" print(\"x5 =\", h5.get_sparse(\"x5\"))"
]
},
{
"cell_type": "markdown",
"id": "50441907",
"metadata": {},
"source": []
},
{
"cell_type": "markdown",
"id": "d0000c8d",

View File

@@ -69,22 +69,22 @@
" -709. -605. -543. -321.\n",
" -674. -571. -341. ]\n",
"variable features (10, 4) \n",
" [[-1.53124309e+03 -3.50000000e+02 0.00000000e+00 9.43468018e+01]\n",
" [-1.53124309e+03 -6.92000000e+02 2.51703322e-01 0.00000000e+00]\n",
" [-1.53124309e+03 -4.54000000e+02 0.00000000e+00 8.25504150e+01]\n",
" [-1.53124309e+03 -7.09000000e+02 1.11373022e-01 0.00000000e+00]\n",
" [-1.53124309e+03 -6.05000000e+02 1.00000000e+00 -1.26055283e+02]\n",
" [-1.53124309e+03 -5.43000000e+02 0.00000000e+00 1.68693771e+02]\n",
" [[-1.53124309e+03 -3.50000000e+02 0.00000000e+00 9.43467993e+01]\n",
" [-1.53124309e+03 -6.92000000e+02 2.51703329e-01 0.00000000e+00]\n",
" [-1.53124309e+03 -4.54000000e+02 0.00000000e+00 8.25504181e+01]\n",
" [-1.53124309e+03 -7.09000000e+02 1.11373019e-01 0.00000000e+00]\n",
" [-1.53124309e+03 -6.05000000e+02 1.00000000e+00 -1.26055279e+02]\n",
" [-1.53124309e+03 -5.43000000e+02 0.00000000e+00 1.68693775e+02]\n",
" [-1.53124309e+03 -3.21000000e+02 1.07488781e-01 0.00000000e+00]\n",
" [-1.53124309e+03 -6.74000000e+02 8.82293701e-01 0.00000000e+00]\n",
" [-1.53124309e+03 -6.74000000e+02 8.82293687e-01 0.00000000e+00]\n",
" [-1.53124309e+03 -5.71000000e+02 0.00000000e+00 1.41129074e+02]\n",
" [-1.53124309e+03 -3.41000000e+02 1.28830120e-01 0.00000000e+00]]\n",
" [-1.53124309e+03 -3.41000000e+02 1.28830116e-01 0.00000000e+00]]\n",
"constraint features (5, 3) \n",
" [[ 1.3100000e+03 -1.5978307e-01 0.0000000e+00]\n",
" [ 9.8800000e+02 -3.2881632e-01 0.0000000e+00]\n",
" [ 1.0040000e+03 -4.0601316e-01 0.0000000e+00]\n",
" [ 1.2690000e+03 -1.3659772e-01 0.0000000e+00]\n",
" [ 1.0070000e+03 -2.8800571e-01 0.0000000e+00]]\n"
" [[ 1.31000000e+03 -1.59783068e-01 0.00000000e+00]\n",
" [ 9.88000000e+02 -3.28816327e-01 0.00000000e+00]\n",
" [ 1.00400000e+03 -4.06013164e-01 0.00000000e+00]\n",
" [ 1.26900000e+03 -1.36597720e-01 0.00000000e+00]\n",
" [ 1.00700000e+03 -2.88005696e-01 0.00000000e+00]]\n"
]
}
],

View File

@@ -15,7 +15,7 @@
"\n",
"Before presenting the primal components themselves, we briefly discuss the three ways a solution may be provided to the solver. Each approach has benefits and limitations, which we also discuss in this section. All primal components can be configured to use any of the following approaches.\n",
"\n",
"The first approach is to provide the solution to the solver as a **warm start**. This is implemented by the class [SetWarmStart](SetWarmStart). The main advantage is that this method maintains all optimality and feasibility guarantees of the MIP solver, while still providing significant performance benefits for various classes of problems. If the machine learning model is able to predict multiple solutions, it is also possible to set multiple warm starts. In this case, the solver evaluates each warm start, discards the infeasible ones, then proceeds with the one that has the best objective value. The main disadvantage of this approach, compared to the next two, is that it provides relatively modest speedups for most problem classes, and no speedup at all for many others, even when the machine learning predictions are 100% accurate.\n",
"The first approach is to provide the solution to the solver as a **warm start**. This is implemented by the class [SetWarmStart][SetWarmStart]. The main advantage is that this method maintains all optimality and feasibility guarantees of the MIP solver, while still providing significant performance benefits for various classes of problems. If the machine learning model is able to predict multiple solutions, it is also possible to set multiple warm starts. In this case, the solver evaluates each warm start, discards the infeasible ones, then proceeds with the one that has the best objective value. The main disadvantage of this approach, compared to the next two, is that it provides relatively modest speedups for most problem classes, and no speedup at all for many others, even when the machine learning predictions are 100% accurate.\n",
"\n",
"[SetWarmStart]: ../../api/components/#miplearn.components.primal.actions.SetWarmStart\n",
"\n",

View File

@@ -129,11 +129,11 @@
"8 [ 8.47 21.9 16.58 15.37 3.76 3.91 1.57 20.57 14.76 18.61] 94.58\n",
"9 [ 8.57 22.77 17.06 16.25 4.14 4. 1.56 22.97 14.09 19.09] 100.79\n",
"\n",
"Restricted license - for non-production use only - expires 2024-10-28\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Restricted license - for non-production use only - expires 2026-11-23\n",
"Gurobi Optimizer version 12.0.3 build v12.0.3rc0 (linux64 - \"Ubuntu 24.04.3 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"CPU model: AMD Ryzen 9 3950X 16-Core Processor, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
"\n",
"Optimize a model with 20 rows, 110 columns and 210 nonzeros\n",
"Model fingerprint: 0x1ff9913f\n",
@@ -155,18 +155,19 @@
"\n",
" 0 0 1.27484 0 4 5.00000 1.27484 74.5% - 0s\n",
"H 0 0 4.0000000 1.27484 68.1% - 0s\n",
"H 0 0 3.0000000 1.27484 57.5% - 0s\n",
"H 0 0 2.0000000 1.27484 36.3% - 0s\n",
" 0 0 1.27484 0 4 2.00000 1.27484 36.3% - 0s\n",
"\n",
"Explored 1 nodes (38 simplex iterations) in 0.03 seconds (0.00 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"Explored 1 nodes (38 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 3: 2 4 5 \n",
"Solution count 4: 2 3 4 5 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 2.000000000000e+00, best bound 2.000000000000e+00, gap 0.0000%\n",
"\n",
"User-callback calls 143, time in user-callback 0.00 sec\n"
"User-callback calls 148, time in user-callback 0.00 sec\n"
]
}
],
@@ -324,10 +325,10 @@
"capacities\n",
" [1310. 988. 1004. 1269. 1007.]\n",
"\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.3 build v12.0.3rc0 (linux64 - \"Ubuntu 24.04.3 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"CPU model: AMD Ryzen 9 3950X 16-Core Processor, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
"\n",
"Optimize a model with 5 rows, 10 columns and 50 nonzeros\n",
"Model fingerprint: 0xaf3ac15e\n",
@@ -349,21 +350,20 @@
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 -1428.7265 0 4 -804.00000 -1428.7265 77.7% - 0s\n",
"H 0 0 -995.0000000 -1428.7265 43.6% - 0s\n",
"H 0 0 -1279.000000 -1428.7265 11.7% - 0s\n",
"\n",
"Cutting planes:\n",
" Cover: 1\n",
" 0 0 -1428.7265 0 4 -1279.0000 -1428.7265 11.7% - 0s\n",
"\n",
"Explored 1 nodes (4 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 2: -1279 -804 \n",
"Solution count 3: -1279 -995 -804 \n",
"No other solutions better than -1279\n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective -1.279000000000e+03, best bound -1.279000000000e+03, gap 0.0000%\n",
"\n",
"User-callback calls 490, time in user-callback 0.00 sec\n"
"User-callback calls 416, time in user-callback 0.00 sec\n"
]
}
],
@@ -501,10 +501,10 @@
"demands = [6.12 1.39 2.92 3.66 4.56 7.85 2. 5.14 5.92 0.46]\n",
"capacities = [151.89 42.63 16.26 237.22 241.41 202.1 76.15 24.42 171.06 110.04]\n",
"\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.3 build v12.0.3rc0 (linux64 - \"Ubuntu 24.04.3 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"CPU model: AMD Ryzen 9 3950X 16-Core Processor, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
"\n",
"Optimize a model with 21 rows, 110 columns and 220 nonzeros\n",
"Model fingerprint: 0x8d8d9346\n",
@@ -518,35 +518,38 @@
"Presolve time: 0.00s\n",
"Presolved: 21 rows, 110 columns, 220 nonzeros\n",
"Variable types: 0 continuous, 110 integer (110 binary)\n",
"Found heuristic solution: objective 245.6400000\n",
"\n",
"Root relaxation: objective 0.000000e+00, 18 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 0.00000 0 6 245.64000 0.00000 100% - 0s\n",
" 0 0 0.00000 0 6 368.79000 0.00000 100% - 0s\n",
"H 0 0 301.7200000 0.00000 100% - 0s\n",
"H 0 0 185.1900000 0.00000 100% - 0s\n",
"H 0 0 148.6300000 17.14595 88.5% - 0s\n",
"H 0 0 113.1800000 17.14595 84.9% - 0s\n",
" 0 0 17.14595 0 10 113.18000 17.14595 84.9% - 0s\n",
"H 0 0 99.5000000 17.14595 82.8% - 0s\n",
"H 0 0 98.3900000 17.14595 82.6% - 0s\n",
"H 0 0 93.9800000 64.28872 31.6% - 0s\n",
" 0 0 64.28872 0 15 93.98000 64.28872 31.6% - 0s\n",
"H 0 0 93.9200000 64.28872 31.5% - 0s\n",
" 0 0 86.06884 0 15 93.92000 86.06884 8.36% - 0s\n",
"* 0 0 0 91.2300000 91.23000 0.00% - 0s\n",
"H 0 0 153.5000000 0.00000 100% - 0s\n",
"H 0 0 131.7700000 0.00000 100% - 0s\n",
" 0 0 17.14595 0 10 131.77000 17.14595 87.0% - 0s\n",
"H 0 0 115.6500000 17.14595 85.2% - 0s\n",
"H 0 0 114.5300000 64.28872 43.9% - 0s\n",
"H 0 0 98.3900000 64.28872 34.7% - 0s\n",
" 0 0 74.01104 0 15 98.39000 74.01104 24.8% - 0s\n",
"H 0 0 91.2300000 74.01104 18.9% - 0s\n",
"\n",
"Explored 1 nodes (70 simplex iterations) in 0.08 seconds (0.00 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"Cutting planes:\n",
" Cover: 16\n",
" MIR: 1\n",
" StrongCG: 1\n",
"\n",
"Solution count 10: 91.23 93.92 93.98 ... 368.79\n",
"Explored 1 nodes (42 simplex iterations) in 0.02 seconds (0.00 work units)\n",
"Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 9: 91.23 98.39 114.53 ... 368.79\n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 9.123000000000e+01, best bound 9.123000000000e+01, gap 0.0000%\n",
"\n",
"User-callback calls 190, time in user-callback 0.00 sec\n"
"User-callback calls 187, time in user-callback 0.00 sec\n"
]
}
],
@@ -675,10 +678,10 @@
"costs [1044.58 850.13 1014.5 944.83 697.9 971.87 213.49 220.98 70.23\n",
" 425.33]\n",
"\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.3 build v12.0.3rc0 (linux64 - \"Ubuntu 24.04.3 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"CPU model: AMD Ryzen 9 3950X 16-Core Processor, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
"\n",
"Optimize a model with 5 rows, 10 columns and 28 nonzeros\n",
"Model fingerprint: 0xe5c2d4fa\n",
@@ -693,15 +696,15 @@
"Presolve time: 0.00s\n",
"Presolve: All rows and columns removed\n",
"\n",
"Explored 0 nodes (0 simplex iterations) in 0.00 seconds (0.00 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"Explored 0 nodes (0 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 1 (of 32 available processors)\n",
"\n",
"Solution count 1: 213.49 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 2.134900000000e+02, best bound 2.134900000000e+02, gap 0.0000%\n",
"\n",
"User-callback calls 178, time in user-callback 0.00 sec\n"
"User-callback calls 183, time in user-callback 0.00 sec\n"
]
}
],
@@ -816,10 +819,10 @@
"costs [1044.58 850.13 1014.5 944.83 697.9 971.87 213.49 220.98 70.23\n",
" 425.33]\n",
"\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.3 build v12.0.3rc0 (linux64 - \"Ubuntu 24.04.3 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"CPU model: AMD Ryzen 9 3950X 16-Core Processor, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
"\n",
"Optimize a model with 5 rows, 10 columns and 28 nonzeros\n",
"Model fingerprint: 0x4ee91388\n",
@@ -834,8 +837,8 @@
"Presolve time: 0.00s\n",
"Presolve: All rows and columns removed\n",
"\n",
"Explored 0 nodes (0 simplex iterations) in 0.00 seconds (0.00 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"Explored 0 nodes (0 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 1 (of 32 available processors)\n",
"\n",
"Solution count 2: -1986.37 -1265.56 \n",
"No other solutions better than -1986.37\n",
@@ -843,7 +846,7 @@
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective -1.986370000000e+03, best bound -1.986370000000e+03, gap 0.0000%\n",
"\n",
"User-callback calls 238, time in user-callback 0.00 sec\n"
"User-callback calls 244, time in user-callback 0.00 sec\n"
]
}
],
@@ -943,10 +946,13 @@
"weights[1] [ 2.06 96.99 83.24 21.23 18.18 18.34 30.42 52.48 43.19 29.12]\n",
"\n",
"Set parameter PreCrush to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.3 build v12.0.3rc0 (linux64 - \"Ubuntu 24.04.3 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"CPU model: AMD Ryzen 9 3950X 16-Core Processor, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
"\n",
"Non-default parameters:\n",
"PreCrush 1\n",
"\n",
"Optimize a model with 15 rows, 10 columns and 30 nonzeros\n",
"Model fingerprint: 0x3240ea4a\n",
@@ -970,7 +976,7 @@
" 0 0 infeasible 0 -219.14000 -219.14000 0.00% - 0s\n",
"\n",
"Explored 1 nodes (5 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 1: -219.14 \n",
"No other solutions better than -219.14\n",
@@ -978,7 +984,7 @@
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective -2.191400000000e+02, best bound -2.191400000000e+02, gap 0.0000%\n",
"\n",
"User-callback calls 299, time in user-callback 0.00 sec\n"
"User-callback calls 303, time in user-callback 0.00 sec\n"
]
}
],
@@ -1123,10 +1129,14 @@
"\n",
"Set parameter PreCrush to value 1\n",
"Set parameter LazyConstraints to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.3 build v12.0.3rc0 (linux64 - \"Ubuntu 24.04.3 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"CPU model: AMD Ryzen 9 3950X 16-Core Processor, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
"\n",
"Non-default parameters:\n",
"PreCrush 1\n",
"LazyConstraints 1\n",
"\n",
"Optimize a model with 10 rows, 45 columns and 90 nonzeros\n",
"Model fingerprint: 0x719675e5\n",
@@ -1151,14 +1161,14 @@
" Lazy constraints: 3\n",
"\n",
"Explored 1 nodes (17 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 1: 2921 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 2.921000000000e+03, best bound 2.921000000000e+03, gap 0.0000%\n",
"\n",
"User-callback calls 106, time in user-callback 0.00 sec\n"
"User-callback calls 111, time in user-callback 0.00 sec\n"
]
}
],
@@ -1211,7 +1221,6 @@
"id": "7048d771",
"metadata": {},
"source": [
"\n",
"<div class=\"alert alert-info\">\n",
"Note\n",
"\n",
@@ -1220,7 +1229,7 @@
"\n",
"### Formulation\n",
"\n",
"Let $T$ be the number of time steps, $G$ be the number of generation units, and let $D_t$ be the power demand (in MW) at time $t$. For each generating unit $g$, let $P^\\max_g$ and $P^\\min_g$ be the maximum and minimum amount of power the unit is able to produce when switched on; let $L_g$ and $l_g$ be the minimum up- and down-time for unit $g$; let $C^\\text{fixed}$ be the cost to keep unit $g$ on for one time step, regardless of its power output level; let $C^\\text{start}$ be the cost to switch unit $g$ on; and let $C^\\text{var}$ be the cost for generator $g$ to produce 1 MW of power. In this formulation, we assume linear production costs. For each generator $g$ and time $t$, let $x_{gt}$ be a binary variable which equals one if unit $g$ is on at time $t$, let $w_{gt}$ be a binary variable which equals one if unit $g$ switches from being off at time $t-1$ to being on at time $t$, and let $p_{gt}$ be a continuous variable which indicates the amount of power generated. The formulation is given by:"
"Let $T$ be the number of time steps, $G$ be the number of generation units, and let $D_t$ be the power demand (in MW) at time $t$. For each generating unit $g$, let $P^\\max_g$ and $P^\\min_g$ be the maximum and minimum amount of power the unit is able to produce when switched on; let $L_g$ and $l_g$ be the minimum up- and down-time for unit $g$; let $C^\\text{fixed}$ be the cost to keep unit $g$ on for one time step, regardless of its power output level; let $C^\\text{start}$ be the cost to switch unit $g$ on; let $C^\\text{prod-lin}$ be the linear cost coefficient for generator $g$ to produce 1 MW of power; and let $C^\\text{prod-quad}$ be the quadratic cost coefficient for unit $g$. For each generator $g$ and time $t$, let $x_{gt}$ be a binary variable which equals one if unit $g$ is on at time $t$, let $w_{gt}$ be a binary variable which equals one if unit $g$ switches from being off at time $t-1$ to being on at time $t$, and let $p_{gt}$ be a continuous variable which indicates the amount of power generated. The formulation is given by:"
]
},
{
@@ -1228,14 +1237,14 @@
"id": "bec5ee1c",
"metadata": {},
"source": [
"\n",
"$$\n",
"\\begin{align*}\n",
"\\text{minimize} \\;\\;\\;\n",
" & \\sum_{t=1}^T \\sum_{g=1}^G \\left(\n",
" x_{gt} C^\\text{fixed}_g\n",
" + w_{gt} C^\\text{start}_g\n",
" + p_{gt} C^\\text{var}_g\n",
" + p_{gt} C^\\text{prod-lin}_g\n",
" + p_{gt}^2 C^\\text{prod-quad}_g\n",
" \\right)\n",
" \\\\\n",
"\\text{such that} \\;\\;\\;\n",
@@ -1277,12 +1286,11 @@
"id": "01bed9fc",
"metadata": {},
"source": [
"\n",
"### Random instance generator\n",
"\n",
"The class `UnitCommitmentGenerator` can be used to generate random instances of this problem.\n",
"\n",
"First, the user-provided probability distributions `n_units` and `n_periods` are sampled to determine the number of generating units and the number of time steps, respectively. Then, for each unit, the probabilities `max_power` and `min_power` are sampled to determine the unit's maximum and minimum power output. To make it easier to generate valid ranges, `min_power` is not specified as the absolute power level in MW, but rather as a multiplier of `max_power`; for example, if `max_power` samples to 100 and `min_power` samples to 0.5, then the unit's power range is set to `[50,100]`. Then, the distributions `cost_startup`, `cost_prod` and `cost_fixed` are sampled to determine the unit's startup, variable and fixed costs, while the distributions `min_uptime` and `min_downtime` are sampled to determine its minimum up/down-time.\n",
"First, the user-provided probability distributions `n_units` and `n_periods` are sampled to determine the number of generating units and the number of time steps, respectively. Then, for each unit, the probabilities `max_power` and `min_power` are sampled to determine the unit's maximum and minimum power output. To make it easier to generate valid ranges, `min_power` is not specified as the absolute power level in MW, but rather as a multiplier of `max_power`; for example, if `max_power` samples to 100 and `min_power` samples to 0.5, then the unit's power range is set to `[50,100]`. Then, the distributions `cost_startup`, `cost_prod`, `cost_prod_quad` and `cost_fixed` are sampled to determine the unit's startup, linear variable, quadratic variable, and fixed costs, while the distributions `min_uptime` and `min_downtime` are sampled to determine its minimum up/down-time.\n",
"\n",
"After parameters for the units have been generated, the class then generates a periodic demand curve, with a peak every 12 time steps, in the range $(0.4C, 0.8C)$, where $C$ is the sum of all units' maximum power output. Finally, all costs and demand values are perturbed by random scaling factors independently sampled from the distributions `cost_jitter` and `demand_jitter`, respectively.\n",
"\n",
@@ -1299,7 +1307,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 12,
"id": "6217da7c",
"metadata": {
"ExecuteTime": {
@@ -1323,75 +1331,94 @@
"min_power[0] [117.79 245.85 271.85 207.7 81.38]\n",
"cost_startup[0] [3042.42 5247.56 4319.45 2912.29 6118.53]\n",
"cost_prod[0] [ 6.97 14.61 18.32 22.8 39.26]\n",
"cost_fixed[0] [199.67 514.23 592.41 46.45 607.54]\n",
"cost_prod_quad[0] [0.02 0.0514 0.0592 0.0046 0.0608]\n",
"cost_fixed[0] [170.52 65.05 948.89 965.63 808.4 ]\n",
"demand[0]\n",
" [ 905.06 915.41 1166.52 1212.29 1127.81 953.52 905.06 796.21 783.78\n",
" 866.23 768.62 899.59 905.06 946.23 1087.61 1004.24 1048.36 992.03\n",
" 905.06 750.82 691.48 606.15 658.5 809.95]\n",
" [ 869.31 897.58 1212.29 1124.08 930.48 1012.62 869.31 606.15]\n",
"\n",
"min_power[1] [117.79 245.85 271.85 207.7 81.38]\n",
"max_power[1] [218.54 477.82 379.4 319.4 120.21]\n",
"min_uptime[1] [7 6 3 5 7]\n",
"min_downtime[1] [7 3 5 6 2]\n",
"min_power[1] [117.79 245.85 271.85 207.7 81.38]\n",
"cost_startup[1] [2458.08 6200.26 4585.74 2666.05 4783.34]\n",
"cost_prod[1] [ 6.31 13.33 20.42 24.37 46.86]\n",
"cost_fixed[1] [196.9 416.42 655.57 52.51 626.15]\n",
"cost_startup[1] [3710.99 6283.5 4530.89 3526.6 4859.62]\n",
"cost_prod[1] [ 5.91 11.29 16.72 21.53 34.77]\n",
"cost_prod_quad[1] [0.0233 0.0477 0.0527 0.0047 0.0499]\n",
"cost_fixed[1] [ 196.29 51.21 1179.89 1097.07 686.62]\n",
"demand[1]\n",
" [ 981.42 840.07 1095.59 1102.03 1088.41 932.29 863.67 848.56 761.33\n",
" 828.28 775.18 834.99 959.76 865.72 1193.52 1058.92 985.19 893.92\n",
" 962.16 781.88 723.15 639.04 602.4 787.02]\n",
" [ 827.37 926.76 1166.64 1128.59 939.17 948.8 950.95 639.5 ]\n",
"\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.3 build v12.0.3rc0 (linux64 - \"Ubuntu 24.04.3 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"CPU model: AMD Ryzen 9 3950X 16-Core Processor, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
"\n",
"Optimize a model with 578 rows, 360 columns and 2128 nonzeros\n",
"Model fingerprint: 0x4dc1c661\n",
"Variable types: 120 continuous, 240 integer (240 binary)\n",
"Optimize a model with 162 rows, 120 columns and 512 nonzeros\n",
"Model fingerprint: 0x1e3651da\n",
"Model has 40 quadratic objective terms\n",
"Variable types: 40 continuous, 80 integer (80 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 5e+02]\n",
" Objective range [7e+00, 6e+03]\n",
" QObjective range [9e-03, 1e-01]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [1e+00, 1e+03]\n",
"Presolve removed 244 rows and 131 columns\n",
"Presolve time: 0.01s\n",
"Presolved: 334 rows, 229 columns, 842 nonzeros\n",
"Variable types: 116 continuous, 113 integer (113 binary)\n",
"Found heuristic solution: objective 440662.46430\n",
"Found heuristic solution: objective 429461.97680\n",
"Found heuristic solution: objective 374043.64040\n",
"Found heuristic solution: objective 282371.35206\n",
"Presolve removed 61 rows and 40 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 137 rows, 98 columns, 362 nonzeros\n",
"Presolved model has 40 quadratic objective terms\n",
"Variable types: 58 continuous, 40 integer (40 binary)\n",
"\n",
"Root relaxation: objective 3.361348e+05, 142 iterations, 0.00 seconds (0.00 work units)\n",
"Root relaxation: objective 1.995341e+05, 126 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 336134.820 0 18 374043.640 336134.820 10.1% - 0s\n",
"H 0 0 368600.14450 336134.820 8.81% - 0s\n",
"H 0 0 364721.76610 336134.820 7.84% - 0s\n",
" 0 0 cutoff 0 364721.766 364721.766 0.00% - 0s\n",
" 0 0 199534.058 0 8 282371.352 199534.058 29.3% - 0s\n",
"H 0 0 212978.80635 199534.058 6.31% - 0s\n",
"H 0 0 208079.69920 199534.058 4.11% - 0s\n",
" 0 0 203097.772 0 16 208079.699 203097.772 2.39% - 0s\n",
" 0 0 203097.772 0 4 208079.699 203097.772 2.39% - 0s\n",
" 0 0 203097.772 0 4 208079.699 203097.772 2.39% - 0s\n",
" 0 0 203097.772 0 3 208079.699 203097.772 2.39% - 0s\n",
" 0 0 203097.772 0 3 208079.699 203097.772 2.39% - 0s\n",
" 0 0 203097.772 0 3 208079.699 203097.772 2.39% - 0s\n",
" 0 0 205275.299 0 - 208079.699 205275.299 1.35% - 0s\n",
" 0 0 205777.846 0 2 208079.699 205777.846 1.11% - 0s\n",
" 0 0 205789.407 0 - 208079.699 205789.407 1.10% - 0s\n",
" 0 0 postponed 0 208079.699 205789.407 1.10% - 0s\n",
" 0 0 postponed 0 208079.699 205789.408 1.10% - 0s\n",
" 0 0 205789.408 0 4 208079.699 205789.408 1.10% - 0s\n",
" 0 0 205789.408 0 4 208079.699 205789.408 1.10% - 0s\n",
" 0 0 205789.408 0 1 208079.699 205789.408 1.10% - 0s\n",
" 0 0 205789.408 0 - 208079.699 205789.408 1.10% - 0s\n",
" 0 0 205789.408 0 - 208079.699 205789.408 1.10% - 0s\n",
" 0 0 205789.408 0 - 208079.699 205789.408 1.10% - 0s\n",
" 0 0 205789.408 0 - 208079.699 205789.408 1.10% - 0s\n",
" 0 0 205789.408 0 2 208079.699 205789.408 1.10% - 0s\n",
"H 0 0 207525.63560 205789.408 0.84% - 0s\n",
" 0 0 205789.408 0 2 207525.636 205789.408 0.84% - 0s\n",
" 0 2 205789.408 0 2 207525.636 205789.408 0.84% - 0s\n",
"* 9 0 5 205789.40812 205789.408 0.00% 0.0 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 3\n",
" Cover: 8\n",
" Implied bound: 29\n",
" Clique: 222\n",
" MIR: 7\n",
" Flow cover: 7\n",
" RLT: 1\n",
" Relax-and-lift: 7\n",
" Cover: 1\n",
" Implied bound: 6\n",
" MIR: 2\n",
" Flow cover: 2\n",
" RLT: 2\n",
"\n",
"Explored 1 nodes (234 simplex iterations) in 0.02 seconds (0.02 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"Explored 11 nodes (621 simplex iterations) in 0.32 seconds (0.19 work units)\n",
"Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 5: 364722 368600 374044 ... 440662\n",
"Solution count 5: 205789 207526 208080 ... 282371\n",
"No other solutions better than 205789\n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 3.647217661000e+05, best bound 3.647217661000e+05, gap 0.0000%\n",
"Best objective 2.057894080889e+05, best bound 2.057894081187e+05, gap 0.0000%\n",
"\n",
"User-callback calls 677, time in user-callback 0.00 sec\n"
"User-callback calls 650, time in user-callback 0.00 sec\n"
]
}
],
@@ -1405,14 +1432,15 @@
"random.seed(42)\n",
"np.random.seed(42)\n",
"\n",
"# Generate a random instance with 5 generators and 24 time steps\n",
"# Generate a random instance with 5 generators and 8 time steps\n",
"data = UnitCommitmentGenerator(\n",
" n_units=randint(low=5, high=6),\n",
" n_periods=randint(low=24, high=25),\n",
" n_periods=randint(low=8, high=9),\n",
" max_power=uniform(loc=50, scale=450),\n",
" min_power=uniform(loc=0.5, scale=0.25),\n",
" cost_startup=uniform(loc=0, scale=10_000),\n",
" cost_prod=uniform(loc=0, scale=50),\n",
" cost_prod_quad=uniform(loc=0, scale=0.1),\n",
" cost_fixed=uniform(loc=0, scale=1_000),\n",
" min_uptime=randint(low=2, high=8),\n",
" min_downtime=randint(low=2, high=8),\n",
@@ -1430,6 +1458,7 @@
" print(f\"min_power[{i}]\", data[i].min_power)\n",
" print(f\"cost_startup[{i}]\", data[i].cost_startup)\n",
" print(f\"cost_prod[{i}]\", data[i].cost_prod)\n",
" print(f\"cost_prod_quad[{i}]\", data[i].cost_prod_quad)\n",
" print(f\"cost_fixed[{i}]\", data[i].cost_fixed)\n",
" print(f\"demand[{i}]\\n\", data[i].demand)\n",
" print()\n",
@@ -1511,10 +1540,10 @@
"weights[0] [37.45 95.07 73.2 59.87 15.6 15.6 5.81 86.62 60.11 70.81]\n",
"weights[1] [ 2.06 96.99 83.24 21.23 18.18 18.34 30.42 52.48 43.19 29.12]\n",
"\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.3 build v12.0.3rc0 (linux64 - \"Ubuntu 24.04.3 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"CPU model: AMD Ryzen 9 3950X 16-Core Processor, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
"\n",
"Optimize a model with 15 rows, 10 columns and 30 nonzeros\n",
"Model fingerprint: 0x2d2d1390\n",
@@ -1530,22 +1559,22 @@
"Presolved: 8 rows, 8 columns, 19 nonzeros\n",
"Variable types: 0 continuous, 8 integer (8 binary)\n",
"\n",
"Root relaxation: objective 2.995750e+02, 8 iterations, 0.00 seconds (0.00 work units)\n",
"Root relaxation: cutoff, 8 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 infeasible 0 301.00000 301.00000 0.00% - 0s\n",
" 0 0 cutoff 0 301.00000 301.00000 0.00% - 0s\n",
"\n",
"Explored 1 nodes (8 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 1: 301 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 3.010000000000e+02, best bound 3.010000000000e+02, gap 0.0000%\n",
"\n",
"User-callback calls 326, time in user-callback 0.00 sec\n"
"User-callback calls 333, time in user-callback 0.00 sec\n"
]
}
],
@@ -1581,6 +1610,148 @@
"model = build_vertexcover_model_gurobipy(data[0])\n",
"model.optimize()"
]
},
{
"cell_type": "markdown",
"id": "k4ojjjni3z",
"metadata": {},
"source": [
"## Maximum Cut\n",
"\n",
"The **maximum cut problem** is a classical optimization problem in graph theory and combinatorial optimization. Given a graph with weighted edges, the goal is to partition the vertices into two disjoint sets such that the sum of the weights of the edges crossing the partition is maximized. This problem is one of Karp's 21 NP-complete problems and has important applications in theoretical physics, machine learning, and VLSI design."
]
},
{
"cell_type": "markdown",
"id": "kzdqjib7lac",
"metadata": {},
"source": [
"### Formulation\n",
"\n",
"Let $G=(V,E)$ be an undirected graph, and for each edge $e \\in E$, let $w_e$ be its weight. For each vertex $v \\in V$, let $x_v$ be a binary decision variable that equals one if vertex $v$ is assigned to the first partition, and zero if it is assigned to the second partition. The maximum cut problem is formulated as:"
]
},
{
"cell_type": "markdown",
"id": "lnmj134ojad",
"metadata": {},
"source": [
"$$\n",
"\\begin{align*}\n",
"\\text{minimize} \\;\\;\\;\n",
" & -\\sum_{(i,j) \\in E} w_{ij} x_i \\left( 1 - x_j \\right) \\\\\n",
"\\text{such that} \\;\\;\\;\n",
" & x_v \\in \\{0, 1\\} & \\forall v \\in V\n",
"\\end{align*}\n",
"$$\n",
"\n",
"The objective function counts the total weight of edges that cross the cut (i.e., edges connecting vertices in different partitions)."
]
},
{
"cell_type": "markdown",
"id": "j49upfw2o8k",
"metadata": {},
"source": [
"### Random instance generator\n",
"\n",
"The class [MaxCutGenerator][MaxCutGenerator] can be used to generate random instances of this problem. The generator operates in two modes:\n",
"\n",
"When `fix_graph=False`, a new random Erdős-Rényi graph $G_{n,p}$ is generated for each instance, where $n$ (number of vertices) and $p$ (edge probability) are sampled from the provided probability distributions. Each edge is assigned a random weight drawn from the set $\\{-1, +1\\}$ with equal probability.\n",
"\n",
"When `fix_graph=True`, a single random graph is generated during initialization and reused across all instances. To create variations, the generator randomly flips the sign of each edge weight with probability `w_jitter`, allowing for instances with the same graph structure but different edge weight patterns.\n",
"\n",
"[MaxCutGenerator]: ../../api/problems/#miplearn.problems.maxcut.MaxCutGenerator"
]
},
{
"cell_type": "markdown",
"id": "wsd5jlowc4k",
"metadata": {},
"source": [
"### Example"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "uge28hmv3a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"graph edges: [(0, 2), (0, 3), (0, 4), (0, 8), (1, 2), (1, 3), (1, 5), (1, 6), (1, 9), (2, 5), (2, 9), (3, 6), (3, 7), (6, 9), (7, 8), (7, 9), (8, 9)]\n",
"weights[0]: [ 1 1 1 -1 -1 -1 -1 -1 -1 -1 1 -1 -1 1 1 -1 -1]\n",
"weights[1]: [-1 1 -1 -1 -1 1 -1 1 -1 1 -1 1 -1 -1 1 -1 1]\n",
"\n",
"Gurobi Optimizer version 12.0.3 build v12.0.3rc0 (linux64 - \"Ubuntu 24.04.3 LTS\")\n",
"\n",
"CPU model: AMD Ryzen 9 3950X 16-Core Processor, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
"\n",
"Optimize a model with 0 rows, 10 columns and 0 nonzeros\n",
"Model fingerprint: 0x005f9eac\n",
"Model has 17 quadratic objective terms\n",
"Variable types: 0 continuous, 10 integer (10 binary)\n",
"Coefficient statistics:\n",
" Matrix range [0e+00, 0e+00]\n",
" Objective range [1e+00, 5e+00]\n",
" QObjective range [2e+00, 2e+00]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [0e+00, 0e+00]\n",
"Found heuristic solution: objective 0.0000000\n",
"Found heuristic solution: objective -3.0000000\n",
"Presolve removed 0 rows and 10 columns\n",
"Presolve time: 0.00s\n",
"Presolve: All rows and columns removed\n",
"\n",
"Explored 0 nodes (0 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 1 (of 32 available processors)\n",
"\n",
"Solution count 2: -3 0 \n",
"No other solutions better than -3\n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective -3.000000000000e+00, best bound -3.000000000000e+00, gap 0.0000%\n",
"\n",
"User-callback calls 86, time in user-callback 0.00 sec\n"
]
}
],
"source": [
"import random\n",
"import numpy as np\n",
"from scipy.stats import uniform, randint\n",
"from miplearn.problems.maxcut import (\n",
" MaxCutGenerator,\n",
" build_maxcut_model_gurobipy,\n",
")\n",
"\n",
"# Set random seed to make example reproducible\n",
"random.seed(42)\n",
"np.random.seed(42)\n",
"\n",
"# Generate random instances with a fixed 10-node graph,\n",
"# 30% edge probability, and random weight jittering\n",
"data = MaxCutGenerator(\n",
" n=randint(low=10, high=11),\n",
" p=uniform(loc=0.3, scale=0.0),\n",
" w_jitter=0.2,\n",
" fix_graph=True,\n",
").generate(10)\n",
"\n",
"# Print the graph and weights for two instances\n",
"print(\"graph edges:\", list(data[0].graph.edges()))\n",
"print(\"weights[0]:\", data[0].weights)\n",
"print(\"weights[1]:\", data[1].weights)\n",
"print()\n",
"\n",
"# Build and optimize the first instance\n",
"model = build_maxcut_model_gurobipy(data[0])\n",
"model.optimize()"
]
}
],
"metadata": {
@@ -1599,7 +1770,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.7"
"version": "3.12.3"
}
},
"nbformat": 4,

View File

@@ -70,8 +70,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Restricted license - for non-production use only - expires 2024-10-28\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Restricted license - for non-production use only - expires 2026-11-23\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
@@ -90,17 +90,21 @@
" 0 6.3600000e+02 1.700000e+01 0.000000e+00 0s\n",
" 15 2.7610000e+03 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 15 iterations and 0.00 seconds (0.00 work units)\n",
"Solved in 15 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 2.761000000e+03\n",
"\n",
"User-callback calls 56, time in user-callback 0.00 sec\n",
"Set parameter PreCrush to value 1\n",
"Set parameter LazyConstraints to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Non-default parameters:\n",
"PreCrush 1\n",
"LazyConstraints 1\n",
"\n",
"Optimize a model with 10 rows, 45 columns and 90 nonzeros\n",
"Model fingerprint: 0x74ca3d0a\n",
"Variable types: 0 continuous, 45 integer (45 binary)\n",
@@ -123,12 +127,11 @@
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 2761.00000 0 - 2796.00000 2761.00000 1.25% - 0s\n",
" 0 0 cutoff 0 2796.00000 2796.00000 0.00% - 0s\n",
"\n",
"Cutting planes:\n",
" Lazy constraints: 3\n",
"\n",
"Explored 1 nodes (16 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Explored 1 nodes (14 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 1: 2796 \n",
@@ -136,18 +139,8 @@
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 2.796000000000e+03, best bound 2.796000000000e+03, gap 0.0000%\n",
"\n",
"User-callback calls 110, time in user-callback 0.00 sec\n"
"User-callback calls 114, time in user-callback 0.00 sec\n"
]
},
{
"data": {
"text/plain": [
"{'WS: Count': 1, 'WS: Number of variables set': 41.0}"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
@@ -215,7 +208,7 @@
"solver.fit(train_data)\n",
"\n",
"# Solve a test instance\n",
"solver.optimize(test_data[0], build_tsp_model_gurobipy)"
"solver.optimize(test_data[0], build_tsp_model_gurobipy);"
]
},
{

View File

@@ -1,6 +1,6 @@
MIPLearn
========
**MIPLearn** is an extensible framework for solving discrete optimization problems using a combination of Mixed-Integer Linear Programming (MIP) and Machine Learning (ML). MIPLearn uses ML methods to automatically identify patterns in previously solved instances of the problem, then uses these patterns to accelerate the performance of conventional state-of-the-art MIP solvers such as CPLEX, Gurobi or XPRESS.
**MIPLearn** is an extensible framework for solving discrete optimization problems using a combination of Mixed-Integer Programming (MIP) and Machine Learning (ML). MIPLearn uses ML methods to automatically identify patterns in previously solved instances of the problem, then uses these patterns to accelerate the performance of conventional state-of-the-art MIP solvers such as CPLEX, Gurobi or XPRESS.
Unlike pure ML methods, MIPLearn is not only able to find high-quality solutions to discrete optimization problems, but it can also prove the optimality and feasibility of these solutions. Unlike conventional MIP solvers, MIPLearn can take full advantage of very specific observations that happen to be true in a particular family of instances (such as the observation that a particular constraint is typically redundant, or that a particular variable typically assumes a certain value). For certain classes of problems, this approach may provide significant performance benefits.

View File

@@ -286,12 +286,16 @@
"output_type": "stream",
"text": [
"Set parameter Threads to value 1\n",
"Restricted license - for non-production use only - expires 2024-10-28\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Read parameters from file gurobi.env\n",
"Restricted license - for non-production use only - expires 2026-11-23\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"Threads 1\n",
"\n",
"Optimize a model with 50 rows, 1225 columns and 2450 nonzeros\n",
"Model fingerprint: 0x04d7bec1\n",
"Coefficient statistics:\n",
@@ -309,7 +313,7 @@
"Solved in 66 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 5.588000000e+03\n",
"\n",
"User-callback calls 107, time in user-callback 0.00 sec\n"
"User-callback calls 110, time in user-callback 0.00 sec\n"
]
},
{
@@ -327,11 +331,16 @@
"Enforcing 19 subtour elimination constraints\n",
"Set parameter PreCrush to value 1\n",
"Set parameter LazyConstraints to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"PreCrush 1\n",
"Threads 1\n",
"LazyConstraints 1\n",
"\n",
"Optimize a model with 69 rows, 1225 columns and 6091 nonzeros\n",
"Model fingerprint: 0x09bd34d6\n",
"Variable types: 0 continuous, 1225 integer (1225 binary)\n",
@@ -356,23 +365,29 @@
"Enforcing 3 subtour elimination constraints\n",
" 0 0 6165.50000 0 6 6390.00000 6165.50000 3.51% - 0s\n",
" 0 0 6198.50000 0 16 6390.00000 6198.50000 3.00% - 0s\n",
" 0 0 6210.50000 0 6 6390.00000 6210.50000 2.81% - 0s\n",
" 0 0 6212.60000 0 31 6390.00000 6212.60000 2.78% - 0s\n",
"H 0 0 6241.0000000 6212.60000 0.46% - 0s\n",
"* 0 0 0 6219.0000000 6219.00000 0.00% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 11\n",
" Gomory: 6\n",
" Clique: 1\n",
" MIR: 1\n",
" StrongCG: 1\n",
" Zero half: 4\n",
" RLT: 1\n",
" Lazy constraints: 3\n",
"\n",
"Explored 1 nodes (222 simplex iterations) in 0.03 seconds (0.02 work units)\n",
"Explored 1 nodes (219 simplex iterations) in 0.04 seconds (0.03 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"\n",
"Solution count 3: 6219 6390 29853 \n",
"Solution count 4: 6219 6241 6390 29853 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 6.219000000000e+03, best bound 6.219000000000e+03, gap 0.0000%\n",
"\n",
"User-callback calls 141, time in user-callback 0.00 sec\n"
"User-callback calls 163, time in user-callback 0.00 sec\n"
]
}
],
@@ -402,11 +417,14 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"Threads 1\n",
"\n",
"Optimize a model with 50 rows, 1225 columns and 2450 nonzeros\n",
"Model fingerprint: 0x04d7bec1\n",
"Coefficient statistics:\n",
@@ -424,14 +442,19 @@
"Solved in 66 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 5.588000000e+03\n",
"\n",
"User-callback calls 107, time in user-callback 0.00 sec\n",
"User-callback calls 110, time in user-callback 0.00 sec\n",
"Set parameter PreCrush to value 1\n",
"Set parameter LazyConstraints to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"PreCrush 1\n",
"Threads 1\n",
"LazyConstraints 1\n",
"\n",
"Optimize a model with 50 rows, 1225 columns and 2450 nonzeros\n",
"Model fingerprint: 0x77a94572\n",
"Variable types: 0 continuous, 1225 integer (1225 binary)\n",
@@ -452,39 +475,46 @@
"\n",
" 0 0 5588.00000 0 12 29695.0000 5588.00000 81.2% - 0s\n",
"Enforcing 9 subtour elimination constraints\n",
"Enforcing 11 subtour elimination constraints\n",
"H 0 0 27241.000000 5588.00000 79.5% - 0s\n",
" 0 0 5898.00000 0 8 27241.0000 5898.00000 78.3% - 0s\n",
"Enforcing 4 subtour elimination constraints\n",
"Enforcing 9 subtour elimination constraints\n",
"H 0 0 24919.000000 5588.00000 77.6% - 0s\n",
" 0 0 5847.50000 0 14 24919.0000 5847.50000 76.5% - 0s\n",
"Enforcing 5 subtour elimination constraints\n",
"Enforcing 5 subtour elimination constraints\n",
"Enforcing 3 subtour elimination constraints\n",
" 0 0 6066.00000 0 - 27241.0000 6066.00000 77.7% - 0s\n",
"Enforcing 2 subtour elimination constraints\n",
" 0 0 6128.00000 0 - 27241.0000 6128.00000 77.5% - 0s\n",
" 0 0 6139.00000 0 6 27241.0000 6139.00000 77.5% - 0s\n",
"H 0 0 6368.0000000 6139.00000 3.60% - 0s\n",
" 0 0 6154.75000 0 15 6368.00000 6154.75000 3.35% - 0s\n",
"Enforcing 2 subtour elimination constraints\n",
" 0 0 6154.75000 0 6 6368.00000 6154.75000 3.35% - 0s\n",
" 0 0 6165.75000 0 11 6368.00000 6165.75000 3.18% - 0s\n",
"H 0 0 7764.0000000 5847.50000 24.7% - 0s\n",
"H 0 0 6684.0000000 5847.50000 12.5% - 0s\n",
" 0 0 6013.75000 0 11 6684.00000 6013.75000 10.0% - 0s\n",
"H 0 0 6340.0000000 6013.75000 5.15% - 0s\n",
"Enforcing 3 subtour elimination constraints\n",
" 0 0 6204.00000 0 6 6368.00000 6204.00000 2.58% - 0s\n",
"* 0 0 0 6219.0000000 6219.00000 0.00% - 0s\n",
"Enforcing 2 subtour elimination constraints\n",
" 0 0 6095.00000 0 10 6340.00000 6095.00000 3.86% - 0s\n",
"Enforcing 3 subtour elimination constraints\n",
"Enforcing 2 subtour elimination constraints\n",
" 0 0 6128.00000 0 - 6340.00000 6128.00000 3.34% - 0s\n",
" 0 0 6139.00000 0 6 6340.00000 6139.00000 3.17% - 0s\n",
"Enforcing 2 subtour elimination constraints\n",
" 0 0 6187.25000 0 17 6340.00000 6187.25000 2.41% - 0s\n",
"Enforcing 2 subtour elimination constraints\n",
"Enforcing 2 subtour elimination constraints\n",
" 0 0 6201.00000 0 15 6340.00000 6201.00000 2.19% - 0s\n",
" 0 0 6201.00000 0 15 6340.00000 6201.00000 2.19% - 0s\n",
"H 0 0 6219.0000000 6201.00000 0.29% - 0s\n",
"Enforcing 3 subtour elimination constraints\n",
" 0 0 infeasible 0 6219.00000 6219.00000 0.00% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 5\n",
" MIR: 1\n",
" Zero half: 4\n",
" Lazy constraints: 4\n",
" Lazy constraints: 2\n",
"\n",
"Explored 1 nodes (224 simplex iterations) in 0.10 seconds (0.03 work units)\n",
"Explored 1 nodes (217 simplex iterations) in 0.12 seconds (0.05 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"\n",
"Solution count 4: 6219 6368 27241 29695 \n",
"Solution count 6: 6219 6340 6684 ... 29695\n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 6.219000000000e+03, best bound 6.219000000000e+03, gap 0.0000%\n",
"\n",
"User-callback calls 170, time in user-callback 0.01 sec\n"
"User-callback calls 216, time in user-callback 0.06 sec\n"
]
}
],

View File

@@ -215,12 +215,16 @@
"output_type": "stream",
"text": [
"Set parameter Threads to value 1\n",
"Restricted license - for non-production use only - expires 2024-10-28\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Read parameters from file gurobi.env\n",
"Restricted license - for non-production use only - expires 2026-11-23\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"Threads 1\n",
"\n",
"Optimize a model with 7 rows, 6 columns and 15 nonzeros\n",
"Model fingerprint: 0x58dfdd53\n",
"Variable types: 3 continuous, 3 integer (3 binary)\n",
@@ -229,30 +233,28 @@
" Objective range [2e+00, 7e+02]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [1e+02, 1e+02]\n",
"Presolve removed 2 rows and 1 columns\n",
"Presolve removed 6 rows and 3 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 5 rows, 5 columns, 13 nonzeros\n",
"Variable types: 0 continuous, 5 integer (3 binary)\n",
"Found heuristic solution: objective 1400.0000000\n",
"Presolved: 1 rows, 3 columns, 3 nonzeros\n",
"Variable types: 0 continuous, 3 integer (1 binary)\n",
"Found heuristic solution: objective 1990.0000000\n",
"\n",
"Root relaxation: objective 1.035000e+03, 3 iterations, 0.00 seconds (0.00 work units)\n",
"Root relaxation: objective 1.320000e+03, 0 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 1035.00000 0 1 1400.00000 1035.00000 26.1% - 0s\n",
" 0 0 1105.71429 0 1 1400.00000 1105.71429 21.0% - 0s\n",
"* 0 0 0 1320.0000000 1320.00000 0.00% - 0s\n",
"\n",
"Explored 1 nodes (5 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Explored 1 nodes (0 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"\n",
"Solution count 2: 1320 1400 \n",
"Solution count 2: 1320 1990 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 1.320000000000e+03, best bound 1.320000000000e+03, gap 0.0000%\n",
"\n",
"User-callback calls 371, time in user-callback 0.00 sec\n",
"User-callback calls 541, time in user-callback 0.00 sec\n",
"obj = 1320.0\n",
"x = [-0.0, 1.0, 1.0]\n",
"y = [0.0, 60.0, 40.0]\n"
@@ -477,11 +479,14 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"Threads 1\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0xa8b70287\n",
"Coefficient statistics:\n",
@@ -497,15 +502,18 @@
" 0 6.6166537e+09 5.648803e+04 0.000000e+00 0s\n",
" 1 8.2906219e+09 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.00 seconds (0.00 work units)\n",
"Solved in 1 iterations and 0.02 seconds (0.00 work units)\n",
"Optimal objective 8.290621916e+09\n",
"\n",
"User-callback calls 56, time in user-callback 0.00 sec\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"User-callback calls 59, time in user-callback 0.00 sec\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"Threads 1\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x892e56b2\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
@@ -515,15 +523,18 @@
" Bounds range [1e+00, 1e+00]\n",
" RHS range [3e+08, 3e+08]\n",
"\n",
"User MIP start produced solution with objective 8.29824e+09 (0.00s)\n",
"User MIP start produced solution with objective 8.29398e+09 (0.00s)\n",
"User MIP start produced solution with objective 8.29153e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.29153e+09 (0.01s)\n",
"Loaded user MIP start with objective 8.29153e+09\n",
"\n",
"Presolve removed 500 rows and 0 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Presolved: 501 rows, 1000 columns, 2000 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"\n",
"Root relaxation: objective 8.290622e+09, 512 iterations, 0.00 seconds (0.00 work units)\n",
"Root relaxation: objective 8.290622e+09, 501 iterations, 0.00 seconds (0.02 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
@@ -531,27 +542,29 @@
" 0 0 8.2906e+09 0 1 8.2915e+09 8.2906e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 3 8.2915e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2915e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 2 8.2915e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2915e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 - 0 8.2915e+09 8.2907e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 1\n",
" Flow cover: 2\n",
" RLT: 2\n",
"\n",
"Explored 1 nodes (565 simplex iterations) in 0.02 seconds (0.01 work units)\n",
"Explored 1 nodes (550 simplex iterations) in 0.04 seconds (0.04 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"\n",
"Solution count 1: 8.29153e+09 \n",
"Solution count 4: 8.29153e+09 8.29398e+09 8.29695e+09 8.29824e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.291528276179e+09, best bound 8.290733258025e+09, gap 0.0096%\n",
"Best objective 8.291528276179e+09, best bound 8.290709658754e+09, gap 0.0099%\n",
"\n",
"User-callback calls 193, time in user-callback 0.00 sec\n"
"User-callback calls 799, time in user-callback 0.00 sec\n"
]
},
{
"data": {
"text/plain": [
"{'WS: Count': 1, 'WS: Number of variables set': 477.0}"
"(<miplearn.solvers.gurobi.GurobiModel at 0x7f2bcd72cfd0>,\n",
" {'WS: Count': 1, 'WS: Number of variables set': 477.0})"
]
},
"execution_count": 8,
@@ -590,11 +603,14 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"Threads 1\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0xa8b70287\n",
"Coefficient statistics:\n",
@@ -613,12 +629,15 @@
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.290621916e+09\n",
"\n",
"User-callback calls 56, time in user-callback 0.00 sec\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"User-callback calls 59, time in user-callback 0.00 sec\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"Threads 1\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x4cbbf7c7\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
@@ -627,48 +646,52 @@
" Objective range [1e+00, 6e+07]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [3e+08, 3e+08]\n",
"Presolve removed 500 rows and 0 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Presolved: 501 rows, 1000 columns, 2000 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"Found heuristic solution: objective 9.757128e+09\n",
"Found heuristic solution: objective 1.729688e+10\n",
"\n",
"Root relaxation: objective 8.290622e+09, 512 iterations, 0.00 seconds (0.00 work units)\n",
"Root relaxation: objective 8.290622e+09, 501 iterations, 0.00 seconds (0.02 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 8.2906e+09 0 1 9.7571e+09 8.2906e+09 15.0% - 0s\n",
"H 0 0 8.298273e+09 8.2906e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 4 8.2983e+09 8.2907e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2983e+09 8.2907e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 4 8.2983e+09 8.2907e+09 0.09% - 0s\n",
" 0 0 8.2906e+09 0 1 1.7297e+10 8.2906e+09 52.1% - 0s\n",
"H 0 0 8.298243e+09 8.2906e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 3 8.2982e+09 8.2907e+09 0.09% - 0s\n",
"H 0 0 8.293980e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 5 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 2 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 2 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 2 8.2908e+09 0 2 8.2940e+09 8.2908e+09 0.04% - 0s\n",
"H 9 9 8.292131e+09 8.2908e+09 0.02% 1.0 0s\n",
"H 132 88 8.292121e+09 8.2908e+09 0.02% 2.0 0s\n",
"* 133 88 28 8.292121e+09 8.2908e+09 0.02% 2.2 0s\n",
"H 216 136 8.291918e+09 8.2909e+09 0.01% 2.4 0s\n",
"* 232 136 28 8.291664e+09 8.2909e+09 0.01% 2.4 0s\n",
" 0 0 8.2907e+09 0 1 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 3 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 3 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 4 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 3 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 3 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 4 8.2940e+09 8.2907e+09 0.04% - 0s\n",
"H 0 0 8.291961e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2920e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 3 8.2920e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 4 8.2920e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 2 8.2920e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2908e+09 0 3 8.2920e+09 8.2908e+09 0.01% - 0s\n",
" 0 0 8.2908e+09 0 5 8.2920e+09 8.2908e+09 0.01% - 0s\n",
" 0 0 8.2908e+09 0 5 8.2920e+09 8.2908e+09 0.01% - 0s\n",
" 0 2 8.2908e+09 0 5 8.2920e+09 8.2908e+09 0.01% - 0s\n",
"H 9 9 8.291298e+09 8.2908e+09 0.01% 1.4 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 2\n",
" Cover: 1\n",
" MIR: 1\n",
" Inf proof: 3\n",
" MIR: 2\n",
"\n",
"Explored 233 nodes (1577 simplex iterations) in 0.09 seconds (0.06 work units)\n",
"Explored 10 nodes (759 simplex iterations) in 0.09 seconds (0.11 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"\n",
"Solution count 7: 8.29166e+09 8.29192e+09 8.29212e+09 ... 9.75713e+09\n",
"Solution count 6: 8.2913e+09 8.29196e+09 8.29398e+09 ... 1.72969e+10\n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.291663722826e+09, best bound 8.290885027548e+09, gap 0.0094%\n",
"Best objective 8.291298126440e+09, best bound 8.290812450252e+09, gap 0.0059%\n",
"\n",
"User-callback calls 708, time in user-callback 0.00 sec\n"
"User-callback calls 910, time in user-callback 0.00 sec\n"
]
}
],
@@ -713,11 +736,14 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"Threads 1\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x19042f12\n",
"Coefficient statistics:\n",
@@ -733,15 +759,18 @@
" 0 6.5917580e+09 5.627453e+04 0.000000e+00 0s\n",
" 1 8.2535968e+09 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Solved in 1 iterations and 0.00 seconds (0.00 work units)\n",
"Optimal objective 8.253596777e+09\n",
"\n",
"User-callback calls 56, time in user-callback 0.00 sec\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"User-callback calls 59, time in user-callback 0.00 sec\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"Threads 1\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x6926c32f\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
@@ -751,44 +780,44 @@
" Bounds range [1e+00, 1e+00]\n",
" RHS range [3e+08, 3e+08]\n",
"\n",
"User MIP start produced solution with objective 8.25814e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25512e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.2551e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25508e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25508e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25499e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25448e+09 (0.02s)\n",
"User MIP start produced solution with objective 8.25448e+09 (0.02s)\n",
"User MIP start produced solution with objective 8.25989e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25699e+09 (0.05s)\n",
"User MIP start produced solution with objective 8.25678e+09 (0.05s)\n",
"User MIP start produced solution with objective 8.25668e+09 (0.05s)\n",
"User MIP start produced solution with objective 8.2554e+09 (0.05s)\n",
"User MIP start produced solution with objective 8.25448e+09 (0.05s)\n",
"User MIP start produced solution with objective 8.25448e+09 (0.05s)\n",
"Loaded user MIP start with objective 8.25448e+09\n",
"\n",
"Presolve removed 500 rows and 0 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Presolved: 501 rows, 1000 columns, 2000 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"\n",
"Root relaxation: objective 8.253597e+09, 512 iterations, 0.00 seconds (0.00 work units)\n",
"Root relaxation: objective 8.253597e+09, 501 iterations, 0.00 seconds (0.02 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 8.2536e+09 0 1 8.2545e+09 8.2536e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 3 8.2545e+09 8.2537e+09 0.01% - 0s\n",
"H 0 0 8.254435e+09 8.2536e+09 0.01% - 0s\n",
" 0 0 - 0 8.2544e+09 8.2537e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Cover: 1\n",
" Flow cover: 2\n",
" RLT: 2\n",
"\n",
"Explored 1 nodes (515 simplex iterations) in 0.03 seconds (0.02 work units)\n",
"Explored 1 nodes (503 simplex iterations) in 0.07 seconds (0.03 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"\n",
"Solution count 6: 8.25448e+09 8.25499e+09 8.25508e+09 ... 8.25814e+09\n",
"Solution count 7: 8.25443e+09 8.25448e+09 8.2554e+09 ... 8.25989e+09\n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.254479145594e+09, best bound 8.253689731796e+09, gap 0.0096%\n",
"Best objective 8.254434593504e+09, best bound 8.253676932849e+09, gap 0.0092%\n",
"\n",
"User-callback calls 203, time in user-callback 0.00 sec\n",
"obj = 8254479145.594168\n",
"User-callback calls 787, time in user-callback 0.00 sec\n",
"obj = 8254434593.503945\n",
"x = [1.0, 1.0, 0.0]\n",
"y = [935662.0949262811, 1604270.0218116897, 0.0]\n"
"y = [935662.09492646, 1604270.0218116897, 0.0]\n"
]
}
],

View File

@@ -220,12 +220,19 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Restricted license - for non-production use only - expires 2024-10-28\n",
"Set parameter Threads to value 1\n",
"Read parameters from file gurobi.env\n",
"Restricted license - for non-production use only - expires 2026-11-23\n",
"Set parameter OutputFlag to value 1\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"QCPDual 1\n",
"Threads 1\n",
"\n",
"Optimize a model with 7 rows, 6 columns and 15 nonzeros\n",
"Model fingerprint: 0x15c7a953\n",
@@ -235,25 +242,23 @@
" Objective range [2e+00, 7e+02]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [1e+02, 1e+02]\n",
"Presolve removed 2 rows and 1 columns\n",
"Presolve removed 6 rows and 3 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 5 rows, 5 columns, 13 nonzeros\n",
"Variable types: 0 continuous, 5 integer (3 binary)\n",
"Found heuristic solution: objective 1400.0000000\n",
"Presolved: 1 rows, 3 columns, 3 nonzeros\n",
"Variable types: 0 continuous, 3 integer (1 binary)\n",
"Found heuristic solution: objective 1990.0000000\n",
"\n",
"Root relaxation: objective 1.035000e+03, 3 iterations, 0.00 seconds (0.00 work units)\n",
"Root relaxation: objective 1.320000e+03, 0 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 1035.00000 0 1 1400.00000 1035.00000 26.1% - 0s\n",
" 0 0 1105.71429 0 1 1400.00000 1105.71429 21.0% - 0s\n",
"* 0 0 0 1320.0000000 1320.00000 0.00% - 0s\n",
"\n",
"Explored 1 nodes (5 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"Explored 1 nodes (0 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"\n",
"Solution count 2: 1320 1400 \n",
"Solution count 2: 1320 1990 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 1.320000000000e+03, best bound 1.320000000000e+03, gap 0.0000%\n",
@@ -483,11 +488,16 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter OutputFlag to value 1\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"QCPDual 1\n",
"Threads 1\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x5e67c6ee\n",
@@ -506,14 +516,19 @@
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.290621916e+09\n",
"Set parameter OutputFlag to value 1\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"QCPDual 1\n",
"Threads 1\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x4a7cfe2b\n",
"Model fingerprint: 0xff6a55c5\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 2e+06]\n",
@@ -521,8 +536,8 @@
" Bounds range [1e+00, 1e+00]\n",
" RHS range [3e+08, 3e+08]\n",
"\n",
"User MIP start produced solution with objective 8.29153e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.29153e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.29153e+09 (0.00s)\n",
"User MIP start produced solution with objective 8.29153e+09 (0.00s)\n",
"Loaded user MIP start with objective 8.29153e+09\n",
"\n",
"Presolve time: 0.00s\n",
@@ -537,19 +552,20 @@
" 0 0 8.2906e+09 0 1 8.2915e+09 8.2906e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 3 8.2915e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2915e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 2 8.2915e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 - 0 8.2915e+09 8.2907e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 1\n",
" Cover: 1\n",
" Flow cover: 2\n",
"\n",
"Explored 1 nodes (565 simplex iterations) in 0.04 seconds (0.01 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"Explored 1 nodes (564 simplex iterations) in 0.03 seconds (0.01 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"\n",
"Solution count 1: 8.29153e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.291528276179e+09, best bound 8.290733258025e+09, gap 0.0096%\n",
"Best objective 8.291528276179e+09, best bound 8.290729173948e+09, gap 0.0096%\n",
"WARNING: Cannot get reduced costs for MIP.\n",
"WARNING: Cannot get duals for MIP.\n"
]
@@ -557,7 +573,7 @@
{
"data": {
"text/plain": [
"{}"
"(<miplearn.solvers.pyomo.PyomoModel at 0x7fdb38952450>, {})"
]
},
"execution_count": 8,
@@ -596,11 +612,16 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter OutputFlag to value 1\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"QCPDual 1\n",
"Threads 1\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x5e67c6ee\n",
@@ -619,11 +640,16 @@
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.290621916e+09\n",
"Set parameter OutputFlag to value 1\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"QCPDual 1\n",
"Threads 1\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x8a0f9587\n",
@@ -652,22 +678,25 @@
" 0 0 8.2907e+09 0 5 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 2 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 1 8.2940e+09 8.2908e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 4 8.2940e+09 8.2908e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 4 8.2940e+09 8.2908e+09 0.04% - 0s\n",
"H 0 0 8.291465e+09 8.2908e+09 0.01% - 0s\n",
" 0 0 8.2908e+09 0 3 8.2940e+09 8.2908e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 3 8.2940e+09 8.2908e+09 0.04% - 0s\n",
" 0 2 8.2908e+09 0 3 8.2940e+09 8.2908e+09 0.04% - 0s\n",
"H 9 9 8.292471e+09 8.2908e+09 0.02% 1.3 0s\n",
"* 90 41 44 8.291525e+09 8.2908e+09 0.01% 1.5 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 2\n",
" MIR: 1\n",
" Gomory: 1\n",
" Cover: 1\n",
" MIR: 2\n",
"\n",
"Explored 1 nodes (1025 simplex iterations) in 0.12 seconds (0.03 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"Explored 91 nodes (1166 simplex iterations) in 0.06 seconds (0.05 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"\n",
"Solution count 4: 8.29147e+09 8.29398e+09 8.29827e+09 9.75713e+09 \n",
"Solution count 7: 8.29152e+09 8.29247e+09 8.29398e+09 ... 1.0319e+10\n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.291465302389e+09, best bound 8.290781665333e+09, gap 0.0082%\n",
"Best objective 8.291524908632e+09, best bound 8.290823611882e+09, gap 0.0085%\n",
"WARNING: Cannot get reduced costs for MIP.\n",
"WARNING: Cannot get duals for MIP.\n"
]
@@ -675,7 +704,7 @@
{
"data": {
"text/plain": [
"{}"
"(<miplearn.solvers.pyomo.PyomoModel at 0x7fdb2f563f50>, {})"
]
},
"execution_count": 9,
@@ -724,11 +753,16 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter OutputFlag to value 1\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"QCPDual 1\n",
"Threads 1\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x2dfe4e1c\n",
@@ -747,14 +781,19 @@
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.253596777e+09\n",
"Set parameter OutputFlag to value 1\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - \"Ubuntu 22.04.4 LTS\")\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Non-default parameters:\n",
"QCPDual 1\n",
"Threads 1\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x0f0924a1\n",
"Model fingerprint: 0xd941f1ed\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 2e+06]\n",
@@ -762,14 +801,11 @@
" Bounds range [1e+00, 1e+00]\n",
" RHS range [3e+08, 3e+08]\n",
"\n",
"User MIP start produced solution with objective 8.25814e+09 (0.00s)\n",
"User MIP start produced solution with objective 8.25814e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25512e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25483e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25483e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25483e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25459e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25459e+09 (0.01s)\n",
"Loaded user MIP start with objective 8.25459e+09\n",
"User MIP start produced solution with objective 8.25448e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25448e+09 (0.02s)\n",
"Loaded user MIP start with objective 8.25448e+09\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
@@ -780,31 +816,23 @@
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 8.2536e+09 0 1 8.2546e+09 8.2536e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 3 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 1 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 4 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 4 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2538e+09 0 4 8.2546e+09 8.2538e+09 0.01% - 0s\n",
" 0 0 8.2538e+09 0 5 8.2546e+09 8.2538e+09 0.01% - 0s\n",
" 0 0 8.2538e+09 0 6 8.2546e+09 8.2538e+09 0.01% - 0s\n",
" 0 0 8.2536e+09 0 1 8.2545e+09 8.2536e+09 0.01% - 0s\n",
" 0 0 - 0 8.2545e+09 8.2537e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Cover: 1\n",
" MIR: 2\n",
" StrongCG: 1\n",
" Flow cover: 1\n",
" Flow cover: 2\n",
"\n",
"Explored 1 nodes (575 simplex iterations) in 0.09 seconds (0.01 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"Explored 1 nodes (514 simplex iterations) in 0.03 seconds (0.01 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"\n",
"Solution count 4: 8.25459e+09 8.25483e+09 8.25512e+09 8.25814e+09 \n",
"Solution count 3: 8.25448e+09 8.25512e+09 8.25814e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.254590409970e+09, best bound 8.253768093811e+09, gap 0.0100%\n",
"Best objective 8.254479145594e+09, best bound 8.253676932849e+09, gap 0.0097%\n",
"WARNING: Cannot get reduced costs for MIP.\n",
"WARNING: Cannot get duals for MIP.\n",
"obj = 8254590409.96973\n",
"obj = 8254479145.594172\n",
" x = [1.0, 1.0, 0.0, 1.0, 1.0]\n",
" y = [935662.0949262811, 1604270.0218116897, 0.0, 1369560.835229226, 602828.5321028307]\n"
]

View File

@@ -17,9 +17,15 @@ from ..parallel import p_umap
class BasicCollector:
def __init__(self, skip_lp: bool = False, write_mps: bool = True) -> None:
def __init__(
self,
skip_lp: bool = False,
write_mps: bool = True,
write_log: bool = True,
) -> None:
self.skip_lp = skip_lp
self.write_mps = write_mps
self.write_log = write_log
def collect(
self,
@@ -33,6 +39,7 @@ class BasicCollector:
try:
h5_filename = _to_h5_filename(data_filename)
mps_filename = h5_filename.replace(".h5", ".mps")
log_filename = h5_filename.replace(".h5", ".h5.log")
if exists(h5_filename):
# Try to read optimal solution
@@ -50,6 +57,7 @@ class BasicCollector:
return
with H5File(h5_filename, "w") as h5:
h5.put_scalar("data_filename", data_filename)
streams: List[Any] = [StringIO()]
if verbose:
streams += [sys.stdout]
@@ -76,7 +84,11 @@ class BasicCollector:
model.write(mps_filename)
gzip(mps_filename)
h5.put_scalar("mip_log", streams[0].getvalue())
log = streams[0].getvalue()
h5.put_scalar("mip_log", log)
if self.write_log:
with open(log_filename, "w") as log_file:
log_file.write(log)
except:
print(f"Error processing: data_filename")
traceback.print_exc()

163
miplearn/problems/maxcut.py Normal file
View File

@@ -0,0 +1,163 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020-2025, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
from dataclasses import dataclass
from typing import List, Union, Optional, Any
import gurobipy as gp
import networkx as nx
import numpy as np
import pyomo.environ as pe
from networkx import Graph
from scipy.stats.distributions import rv_frozen
from miplearn.io import read_pkl_gz
from miplearn.problems import _gurobipy_set_params, _pyomo_set_params
from miplearn.solvers.gurobi import GurobiModel
from miplearn.solvers.pyomo import PyomoModel
@dataclass
class MaxCutData:
graph: Graph
weights: np.ndarray
class MaxCutGenerator:
"""
Random instance generator for the Maximum Cut Problem.
The generator operates in two modes. When `fix_graph=True`, a single random Erdős-Rényi graph $G_{n,
p}$ is generated during initialization, with parameters $n$ and $p$ drawn from their respective probability
distributions, and each edge is assigned a random weight drawn from the set {-1, 1}, with equal probability. To
generate each instance variation, the generator randomly flips the sign of each edge weight with probability
`w_jitter`. The graph remains the same across all variations.
When `fix_graph=False`, a new random graph is generated for each instance, with random {-1,1} edge weights.
"""
def __init__(
self,
n: rv_frozen,
p: rv_frozen,
w_jitter: float = 0.0,
fix_graph: bool = False,
):
"""
Initialize the problem generator.
Parameters
----------
n: rv_discrete
Probability distribution for the number of nodes.
p: rv_continuous
Probability distribution for the graph density.
w_jitter: float
Probability that each edge weight flips from -1 to 1. Only applicable if fix_graph is True.
fix_graph: bool
Controls graph generation for instances. If false, a new random graph is
generated for each instance. If true, the same graph is reused across instances.
"""
assert isinstance(n, rv_frozen), "n should be a SciPy probability distribution"
assert isinstance(p, rv_frozen), "p should be a SciPy probability distribution"
self.n = n
self.p = p
self.w_jitter = w_jitter
self.fix_graph = fix_graph
self.graph = None
self.weights = None
if fix_graph:
self.graph = self._generate_graph()
self.weights = self._generate_weights(self.graph)
def generate(self, n_samples: int) -> List[MaxCutData]:
def _sample() -> MaxCutData:
if self.graph is not None:
graph = self.graph
weights = self.weights
jitter = self._generate_jitter(graph)
weights = weights * jitter
else:
graph = self._generate_graph()
weights = self._generate_weights(graph)
return MaxCutData(graph, weights)
return [_sample() for _ in range(n_samples)]
def _generate_graph(self) -> Graph:
return nx.generators.random_graphs.binomial_graph(self.n.rvs(), self.p.rvs())
@staticmethod
def _generate_weights(graph: Graph) -> np.ndarray:
m = graph.number_of_edges()
return np.random.randint(2, size=(m,)) * 2 - 1
def _generate_jitter(self, graph: Graph) -> np.ndarray:
m = graph.number_of_edges()
return (np.random.rand(m) >= self.w_jitter).astype(int) * 2 - 1
def build_maxcut_model_gurobipy(
data: Union[str, MaxCutData],
params: Optional[dict[str, Any]] = None,
) -> GurobiModel:
# Initialize model
model = gp.Model()
_gurobipy_set_params(model, params)
# Read data
data = _maxcut_read(data)
nodes = list(data.graph.nodes())
edges = list(data.graph.edges())
# Add decision variables
x = model.addVars(nodes, vtype=gp.GRB.BINARY, name="x")
# Add the objective function
model.setObjective(
gp.quicksum(
-data.weights[i] * x[e[0]] * (1 - x[e[1]]) for (i, e) in enumerate(edges)
)
)
model.update()
return GurobiModel(model)
def build_maxcut_model_pyomo(
data: Union[str, MaxCutData],
solver: str = "gurobi_persistent",
params: Optional[dict[str, Any]] = None,
) -> PyomoModel:
# Initialize model
model = pe.ConcreteModel()
# Read data
data = _maxcut_read(data)
nodes = pe.Set(initialize=list(data.graph.nodes))
edges = list(data.graph.edges())
# Add decision variables
model.x = pe.Var(nodes, domain=pe.Binary, name="x")
# Add the objective function
model.obj = pe.Objective(
expr=pe.quicksum(
-data.weights[i] * model.x[e[0]]
+ data.weights[i] * model.x[e[0]] * model.x[e[1]]
for (i, e) in enumerate(edges)
),
sense=pe.minimize,
)
model.pprint()
pm = PyomoModel(model, solver)
_pyomo_set_params(model, params, solver)
return pm
def _maxcut_read(data: Union[str, MaxCutData]) -> MaxCutData:
if isinstance(data, str):
data = read_pkl_gz(data)
assert isinstance(data, MaxCutData)
return data

View File

@@ -25,6 +25,7 @@ class UnitCommitmentData:
min_downtime: np.ndarray
cost_startup: np.ndarray
cost_prod: np.ndarray
cost_prod_quad: np.ndarray
cost_fixed: np.ndarray
@@ -37,6 +38,7 @@ class UnitCommitmentGenerator:
min_power: rv_frozen = uniform(loc=0.5, scale=0.25),
cost_startup: rv_frozen = uniform(loc=0, scale=10_000),
cost_prod: rv_frozen = uniform(loc=0, scale=50),
cost_prod_quad: rv_frozen = uniform(loc=0, scale=0),
cost_fixed: rv_frozen = uniform(loc=0, scale=1_000),
min_uptime: rv_frozen = randint(low=2, high=8),
min_downtime: rv_frozen = randint(low=2, high=8),
@@ -50,6 +52,7 @@ class UnitCommitmentGenerator:
self.min_power = min_power
self.cost_startup = cost_startup
self.cost_prod = cost_prod
self.cost_prod_quad = cost_prod_quad
self.cost_fixed = cost_fixed
self.min_uptime = min_uptime
self.min_downtime = min_downtime
@@ -72,6 +75,7 @@ class UnitCommitmentGenerator:
min_downtime = self.min_downtime.rvs(G)
cost_startup = self.cost_startup.rvs(G)
cost_prod = self.cost_prod.rvs(G)
cost_prod_quad = self.cost_prod_quad.rvs(G)
cost_fixed = self.cost_fixed.rvs(G)
capacity = max_power.sum()
@@ -91,6 +95,7 @@ class UnitCommitmentGenerator:
min_downtime = self.ref_data.min_downtime
cost_startup = self.ref_data.cost_startup * self.cost_jitter.rvs(G)
cost_prod = self.ref_data.cost_prod * self.cost_jitter.rvs(G)
cost_prod_quad = self.ref_data.cost_prod_quad * self.cost_jitter.rvs(G)
cost_fixed = self.ref_data.cost_fixed * self.cost_jitter.rvs(G)
data = UnitCommitmentData(
@@ -101,6 +106,7 @@ class UnitCommitmentGenerator:
min_downtime,
cost_startup.round(2),
cost_prod.round(2),
cost_prod_quad.round(4),
cost_fixed.round(2),
)
@@ -143,6 +149,7 @@ def build_uc_model_gurobipy(data: Union[str, UnitCommitmentData]) -> GurobiModel
is_on[g, t] * data.cost_fixed[g]
+ switch_on[g, t] * data.cost_startup[g]
+ prod[g, t] * data.cost_prod[g]
+ prod[g, t] * prod[g, t] * data.cost_prod_quad[g]
for g in range(G)
for t in range(T)
)

View File

@@ -264,6 +264,13 @@ class GurobiModel(AbstractModel):
h5.put_array(
h5_field, np.array(self.inner.getAttr(gp_field, gp_vars), dtype=float)
)
obj = self.inner.getObjective()
if isinstance(obj, gp.QuadExpr):
nvars = len(self.inner.getVars())
obj_q = np.zeros((nvars, nvars))
for i in range(obj.size()):
obj_q[obj.getVar1(i).index, obj.getVar2(i).index] = obj.getCoeff(i)
h5.put_array("static_var_obj_coeffs_quad", obj_q)
def _extract_after_load_constrs(self, h5: H5File) -> None:
gp_constrs = self.inner.getConstrs()

View File

@@ -8,7 +8,8 @@ import numpy as np
import pyomo
import pyomo.environ as pe
from pyomo.core import Objective, Var, Suffix
from pyomo.core.base import _GeneralVarData
from pyomo.core.base import VarData
from pyomo.core.expr import ProductExpression
from pyomo.core.expr.numeric_expr import SumExpression, MonomialTermExpression
from scipy.sparse import coo_matrix
@@ -207,19 +208,23 @@ class PyomoModel(AbstractModel):
lower_bounds: List[float] = []
obj_coeffs: List[float] = []
obj = None
obj_quad, obj_linear = None, None
obj_offset = 0.0
obj_count = 0
for obj in self.inner.component_objects(Objective):
obj, obj_offset = self._parse_pyomo_expr(obj.expr)
obj_quad, obj_linear, obj_offset = self._parse_obj_expr(obj.expr)
obj_count += 1
assert obj_count == 1, f"One objective function expected; found {obj_count}"
assert obj_quad is not None
assert obj_linear is not None
varname_to_idx: Dict[str, int] = {}
for i, var in enumerate(self.inner.component_objects(pyomo.core.Var)):
for idx in var:
v = var[idx]
# Variable name
varname_to_idx[v.name] = len(names)
if idx is None:
names.append(var.name)
else:
@@ -249,11 +254,22 @@ class PyomoModel(AbstractModel):
lower_bounds.append(float(lb))
# Objective coefficients
if v.name in obj:
obj_coeffs.append(obj[v.name])
if v.name in obj_linear:
obj_coeffs.append(obj_linear[v.name])
else:
obj_coeffs.append(0.0)
if len(obj_quad) > 0:
nvars = len(names)
matrix = np.zeros((nvars, nvars))
for ((left_varname, right_varname), coeff) in obj_quad.items():
assert left_varname in varname_to_idx
assert right_varname in varname_to_idx
left_idx = varname_to_idx[left_varname]
right_idx = varname_to_idx[right_varname]
matrix[left_idx, right_idx] = coeff
h5.put_array("static_var_obj_coeffs_quad", matrix)
h5.put_array("static_var_names", np.array(names, dtype="S"))
h5.put_array("static_var_types", np.array(types, dtype="S"))
h5.put_array("static_var_lower_bounds", np.array(lower_bounds))
@@ -302,13 +318,13 @@ class PyomoModel(AbstractModel):
lhs_row.append(row)
lhs_col.append(varname_to_idx[term._args_[1].name])
lhs_data.append(float(term._args_[0]))
elif isinstance(term, _GeneralVarData):
elif isinstance(term, VarData):
lhs_row.append(row)
lhs_col.append(varname_to_idx[term.name])
lhs_data.append(1.0)
else:
raise Exception(f"Unknown term type: {term.__class__.__name__}")
elif isinstance(expr, _GeneralVarData):
elif isinstance(expr, VarData):
lhs_row.append(row)
lhs_col.append(varname_to_idx[expr.name])
lhs_data.append(1.0)
@@ -327,8 +343,9 @@ class PyomoModel(AbstractModel):
_parse_constraint(constr, curr_row)
curr_row += 1
lhs = coo_matrix((lhs_data, (lhs_row, lhs_col))).tocoo()
h5.put_sparse("static_constr_lhs", lhs)
if len(lhs_data) > 0:
lhs = coo_matrix((lhs_data, (lhs_row, lhs_col))).tocoo()
h5.put_sparse("static_constr_lhs", lhs)
h5.put_array("static_constr_names", np.array(names, dtype="S"))
h5.put_array("static_constr_rhs", np.array(rhs))
h5.put_array("static_constr_sense", np.array(senses, dtype="S"))
@@ -372,24 +389,47 @@ class PyomoModel(AbstractModel):
slacks.append(abs(self.inner.slack[c]))
h5.put_array("mip_constr_slacks", np.array(slacks))
def _parse_pyomo_expr(self, expr: Any) -> Tuple[Dict[str, float], float]:
lhs = {}
offset = 0.0
def _parse_term(self, t: Any) -> Tuple[str, float]:
if isinstance(t, MonomialTermExpression):
return t._args_[1].name, float(t._args_[0])
elif isinstance(t, VarData):
return t.name, 1.0
else:
raise Exception(f"Unknown term type: {t.__class__.__name__}")
def _parse_obj_expr(
self, expr: Any
) -> Tuple[Dict[Tuple[str, str], float], Dict[str, float], float]:
obj_coeff_linear = {}
obj_coeff_quadratic = {}
obj_offset = 0.0
if isinstance(expr, SumExpression):
for term in expr._args_:
if isinstance(term, MonomialTermExpression):
lhs[term._args_[1].name] = float(term._args_[0])
elif isinstance(term, _GeneralVarData):
lhs[term.name] = 1.0
elif isinstance(term, float):
offset += term
if isinstance(term, (int, float)):
# Constant term
obj_offset += term
elif isinstance(term, (MonomialTermExpression, VarData)):
# Linear term
var_name, var_coeff = self._parse_term(term)
if var_name not in obj_coeff_linear:
obj_coeff_linear[var_name] = 0.0
obj_coeff_linear[var_name] += var_coeff
elif isinstance(term, ProductExpression):
# Quadratic terms
left_var_nane, left_coeff = self._parse_term(term._args_[0])
right_var_nane, right_coeff = self._parse_term(term._args_[1])
if (left_var_nane, right_var_nane) not in obj_coeff_quadratic:
obj_coeff_quadratic[(left_var_nane, right_var_nane)] = 0.0
obj_coeff_quadratic[(left_var_nane, right_var_nane)] += (
left_coeff * right_coeff
)
else:
raise Exception(f"Unknown term type: {term.__class__.__name__}")
elif isinstance(expr, _GeneralVarData):
lhs[expr.name] = 1.0
elif isinstance(expr, VarData):
obj_coeff_linear[expr.name] = 1.0
else:
raise Exception(f"Unknown expression type: {expr.__class__.__name__}")
return lhs, offset
return obj_coeff_quadratic, obj_coeff_linear, obj_offset
def _gap(self, zp: float, zd: float, tol: float = 1e-6) -> float:
# Reference: https://www.gurobi.com/documentation/9.5/refman/mipgap2.html

View File

@@ -14,12 +14,11 @@ setup(
packages=find_namespace_packages(),
python_requires=">=3.9",
install_requires=[
"Jinja2<3.1",
"gurobipy>=12,<13",
"h5py>=3,<4",
"networkx>=2,<3",
"numpy>=1,<2",
"pandas>=1,<2",
"pandas>=2,<3",
"pathos>=0.2,<0.3",
"pyomo>=6,<7",
"scikit-learn>=1,<2",
@@ -28,21 +27,17 @@ setup(
],
extras_require={
"dev": [
"Sphinx>=3,<4",
"Sphinx>=8,<9",
"black==22.6.0",
"mypy==1.8",
"myst-parser==0.14.0",
"myst-parser>=4,<5",
"nbsphinx>=0.9,<0.10",
"pyflakes==2.5.0",
"pytest>=7,<8",
"sphinx-book-theme==0.1.0",
"sphinxcontrib-applehelp==1.0.4",
"sphinxcontrib-devhelp==1.0.2",
"sphinxcontrib-htmlhelp==2.0.1",
"sphinxcontrib-serializinghtml==1.1.5",
"sphinxcontrib-qthelp==1.0.3",
"sphinx-multitoc-numbering>=0.1,<0.2",
"sphinx-book-theme>=1,<2",
"sphinx-multitoc-numbering==0.1.3",
"twine>=6,<7",
"ipython>=9,<10",
]
},
)

View File

@@ -0,0 +1,119 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020-2025, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
import random
from tempfile import TemporaryDirectory
import numpy as np
from scipy.stats import randint, uniform
from miplearn.h5 import H5File
from miplearn.problems.maxcut import (
MaxCutGenerator,
build_maxcut_model_gurobipy,
build_maxcut_model_pyomo,
)
def _set_seed():
random.seed(42)
np.random.seed(42)
def test_maxcut_generator_not_fixed() -> None:
_set_seed()
gen = MaxCutGenerator(
n=randint(low=5, high=6),
p=uniform(loc=0.5, scale=0.0),
fix_graph=False,
)
data = gen.generate(3)
assert len(data) == 3
assert list(data[0].graph.nodes()) == [0, 1, 2, 3, 4]
assert list(data[0].graph.edges()) == [
(0, 2),
(0, 3),
(0, 4),
(2, 3),
(2, 4),
(3, 4),
]
assert data[0].weights.tolist() == [-1, 1, -1, -1, -1, 1]
assert list(data[1].graph.nodes()) == [0, 1, 2, 3, 4]
assert list(data[1].graph.edges()) == [(0, 1), (0, 3), (0, 4), (1, 4), (3, 4)]
assert data[1].weights.tolist() == [-1, -1, -1, 1, -1]
def test_maxcut_generator_fixed() -> None:
random.seed(42)
np.random.seed(42)
gen = MaxCutGenerator(
n=randint(low=5, high=6),
p=uniform(loc=0.5, scale=0.0),
fix_graph=True,
w_jitter=0.25,
)
data = gen.generate(3)
assert len(data) == 3
for i in range(3):
assert list(data[i].graph.nodes()) == [0, 1, 2, 3, 4]
assert list(data[i].graph.edges()) == [
(0, 2),
(0, 3),
(0, 4),
(2, 3),
(2, 4),
(3, 4),
]
assert data[0].weights.tolist() == [-1, -1, 1, 1, -1, 1]
assert data[1].weights.tolist() == [-1, -1, -1, -1, 1, -1]
assert data[2].weights.tolist() == [1, 1, -1, -1, -1, 1]
def test_maxcut_model():
_set_seed()
data = MaxCutGenerator(
n=randint(low=10, high=11),
p=uniform(loc=0.5, scale=0.0),
fix_graph=True,
).generate(1)[0]
for build_model in [
build_maxcut_model_gurobipy,
build_maxcut_model_pyomo,
]:
model = build_model(data)
with TemporaryDirectory() as tempdir:
with H5File(f"{tempdir}/data.h5", "w") as h5:
model.extract_after_load(h5)
obj_lin = h5.get_array("static_var_obj_coeffs")
assert obj_lin is not None
assert obj_lin.tolist() == [
3.0,
1.0,
3.0,
1.0,
-1.0,
0.0,
-1.0,
0.0,
-1.0,
0.0,
]
obj_quad = h5.get_array("static_var_obj_coeffs_quad")
assert obj_quad is not None
assert obj_quad.tolist() == [
[0.0, 0.0, -1.0, 1.0, -1.0, 0.0, 0.0, 0.0, -1.0, -1.0],
[0.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0, 0.0, 1.0],
[0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, -1.0],
[0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 1.0, -1.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, -1.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
]
model.optimize()
model.extract_after_mip(h5)
assert h5.get_scalar("mip_obj_value") == -4

View File

@@ -2,7 +2,7 @@
# Copyright (C) 2020-2022, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
from tempfile import NamedTemporaryFile
from tempfile import TemporaryDirectory
import numpy as np
from scipy.stats import randint, uniform
@@ -86,8 +86,8 @@ def test_set_cover() -> None:
build_setcover_model_gurobipy(data),
]:
assert isinstance(model, AbstractModel)
with NamedTemporaryFile() as tempfile:
with H5File(tempfile.name) as h5:
with TemporaryDirectory() as tempdir:
with H5File(f"{tempdir}/data.h5", "w") as h5:
model.optimize()
model.extract_after_mip(h5)
assert h5.get_scalar("mip_obj_value") == 11.0

View File

@@ -1,7 +1,7 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020-2022, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
from tempfile import NamedTemporaryFile
from tempfile import TemporaryDirectory
import networkx as nx
import numpy as np
@@ -25,8 +25,8 @@ def test_stab() -> None:
build_stab_model_pyomo(data),
]:
assert isinstance(model, AbstractModel)
with NamedTemporaryFile() as tempfile:
with H5File(tempfile.name) as h5:
with TemporaryDirectory() as tempdir:
with H5File(f"{tempdir}/data.h5", "w") as h5:
model.optimize()
model.extract_after_mip(h5)
assert h5.get_scalar("mip_obj_value") == -2.0

View File

@@ -1,7 +1,7 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020-2022, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
from tempfile import NamedTemporaryFile
from tempfile import TemporaryDirectory
from typing import Any
import numpy as np
@@ -11,31 +11,31 @@ from miplearn.h5 import H5File
def test_h5() -> None:
file = NamedTemporaryFile()
h5 = H5File(file.name)
_assert_roundtrip_scalar(h5, "A")
_assert_roundtrip_scalar(h5, True)
_assert_roundtrip_scalar(h5, 1)
_assert_roundtrip_scalar(h5, 1.0)
assert h5.get_scalar("unknown-key") is None
with TemporaryDirectory() as tempdir:
with H5File(f"{tempdir}/data.h5", "w") as h5:
_assert_roundtrip_scalar(h5, "A")
_assert_roundtrip_scalar(h5, True)
_assert_roundtrip_scalar(h5, 1)
_assert_roundtrip_scalar(h5, 1.0)
assert h5.get_scalar("unknown-key") is None
_assert_roundtrip_array(h5, np.array([True, False]))
_assert_roundtrip_array(h5, np.array([1, 2, 3]))
_assert_roundtrip_array(h5, np.array([1.0, 2.0, 3.0]))
_assert_roundtrip_array(h5, np.array(["A", "BB", "CCC"], dtype="S"))
assert h5.get_array("unknown-key") is None
_assert_roundtrip_array(h5, np.array([True, False]))
_assert_roundtrip_array(h5, np.array([1, 2, 3]))
_assert_roundtrip_array(h5, np.array([1.0, 2.0, 3.0]))
_assert_roundtrip_array(h5, np.array(["A", "BB", "CCC"], dtype="S"))
assert h5.get_array("unknown-key") is None
_assert_roundtrip_sparse(
h5,
coo_matrix(
[
[1.0, 0.0, 0.0],
[0.0, 2.0, 3.0],
[0.0, 0.0, 4.0],
],
),
)
assert h5.get_sparse("unknown-key") is None
_assert_roundtrip_sparse(
h5,
coo_matrix(
[
[1.0, 0.0, 0.0],
[0.0, 2.0, 3.0],
[0.0, 0.0, 4.0],
],
),
)
assert h5.get_sparse("unknown-key") is None
def _assert_roundtrip_array(h5: H5File, original: np.ndarray) -> None:

View File

@@ -2,7 +2,7 @@
# Copyright (C) 2020-2022, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
from tempfile import NamedTemporaryFile
from tempfile import TemporaryDirectory
from typing import Callable, Any
import numpy as np
@@ -49,8 +49,8 @@ def _test_solver(build_model: Callable, data: Any) -> None:
def _test_extract(model: AbstractModel) -> None:
with NamedTemporaryFile() as tempfile:
with H5File(tempfile.name) as h5:
with TemporaryDirectory() as tempdir:
with H5File(f"{tempdir}/data.h5", "w") as h5:
def test_scalar(key: str, expected_value: Any) -> None:
actual_value = h5.get_scalar(key)
@@ -129,7 +129,6 @@ def _test_extract(model: AbstractModel) -> None:
test_scalar("mip_obj_value", 11.0)
mip_wallclock_time = h5.get_scalar("mip_wallclock_time")
assert mip_wallclock_time is not None
assert mip_wallclock_time > 0
if model._supports_node_count:
count = h5.get_scalar("mip_node_count")
assert count is not None
@@ -145,8 +144,8 @@ def _test_extract(model: AbstractModel) -> None:
def _test_add_constr(model: AbstractModel) -> None:
with NamedTemporaryFile() as tempfile:
with H5File(tempfile.name) as h5:
with TemporaryDirectory() as tempdir:
with H5File(f"{tempdir}/data.h5", "w") as h5:
model.add_constrs(
np.array([b"x[2]", b"x[3]"], dtype="S"),
np.array([[0, 1], [1, 0]]),
@@ -161,8 +160,8 @@ def _test_add_constr(model: AbstractModel) -> None:
def _test_fix_vars(model: AbstractModel) -> None:
with NamedTemporaryFile() as tempfile:
with H5File(tempfile.name) as h5:
with TemporaryDirectory() as tempdir:
with H5File(f"{tempdir}/data.h5", "w") as h5:
model.fix_variables(
var_names=np.array([b"x[2]", b"x[3]"], dtype="S"),
var_values=np.array([0, 0]),
@@ -175,8 +174,8 @@ def _test_fix_vars(model: AbstractModel) -> None:
def _test_infeasible(model: AbstractModel) -> None:
with NamedTemporaryFile() as tempfile:
with H5File(tempfile.name) as h5:
with TemporaryDirectory() as tempdir:
with H5File(f"{tempdir}/data.h5", "w") as h5:
model.fix_variables(
var_names=np.array([b"x[0]", b"x[3]"], dtype="S"),
var_values=np.array([0, 0]),