mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 09:28:51 -06:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aa291410d8 | |||
| ca05429203 | |||
| 4eeb1c1ab3 | |||
| bfaae7c005 | |||
| 596f41c477 | |||
| 19e1f52b4f | |||
| 7ed213d4ce | |||
| daa801b5e9 | |||
| 2ca2794457 | |||
| 1c6912cc51 | |||
| eb914a4bdd | |||
| a306f0df26 | |||
| e0b4181579 | |||
| 332b2b9fca | |||
| af65069202 | |||
| dadd2216f1 | |||
| 5fefb49566 | |||
| 3775c3f780 | |||
| e66e6d7660 | |||
| 8e05a69351 | |||
| 7ccb7875b9 | |||
|
f085ab538b
|
|||
|
7f273ebb70
|
|||
|
26cfab0ebd
|
|||
| 52ed34784d | |||
|
0534d50af3
|
|||
| 8a02e22a35 |
49
CHANGELOG.md
49
CHANGELOG.md
@@ -3,7 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
and this project adheres to
|
||||
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.4.3] - 2025-05-10
|
||||
|
||||
## Changed
|
||||
|
||||
- Update dependency: Gurobi 12
|
||||
|
||||
## [0.4.2] - 2024-12-10
|
||||
|
||||
## Changed
|
||||
|
||||
- H5File: Use float64 precision instead of float32
|
||||
- LearningSolver: optimize now returns (model, stats) instead of just stats
|
||||
- Update dependency: Gurobi 11
|
||||
|
||||
## [0.4.0] - 2024-02-06
|
||||
|
||||
@@ -15,31 +30,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Changed
|
||||
|
||||
- LearningSolver.solve no longer generates HDF5 files; use a collector instead.
|
||||
- Add `_gurobipy` suffix to all `build_model` functions; implement some `_pyomo` and `_jump` functions.
|
||||
- Add `_gurobipy` suffix to all `build_model` functions; implement some `_pyomo`
|
||||
and `_jump` functions.
|
||||
|
||||
## [0.3.0] - 2023-06-08
|
||||
|
||||
This is a complete rewrite of the original prototype package, with an entirely new API, focused on performance, scalability and flexibility.
|
||||
This is a complete rewrite of the original prototype package, with an entirely
|
||||
new API, focused on performance, scalability and flexibility.
|
||||
|
||||
### Added
|
||||
|
||||
- Add support for Python/Gurobipy and Julia/JuMP, in addition to the existing Python/Pyomo interface.
|
||||
- Add six new random instance generators (bin packing, capacitated p-median, set cover, set packing, unit commitment, vertex cover), in addition to the three existing generators (multiknapsack, stable set, tsp).
|
||||
- Collect some additional raw training data (e.g. basis status, reduced costs, etc)
|
||||
- Add new primal solution ML strategies (memorizing, independent vars and joint vars)
|
||||
- Add new primal solution actions (set warm start, fix variables, enforce proximity)
|
||||
- Add support for Python/Gurobipy and Julia/JuMP, in addition to the existing
|
||||
Python/Pyomo interface.
|
||||
- Add six new random instance generators (bin packing, capacitated p-median, set
|
||||
cover, set packing, unit commitment, vertex cover), in addition to the three
|
||||
existing generators (multiknapsack, stable set, tsp).
|
||||
- Collect some additional raw training data (e.g. basis status, reduced costs,
|
||||
etc)
|
||||
- Add new primal solution ML strategies (memorizing, independent vars and joint
|
||||
vars)
|
||||
- Add new primal solution actions (set warm start, fix variables, enforce
|
||||
proximity)
|
||||
- Add runnable tutorials and user guides to the documentation.
|
||||
|
||||
### Changed
|
||||
|
||||
- To support large-scale problems and datasets, switch from an in-memory architecture to a file-based architecture, using HDF5 files.
|
||||
- To accelerate development cycle, split training data collection from feature extraction.
|
||||
- To support large-scale problems and datasets, switch from an in-memory
|
||||
architecture to a file-based architecture, using HDF5 files.
|
||||
- To accelerate development cycle, split training data collection from feature
|
||||
extraction.
|
||||
|
||||
### Removed
|
||||
|
||||
- Temporarily remove ML strategies for lazy constraints
|
||||
- Remove benchmarks from documentation. These will be published in a separate paper.
|
||||
|
||||
- Remove benchmarks from documentation. These will be published in a separate
|
||||
paper.
|
||||
|
||||
## [0.1.0] - 2020-11-23
|
||||
|
||||
|
||||
4
Makefile
4
Makefile
@@ -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/*
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
10
docs/_static/custom.css
vendored
10
docs/_static/custom.css
vendored
@@ -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;
|
||||
}
|
||||
@@ -55,3 +55,9 @@ miplearn.problems.vertexcover
|
||||
.. automodule:: miplearn.problems.vertexcover
|
||||
:members:
|
||||
|
||||
miplearn.problems.maxcut
|
||||
-----------------------------
|
||||
|
||||
.. automodule:: miplearn.problems.maxcut
|
||||
:members:
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"\n",
|
||||
"Benchmark sets such as [MIPLIB](https://miplib.zib.de/) or [TSPLIB](http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/) are usually employed to evaluate the performance of conventional MIP solvers. Two shortcomings, however, make existing benchmark sets less suitable for evaluating the performance of learning-enhanced MIP solvers: (i) while existing benchmark sets typically contain hundreds or thousands of instances, machine learning (ML) methods typically benefit from having orders of magnitude more instances available for training; (ii) current machine learning methods typically provide best performance on sets of homogeneous instances, buch general-purpose benchmark sets contain relatively few examples of each problem type.\n",
|
||||
"\n",
|
||||
"To tackle this challenge, MIPLearn provides random instance generators for a wide variety of classical optimization problems, covering applications from different fields, that can be used to evaluate new learning-enhanced MIP techniques in a measurable and reproducible way. As of MIPLearn 0.3, nine problem generators are available, each customizable with user-provided probability distribution and flexible parameters. The generators can be configured, for example, to produce large sets of very similar instances of same size, where only the objective function changes, or more diverse sets of instances, with various sizes and characteristics, belonging to a particular problem class.\n",
|
||||
"To tackle this challenge, MIPLearn provides random instance generators for a wide variety of classical optimization problems, covering applications from different fields, that can be used to evaluate new learning-enhanced MIP techniques in a measurable and reproducible way. Nine problem generators are available, each customizable with user-provided probability distribution and flexible parameters. The generators can be configured, for example, to produce large sets of very similar instances of same size, where only the objective function changes, or more diverse sets of instances, with various sizes and characteristics, belonging to a particular problem class.\n",
|
||||
"\n",
|
||||
"In the following, we describe the problems included in the library, their MIP formulation and the generation algorithm."
|
||||
]
|
||||
@@ -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,
|
||||
|
||||
@@ -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);"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -61,7 +61,7 @@ Citing MIPLearn
|
||||
|
||||
If you use MIPLearn in your research (either the solver or the included problem generators), we kindly request that you cite the package as follows:
|
||||
|
||||
* **Alinson S. Xavier, Feng Qiu, Xiaoyi Gu, Berkay Becu, Santanu S. Dey.** *MIPLearn: An Extensible Framework for Learning-Enhanced Optimization (Version 0.3)*. Zenodo (2023). DOI: https://doi.org/10.5281/zenodo.4287567
|
||||
* **Alinson S. Xavier, Feng Qiu, Xiaoyi Gu, Berkay Becu, Santanu S. Dey.** *MIPLearn: An Extensible Framework for Learning-Enhanced Optimization (Version 0.4)*. Zenodo (2024). DOI: https://doi.org/10.5281/zenodo.4287567
|
||||
|
||||
If you use MIPLearn in the field of power systems optimization, we kindly request that you cite the reference below, in which the main techniques implemented in MIPLearn were first developed:
|
||||
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -45,16 +45,10 @@
|
||||
"- Python version, compatible with the Pyomo and Gurobipy modeling languages,\n",
|
||||
"- Julia version, compatible with the JuMP modeling language.\n",
|
||||
"\n",
|
||||
"In this tutorial, we will demonstrate how to use and install the Python/Gurobipy version of the package. The first step is to install Python 3.8+ in your computer. See the [official Python website for more instructions](https://www.python.org/downloads/). After Python is installed, we proceed to install MIPLearn using `pip`:\n",
|
||||
"In this tutorial, we will demonstrate how to use and install the Python/Gurobipy version of the package. The first step is to install Python 3.9+ in your computer. See the [official Python website for more instructions](https://www.python.org/downloads/). After Python is installed, we proceed to install MIPLearn using `pip`:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"$ pip install MIPLearn==0.3\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"In addition to MIPLearn itself, we will also install Gurobi 10.0, a state-of-the-art commercial MILP solver. This step also install a demo license for Gurobi, which should able to solve the small optimization problems in this tutorial. A license is required for solving larger-scale problems.\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"$ pip install 'gurobipy>=10,<10.1'\n",
|
||||
"$ pip install MIPLearn~=0.4\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
@@ -220,11 +214,16 @@
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"Threads 1\n",
|
||||
"\n",
|
||||
"Optimize a model with 7 rows, 6 columns and 15 nonzeros\n",
|
||||
"Model fingerprint: 0x58dfdd53\n",
|
||||
@@ -234,28 +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",
|
||||
"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",
|
||||
"\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"
|
||||
@@ -401,7 +400,7 @@
|
||||
"from miplearn.collectors.basic import BasicCollector\n",
|
||||
"\n",
|
||||
"bc = BasicCollector()\n",
|
||||
"bc.collect(train_data, build_uc_model, n_jobs=4)"
|
||||
"bc.collect(train_data, build_uc_model)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -480,10 +479,13 @@
|
||||
"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 20 threads\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",
|
||||
@@ -493,22 +495,27 @@
|
||||
" Bounds range [1e+00, 1e+00]\n",
|
||||
" RHS range [3e+08, 3e+08]\n",
|
||||
"Presolve removed 1000 rows and 500 columns\n",
|
||||
"Presolve time: 0.01s\n",
|
||||
"Presolve time: 0.00s\n",
|
||||
"Presolved: 1 rows, 500 columns, 500 nonzeros\n",
|
||||
"\n",
|
||||
"Iteration Objective Primal Inf. Dual Inf. Time\n",
|
||||
" 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.01 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",
|
||||
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
|
||||
"\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 20 threads\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: 0xcf27855a\n",
|
||||
"Model fingerprint: 0x892e56b2\n",
|
||||
"Variable types: 500 continuous, 500 integer (500 binary)\n",
|
||||
"Coefficient statistics:\n",
|
||||
" Matrix range [1e+00, 2e+06]\n",
|
||||
@@ -516,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",
|
||||
@@ -532,25 +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.03 seconds (0.01 work units)\n",
|
||||
"Thread count was 20 (of 20 available processors)\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 799, time in user-callback 0.00 sec\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'WS: Count': 1, 'WS: Number of variables set': 482.0}"
|
||||
"(<miplearn.solvers.gurobi.GurobiModel at 0x7f2bcd72cfd0>,\n",
|
||||
" {'WS: Count': 1, 'WS: Number of variables set': 477.0})"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
@@ -589,10 +603,13 @@
|
||||
"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 20 threads\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",
|
||||
@@ -611,10 +628,15 @@
|
||||
"\n",
|
||||
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
|
||||
"Optimal objective 8.290621916e+09\n",
|
||||
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
|
||||
"\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 20 threads\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",
|
||||
@@ -624,58 +646,59 @@
|
||||
" 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.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.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",
|
||||
" MIR: 1\n",
|
||||
" MIR: 2\n",
|
||||
"\n",
|
||||
"Explored 1 nodes (1031 simplex iterations) in 0.15 seconds (0.03 work units)\n",
|
||||
"Thread count was 20 (of 20 available processors)\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 4: 8.29147e+09 8.29398e+09 8.29827e+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.291465302389e+09, best bound 8.290781665333e+09, gap 0.0082%\n"
|
||||
"Best objective 8.291298126440e+09, best bound 8.290812450252e+09, gap 0.0059%\n",
|
||||
"\n",
|
||||
"User-callback calls 910, time in user-callback 0.00 sec\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{}"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"solver_baseline = LearningSolver(components=[])\n",
|
||||
"solver_baseline.fit(train_data)\n",
|
||||
"solver_baseline.optimize(test_data[0], build_uc_model)"
|
||||
"solver_baseline.optimize(test_data[0], build_uc_model);"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -713,10 +736,13 @@
|
||||
"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 20 threads\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",
|
||||
@@ -733,15 +759,20 @@
|
||||
" 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",
|
||||
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
|
||||
"\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 20 threads\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: 0xf97cde91\n",
|
||||
"Model fingerprint: 0x6926c32f\n",
|
||||
"Variable types: 500 continuous, 500 integer (500 binary)\n",
|
||||
"Coefficient statistics:\n",
|
||||
" Matrix range [1e+00, 2e+06]\n",
|
||||
@@ -749,49 +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.00s)\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.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.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",
|
||||
"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",
|
||||
" MIR: 2\n",
|
||||
" StrongCG: 1\n",
|
||||
" Flow cover: 1\n",
|
||||
" RLT: 2\n",
|
||||
"\n",
|
||||
"Explored 1 nodes (575 simplex iterations) in 0.05 seconds (0.01 work units)\n",
|
||||
"Thread count was 20 (of 20 available processors)\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 4: 8.25459e+09 8.25483e+09 8.25512e+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.254590409970e+09, best bound 8.253768093811e+09, gap 0.0100%\n",
|
||||
"obj = 8254590409.969726\n",
|
||||
"Best objective 8.254434593504e+09, best bound 8.253676932849e+09, gap 0.0092%\n",
|
||||
"\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"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"In this tutorial, we will demonstrate how to use and install the Python/Pyomo version of the package. The first step is to install Julia in your machine. See the [official Julia website for more instructions](https://julialang.org/downloads/). After Julia is installed, launch the Julia REPL, type `]` to enter package mode, then install MIPLearn:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"pkg> add MIPLearn@0.3\n",
|
||||
"pkg> add MIPLearn@0.4\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -45,16 +45,10 @@
|
||||
"- Python version, compatible with the Pyomo and Gurobipy modeling languages,\n",
|
||||
"- Julia version, compatible with the JuMP modeling language.\n",
|
||||
"\n",
|
||||
"In this tutorial, we will demonstrate how to use and install the Python/Pyomo version of the package. The first step is to install Python 3.8+ in your computer. See the [official Python website for more instructions](https://www.python.org/downloads/). After Python is installed, we proceed to install MIPLearn using `pip`:\n",
|
||||
"In this tutorial, we will demonstrate how to use and install the Python/Pyomo version of the package. The first step is to install Python 3.9+ in your computer. See the [official Python website for more instructions](https://www.python.org/downloads/). After Python is installed, we proceed to install MIPLearn using `pip`:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"$ pip install MIPLearn==0.3\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"In addition to MIPLearn itself, we will also install Gurobi 10.0, a state-of-the-art commercial MILP solver. This step also install a demo license for Gurobi, which should able to solve the small optimization problems in this tutorial. A license is required for solving larger-scale problems.\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"$ pip install 'gurobipy>=10,<10.1'\n",
|
||||
"$ pip install MIPLearn~=0.4\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
@@ -226,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",
|
||||
@@ -241,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",
|
||||
@@ -489,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",
|
||||
@@ -512,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",
|
||||
@@ -527,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",
|
||||
@@ -543,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"
|
||||
]
|
||||
@@ -563,7 +573,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{}"
|
||||
"(<miplearn.solvers.pyomo.PyomoModel at 0x7fdb38952450>, {})"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
@@ -602,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",
|
||||
@@ -625,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",
|
||||
@@ -658,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"
|
||||
]
|
||||
@@ -681,7 +704,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{}"
|
||||
"(<miplearn.solvers.pyomo.PyomoModel at 0x7fdb2f563f50>, {})"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
@@ -730,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",
|
||||
@@ -753,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",
|
||||
@@ -768,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",
|
||||
@@ -786,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"
|
||||
]
|
||||
|
||||
BIN
miplearn/.io.py.swp
Normal file
BIN
miplearn/.io.py.swp
Normal file
Binary file not shown.
@@ -9,6 +9,7 @@ import sys
|
||||
from io import StringIO
|
||||
from os.path import exists
|
||||
from typing import Callable, List, Any
|
||||
import traceback
|
||||
|
||||
from ..h5 import H5File
|
||||
from ..io import _RedirectOutput, gzip, _to_h5_filename
|
||||
@@ -16,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,
|
||||
@@ -29,52 +36,62 @@ class BasicCollector:
|
||||
verbose: bool = False,
|
||||
) -> None:
|
||||
def _collect(data_filename: str) -> None:
|
||||
h5_filename = _to_h5_filename(data_filename)
|
||||
mps_filename = h5_filename.replace(".h5", ".mps")
|
||||
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
|
||||
mip_var_values = None
|
||||
try:
|
||||
with H5File(h5_filename, "r") as h5:
|
||||
mip_var_values = h5.get_array("mip_var_values")
|
||||
except:
|
||||
pass
|
||||
if exists(h5_filename):
|
||||
# Try to read optimal solution
|
||||
mip_var_values = None
|
||||
try:
|
||||
with H5File(h5_filename, "r") as h5:
|
||||
mip_var_values = h5.get_array("mip_var_values")
|
||||
except:
|
||||
pass
|
||||
|
||||
if mip_var_values is None:
|
||||
print(f"Removing empty/corrupted h5 file: {h5_filename}")
|
||||
os.remove(h5_filename)
|
||||
else:
|
||||
return
|
||||
if mip_var_values is None:
|
||||
print(f"Removing empty/corrupted h5 file: {h5_filename}")
|
||||
os.remove(h5_filename)
|
||||
else:
|
||||
return
|
||||
|
||||
with H5File(h5_filename, "w") as h5:
|
||||
streams: List[Any] = [StringIO()]
|
||||
if verbose:
|
||||
streams += [sys.stdout]
|
||||
with _RedirectOutput(streams):
|
||||
# Load and extract static features
|
||||
model = build_model(data_filename)
|
||||
model.extract_after_load(h5)
|
||||
with H5File(h5_filename, "w") as h5:
|
||||
h5.put_scalar("data_filename", data_filename)
|
||||
streams: List[Any] = [StringIO()]
|
||||
if verbose:
|
||||
streams += [sys.stdout]
|
||||
with _RedirectOutput(streams):
|
||||
# Load and extract static features
|
||||
model = build_model(data_filename)
|
||||
model.extract_after_load(h5)
|
||||
|
||||
if not self.skip_lp:
|
||||
# Solve LP relaxation
|
||||
relaxed = model.relax()
|
||||
relaxed.optimize()
|
||||
relaxed.extract_after_lp(h5)
|
||||
if not self.skip_lp:
|
||||
# Solve LP relaxation
|
||||
relaxed = model.relax()
|
||||
relaxed.optimize()
|
||||
relaxed.extract_after_lp(h5)
|
||||
|
||||
# Solve MIP
|
||||
model.optimize()
|
||||
model.extract_after_mip(h5)
|
||||
# Solve MIP
|
||||
model.optimize()
|
||||
model.extract_after_mip(h5)
|
||||
|
||||
if self.write_mps:
|
||||
# Add lazy constraints to model
|
||||
model._lazy_enforce_collected()
|
||||
if self.write_mps:
|
||||
# Add lazy constraints to model
|
||||
model._lazy_enforce_collected()
|
||||
|
||||
# Save MPS file
|
||||
model.write(mps_filename)
|
||||
gzip(mps_filename)
|
||||
# Save MPS file
|
||||
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()
|
||||
|
||||
if n_jobs > 1:
|
||||
p_umap(
|
||||
|
||||
@@ -1,29 +1,53 @@
|
||||
# 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 typing import Tuple
|
||||
from typing import Tuple, List
|
||||
|
||||
import numpy as np
|
||||
|
||||
from miplearn.h5 import H5File
|
||||
|
||||
|
||||
def _extract_bin_var_names_values(
|
||||
def _extract_var_names_values(
|
||||
h5: H5File,
|
||||
selected_var_types: List[bytes],
|
||||
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
|
||||
bin_var_names, bin_var_indices = _extract_bin_var_names(h5)
|
||||
bin_var_names, bin_var_indices = _extract_var_names(h5, selected_var_types)
|
||||
var_values = h5.get_array("mip_var_values")
|
||||
assert var_values is not None
|
||||
bin_var_values = var_values[bin_var_indices].astype(int)
|
||||
return bin_var_names, bin_var_values, bin_var_indices
|
||||
|
||||
|
||||
def _extract_bin_var_names(h5: H5File) -> Tuple[np.ndarray, np.ndarray]:
|
||||
def _extract_var_names(
|
||||
h5: H5File,
|
||||
selected_var_types: List[bytes],
|
||||
) -> Tuple[np.ndarray, np.ndarray]:
|
||||
var_types = h5.get_array("static_var_types")
|
||||
var_names = h5.get_array("static_var_names")
|
||||
assert var_types is not None
|
||||
assert var_names is not None
|
||||
bin_var_indices = np.where(var_types == b"B")[0]
|
||||
bin_var_indices = np.where(np.isin(var_types, selected_var_types))[0]
|
||||
bin_var_names = var_names[bin_var_indices]
|
||||
assert len(bin_var_names.shape) == 1
|
||||
return bin_var_names, bin_var_indices
|
||||
|
||||
|
||||
def _extract_bin_var_names_values(
|
||||
h5: H5File,
|
||||
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
|
||||
return _extract_var_names_values(h5, [b"B"])
|
||||
|
||||
|
||||
def _extract_bin_var_names(h5: H5File) -> Tuple[np.ndarray, np.ndarray]:
|
||||
return _extract_var_names(h5, [b"B"])
|
||||
|
||||
|
||||
def _extract_int_var_names_values(
|
||||
h5: H5File,
|
||||
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
|
||||
return _extract_var_names_values(h5, [b"B", b"I"])
|
||||
|
||||
|
||||
def _extract_int_var_names(h5: H5File) -> Tuple[np.ndarray, np.ndarray]:
|
||||
return _extract_var_names(h5, [b"B", b"I"])
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import logging
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from . import _extract_bin_var_names_values
|
||||
from . import _extract_int_var_names_values
|
||||
from .actions import PrimalComponentAction
|
||||
from ...solvers.abstract import AbstractModel
|
||||
from ...h5 import H5File
|
||||
@@ -28,5 +28,5 @@ class ExpertPrimalComponent:
|
||||
self, test_h5: str, model: AbstractModel, stats: Dict[str, Any]
|
||||
) -> None:
|
||||
with H5File(test_h5, "r") as h5:
|
||||
names, values, _ = _extract_bin_var_names_values(h5)
|
||||
names, values, _ = _extract_int_var_names_values(h5)
|
||||
self.action.perform(model, names, values.reshape(1, -1), stats)
|
||||
|
||||
@@ -28,4 +28,5 @@ class ExpertBranchPriorityComponent:
|
||||
for var_idx, var_name in enumerate(var_names):
|
||||
if np.isfinite(var_priority[var_idx]):
|
||||
var = model.getVarByName(var_name.decode())
|
||||
var.branchPriority = int(log(1 + var_priority[var_idx]))
|
||||
assert var is not None, f"unknown var: {var_name}"
|
||||
var.BranchPriority = int(log(1 + var_priority[var_idx]))
|
||||
|
||||
@@ -68,7 +68,7 @@ class H5File:
|
||||
return
|
||||
self._assert_is_array(value)
|
||||
if value.dtype.kind == "f":
|
||||
value = value.astype("float32")
|
||||
value = value.astype("float64")
|
||||
if key in self.file:
|
||||
del self.file[key]
|
||||
return self.file.create_dataset(key, data=value, compression="gzip")
|
||||
|
||||
@@ -87,7 +87,10 @@ def read_pkl_gz(filename: str) -> Any:
|
||||
def _to_h5_filename(data_filename: str) -> str:
|
||||
output = f"{data_filename}.h5"
|
||||
output = output.replace(".gz.h5", ".h5")
|
||||
output = output.replace(".json.h5", ".h5")
|
||||
output = output.replace(".pkl.h5", ".h5")
|
||||
output = output.replace(".csv.h5", ".h5")
|
||||
output = output.replace(".jld2.h5", ".h5")
|
||||
output = output.replace(".json.h5", ".h5")
|
||||
output = output.replace(".lp.h5", ".h5")
|
||||
output = output.replace(".mps.h5", ".h5")
|
||||
output = output.replace(".pkl.h5", ".h5")
|
||||
return output
|
||||
|
||||
163
miplearn/problems/maxcut.py
Normal file
163
miplearn/problems/maxcut.py
Normal 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
|
||||
@@ -8,7 +8,7 @@ from typing import List, Union
|
||||
import gurobipy as gp
|
||||
import numpy as np
|
||||
import pyomo.environ as pe
|
||||
from gurobipy.gurobipy import GRB
|
||||
from gurobipy import GRB
|
||||
from scipy.stats import uniform, randint
|
||||
from scipy.stats.distributions import rv_frozen
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from typing import List, Union
|
||||
|
||||
import gurobipy as gp
|
||||
import numpy as np
|
||||
from gurobipy.gurobipy import GRB
|
||||
from gurobipy import GRB
|
||||
from scipy.stats import uniform, randint
|
||||
from scipy.stats.distributions import rv_frozen
|
||||
|
||||
|
||||
@@ -105,7 +105,8 @@ def build_stab_model_gurobipy(
|
||||
model.addConstr(x[i1] + x[i2] <= 1)
|
||||
|
||||
def cuts_separate(m: GurobiModel) -> List[Hashable]:
|
||||
x_val = m.inner.cbGetNodeRel(x)
|
||||
x_val_dict = m.inner.cbGetNodeRel(x)
|
||||
x_val = [x_val_dict[i] for i in nodes]
|
||||
return _stab_separate(data, x_val)
|
||||
|
||||
def cuts_enforce(m: GurobiModel, violations: List[Any]) -> None:
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
import logging
|
||||
import json
|
||||
from typing import Dict, Optional, Callable, Any, List
|
||||
from typing import Dict, Optional, Callable, Any, List, Sequence
|
||||
|
||||
import gurobipy as gp
|
||||
from gurobipy import GRB, GurobiError
|
||||
from gurobipy import GRB, GurobiError, Var
|
||||
import numpy as np
|
||||
from scipy.sparse import lil_matrix
|
||||
|
||||
@@ -109,7 +109,11 @@ class GurobiModel(AbstractModel):
|
||||
assert constrs_sense.shape == (nconstrs,)
|
||||
assert constrs_rhs.shape == (nconstrs,)
|
||||
|
||||
gp_vars = [self.inner.getVarByName(var_name.decode()) for var_name in var_names]
|
||||
gp_vars: list[Var] = []
|
||||
for var_name in var_names:
|
||||
v = self.inner.getVarByName(var_name.decode())
|
||||
assert v is not None, f"unknown var: {var_name}"
|
||||
gp_vars.append(v)
|
||||
self.inner.addMConstr(constrs_lhs, gp_vars, constrs_sense, constrs_rhs)
|
||||
|
||||
if stats is not None:
|
||||
@@ -188,9 +192,10 @@ class GurobiModel(AbstractModel):
|
||||
var_val = var_values[var_idx]
|
||||
if np.isfinite(var_val):
|
||||
var = self.inner.getVarByName(var_name.decode())
|
||||
var.vtype = "C"
|
||||
var.lb = var_val
|
||||
var.ub = var_val
|
||||
assert var is not None, f"unknown var: {var_name}"
|
||||
var.VType = "c"
|
||||
var.LB = var_val
|
||||
var.UB = var_val
|
||||
n_fixed += 1
|
||||
if stats is not None:
|
||||
stats["Fixed variables"] = n_fixed
|
||||
@@ -213,7 +218,7 @@ class GurobiModel(AbstractModel):
|
||||
return GurobiModel(self.inner.relax())
|
||||
|
||||
def set_time_limit(self, time_limit_sec: float) -> None:
|
||||
self.inner.params.timeLimit = time_limit_sec
|
||||
self.inner.params.TimeLimit = time_limit_sec
|
||||
|
||||
def set_warm_starts(
|
||||
self,
|
||||
@@ -228,12 +233,13 @@ class GurobiModel(AbstractModel):
|
||||
|
||||
self.inner.numStart = n_starts
|
||||
for start_idx in range(n_starts):
|
||||
self.inner.params.startNumber = start_idx
|
||||
self.inner.params.StartNumber = start_idx
|
||||
for var_idx, var_name in enumerate(var_names):
|
||||
var_val = var_values[start_idx, var_idx]
|
||||
if np.isfinite(var_val):
|
||||
var = self.inner.getVarByName(var_name.decode())
|
||||
var.start = var_val
|
||||
assert var is not None, f"unknown var: {var_name}"
|
||||
var.Start = var_val
|
||||
|
||||
if stats is not None:
|
||||
stats["WS: Count"] = n_starts
|
||||
@@ -258,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()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
from os.path import exists
|
||||
from tempfile import NamedTemporaryFile
|
||||
from typing import List, Any, Union, Dict, Callable, Optional
|
||||
from typing import List, Any, Union, Dict, Callable, Optional, Tuple
|
||||
|
||||
from miplearn.h5 import H5File
|
||||
from miplearn.io import _to_h5_filename
|
||||
@@ -25,7 +25,7 @@ class LearningSolver:
|
||||
self,
|
||||
model: Union[str, AbstractModel],
|
||||
build_model: Optional[Callable] = None,
|
||||
) -> Dict[str, Any]:
|
||||
) -> Tuple[AbstractModel, Dict[str, Any]]:
|
||||
h5_filename, mode = NamedTemporaryFile().name, "w"
|
||||
if isinstance(model, str):
|
||||
assert build_model is not None
|
||||
@@ -47,8 +47,10 @@ class LearningSolver:
|
||||
relaxed.optimize()
|
||||
relaxed.extract_after_lp(h5)
|
||||
for comp in self.components:
|
||||
comp.before_mip(h5_filename, model, stats)
|
||||
comp_stats = comp.before_mip(h5_filename, model, stats)
|
||||
if comp_stats is not None:
|
||||
stats.update(comp_stats)
|
||||
model.optimize()
|
||||
model.extract_after_mip(h5)
|
||||
|
||||
return stats
|
||||
return model, stats
|
||||
|
||||
@@ -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
|
||||
|
||||
18
setup.py
18
setup.py
@@ -6,7 +6,7 @@ from setuptools import setup, find_namespace_packages
|
||||
|
||||
setup(
|
||||
name="miplearn",
|
||||
version="0.4.0",
|
||||
version="0.4.3",
|
||||
author="Alinson S. Xavier",
|
||||
author_email="axavier@anl.gov",
|
||||
description="Extensible Framework for Learning-Enhanced Mixed-Integer Optimization",
|
||||
@@ -14,12 +14,11 @@ setup(
|
||||
packages=find_namespace_packages(),
|
||||
python_requires=">=3.9",
|
||||
install_requires=[
|
||||
"Jinja2<3.1",
|
||||
"gurobipy>=10,<11",
|
||||
"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,16 +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",
|
||||
"sphinx-multitoc-numbering>=0.1,<0.2",
|
||||
"twine>=4,<5",
|
||||
"sphinx-book-theme>=1,<2",
|
||||
"sphinx-multitoc-numbering==0.1.3",
|
||||
"twine>=6,<7",
|
||||
"ipython>=9,<10",
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
@@ -71,5 +71,5 @@ def test_usage_stab(
|
||||
comp = MemorizingCutsComponent(clf=clf, extractor=default_extractor)
|
||||
solver = LearningSolver(components=[comp])
|
||||
solver.fit(data_filenames)
|
||||
stats = solver.optimize(data_filenames[0], build_model) # type: ignore
|
||||
model, stats = solver.optimize(data_filenames[0], build_model) # type: ignore
|
||||
assert stats["Cuts: AOT"] > 0
|
||||
|
||||
@@ -65,5 +65,5 @@ def test_usage_tsp(
|
||||
comp = MemorizingLazyComponent(clf=clf, extractor=default_extractor)
|
||||
solver = LearningSolver(components=[comp])
|
||||
solver.fit(data_filenames)
|
||||
stats = solver.optimize(data_filenames[0], build_model) # type: ignore
|
||||
model, stats = solver.optimize(data_filenames[0], build_model) # type: ignore
|
||||
assert stats["Lazy Constraints: AOT"] > 0
|
||||
|
||||
119
tests/problems/test_maxcut.py
Normal file
119
tests/problems/test_maxcut.py
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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]),
|
||||
|
||||
Reference in New Issue
Block a user