Update v0.3 docs

docs
Alinson S. Xavier 2 years ago
parent d9d44ce4b2
commit 14a428e49e
Signed by: isoron
GPG Key ID: 0DA8E4B9E1109DCA

@ -39,8 +39,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 3, "execution_count": 3,
"id": "f906fe9c",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [ "outputs": [
{ {
@ -102,16 +106,14 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "50441907",
"collapsed": false "metadata": {},
},
"source": [] "source": []
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "d0000c8d",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"## Basic collector\n", "## Basic collector\n",
"\n", "\n",
@ -128,9 +130,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "6529f667",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"### Data fields\n", "### Data fields\n",
"\n", "\n",
@ -168,9 +169,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "f2894594",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"### Example\n", "### Example\n",
"\n", "\n",
@ -180,8 +180,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 4, "execution_count": 4,
"id": "ac6f8c6f",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [ "outputs": [
{ {
@ -238,8 +242,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"id": "78f0b07a",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [], "outputs": [],
"source": [] "source": []
@ -247,7 +255,7 @@
], ],
"metadata": { "metadata": {
"kernelspec": { "kernelspec": {
"display_name": "Python 3", "display_name": "Python 3 (ipykernel)",
"language": "python", "language": "python",
"name": "python3" "name": "python3"
}, },
@ -261,7 +269,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.9.12" "version": "3.9.16"
} }
}, },
"nbformat": 4, "nbformat": 4,

@ -7,14 +7,13 @@
"source": [ "source": [
"# Feature Extractors\n", "# Feature Extractors\n",
"\n", "\n",
"In the previous page, we introduced *training data collectors*, which solve the optimization problem and collect raw training data, such as the optimal solution. In this page, we introduce **feature extractors**, which take the raw training data, stored in HDF5 files, and extract relevant information in order to train a machine learning model. We describe the extractors readily available in MIPLearn." "In the previous page, we introduced *training data collectors*, which solve the optimization problem and collect raw training data, such as the optimal solution. In this page, we introduce **feature extractors**, which take the raw training data, stored in HDF5 files, and extract relevant information in order to train a machine learning model."
] ]
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "b4026de5",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"\n", "\n",
"## Overview\n", "## Overview\n",
@ -31,9 +30,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "b2d9736c",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"\n", "\n",
"## H5FieldsExtractor\n", "## H5FieldsExtractor\n",
@ -43,9 +41,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "e8184dff",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"### Example\n", "### Example\n",
"\n", "\n",
@ -55,8 +52,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 5, "execution_count": 5,
"id": "ed9a18c8",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [ "outputs": [
{ {
@ -170,9 +171,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "2da2e74e",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"\n", "\n",
"[H5FieldsExtractor]: ../../api/collectors/#miplearn.features.fields.H5FieldsExtractor" "[H5FieldsExtractor]: ../../api/collectors/#miplearn.features.fields.H5FieldsExtractor"
@ -180,9 +180,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "d879c0d3",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"<div class=\"alert alert-warning\">\n", "<div class=\"alert alert-warning\">\n",
"Warning\n", "Warning\n",
@ -193,9 +192,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "cd0ba071",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"## AlvLouWeh2017Extractor\n", "## AlvLouWeh2017Extractor\n",
"\n", "\n",
@ -207,8 +205,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 6, "execution_count": 6,
"id": "a1bc38fe",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [ "outputs": [
{ {
@ -295,9 +297,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "286c9927",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"<div class=\"alert alert-info\">\n", "<div class=\"alert alert-info\">\n",
"References\n", "References\n",
@ -311,7 +312,7 @@
], ],
"metadata": { "metadata": {
"kernelspec": { "kernelspec": {
"display_name": "Python 3", "display_name": "Python 3 (ipykernel)",
"language": "python", "language": "python",
"name": "python3" "name": "python3"
}, },
@ -325,7 +326,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.9.12" "version": "3.9.16"
} }
}, },
"nbformat": 4, "nbformat": 4,

@ -283,7 +283,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.9.12" "version": "3.9.16"
} }
}, },
"nbformat": 4, "nbformat": 4,

@ -9,7 +9,7 @@
"\n", "\n",
"## Overview\n", "## Overview\n",
"\n", "\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, unfortunately, make existing benchmark sets less than ideal 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", "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", "\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. 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",
"\n", "\n",
@ -18,9 +18,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "bd99c51f",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"<div class=\"alert alert-warning\">\n", "<div class=\"alert alert-warning\">\n",
"Warning\n", "Warning\n",
@ -63,9 +62,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "5e502345",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"\n", "\n",
"$$\n", "$$\n",
@ -83,9 +81,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "9cba2077",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"### Random instance generator\n", "### Random instance generator\n",
"\n", "\n",
@ -100,9 +97,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "2bc62803",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"### Example" "### Example"
] ]
@ -230,9 +226,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "d0d3ea42",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"\n", "\n",
"$$\n", "$$\n",
@ -299,9 +294,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "f12a066f",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"### Example" "### Example"
] ]
@ -466,9 +460,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "4e701397",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"### Example" "### Example"
] ]
@ -592,9 +585,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "d5254e7a",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"### Formulation\n", "### Formulation\n",
"\n", "\n",
@ -737,9 +729,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "19342eb1",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"### Formulation\n", "### Formulation\n",
"\n", "\n",
@ -748,9 +739,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "0391b35b",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"$$\n", "$$\n",
"\\begin{align*}\n", "\\begin{align*}\n",
@ -766,9 +756,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "c2d7df7b",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"### Random instance generator\n", "### Random instance generator\n",
"\n", "\n",
@ -783,8 +772,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 5, "execution_count": 5,
"id": "cc797da7",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [ "outputs": [
{ {
@ -876,9 +869,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "2f74dd10",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"$$\n", "$$\n",
"\\begin{align*}\n", "\\begin{align*}\n",
@ -892,9 +884,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "ef030168",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"\n", "\n",
"### Random instance generator\n", "### Random instance generator\n",
@ -1004,9 +995,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "da3ca69c",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"### Formulation\n", "### Formulation\n",
"\n", "\n",
@ -1015,9 +1005,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "9cf296e9",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"$$\n", "$$\n",
"\\begin{align*}\n", "\\begin{align*}\n",
@ -1034,9 +1023,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "eba3dbe5",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"### Random instance generator\n", "### Random instance generator\n",
"\n", "\n",
@ -1053,9 +1041,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "61f16c56",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"### Example" "### Example"
] ]
@ -1063,8 +1050,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 7, "execution_count": 7,
"id": "9d0c56c6",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [ "outputs": [
{ {
@ -1177,9 +1168,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "7048d771",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"\n", "\n",
"<div class=\"alert alert-info\">\n", "<div class=\"alert alert-info\">\n",
@ -1195,9 +1185,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "bec5ee1c",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"\n", "\n",
"$$\n", "$$\n",
@ -1230,9 +1219,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "4a1ffb4c",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"\n", "\n",
"The first set of inequalities enforces minimum up-time constraints: if unit $g$ is down at time $t$, then it cannot start up during the previous $L_g$ time steps. The second set of inequalities enforces minimum down-time constraints, and is symmetrical to the previous one. The third set ensures that if unit $g$ starts up at time $t$, then the start up variable must be one. The fourth set ensures that demand is satisfied at each time period. The fifth and sixth sets enforce bounds to the quantity of power generated by each unit.\n", "The first set of inequalities enforces minimum up-time constraints: if unit $g$ is down at time $t$, then it cannot start up during the previous $L_g$ time steps. The second set of inequalities enforces minimum down-time constraints, and is symmetrical to the previous one. The third set ensures that if unit $g$ starts up at time $t$, then the start up variable must be one. The fourth set ensures that demand is satisfied at each time period. The fifth and sixth sets enforce bounds to the quantity of power generated by each unit.\n",
@ -1246,9 +1234,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "01bed9fc",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"\n", "\n",
"### Random instance generator\n", "### Random instance generator\n",
@ -1264,9 +1251,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "855b87b4",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"### Example" "### Example"
] ]
@ -1274,8 +1260,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 8, "execution_count": 8,
"id": "6217da7c",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [ "outputs": [
{ {
@ -1415,9 +1405,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "91f5781a",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"\n", "\n",
"### Formulation\n", "### Formulation\n",
@ -1427,9 +1416,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "544754cb",
"collapsed": false "metadata": {},
},
"source": [ "source": [
" $$\n", " $$\n",
"\\begin{align*}\n", "\\begin{align*}\n",
@ -1445,9 +1433,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": { "id": "35c99166",
"collapsed": false "metadata": {},
},
"source": [ "source": [
"### Random instance generator\n", "### Random instance generator\n",
"\n", "\n",
@ -1545,8 +1532,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"id": "9f12e91f",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [], "outputs": [],
"source": [] "source": []
@ -1554,7 +1545,7 @@
], ],
"metadata": { "metadata": {
"kernelspec": { "kernelspec": {
"display_name": "Python 3", "display_name": "Python 3 (ipykernel)",
"language": "python", "language": "python",
"name": "python3" "name": "python3"
}, },
@ -1568,7 +1559,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.9.12" "version": "3.9.16"
} }
}, },
"nbformat": 4, "nbformat": 4,

@ -1,20 +1,63 @@
{ {
"cells": [ "cells": [
{ {
"attachments": {},
"cell_type": "markdown", "cell_type": "markdown",
"id": "3371f072-be1e-4c47-b765-b5d30fdbfae6", "id": "9ec1907b-db93-4840-9439-c9005902b968",
"metadata": {}, "metadata": {},
"source": [ "source": [
"# Solvers\n", "# Learning Solver\n",
"\n", "\n",
"## LearningSolver\n", "On previous pages, we discussed various components of the MIPLearn framework, including training data collectors, feature extractors, and individual machine learning components. In this page, we introduce **LearningSolver**, the main class of the framework which integrates all the aforementioned components into a cohesive whole. Using **LearningSolver** involves three steps: (i) configuring the solver; (ii) training the ML components; and (iii) solving new MIP instances. In the following, we describe each of these steps, then conclude with a complete runnable example.\n",
"\n", "\n",
"### Example" "### Configuring the solver\n",
"\n",
"**LearningSolver** is composed by multiple individual machine learning components, each targeting a different part of the solution process, or implementing a different machine learning strategy. This architecture allows strategies to be easily enabled, disabled or customized, making the framework flexible. By default, no components are provided and **LearningSolver** is equivalent to a traditional MIP solver. To specify additional components, the `components` constructor argument may be used:\n",
"\n",
"```python\n",
"solver = LearningSolver(\n",
" components=[\n",
" comp1,\n",
" comp2,\n",
" comp3,\n",
" ]\n",
")\n",
"```\n",
"\n",
"In this example, three components `comp1`, `comp2` and `comp3` are provided. The strategies implemented by these components are applied sequentially when solving the problem. For example, `comp1` and `comp2` could fix a subset of decision variables, while `comp3` constructs a warm start for the remaining problem.\n",
"\n",
"### Training and solving new instances\n",
"\n",
"Once a solver is configured, its ML components need to be trained. This can be achieved by the `solver.fit` method, as illustrated below. The method accepts a list of HDF5 files and trains each individual component sequentially. Once the solver is trained, new instances can be solved using `solver.optimize`. The method returns a dictionary of statistics collected by each component, such as the number of variables fixed.\n",
"\n",
"```python\n",
"# Build instances\n",
"train_data = ...\n",
"test_data = ...\n",
"\n",
"# Collect training data\n",
"bc = BasicCollector()\n",
"bc.collect(train_data, build_model)\n",
"\n",
"# Build solver\n",
"solver = LearningSolver(...)\n",
"\n",
"# Train components\n",
"solver.fit(train_data)\n",
"\n",
"# Solve a new test instance\n",
"stats = solver.optimize(test_data[0], build_model)\n",
"\n",
"```\n",
"\n",
"### Complete example\n",
"\n",
"In the example below, we illustrate the usage of **LearningSolver** by building instances of the Traveling Salesman Problem, collecting training data, training the ML components, then solving a new instance."
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 1, "execution_count": 3,
"id": "92b09b98", "id": "92b09b98",
"metadata": { "metadata": {
"collapsed": false, "collapsed": false,
@ -23,21 +66,15 @@
} }
}, },
"outputs": [ "outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/axavier/Software/anaconda3/envs/miplearn/lib/python3.8/site-packages/tqdm/auto.py:22: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
" from .autonotebook import tqdm as notebook_tqdm\n"
]
},
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Restricted license - for non-production use only - expires 2023-10-25\n", "Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)\n",
"Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (linux64)\n", "\n",
"Thread count: 6 physical cores, 12 logical processors, using up to 12 threads\n", "CPU model: AMD Ryzen 9 7950X 16-Core Processor, instruction set [SSE2|AVX|AVX2|AVX512]\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
"\n",
"Optimize a model with 10 rows, 45 columns and 90 nonzeros\n", "Optimize a model with 10 rows, 45 columns and 90 nonzeros\n",
"Model fingerprint: 0x6ddcd141\n", "Model fingerprint: 0x6ddcd141\n",
"Coefficient statistics:\n", "Coefficient statistics:\n",
@ -52,11 +89,14 @@
" 0 6.3600000e+02 1.700000e+01 0.000000e+00 0s\n", " 0 6.3600000e+02 1.700000e+01 0.000000e+00 0s\n",
" 15 2.7610000e+03 0.000000e+00 0.000000e+00 0s\n", " 15 2.7610000e+03 0.000000e+00 0.000000e+00 0s\n",
"\n", "\n",
"Solved in 15 iterations and 0.01 seconds (0.00 work units)\n", "Solved in 15 iterations and 0.00 seconds (0.00 work units)\n",
"Optimal objective 2.761000000e+03\n", "Optimal objective 2.761000000e+03\n",
"Set parameter LazyConstraints to value 1\n", "Set parameter LazyConstraints to value 1\n",
"Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (linux64)\n", "Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)\n",
"Thread count: 6 physical cores, 12 logical processors, using up to 12 threads\n", "\n",
"CPU model: AMD Ryzen 9 7950X 16-Core Processor, instruction set [SSE2|AVX|AVX2|AVX512]\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
"\n",
"Optimize a model with 10 rows, 45 columns and 90 nonzeros\n", "Optimize a model with 10 rows, 45 columns and 90 nonzeros\n",
"Model fingerprint: 0x74ca3d0a\n", "Model fingerprint: 0x74ca3d0a\n",
"Variable types: 0 continuous, 45 integer (45 binary)\n", "Variable types: 0 continuous, 45 integer (45 binary)\n",
@ -66,7 +106,7 @@
" Bounds range [1e+00, 1e+00]\n", " Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+00, 2e+00]\n", " RHS range [2e+00, 2e+00]\n",
"\n", "\n",
"User MIP start produced solution with objective 2796 (0.01s)\n", "User MIP start produced solution with objective 2796 (0.00s)\n",
"Loaded user MIP start with objective 2796\n", "Loaded user MIP start with objective 2796\n",
"\n", "\n",
"Presolve time: 0.00s\n", "Presolve time: 0.00s\n",
@ -78,48 +118,32 @@
" Nodes | Current Node | Objective Bounds | Work\n", " Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n", " Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\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", " 0 0 cutoff 0 2796.00000 2796.00000 0.00% - 0s\n",
"\n", "\n",
"Cutting planes:\n", "Cutting planes:\n",
" Lazy constraints: 3\n", " Lazy constraints: 3\n",
"\n", "\n",
"Explored 1 nodes (15 simplex iterations) in 0.01 seconds (0.00 work units)\n", "Explored 1 nodes (16 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 12 (of 12 available processors)\n", "Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 1: 2796 \n",
"\n",
"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 103, time in user-callback 0.00 sec\n",
"Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (linux64)\n",
"Thread count: 6 physical cores, 12 logical processors, using up to 12 threads\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",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+00]\n",
" Objective range [4e+01, 1e+03]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+00, 2e+00]\n",
"Presolved: 10 rows, 45 columns, 90 nonzeros\n",
"\n",
"Continuing optimization...\n",
"\n",
"\n",
"Cutting planes:\n",
" Lazy constraints: 3\n",
"\n",
"Explored 1 nodes (15 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 12 (of 12 available processors)\n",
"\n", "\n",
"Solution count 1: 2796 \n", "Solution count 1: 2796 \n",
"\n", "\n",
"Optimal solution found (tolerance 1.00e-04)\n", "Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 2.796000000000e+03, best bound 2.796000000000e+03, gap 0.0000%\n", "Best objective 2.796000000000e+03, best bound 2.796000000000e+03, gap 0.0000%\n",
"\n", "\n",
"User-callback calls 27, time in user-callback 0.00 sec\n" "User-callback calls 110, time in user-callback 0.00 sec\n"
] ]
},
{
"data": {
"text/plain": [
"{'WS: Count': 1, 'WS: Number of variables set': 41.0}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
} }
], ],
"source": [ "source": [
@ -135,7 +159,7 @@
"from miplearn.components.primal.actions import SetWarmStart\n", "from miplearn.components.primal.actions import SetWarmStart\n",
"from miplearn.components.primal.indep import IndependentVarsPrimalComponent\n", "from miplearn.components.primal.indep import IndependentVarsPrimalComponent\n",
"from miplearn.extractors.AlvLouWeh2017 import AlvLouWeh2017Extractor\n", "from miplearn.extractors.AlvLouWeh2017 import AlvLouWeh2017Extractor\n",
"from miplearn.io import save\n", "from miplearn.io import write_pkl_gz\n",
"from miplearn.problems.tsp import (\n", "from miplearn.problems.tsp import (\n",
" TravelingSalesmanGenerator,\n", " TravelingSalesmanGenerator,\n",
" build_tsp_model,\n", " build_tsp_model,\n",
@ -157,7 +181,7 @@
").generate(50)\n", ").generate(50)\n",
"\n", "\n",
"# Save instance data to data/tsp/00000.pkl.gz, data/tsp/00001.pkl.gz, ...\n", "# Save instance data to data/tsp/00000.pkl.gz, data/tsp/00001.pkl.gz, ...\n",
"all_data = save(data, \"data/tsp\")\n", "all_data = write_pkl_gz(data, \"data/tsp\")\n",
"\n", "\n",
"# Split train/test data\n", "# Split train/test data\n",
"train_data = all_data[:40]\n", "train_data = all_data[:40]\n",
@ -187,12 +211,12 @@
"solver.fit(train_data)\n", "solver.fit(train_data)\n",
"\n", "\n",
"# Solve a test instance\n", "# Solve a test instance\n",
"solver.optimize(test_data[0], build_tsp_model);" "solver.optimize(test_data[0], build_tsp_model)\n"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 1, "execution_count": null,
"id": "e27d2cbd-5341-461d-bbc1-8131aee8d949", "id": "e27d2cbd-5341-461d-bbc1-8131aee8d949",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
@ -215,7 +239,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.8.13" "version": "3.9.12"
} }
}, },
"nbformat": 4, "nbformat": 4,

@ -1,625 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "6b8983b1",
"metadata": {
"tags": []
},
"source": [
"# Getting started (JuMP)\n",
"\n",
"## Introduction\n",
"\n",
"**MIPLearn** is an open source framework that uses machine learning (ML) to accelerate the performance of both commercial and open source mixed-integer programming solvers (e.g. Gurobi, CPLEX, XPRESS, Cbc or SCIP). In this tutorial, we will:\n",
"\n",
"1. Install the Python/Pyomo version of MIPLearn\n",
"2. Model a simple optimization problem using JuMP\n",
"3. Generate training data and train the ML models\n",
"4. Use the ML models together Gurobi to solve new instances\n",
"\n",
"<div class=\"alert alert-info\">\n",
"Note\n",
" \n",
"The Python/Pyomo version of MIPLearn is currently only compatible with with Gurobi, CPLEX and XPRESS. For broader solver compatibility, see the Julia/JuMP version of the package.\n",
"</div>\n",
"\n",
"<div class=\"alert alert-warning\">\n",
"Warning\n",
" \n",
"MIPLearn is still in early development stage. If run into any bugs or issues, please submit a bug report in our GitHub repository. Comments, suggestions and pull requests are also very welcome!\n",
" \n",
"</div>\n"
]
},
{
"cell_type": "markdown",
"id": "02f0a927",
"metadata": {},
"source": [
"## Installation\n",
"\n",
"MIPLearn is available in two versions:\n",
"\n",
"- Python version, compatible with the Pyomo modeling language,\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`:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "cd8a69c1",
"metadata": {},
"outputs": [],
"source": [
"# !pip install MIPLearn==0.2.0.dev13"
]
},
{
"cell_type": "markdown",
"id": "e8274543",
"metadata": {},
"source": [
"In addition to MIPLearn itself, we will also install Gurobi 9.5, a state-of-the-art commercial MILP solver. This step also install a demo license for Gurobi, which should able to solve the small optimization problems in this tutorial. A paid license is required for solving large-scale problems."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "dcc8756c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Looking in indexes: https://pypi.gurobi.com\n",
"Requirement already satisfied: gurobipy<9.6,>=9.5 in /opt/anaconda3/envs/miplearn/lib/python3.8/site-packages (9.5.1)\n"
]
}
],
"source": [
"!pip install --upgrade -i https://pypi.gurobi.com 'gurobipy>=9.5,<9.6'"
]
},
{
"cell_type": "markdown",
"id": "a14e4550",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
" \n",
"Note\n",
" \n",
"In the code above, we install specific version of all packages to ensure that this tutorial keeps running in the future, even when newer (and possibly incompatible) versions of the packages are released. This is usually a recommended practice for all Python projects.\n",
" \n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "16b86823",
"metadata": {},
"source": [
"## Modeling a simple optimization problem\n",
"\n",
"To illustrate how can MIPLearn be used, we will model and solve a small optimization problem related to power systems optimization. The problem we discuss below is a simplification of the **unit commitment problem,** a practical optimization problem solved daily by electric grid operators around the world. \n",
"\n",
"Suppose that you work at a utility company, and that it is your job to decide which electrical generators should be online at a certain hour of the day, as well as how much power should each generator produce. More specifically, assume that your company owns $n$ generators, denoted by $g_1, \\ldots, g_n$. Each generator can either be online or offline. An online generator $g_i$ can produce between $p^\\text{min}_i$ to $p^\\text{max}_i$ megawatts of power, and it costs your company $c^\\text{fix}_i + c^\\text{var}_i y_i$, where $y_i$ is the amount of power produced. An offline generator produces nothing and costs nothing. You also know that the total amount of power to be produced needs to be exactly equal to the total demand $d$ (in megawatts). To minimize the costs to your company, which generators should be online, and how much power should they produce?\n",
"\n",
"This simple problem can be modeled as a *mixed-integer linear optimization* problem as follows. For each generator $g_i$, let $x_i \\in \\{0,1\\}$ be a decision variable indicating whether $g_i$ is online, and let $y_i \\geq 0$ be a decision variable indicating how much power does $g_i$ produce. The problem is then given by:\n",
"\n",
"$$\n",
"\\begin{align}\n",
"\\text{minimize } \\quad & \\sum_{i=1}^n \\left( c^\\text{fix}_i x_i + c^\\text{var}_i y_i \\right) \\\\\n",
"\\text{subject to } \\quad & y_i \\leq p^\\text{max}_i x_i & i=1,\\ldots,n \\\\\n",
"& y_i \\geq p^\\text{min}_i x_i & i=1,\\ldots,n \\\\\n",
"& \\sum_{i=1}^n y_i = d \\\\\n",
"& x_i \\in \\{0,1\\} & i=1,\\ldots,n \\\\\n",
"& y_i \\geq 0 & i=1,\\ldots,n\n",
"\\end{align}\n",
"$$\n",
"\n",
"<div class=\"alert alert-info\">\n",
" \n",
"Note\n",
" \n",
"We use a simplified version of the unit commitment problem in this tutorial just to make it easier to follow. MIPLearn can also handle realistic, large-scale versions of this problem. See benchmarks for more details.\n",
" \n",
"</div>\n",
"\n",
"Next, let us convert this abstract mathematical formulation into a concrete optimization model, using Python and Pyomo. We start by defining a data class `UnitCommitmentData`, which holds all the input data."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "22a67170-10b4-43d3-8708-014d91141e73",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from dataclasses import dataclass\n",
"import numpy as np\n",
"\n",
"@dataclass\n",
"class UnitCommitmentData:\n",
" demand: float\n",
" pmin: np.ndarray\n",
" pmax: np.ndarray\n",
" cfix: np.ndarray\n",
" cvar: np.ndarray"
]
},
{
"cell_type": "markdown",
"id": "29f55efa-0751-465a-9b0a-a821d46a3d40",
"metadata": {},
"source": [
"Next, we write a `build_uc_model` function, which converts the input data into a concrete Pyomo model."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "2f67032f-0d74-4317-b45c-19da0ec859e9",
"metadata": {},
"outputs": [],
"source": [
"import pyomo.environ as pe\n",
"\n",
"def build_uc_model(data: UnitCommitmentData) -> pe.ConcreteModel:\n",
" model = pe.ConcreteModel()\n",
" n = len(data.pmin)\n",
" model.x = pe.Var(range(n), domain=pe.Binary)\n",
" model.y = pe.Var(range(n), domain=pe.NonNegativeReals)\n",
" model.obj = pe.Objective(\n",
" expr=sum(\n",
" data.cfix[i] * model.x[i] +\n",
" data.cvar[i] * model.y[i]\n",
" for i in range(n)\n",
" )\n",
" )\n",
" model.eq_max_power = pe.ConstraintList()\n",
" model.eq_min_power = pe.ConstraintList()\n",
" for i in range(n):\n",
" model.eq_max_power.add(model.y[i] <= data.pmax[i] * model.x[i])\n",
" model.eq_min_power.add(model.y[i] >= data.pmin[i] * model.x[i])\n",
" model.eq_demand = pe.Constraint(\n",
" expr=sum(model.y[i] for i in range(n)) == data.demand,\n",
" )\n",
" return model"
]
},
{
"cell_type": "markdown",
"id": "c22714a3",
"metadata": {},
"source": [
"At this point, we can already use Pyomo and any mixed-integer linear programming solver to find optimal solutions to any instance of this problem. To illustrate this, let us solve a small instance with three generators:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "2a896f47",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter Threads to value 1\n",
"Set parameter Seed to value 42\n",
"Restricted license - for non-production use only - expires 2023-10-25\n",
"obj = 1320.0\n",
"x = [-0.0, 1.0, 1.0]\n",
"y = [0.0, 60.0, 40.0]\n"
]
}
],
"source": [
"model = build_uc_model(\n",
" UnitCommitmentData(\n",
" demand = 100.0,\n",
" pmin = [10, 20, 30],\n",
" pmax = [50, 60, 70],\n",
" cfix = [700, 600, 500],\n",
" cvar = [1.5, 2.0, 2.5],\n",
" )\n",
")\n",
"\n",
"solver = pe.SolverFactory(\"gurobi_persistent\")\n",
"solver.set_instance(model)\n",
"solver.solve()\n",
"print(\"obj =\", model.obj())\n",
"print(\"x =\", [model.x[i].value for i in range(3)])\n",
"print(\"y =\", [model.y[i].value for i in range(3)])"
]
},
{
"cell_type": "markdown",
"id": "41b03bbc",
"metadata": {},
"source": [
"Running the code above, we found that the optimal solution for our small problem instance costs \\$1320. It is achieve by keeping generators 2 and 3 online and producing, respectively, 60 MW and 40 MW of power."
]
},
{
"cell_type": "markdown",
"id": "cf60c1dd",
"metadata": {},
"source": [
"## Generating training data\n",
"\n",
"Although Gurobi could solve the small example above in a fraction of a second, it gets slower for larger and more complex versions of the problem. If this is a problem that needs to be solved frequently, as it is often the case in practice, it could make sense to spend some time upfront generating a **trained** version of Gurobi, which can solve new instances (similar to the ones it was trained on) faster.\n",
"\n",
"In the following, we will use MIPLearn to train machine learning models that is able to predict the optimal solution for instances that follow a given probability distribution, then it will provide this predicted solution to Gurobi as a warm start. Before we can train the model, we need to collect training data by solving a large number of instances. In real-world situations, we may construct these training instances based on historical data. In this tutorial, we will construct them using a random instance generator:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "5eb09fab",
"metadata": {},
"outputs": [],
"source": [
"from scipy.stats import uniform\n",
"from typing import List\n",
"import random\n",
"\n",
"def random_uc_data(samples: int, n: int, seed: int = 42) -> List[UnitCommitmentData]:\n",
" random.seed(seed)\n",
" np.random.seed(seed)\n",
" pmin = uniform(loc=100_000.0, scale=400_000.0).rvs(n)\n",
" pmax = pmin * uniform(loc=2.0, scale=2.5).rvs(n)\n",
" cfix = pmin * uniform(loc=100.0, scale=25.0).rvs(n)\n",
" cvar = uniform(loc=1.25, scale=0.25).rvs(n)\n",
" return [\n",
" UnitCommitmentData(\n",
" demand = pmax.sum() * uniform(loc=0.5, scale=0.25).rvs(),\n",
" pmin = pmin,\n",
" pmax = pmax,\n",
" cfix = cfix,\n",
" cvar = cvar,\n",
" )\n",
" for i in range(samples)\n",
" ]"
]
},
{
"cell_type": "markdown",
"id": "3a03a7ac",
"metadata": {},
"source": [
"In this example, for simplicity, only the demands change from one instance to the next. We could also have randomized the costs, production limits or even the number of units. The more randomization we have in the training data, however, the more challenging it is for the machine learning models to learn solution patterns.\n",
"\n",
"Now we generate 500 instances of this problem, each one with 50 generators, and we use 450 of these instances for training. After generating the instances, we write them to individual files. MIPLearn uses files during the training process because, for large-scale optimization problems, it is often impractical to hold in memory the entire training data, as well as the concrete Pyomo models. Files also make it much easier to solve multiple instances simultaneously, potentially even on multiple machines. We will cover parallel and distributed computing in a future tutorial. The code below generates the files `uc/train/00000.pkl.gz`, `uc/train/00001.pkl.gz`, etc., which contain the input data in compressed (gzipped) pickle format."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "6156752c",
"metadata": {},
"outputs": [],
"source": [
"from miplearn import save\n",
"data = random_uc_data(samples=500, n=50)\n",
"train_files = save(data[0:450], \"uc/train/\")\n",
"test_files = save(data[450:500], \"uc/test/\")"
]
},
{
"cell_type": "markdown",
"id": "b17af877",
"metadata": {},
"source": [
"Finally, we use `LearningSolver` to solve all the training instances. `LearningSolver` is the main component provided by MIPLearn, which integrates MIP solvers and ML. The optimal solutions, along with other useful training data, are stored in HDF5 files `uc/train/00000.h5`, `uc/train/00001.h5`, etc."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "7623f002",
"metadata": {},
"outputs": [],
"source": [
"from miplearn import LearningSolver\n",
"solver = LearningSolver()\n",
"solver.solve(train_files, build_uc_model);"
]
},
{
"cell_type": "markdown",
"id": "2f24ee83",
"metadata": {},
"source": [
"## Solving test instances\n",
"\n",
"With training data in hand, we can now fit the ML models, using the `LearningSolver.fit` method, then solve the test instances with `LearningSolver.solve`, as shown below. The `tee=True` parameter asks MIPLearn to print the solver log to the screen."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "c8385030",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter LogFile to value \"/tmp/tmpvbaqbyty.log\"\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
"Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
"Model fingerprint: 0x8de73876\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 2e+06]\n",
" Objective range [1e+00, 6e+07]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+07, 2e+07]\n",
"Presolve removed 100 rows and 50 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1 rows, 50 columns, 50 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 5.7349081e+08 1.044003e+04 0.000000e+00 0s\n",
" 1 6.8268465e+08 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.00 seconds (0.00 work units)\n",
"Optimal objective 6.826846503e+08\n",
"Set parameter LogFile to value \"\"\n",
"Set parameter LogFile to value \"/tmp/tmp48j6n35b.log\"\n",
"Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
"Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
"Model fingerprint: 0x200d64ba\n",
"Variable types: 50 continuous, 50 integer (50 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 2e+06]\n",
" Objective range [1e+00, 6e+07]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+07, 2e+07]\n",
"\n",
"User MIP start produced solution with objective 6.84841e+08 (0.00s)\n",
"Loaded user MIP start with objective 6.84841e+08\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 101 rows, 100 columns, 250 nonzeros\n",
"Variable types: 50 continuous, 50 integer (50 binary)\n",
"\n",
"Root relaxation: objective 6.826847e+08, 56 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 6.8268e+08 0 1 6.8484e+08 6.8268e+08 0.31% - 0s\n",
" 0 0 6.8315e+08 0 3 6.8484e+08 6.8315e+08 0.25% - 0s\n",
" 0 0 6.8315e+08 0 1 6.8484e+08 6.8315e+08 0.25% - 0s\n",
" 0 0 6.8315e+08 0 3 6.8484e+08 6.8315e+08 0.25% - 0s\n",
" 0 0 6.8315e+08 0 4 6.8484e+08 6.8315e+08 0.25% - 0s\n",
" 0 0 6.8315e+08 0 4 6.8484e+08 6.8315e+08 0.25% - 0s\n",
" 0 2 6.8327e+08 0 4 6.8484e+08 6.8327e+08 0.23% - 0s\n",
"\n",
"Cutting planes:\n",
" Flow cover: 3\n",
"\n",
"Explored 32 nodes (155 simplex iterations) in 0.02 seconds (0.00 work units)\n",
"Thread count was 1 (of 32 available processors)\n",
"\n",
"Solution count 1: 6.84841e+08 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 6.848411655488e+08, best bound 6.848411655488e+08, gap 0.0000%\n",
"Set parameter LogFile to value \"\"\n",
"WARNING: Cannot get reduced costs for MIP.\n",
"WARNING: Cannot get duals for MIP.\n"
]
}
],
"source": [
"solver_ml = LearningSolver()\n",
"solver_ml.fit(train_files, build_uc_model)\n",
"solver_ml.solve(test_files[0:1], build_uc_model, tee=True);"
]
},
{
"cell_type": "markdown",
"id": "61da6dad-7f56-4edb-aa26-c00eb5f946c0",
"metadata": {},
"source": [
"By examining the solve log above, specifically the line `Loaded user MIP start with objective...`, we can see that MIPLearn was able to construct an initial solution which turned out to be the optimal solution to the problem. Now let us repeat the code above, but using an untrained solver. Note that the `fit` line is omitted."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "33d15d6c-6db4-477f-bd4b-fe8e84e5f023",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter LogFile to value \"/tmp/tmp3uhhdurw.log\"\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
"Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
"Model fingerprint: 0x8de73876\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 2e+06]\n",
" Objective range [1e+00, 6e+07]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+07, 2e+07]\n",
"Presolve removed 100 rows and 50 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1 rows, 50 columns, 50 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 5.7349081e+08 1.044003e+04 0.000000e+00 0s\n",
" 1 6.8268465e+08 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 6.826846503e+08\n",
"Set parameter LogFile to value \"\"\n",
"Set parameter LogFile to value \"/tmp/tmp18aqg2ic.log\"\n",
"Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
"Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
"Model fingerprint: 0xb90d1075\n",
"Variable types: 50 continuous, 50 integer (50 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 2e+06]\n",
" Objective range [1e+00, 6e+07]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+07, 2e+07]\n",
"Found heuristic solution: objective 8.056576e+08\n",
"Presolve time: 0.00s\n",
"Presolved: 101 rows, 100 columns, 250 nonzeros\n",
"Variable types: 50 continuous, 50 integer (50 binary)\n",
"\n",
"Root relaxation: objective 6.826847e+08, 56 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 6.8268e+08 0 1 8.0566e+08 6.8268e+08 15.3% - 0s\n",
"H 0 0 7.099498e+08 6.8268e+08 3.84% - 0s\n",
" 0 0 6.8315e+08 0 3 7.0995e+08 6.8315e+08 3.78% - 0s\n",
"H 0 0 6.883227e+08 6.8315e+08 0.75% - 0s\n",
" 0 0 6.8352e+08 0 4 6.8832e+08 6.8352e+08 0.70% - 0s\n",
" 0 0 6.8352e+08 0 4 6.8832e+08 6.8352e+08 0.70% - 0s\n",
" 0 0 6.8352e+08 0 1 6.8832e+08 6.8352e+08 0.70% - 0s\n",
"H 0 0 6.862582e+08 6.8352e+08 0.40% - 0s\n",
" 0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s\n",
" 0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s\n",
" 0 0 6.8352e+08 0 1 6.8626e+08 6.8352e+08 0.40% - 0s\n",
" 0 0 6.8352e+08 0 3 6.8626e+08 6.8352e+08 0.40% - 0s\n",
" 0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s\n",
" 0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s\n",
" 0 2 6.8354e+08 0 4 6.8626e+08 6.8354e+08 0.40% - 0s\n",
"* 18 5 6 6.849018e+08 6.8413e+08 0.11% 3.1 0s\n",
"H 24 1 6.848412e+08 6.8426e+08 0.09% 3.2 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 1\n",
" Flow cover: 2\n",
"\n",
"Explored 30 nodes (217 simplex iterations) in 0.02 seconds (0.00 work units)\n",
"Thread count was 1 (of 32 available processors)\n",
"\n",
"Solution count 6: 6.84841e+08 6.84902e+08 6.86258e+08 ... 8.05658e+08\n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 6.848411655488e+08, best bound 6.848411655488e+08, gap 0.0000%\n",
"Set parameter LogFile to value \"\"\n",
"WARNING: Cannot get reduced costs for MIP.\n",
"WARNING: Cannot get duals for MIP.\n"
]
}
],
"source": [
"solver_baseline = LearningSolver()\n",
"solver_baseline.solve(test_files[0:1], build_uc_model, tee=True);"
]
},
{
"cell_type": "markdown",
"id": "b6d37b88-9fcc-43ee-ac1e-2a7b1e51a266",
"metadata": {},
"source": [
"In the log above, the `MIP start` line is missing, and Gurobi had to start with a significantly inferior initial solution. The solver was still able to find the optimal solution at the end, but it required using its own internal heuristic procedures. In this example, because we solve very small optimization problems, there was almost no difference in terms of running time. For larger problems, however, the difference can be significant. See benchmarks for more details.\n",
"\n",
"<div class=\"alert alert-info\">\n",
"Note\n",
" \n",
"In addition to partial initial solutions, MIPLearn is also able to predict lazy constraints, cutting planes and branching priorities. See the next tutorials for more details.\n",
"</div>\n",
"\n",
"<div class=\"alert alert-info\">\n",
"Note\n",
" \n",
"It is not necessary to specify what ML models to use. MIPLearn, by default, will try a number of classical ML models and will choose the one that performs the best, based on k-fold cross validation. MIPLearn is also able to automatically collect features based on the MIP formulation of the problem and the solution to the LP relaxation, among other things, so it does not require handcrafted features. If you do want to customize the models and features, however, that is also possible, as we will see in a later tutorial.\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "eec97f06",
"metadata": {
"tags": []
},
"source": [
"## Accessing the solution\n",
"\n",
"In the example above, we used `LearningSolver.solve` together with data files to solve both the training and the test instances. The optimal solutions were saved to HDF5 files in the train/test folders, and could be retrieved by reading theses files, but that is not very convenient. In the following example, we show how to build and solve a Pyomo model entirely in-memory, using our trained solver."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "67a6cd18",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"obj = 903865807.3536932\n",
" x = [1.0, 1.0, 1.0, 1.0, 1.0]\n",
" y = [1105176.593734543, 1891284.5155055337, 1708177.4224033852, 1438329.610189608, 535496.3347187206]\n"
]
}
],
"source": [
"# Construct model using previously defined functions\n",
"data = random_uc_data(samples=1, n=50)[0]\n",
"model = build_uc_model(data)\n",
"\n",
"# Solve model using ML + Gurobi\n",
"solver_ml.solve(model)\n",
"\n",
"# Print part of the optimal solution\n",
"print(\"obj =\", model.obj())\n",
"print(\" x =\", [model.x[i].value for i in range(5)])\n",
"print(\" y =\", [model.y[i].value for i in range(5)])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5593d23a-83bd-4e16-8253-6300f5e3f63b",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -25,13 +25,6 @@
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" /> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css" type="text/css" /> <link rel="stylesheet" href="../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css" type="text/css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" /> <link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/custom.css" /> <link rel="stylesheet" type="text/css" href="../_static/custom.css" />
<link rel="preload" as="script" href="../_static/js/index.1c5a1a01449ed65a7b51.js"> <link rel="preload" as="script" href="../_static/js/index.1c5a1a01449ed65a7b51.js">
@ -121,7 +114,7 @@
</li> </li>
<li class="toctree-l1"> <li class="toctree-l1">
<a class="reference internal" href="../guide/solvers/"> <a class="reference internal" href="../guide/solvers/">
8. Solvers 8. Learning Solver
</a> </a>
</li> </li>
</ul> </ul>

@ -39,9 +39,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 3, "execution_count": 3,
"id": "6d342a4e", "id": "f906fe9c",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [ "outputs": [
{ {
@ -103,18 +106,14 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "8a46bb8a", "id": "50441907",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [] "source": []
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "d8699092", "id": "d0000c8d",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"## Basic collector\n", "## Basic collector\n",
"\n", "\n",
@ -131,10 +130,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "99a122dc", "id": "6529f667",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"### Data fields\n", "### Data fields\n",
"\n", "\n",
@ -172,10 +169,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "7fe76973", "id": "f2894594",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"### Example\n", "### Example\n",
"\n", "\n",
@ -185,9 +180,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 4, "execution_count": 4,
"id": "425717fe", "id": "ac6f8c6f",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [ "outputs": [
{ {
@ -244,9 +242,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"id": "30023d2b", "id": "78f0b07a",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [], "outputs": [],
"source": [] "source": []
@ -254,7 +255,7 @@
], ],
"metadata": { "metadata": {
"kernelspec": { "kernelspec": {
"display_name": "Python 3", "display_name": "Python 3 (ipykernel)",
"language": "python", "language": "python",
"name": "python3" "name": "python3"
}, },
@ -268,7 +269,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.9.12" "version": "3.9.16"
} }
}, },
"nbformat": 4, "nbformat": 4,

@ -118,7 +118,7 @@
</li> </li>
<li class="toctree-l1"> <li class="toctree-l1">
<a class="reference internal" href="../solvers/"> <a class="reference internal" href="../solvers/">
8. Solvers 8. LearningSolver
</a> </a>
</li> </li>
</ul> </ul>

@ -7,15 +7,13 @@
"source": [ "source": [
"# Feature Extractors\n", "# Feature Extractors\n",
"\n", "\n",
"In the previous page, we introduced *training data collectors*, which solve the optimization problem and collect raw training data, such as the optimal solution. In this page, we introduce **feature extractors**, which take the raw training data, stored in HDF5 files, and extract relevant information in order to train a machine learning model. We describe the extractors readily available in MIPLearn." "In the previous page, we introduced *training data collectors*, which solve the optimization problem and collect raw training data, such as the optimal solution. In this page, we introduce **feature extractors**, which take the raw training data, stored in HDF5 files, and extract relevant information in order to train a machine learning model."
] ]
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "94df359d", "id": "b4026de5",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"\n", "\n",
"## Overview\n", "## Overview\n",
@ -32,10 +30,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "d450370d", "id": "b2d9736c",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"\n", "\n",
"## H5FieldsExtractor\n", "## H5FieldsExtractor\n",
@ -45,10 +41,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "b0e96d25", "id": "e8184dff",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"### Example\n", "### Example\n",
"\n", "\n",
@ -58,9 +52,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 5, "execution_count": 5,
"id": "82609250", "id": "ed9a18c8",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [ "outputs": [
{ {
@ -174,10 +171,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "b6912b56", "id": "2da2e74e",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"\n", "\n",
"[H5FieldsExtractor]: ../../api/collectors/#miplearn.features.fields.H5FieldsExtractor" "[H5FieldsExtractor]: ../../api/collectors/#miplearn.features.fields.H5FieldsExtractor"
@ -185,10 +180,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "81fd1d27", "id": "d879c0d3",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"<div class=\"alert alert-warning\">\n", "<div class=\"alert alert-warning\">\n",
"Warning\n", "Warning\n",
@ -199,10 +192,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "fdbf5674", "id": "cd0ba071",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"## AlvLouWeh2017Extractor\n", "## AlvLouWeh2017Extractor\n",
"\n", "\n",
@ -214,9 +205,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 6, "execution_count": 6,
"id": "85ef526d", "id": "a1bc38fe",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [ "outputs": [
{ {
@ -303,10 +297,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "3e17c5f8", "id": "286c9927",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"<div class=\"alert alert-info\">\n", "<div class=\"alert alert-info\">\n",
"References\n", "References\n",
@ -320,7 +312,7 @@
], ],
"metadata": { "metadata": {
"kernelspec": { "kernelspec": {
"display_name": "Python 3", "display_name": "Python 3 (ipykernel)",
"language": "python", "language": "python",
"name": "python3" "name": "python3"
}, },
@ -334,7 +326,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.9.12" "version": "3.9.16"
} }
}, },
"nbformat": 4, "nbformat": 4,

@ -119,7 +119,7 @@
</li> </li>
<li class="toctree-l1"> <li class="toctree-l1">
<a class="reference internal" href="../solvers/"> <a class="reference internal" href="../solvers/">
8. Solvers 8. LearningSolver
</a> </a>
</li> </li>
</ul> </ul>
@ -263,7 +263,7 @@
<div class="section" id="Feature-Extractors"> <div class="section" id="Feature-Extractors">
<h1><span class="section-number">6. </span>Feature Extractors<a class="headerlink" href="#Feature-Extractors" title="Permalink to this headline"></a></h1> <h1><span class="section-number">6. </span>Feature Extractors<a class="headerlink" href="#Feature-Extractors" title="Permalink to this headline"></a></h1>
<p>In the previous page, we introduced <em>training data collectors</em>, which solve the optimization problem and collect raw training data, such as the optimal solution. In this page, we introduce <strong>feature extractors</strong>, which take the raw training data, stored in HDF5 files, and extract relevant information in order to train a machine learning model. We describe the extractors readily available in MIPLearn.</p> <p>In the previous page, we introduced <em>training data collectors</em>, which solve the optimization problem and collect raw training data, such as the optimal solution. In this page, we introduce <strong>feature extractors</strong>, which take the raw training data, stored in HDF5 files, and extract relevant information in order to train a machine learning model.</p>
<div class="section" id="Overview"> <div class="section" id="Overview">
<h2><span class="section-number">6.1. </span>Overview<a class="headerlink" href="#Overview" title="Permalink to this headline"></a></h2> <h2><span class="section-number">6.1. </span>Overview<a class="headerlink" href="#Overview" title="Permalink to this headline"></a></h2>
<p>Feature extraction is an important step of the process of building a machine learning model because it helps to reduce the complexity of the data and convert it into a format that is more easily processed. Previous research has proposed converting absolute variable coefficients, for example, into relative values which are invariant to various transformations, such as problem scaling, making them more amenable to learning. Various other transformations have also been described.</p> <p>Feature extraction is an important step of the process of building a machine learning model because it helps to reduce the complexity of the data and convert it into a format that is more easily processed. Previous research has proposed converting absolute variable coefficients, for example, into relative values which are invariant to various transformations, such as problem scaling, making them more amenable to learning. Various other transformations have also been described.</p>

@ -283,7 +283,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.9.12" "version": "3.9.16"
} }
}, },
"nbformat": 4, "nbformat": 4,

@ -41,7 +41,7 @@
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script> <script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script>
<link rel="index" title="Index" href="../../genindex/" /> <link rel="index" title="Index" href="../../genindex/" />
<link rel="search" title="Search" href="../../search/" /> <link rel="search" title="Search" href="../../search/" />
<link rel="next" title="8. Solvers" href="../solvers/" /> <link rel="next" title="8. LearningSolver" href="../solvers/" />
<link rel="prev" title="6. Feature Extractors" href="../features/" /> <link rel="prev" title="6. Feature Extractors" href="../features/" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="docsearch:language" content="en" /> <meta name="docsearch:language" content="en" />
@ -120,7 +120,7 @@
</li> </li>
<li class="toctree-l1"> <li class="toctree-l1">
<a class="reference internal" href="../solvers/"> <a class="reference internal" href="../solvers/">
8. Solvers 8. LearningSolver
</a> </a>
</li> </li>
</ul> </ul>
@ -511,7 +511,7 @@ been computed, then directly provides it to the solver using one of the availabl
<div class='prev-next-bottom'> <div class='prev-next-bottom'>
<a class='left-prev' id="prev-link" href="../features/" title="previous page"><span class="section-number">6. </span>Feature Extractors</a> <a class='left-prev' id="prev-link" href="../features/" title="previous page"><span class="section-number">6. </span>Feature Extractors</a>
<a class='right-next' id="next-link" href="../solvers/" title="next page"><span class="section-number">8. </span>Solvers</a> <a class='right-next' id="next-link" href="../solvers/" title="next page"><span class="section-number">8. </span>LearningSolver</a>
</div> </div>

@ -9,7 +9,7 @@
"\n", "\n",
"## Overview\n", "## Overview\n",
"\n", "\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, unfortunately, make existing benchmark sets less than ideal 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", "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", "\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. 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",
"\n", "\n",
@ -18,10 +18,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "1ba30e52", "id": "bd99c51f",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"<div class=\"alert alert-warning\">\n", "<div class=\"alert alert-warning\">\n",
"Warning\n", "Warning\n",
@ -64,10 +62,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "218add9f", "id": "5e502345",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"\n", "\n",
"$$\n", "$$\n",
@ -85,10 +81,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "3ffe5c46", "id": "9cba2077",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"### Random instance generator\n", "### Random instance generator\n",
"\n", "\n",
@ -103,10 +97,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "fd6cb059", "id": "2bc62803",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"### Example" "### Example"
] ]
@ -234,10 +226,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "307ab9bf", "id": "d0d3ea42",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"\n", "\n",
"$$\n", "$$\n",
@ -304,10 +294,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "5caf77ba", "id": "f12a066f",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"### Example" "### Example"
] ]
@ -472,10 +460,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "838ef9d8", "id": "4e701397",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"### Example" "### Example"
] ]
@ -599,10 +585,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "96a26e2d", "id": "d5254e7a",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"### Formulation\n", "### Formulation\n",
"\n", "\n",
@ -745,10 +729,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "7c5d228d", "id": "19342eb1",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"### Formulation\n", "### Formulation\n",
"\n", "\n",
@ -757,10 +739,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "7361cea0", "id": "0391b35b",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"$$\n", "$$\n",
"\\begin{align*}\n", "\\begin{align*}\n",
@ -776,10 +756,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "c32306f4", "id": "c2d7df7b",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"### Random instance generator\n", "### Random instance generator\n",
"\n", "\n",
@ -794,9 +772,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 5, "execution_count": 5,
"id": "4607dbda", "id": "cc797da7",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [ "outputs": [
{ {
@ -888,10 +869,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "93235cdd", "id": "2f74dd10",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"$$\n", "$$\n",
"\\begin{align*}\n", "\\begin{align*}\n",
@ -905,10 +884,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "90b9e623", "id": "ef030168",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"\n", "\n",
"### Random instance generator\n", "### Random instance generator\n",
@ -1018,10 +995,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "aa307ff0", "id": "da3ca69c",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"### Formulation\n", "### Formulation\n",
"\n", "\n",
@ -1030,10 +1005,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "a5436195", "id": "9cf296e9",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"$$\n", "$$\n",
"\\begin{align*}\n", "\\begin{align*}\n",
@ -1050,10 +1023,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "df26c9f5", "id": "eba3dbe5",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"### Random instance generator\n", "### Random instance generator\n",
"\n", "\n",
@ -1070,10 +1041,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "0fd000fe", "id": "61f16c56",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"### Example" "### Example"
] ]
@ -1081,9 +1050,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 7, "execution_count": 7,
"id": "6ee78519", "id": "9d0c56c6",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [ "outputs": [
{ {
@ -1196,10 +1168,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "fd30f83e", "id": "7048d771",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"\n", "\n",
"<div class=\"alert alert-info\">\n", "<div class=\"alert alert-info\">\n",
@ -1215,10 +1185,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "1da000b8", "id": "bec5ee1c",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"\n", "\n",
"$$\n", "$$\n",
@ -1251,10 +1219,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "721f7b0c", "id": "4a1ffb4c",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"\n", "\n",
"The first set of inequalities enforces minimum up-time constraints: if unit $g$ is down at time $t$, then it cannot start up during the previous $L_g$ time steps. The second set of inequalities enforces minimum down-time constraints, and is symmetrical to the previous one. The third set ensures that if unit $g$ starts up at time $t$, then the start up variable must be one. The fourth set ensures that demand is satisfied at each time period. The fifth and sixth sets enforce bounds to the quantity of power generated by each unit.\n", "The first set of inequalities enforces minimum up-time constraints: if unit $g$ is down at time $t$, then it cannot start up during the previous $L_g$ time steps. The second set of inequalities enforces minimum down-time constraints, and is symmetrical to the previous one. The third set ensures that if unit $g$ starts up at time $t$, then the start up variable must be one. The fourth set ensures that demand is satisfied at each time period. The fifth and sixth sets enforce bounds to the quantity of power generated by each unit.\n",
@ -1268,10 +1234,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "f49a5e24", "id": "01bed9fc",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"\n", "\n",
"### Random instance generator\n", "### Random instance generator\n",
@ -1287,10 +1251,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "cae4f51a", "id": "855b87b4",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"### Example" "### Example"
] ]
@ -1298,9 +1260,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 8, "execution_count": 8,
"id": "2d7295e0", "id": "6217da7c",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [ "outputs": [
{ {
@ -1440,10 +1405,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "09ba5ccf", "id": "91f5781a",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"\n", "\n",
"### Formulation\n", "### Formulation\n",
@ -1453,10 +1416,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "c72baa43", "id": "544754cb",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
" $$\n", " $$\n",
"\\begin{align*}\n", "\\begin{align*}\n",
@ -1472,10 +1433,8 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "43bb19ae", "id": "35c99166",
"metadata": { "metadata": {},
"collapsed": false
},
"source": [ "source": [
"### Random instance generator\n", "### Random instance generator\n",
"\n", "\n",
@ -1573,9 +1532,12 @@
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"id": "c0a76d28", "id": "9f12e91f",
"metadata": { "metadata": {
"collapsed": false "collapsed": false,
"jupyter": {
"outputs_hidden": false
}
}, },
"outputs": [], "outputs": [],
"source": [] "source": []
@ -1583,7 +1545,7 @@
], ],
"metadata": { "metadata": {
"kernelspec": { "kernelspec": {
"display_name": "Python 3", "display_name": "Python 3 (ipykernel)",
"language": "python", "language": "python",
"name": "python3" "name": "python3"
}, },
@ -1597,7 +1559,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.9.12" "version": "3.9.16"
} }
}, },
"nbformat": 4, "nbformat": 4,

@ -121,7 +121,7 @@
</li> </li>
<li class="toctree-l1"> <li class="toctree-l1">
<a class="reference internal" href="../solvers/"> <a class="reference internal" href="../solvers/">
8. Solvers 8. LearningSolver
</a> </a>
</li> </li>
</ul> </ul>
@ -441,8 +441,8 @@
<h1><span class="section-number">4. </span>Benchmark Problems<a class="headerlink" href="#Benchmark-Problems" title="Permalink to this headline"></a></h1> <h1><span class="section-number">4. </span>Benchmark Problems<a class="headerlink" href="#Benchmark-Problems" title="Permalink to this headline"></a></h1>
<div class="section" id="Overview"> <div class="section" id="Overview">
<h2><span class="section-number">4.1. </span>Overview<a class="headerlink" href="#Overview" title="Permalink to this headline"></a></h2> <h2><span class="section-number">4.1. </span>Overview<a class="headerlink" href="#Overview" title="Permalink to this headline"></a></h2>
<p>Benchmark sets such as <a class="reference external" href="https://miplib.zib.de/">MIPLIB</a> or <a class="reference external" href="http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/">TSPLIB</a> are usually employed to evaluate the performance of conventional MIP solvers. Two shortcomings, unfortunately, make existing benchmark sets less than ideal 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 <p>Benchmark sets such as <a class="reference external" href="https://miplib.zib.de/">MIPLIB</a> or <a class="reference external" href="http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/">TSPLIB</a> 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
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.</p> 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.</p>
<p>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 <p>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.</p> 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.</p>
<p>In the following, we describe the problems included in the library, their MIP formulation and the generation algorithm.</p> <p>In the following, we describe the problems included in the library, their MIP formulation and the generation algorithm.</p>

@ -1,20 +1,63 @@
{ {
"cells": [ "cells": [
{ {
"attachments": {},
"cell_type": "markdown", "cell_type": "markdown",
"id": "3371f072-be1e-4c47-b765-b5d30fdbfae6", "id": "9ec1907b-db93-4840-9439-c9005902b968",
"metadata": {}, "metadata": {},
"source": [ "source": [
"# Solvers\n", "# Learning Solver\n",
"\n", "\n",
"## LearningSolver\n", "On previous pages, we discussed various components of the MIPLearn framework, including training data collectors, feature extractors, and individual machine learning components. In this page, we introduce **LearningSolver**, the main class of the framework which integrates all the aforementioned components into a cohesive whole. Using **LearningSolver** involves three steps: (i) configuring the solver; (ii) training the ML components; and (iii) solving new MIP instances. In the following, we describe each of these steps, then conclude with a complete runnable example.\n",
"\n", "\n",
"### Example" "### Configuring the solver\n",
"\n",
"**LearningSolver** is composed by multiple individual machine learning components, each targeting a different part of the solution process, or implementing a different machine learning strategy. This architecture allows strategies to be easily enabled, disabled or customized, making the framework flexible. By default, no components are provided and **LearningSolver** is equivalent to a traditional MIP solver. To specify additional components, the `components` constructor argument may be used:\n",
"\n",
"```python\n",
"solver = LearningSolver(\n",
" components=[\n",
" comp1,\n",
" comp2,\n",
" comp3,\n",
" ]\n",
")\n",
"```\n",
"\n",
"In this example, three components `comp1`, `comp2` and `comp3` are provided. The strategies implemented by these components are applied sequentially when solving the problem. For example, `comp1` and `comp2` could fix a subset of decision variables, while `comp3` constructs a warm start for the remaining problem.\n",
"\n",
"### Training and solving new instances\n",
"\n",
"Once a solver is configured, its ML components need to be trained. This can be achieved by the `solver.fit` method, as illustrated below. The method accepts a list of HDF5 files and trains each individual component sequentially. Once the solver is trained, new instances can be solved using `solver.optimize`. The method returns a dictionary of statistics collected by each component, such as the number of variables fixed.\n",
"\n",
"```python\n",
"# Build instances\n",
"train_data = ...\n",
"test_data = ...\n",
"\n",
"# Collect training data\n",
"bc = BasicCollector()\n",
"bc.collect(train_data, build_model)\n",
"\n",
"# Build solver\n",
"solver = LearningSolver(...)\n",
"\n",
"# Train components\n",
"solver.fit(train_data)\n",
"\n",
"# Solve a new test instance\n",
"stats = solver.optimize(test_data[0], build_model)\n",
"\n",
"```\n",
"\n",
"### Complete example\n",
"\n",
"In the example below, we illustrate the usage of **LearningSolver** by building instances of the Traveling Salesman Problem, collecting training data, training the ML components, then solving a new instance."
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 1, "execution_count": 3,
"id": "92b09b98", "id": "92b09b98",
"metadata": { "metadata": {
"collapsed": false, "collapsed": false,
@ -23,21 +66,15 @@
} }
}, },
"outputs": [ "outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/axavier/Software/anaconda3/envs/miplearn/lib/python3.8/site-packages/tqdm/auto.py:22: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
" from .autonotebook import tqdm as notebook_tqdm\n"
]
},
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Restricted license - for non-production use only - expires 2023-10-25\n", "Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)\n",
"Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (linux64)\n", "\n",
"Thread count: 6 physical cores, 12 logical processors, using up to 12 threads\n", "CPU model: AMD Ryzen 9 7950X 16-Core Processor, instruction set [SSE2|AVX|AVX2|AVX512]\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
"\n",
"Optimize a model with 10 rows, 45 columns and 90 nonzeros\n", "Optimize a model with 10 rows, 45 columns and 90 nonzeros\n",
"Model fingerprint: 0x6ddcd141\n", "Model fingerprint: 0x6ddcd141\n",
"Coefficient statistics:\n", "Coefficient statistics:\n",
@ -52,11 +89,14 @@
" 0 6.3600000e+02 1.700000e+01 0.000000e+00 0s\n", " 0 6.3600000e+02 1.700000e+01 0.000000e+00 0s\n",
" 15 2.7610000e+03 0.000000e+00 0.000000e+00 0s\n", " 15 2.7610000e+03 0.000000e+00 0.000000e+00 0s\n",
"\n", "\n",
"Solved in 15 iterations and 0.01 seconds (0.00 work units)\n", "Solved in 15 iterations and 0.00 seconds (0.00 work units)\n",
"Optimal objective 2.761000000e+03\n", "Optimal objective 2.761000000e+03\n",
"Set parameter LazyConstraints to value 1\n", "Set parameter LazyConstraints to value 1\n",
"Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (linux64)\n", "Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)\n",
"Thread count: 6 physical cores, 12 logical processors, using up to 12 threads\n", "\n",
"CPU model: AMD Ryzen 9 7950X 16-Core Processor, instruction set [SSE2|AVX|AVX2|AVX512]\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 32 threads\n",
"\n",
"Optimize a model with 10 rows, 45 columns and 90 nonzeros\n", "Optimize a model with 10 rows, 45 columns and 90 nonzeros\n",
"Model fingerprint: 0x74ca3d0a\n", "Model fingerprint: 0x74ca3d0a\n",
"Variable types: 0 continuous, 45 integer (45 binary)\n", "Variable types: 0 continuous, 45 integer (45 binary)\n",
@ -66,7 +106,7 @@
" Bounds range [1e+00, 1e+00]\n", " Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+00, 2e+00]\n", " RHS range [2e+00, 2e+00]\n",
"\n", "\n",
"User MIP start produced solution with objective 2796 (0.01s)\n", "User MIP start produced solution with objective 2796 (0.00s)\n",
"Loaded user MIP start with objective 2796\n", "Loaded user MIP start with objective 2796\n",
"\n", "\n",
"Presolve time: 0.00s\n", "Presolve time: 0.00s\n",
@ -78,48 +118,32 @@
" Nodes | Current Node | Objective Bounds | Work\n", " Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n", " Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\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", " 0 0 cutoff 0 2796.00000 2796.00000 0.00% - 0s\n",
"\n", "\n",
"Cutting planes:\n", "Cutting planes:\n",
" Lazy constraints: 3\n", " Lazy constraints: 3\n",
"\n", "\n",
"Explored 1 nodes (15 simplex iterations) in 0.01 seconds (0.00 work units)\n", "Explored 1 nodes (16 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 12 (of 12 available processors)\n", "Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 1: 2796 \n",
"\n",
"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 103, time in user-callback 0.00 sec\n",
"Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (linux64)\n",
"Thread count: 6 physical cores, 12 logical processors, using up to 12 threads\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",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+00]\n",
" Objective range [4e+01, 1e+03]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+00, 2e+00]\n",
"Presolved: 10 rows, 45 columns, 90 nonzeros\n",
"\n",
"Continuing optimization...\n",
"\n",
"\n",
"Cutting planes:\n",
" Lazy constraints: 3\n",
"\n",
"Explored 1 nodes (15 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 12 (of 12 available processors)\n",
"\n", "\n",
"Solution count 1: 2796 \n", "Solution count 1: 2796 \n",
"\n", "\n",
"Optimal solution found (tolerance 1.00e-04)\n", "Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 2.796000000000e+03, best bound 2.796000000000e+03, gap 0.0000%\n", "Best objective 2.796000000000e+03, best bound 2.796000000000e+03, gap 0.0000%\n",
"\n", "\n",
"User-callback calls 27, time in user-callback 0.00 sec\n" "User-callback calls 110, time in user-callback 0.00 sec\n"
] ]
},
{
"data": {
"text/plain": [
"{'WS: Count': 1, 'WS: Number of variables set': 41.0}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
} }
], ],
"source": [ "source": [
@ -135,7 +159,7 @@
"from miplearn.components.primal.actions import SetWarmStart\n", "from miplearn.components.primal.actions import SetWarmStart\n",
"from miplearn.components.primal.indep import IndependentVarsPrimalComponent\n", "from miplearn.components.primal.indep import IndependentVarsPrimalComponent\n",
"from miplearn.extractors.AlvLouWeh2017 import AlvLouWeh2017Extractor\n", "from miplearn.extractors.AlvLouWeh2017 import AlvLouWeh2017Extractor\n",
"from miplearn.io import save\n", "from miplearn.io import write_pkl_gz\n",
"from miplearn.problems.tsp import (\n", "from miplearn.problems.tsp import (\n",
" TravelingSalesmanGenerator,\n", " TravelingSalesmanGenerator,\n",
" build_tsp_model,\n", " build_tsp_model,\n",
@ -157,7 +181,7 @@
").generate(50)\n", ").generate(50)\n",
"\n", "\n",
"# Save instance data to data/tsp/00000.pkl.gz, data/tsp/00001.pkl.gz, ...\n", "# Save instance data to data/tsp/00000.pkl.gz, data/tsp/00001.pkl.gz, ...\n",
"all_data = save(data, \"data/tsp\")\n", "all_data = write_pkl_gz(data, \"data/tsp\")\n",
"\n", "\n",
"# Split train/test data\n", "# Split train/test data\n",
"train_data = all_data[:40]\n", "train_data = all_data[:40]\n",
@ -187,12 +211,12 @@
"solver.fit(train_data)\n", "solver.fit(train_data)\n",
"\n", "\n",
"# Solve a test instance\n", "# Solve a test instance\n",
"solver.optimize(test_data[0], build_tsp_model);" "solver.optimize(test_data[0], build_tsp_model)\n"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 1, "execution_count": null,
"id": "e27d2cbd-5341-461d-bbc1-8131aee8d949", "id": "e27d2cbd-5341-461d-bbc1-8131aee8d949",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
@ -215,7 +239,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.8.13" "version": "3.9.12"
} }
}, },
"nbformat": 4, "nbformat": 4,

@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>8. Solvers &#8212; MIPLearn 0.3</title> <title>8. Learning Solver &#8212; MIPLearn 0.3</title>
<link href="../../_static/css/theme.css" rel="stylesheet" /> <link href="../../_static/css/theme.css" rel="stylesheet" />
<link href="../../_static/css/index.c5995385ac14fb8791e8eb36b4908be2.css" rel="stylesheet" /> <link href="../../_static/css/index.c5995385ac14fb8791e8eb36b4908be2.css" rel="stylesheet" />
@ -25,10 +25,6 @@
<link rel="stylesheet" href="../../_static/pygments.css" type="text/css" /> <link rel="stylesheet" href="../../_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css" type="text/css" /> <link rel="stylesheet" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css" type="text/css" />
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" /> <link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../../_static/custom.css" /> <link rel="stylesheet" type="text/css" href="../../_static/custom.css" />
<link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js"> <link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js">
@ -122,7 +118,7 @@
</li> </li>
<li class="toctree-l1 current active"> <li class="toctree-l1 current active">
<a class="current reference internal" href="#"> <a class="current reference internal" href="#">
8. Solvers 8. Learning Solver
</a> </a>
</li> </li>
</ul> </ul>
@ -225,16 +221,19 @@
<nav id="bd-toc-nav"> <nav id="bd-toc-nav">
<ul class="visible nav section-nav flex-column"> <ul class="visible nav section-nav flex-column">
<li class="toc-h2 nav-item toc-entry"> <li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#LearningSolver"> <a class="reference internal nav-link" href="#Configuring-the-solver">
8.1. LearningSolver 8.1. Configuring the solver
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Training-and-solving-new-instances">
8.2. Training and solving new instances
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Complete-example">
8.3. Complete example
</a> </a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#Example">
Example
</a>
</li>
</ul>
</li> </li>
</ul> </ul>
@ -247,14 +246,51 @@
<div> <div>
<div class="section" id="Solvers"> <div class="section" id="Learning-Solver">
<h1><span class="section-number">8. </span>Solvers<a class="headerlink" href="#Solvers" title="Permalink to this headline"></a></h1> <h1><span class="section-number">8. </span>Learning Solver<a class="headerlink" href="#Learning-Solver" title="Permalink to this headline"></a></h1>
<div class="section" id="LearningSolver"> <p>On previous pages, we discussed various components of the MIPLearn framework, including training data collectors, feature extractors, and individual machine learning components. In this page, we introduce <strong>LearningSolver</strong>, the main class of the framework which integrates all the aforementioned components into a cohesive whole. Using <strong>LearningSolver</strong> involves three steps: (i) configuring the solver; (ii) training the ML components; and (iii) solving new MIP instances. In the following, we
<h2><span class="section-number">8.1. </span>LearningSolver<a class="headerlink" href="#LearningSolver" title="Permalink to this headline"></a></h2> describe each of these steps, then conclude with a complete runnable example.</p>
<div class="section" id="Example"> <div class="section" id="Configuring-the-solver">
<h3>Example<a class="headerlink" href="#Example" title="Permalink to this headline"></a></h3> <h2><span class="section-number">8.1. </span>Configuring the solver<a class="headerlink" href="#Configuring-the-solver" title="Permalink to this headline"></a></h2>
<p><strong>LearningSolver</strong> is composed by multiple individual machine learning components, each targeting a different part of the solution process, or implementing a different machine learning strategy. This architecture allows strategies to be easily enabled, disabled or customized, making the framework flexible. By default, no components are provided and <strong>LearningSolver</strong> is equivalent to a traditional MIP solver. To specify additional components, the <code class="docutils literal notranslate"><span class="pre">components</span></code> constructor argument may be used:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">solver</span> <span class="o">=</span> <span class="n">LearningSolver</span><span class="p">(</span>
<span class="n">components</span><span class="o">=</span><span class="p">[</span>
<span class="n">comp1</span><span class="p">,</span>
<span class="n">comp2</span><span class="p">,</span>
<span class="n">comp3</span><span class="p">,</span>
<span class="p">]</span>
<span class="p">)</span>
</pre></div>
</div>
<p>In this example, three components <code class="docutils literal notranslate"><span class="pre">comp1</span></code>, <code class="docutils literal notranslate"><span class="pre">comp2</span></code> and <code class="docutils literal notranslate"><span class="pre">comp3</span></code> are provided. The strategies implemented by these components are applied sequentially when solving the problem. For example, <code class="docutils literal notranslate"><span class="pre">comp1</span></code> and <code class="docutils literal notranslate"><span class="pre">comp2</span></code> could fix a subset of decision variables, while <code class="docutils literal notranslate"><span class="pre">comp3</span></code> constructs a warm start for the remaining problem.</p>
</div>
<div class="section" id="Training-and-solving-new-instances">
<h2><span class="section-number">8.2. </span>Training and solving new instances<a class="headerlink" href="#Training-and-solving-new-instances" title="Permalink to this headline"></a></h2>
<p>Once a solver is configured, its ML components need to be trained. This can be achieved by the <code class="docutils literal notranslate"><span class="pre">solver.fit</span></code> method, as illustrated below. The method accepts a list of HDF5 files and trains each individual component sequentially. Once the solver is trained, new instances can be solved using <code class="docutils literal notranslate"><span class="pre">solver.optimize</span></code>. The method returns a dictionary of statistics collected by each component, such as the number of variables fixed.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># Build instances</span>
<span class="n">train_data</span> <span class="o">=</span> <span class="o">...</span>
<span class="n">test_data</span> <span class="o">=</span> <span class="o">...</span>
<span class="c1"># Collect training data</span>
<span class="n">bc</span> <span class="o">=</span> <span class="n">BasicCollector</span><span class="p">()</span>
<span class="n">bc</span><span class="o">.</span><span class="n">collect</span><span class="p">(</span><span class="n">train_data</span><span class="p">,</span> <span class="n">build_model</span><span class="p">)</span>
<span class="c1"># Build solver</span>
<span class="n">solver</span> <span class="o">=</span> <span class="n">LearningSolver</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="c1"># Train components</span>
<span class="n">solver</span><span class="o">.</span><span class="n">fit</span><span class="p">(</span><span class="n">train_data</span><span class="p">)</span>
<span class="c1"># Solve a new test instance</span>
<span class="n">stats</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">optimize</span><span class="p">(</span><span class="n">test_data</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">build_model</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="section" id="Complete-example">
<h2><span class="section-number">8.3. </span>Complete example<a class="headerlink" href="#Complete-example" title="Permalink to this headline"></a></h2>
<p>In the example below, we illustrate the usage of <strong>LearningSolver</strong> by building instances of the Traveling Salesman Problem, collecting training data, training the ML components, then solving a new instance.</p>
<div class="nbinput docutils container"> <div class="nbinput docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[1]: <div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[3]:
</pre></div> </pre></div>
</div> </div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">random</span> <div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">random</span>
@ -269,7 +305,7 @@
<span class="kn">from</span> <span class="nn">miplearn.components.primal.actions</span> <span class="kn">import</span> <span class="n">SetWarmStart</span> <span class="kn">from</span> <span class="nn">miplearn.components.primal.actions</span> <span class="kn">import</span> <span class="n">SetWarmStart</span>
<span class="kn">from</span> <span class="nn">miplearn.components.primal.indep</span> <span class="kn">import</span> <span class="n">IndependentVarsPrimalComponent</span> <span class="kn">from</span> <span class="nn">miplearn.components.primal.indep</span> <span class="kn">import</span> <span class="n">IndependentVarsPrimalComponent</span>
<span class="kn">from</span> <span class="nn">miplearn.extractors.AlvLouWeh2017</span> <span class="kn">import</span> <span class="n">AlvLouWeh2017Extractor</span> <span class="kn">from</span> <span class="nn">miplearn.extractors.AlvLouWeh2017</span> <span class="kn">import</span> <span class="n">AlvLouWeh2017Extractor</span>
<span class="kn">from</span> <span class="nn">miplearn.io</span> <span class="kn">import</span> <span class="n">save</span> <span class="kn">from</span> <span class="nn">miplearn.io</span> <span class="kn">import</span> <span class="n">write_pkl_gz</span>
<span class="kn">from</span> <span class="nn">miplearn.problems.tsp</span> <span class="kn">import</span> <span class="p">(</span> <span class="kn">from</span> <span class="nn">miplearn.problems.tsp</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">TravelingSalesmanGenerator</span><span class="p">,</span> <span class="n">TravelingSalesmanGenerator</span><span class="p">,</span>
<span class="n">build_tsp_model</span><span class="p">,</span> <span class="n">build_tsp_model</span><span class="p">,</span>
@ -291,7 +327,7 @@
<span class="p">)</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="mi">50</span><span class="p">)</span> <span class="p">)</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="mi">50</span><span class="p">)</span>
<span class="c1"># Save instance data to data/tsp/00000.pkl.gz, data/tsp/00001.pkl.gz, ...</span> <span class="c1"># Save instance data to data/tsp/00000.pkl.gz, data/tsp/00001.pkl.gz, ...</span>
<span class="n">all_data</span> <span class="o">=</span> <span class="n">save</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="s2">&quot;data/tsp&quot;</span><span class="p">)</span> <span class="n">all_data</span> <span class="o">=</span> <span class="n">write_pkl_gz</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="s2">&quot;data/tsp&quot;</span><span class="p">)</span>
<span class="c1"># Split train/test data</span> <span class="c1"># Split train/test data</span>
<span class="n">train_data</span> <span class="o">=</span> <span class="n">all_data</span><span class="p">[:</span><span class="mi">40</span><span class="p">]</span> <span class="n">train_data</span> <span class="o">=</span> <span class="n">all_data</span><span class="p">[:</span><span class="mi">40</span><span class="p">]</span>
@ -321,27 +357,20 @@
<span class="n">solver</span><span class="o">.</span><span class="n">fit</span><span class="p">(</span><span class="n">train_data</span><span class="p">)</span> <span class="n">solver</span><span class="o">.</span><span class="n">fit</span><span class="p">(</span><span class="n">train_data</span><span class="p">)</span>
<span class="c1"># Solve a test instance</span> <span class="c1"># Solve a test instance</span>
<span class="n">solver</span><span class="o">.</span><span class="n">optimize</span><span class="p">(</span><span class="n">test_data</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">build_tsp_model</span><span class="p">);</span> <span class="n">solver</span><span class="o">.</span><span class="n">optimize</span><span class="p">(</span><span class="n">test_data</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">build_tsp_model</span><span class="p">)</span>
</pre></div> <br/></pre></div>
</div> </div>
</div> </div>
<div class="nboutput docutils container"> <div class="nboutput docutils container">
<div class="prompt empty docutils container"> <div class="prompt empty docutils container">
</div> </div>
<div class="output_area stderr docutils container">
<div class="highlight"><pre>
/home/axavier/Software/anaconda3/envs/miplearn/lib/python3.8/site-packages/tqdm/auto.py:22: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm
</pre></div></div>
</div>
<div class="nboutput nblast docutils container">
<div class="prompt empty docutils container">
</div>
<div class="output_area docutils container"> <div class="output_area docutils container">
<div class="highlight"><pre> <div class="highlight"><pre>
Restricted license - for non-production use only - expires 2023-10-25 Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)
Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (linux64)
Thread count: 6 physical cores, 12 logical processors, using up to 12 threads CPU model: AMD Ryzen 9 7950X 16-Core Processor, instruction set [SSE2|AVX|AVX2|AVX512]
Thread count: 16 physical cores, 32 logical processors, using up to 32 threads
Optimize a model with 10 rows, 45 columns and 90 nonzeros Optimize a model with 10 rows, 45 columns and 90 nonzeros
Model fingerprint: 0x6ddcd141 Model fingerprint: 0x6ddcd141
Coefficient statistics: Coefficient statistics:
@ -356,11 +385,14 @@ Iteration Objective Primal Inf. Dual Inf. Time
0 6.3600000e+02 1.700000e+01 0.000000e+00 0s 0 6.3600000e+02 1.700000e+01 0.000000e+00 0s
15 2.7610000e+03 0.000000e+00 0.000000e+00 0s 15 2.7610000e+03 0.000000e+00 0.000000e+00 0s
Solved in 15 iterations and 0.01 seconds (0.00 work units) Solved in 15 iterations and 0.00 seconds (0.00 work units)
Optimal objective 2.761000000e+03 Optimal objective 2.761000000e+03
Set parameter LazyConstraints to value 1 Set parameter LazyConstraints to value 1
Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (linux64) Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)
Thread count: 6 physical cores, 12 logical processors, using up to 12 threads
CPU model: AMD Ryzen 9 7950X 16-Core Processor, instruction set [SSE2|AVX|AVX2|AVX512]
Thread count: 16 physical cores, 32 logical processors, using up to 32 threads
Optimize a model with 10 rows, 45 columns and 90 nonzeros Optimize a model with 10 rows, 45 columns and 90 nonzeros
Model fingerprint: 0x74ca3d0a Model fingerprint: 0x74ca3d0a
Variable types: 0 continuous, 45 integer (45 binary) Variable types: 0 continuous, 45 integer (45 binary)
@ -370,7 +402,7 @@ Coefficient statistics:
Bounds range [1e+00, 1e+00] Bounds range [1e+00, 1e+00]
RHS range [2e+00, 2e+00] RHS range [2e+00, 2e+00]
User MIP start produced solution with objective 2796 (0.01s) User MIP start produced solution with objective 2796 (0.00s)
Loaded user MIP start with objective 2796 Loaded user MIP start with objective 2796
Presolve time: 0.00s Presolve time: 0.00s
@ -382,51 +414,34 @@ Root relaxation: objective 2.761000e+03, 14 iterations, 0.00 seconds (0.00 work
Nodes | Current Node | Objective Bounds | Work Nodes | Current Node | Objective Bounds | Work
Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
0 0 2761.00000 0 - 2796.00000 2761.00000 1.25% - 0s
0 0 cutoff 0 2796.00000 2796.00000 0.00% - 0s 0 0 cutoff 0 2796.00000 2796.00000 0.00% - 0s
Cutting planes: Cutting planes:
Lazy constraints: 3 Lazy constraints: 3
Explored 1 nodes (15 simplex iterations) in 0.01 seconds (0.00 work units) Explored 1 nodes (16 simplex iterations) in 0.01 seconds (0.00 work units)
Thread count was 12 (of 12 available processors) Thread count was 32 (of 32 available processors)
Solution count 1: 2796 Solution count 1: 2796
Optimal solution found (tolerance 1.00e-04) Optimal solution found (tolerance 1.00e-04)
Best objective 2.796000000000e+03, best bound 2.796000000000e+03, gap 0.0000% Best objective 2.796000000000e+03, best bound 2.796000000000e+03, gap 0.0000%
User-callback calls 103, time in user-callback 0.00 sec User-callback calls 110, time in user-callback 0.00 sec
Gurobi Optimizer version 9.5.2 build v9.5.2rc0 (linux64) </pre></div></div>
Thread count: 6 physical cores, 12 logical processors, using up to 12 threads </div>
Optimize a model with 10 rows, 45 columns and 90 nonzeros <div class="nboutput nblast docutils container">
Model fingerprint: 0x74ca3d0a <div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[3]:
Variable types: 0 continuous, 45 integer (45 binary) </pre></div>
Coefficient statistics: </div>
Matrix range [1e+00, 1e+00] <div class="output_area docutils container">
Objective range [4e+01, 1e+03] <div class="highlight"><pre>
Bounds range [1e+00, 1e+00] {&#39;WS: Count&#39;: 1, &#39;WS: Number of variables set&#39;: 41.0}
RHS range [2e+00, 2e+00]
Presolved: 10 rows, 45 columns, 90 nonzeros
Continuing optimization...
Cutting planes:
Lazy constraints: 3
Explored 1 nodes (15 simplex iterations) in 0.01 seconds (0.00 work units)
Thread count was 12 (of 12 available processors)
Solution count 1: 2796
Optimal solution found (tolerance 1.00e-04)
Best objective 2.796000000000e+03, best bound 2.796000000000e+03, gap 0.0000%
User-callback calls 27, time in user-callback 0.00 sec
</pre></div></div> </pre></div></div>
</div> </div>
<div class="nbinput nblast docutils container"> <div class="nbinput nblast docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[1]: <div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[ ]:
</pre></div> </pre></div>
</div> </div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span> <div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>
@ -434,7 +449,6 @@ User-callback calls 27, time in user-callback 0.00 sec
</div> </div>
</div> </div>
</div> </div>
</div>
</div> </div>

@ -25,10 +25,6 @@
<link rel="stylesheet" href="_static/pygments.css" type="text/css" /> <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css" type="text/css" /> <link rel="stylesheet" href="_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css" type="text/css" />
<link rel="stylesheet" type="text/css" href="_static/nbsphinx-code-cells.css" /> <link rel="stylesheet" type="text/css" href="_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" /> <link rel="stylesheet" type="text/css" href="_static/custom.css" />
<link rel="preload" as="script" href="_static/js/index.1c5a1a01449ed65a7b51.js"> <link rel="preload" as="script" href="_static/js/index.1c5a1a01449ed65a7b51.js">
@ -119,7 +115,7 @@
</li> </li>
<li class="toctree-l1"> <li class="toctree-l1">
<a class="reference internal" href="guide/solvers/"> <a class="reference internal" href="guide/solvers/">
8. Solvers 8. Learning Solver
</a> </a>
</li> </li>
</ul> </ul>
@ -383,11 +379,10 @@
</li> </li>
</ul> </ul>
</li> </li>
<li class="toctree-l1"><a class="reference internal" href="guide/solvers/">8. Solvers</a><ul> <li class="toctree-l1"><a class="reference internal" href="guide/solvers/">8. Learning Solver</a><ul>
<li class="toctree-l2"><a class="reference internal" href="guide/solvers/#LearningSolver">8.1. LearningSolver</a><ul> <li class="toctree-l2"><a class="reference internal" href="guide/solvers/#Configuring-the-solver">8.1. Configuring the solver</a></li>
<li class="toctree-l3"><a class="reference internal" href="guide/solvers/#Example">Example</a></li> <li class="toctree-l2"><a class="reference internal" href="guide/solvers/#Training-and-solving-new-instances">8.2. Training and solving new instances</a></li>
</ul> <li class="toctree-l2"><a class="reference internal" href="guide/solvers/#Complete-example">8.3. Complete example</a></li>
</li>
</ul> </ul>
</li> </li>
</ul> </ul>

Binary file not shown.

@ -25,13 +25,6 @@
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" /> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css" type="text/css" /> <link rel="stylesheet" href="../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css" type="text/css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" /> <link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/custom.css" /> <link rel="stylesheet" type="text/css" href="../_static/custom.css" />
<link rel="preload" as="script" href="../_static/js/index.1c5a1a01449ed65a7b51.js"> <link rel="preload" as="script" href="../_static/js/index.1c5a1a01449ed65a7b51.js">
@ -124,7 +117,7 @@
</li> </li>
<li class="toctree-l1"> <li class="toctree-l1">
<a class="reference internal" href="../guide/solvers/"> <a class="reference internal" href="../guide/solvers/">
8. Solvers 8. Learning Solver
</a> </a>
</li> </li>
</ul> </ul>

@ -25,13 +25,6 @@
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" /> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css" type="text/css" /> <link rel="stylesheet" href="../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css" type="text/css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" /> <link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/nbsphinx-code-cells.css" />
<link rel="stylesheet" type="text/css" href="../_static/custom.css" /> <link rel="stylesheet" type="text/css" href="../_static/custom.css" />
<link rel="preload" as="script" href="../_static/js/index.1c5a1a01449ed65a7b51.js"> <link rel="preload" as="script" href="../_static/js/index.1c5a1a01449ed65a7b51.js">
@ -127,7 +120,7 @@
</li> </li>
<li class="toctree-l1"> <li class="toctree-l1">
<a class="reference internal" href="../guide/solvers/"> <a class="reference internal" href="../guide/solvers/">
8. Solvers 8. Learning Solver
</a> </a>
</li> </li>
</ul> </ul>

File diff suppressed because one or more lines are too long

@ -1,625 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "6b8983b1",
"metadata": {
"tags": []
},
"source": [
"# Getting started (JuMP)\n",
"\n",
"## Introduction\n",
"\n",
"**MIPLearn** is an open source framework that uses machine learning (ML) to accelerate the performance of both commercial and open source mixed-integer programming solvers (e.g. Gurobi, CPLEX, XPRESS, Cbc or SCIP). In this tutorial, we will:\n",
"\n",
"1. Install the Python/Pyomo version of MIPLearn\n",
"2. Model a simple optimization problem using JuMP\n",
"3. Generate training data and train the ML models\n",
"4. Use the ML models together Gurobi to solve new instances\n",
"\n",
"<div class=\"alert alert-info\">\n",
"Note\n",
" \n",
"The Python/Pyomo version of MIPLearn is currently only compatible with with Gurobi, CPLEX and XPRESS. For broader solver compatibility, see the Julia/JuMP version of the package.\n",
"</div>\n",
"\n",
"<div class=\"alert alert-warning\">\n",
"Warning\n",
" \n",
"MIPLearn is still in early development stage. If run into any bugs or issues, please submit a bug report in our GitHub repository. Comments, suggestions and pull requests are also very welcome!\n",
" \n",
"</div>\n"
]
},
{
"cell_type": "markdown",
"id": "02f0a927",
"metadata": {},
"source": [
"## Installation\n",
"\n",
"MIPLearn is available in two versions:\n",
"\n",
"- Python version, compatible with the Pyomo modeling language,\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`:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "cd8a69c1",
"metadata": {},
"outputs": [],
"source": [
"# !pip install MIPLearn==0.2.0.dev13"
]
},
{
"cell_type": "markdown",
"id": "e8274543",
"metadata": {},
"source": [
"In addition to MIPLearn itself, we will also install Gurobi 9.5, a state-of-the-art commercial MILP solver. This step also install a demo license for Gurobi, which should able to solve the small optimization problems in this tutorial. A paid license is required for solving large-scale problems."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "dcc8756c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Looking in indexes: https://pypi.gurobi.com\n",
"Requirement already satisfied: gurobipy<9.6,>=9.5 in /opt/anaconda3/envs/miplearn/lib/python3.8/site-packages (9.5.1)\n"
]
}
],
"source": [
"!pip install --upgrade -i https://pypi.gurobi.com 'gurobipy>=9.5,<9.6'"
]
},
{
"cell_type": "markdown",
"id": "a14e4550",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
" \n",
"Note\n",
" \n",
"In the code above, we install specific version of all packages to ensure that this tutorial keeps running in the future, even when newer (and possibly incompatible) versions of the packages are released. This is usually a recommended practice for all Python projects.\n",
" \n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "16b86823",
"metadata": {},
"source": [
"## Modeling a simple optimization problem\n",
"\n",
"To illustrate how can MIPLearn be used, we will model and solve a small optimization problem related to power systems optimization. The problem we discuss below is a simplification of the **unit commitment problem,** a practical optimization problem solved daily by electric grid operators around the world. \n",
"\n",
"Suppose that you work at a utility company, and that it is your job to decide which electrical generators should be online at a certain hour of the day, as well as how much power should each generator produce. More specifically, assume that your company owns $n$ generators, denoted by $g_1, \\ldots, g_n$. Each generator can either be online or offline. An online generator $g_i$ can produce between $p^\\text{min}_i$ to $p^\\text{max}_i$ megawatts of power, and it costs your company $c^\\text{fix}_i + c^\\text{var}_i y_i$, where $y_i$ is the amount of power produced. An offline generator produces nothing and costs nothing. You also know that the total amount of power to be produced needs to be exactly equal to the total demand $d$ (in megawatts). To minimize the costs to your company, which generators should be online, and how much power should they produce?\n",
"\n",
"This simple problem can be modeled as a *mixed-integer linear optimization* problem as follows. For each generator $g_i$, let $x_i \\in \\{0,1\\}$ be a decision variable indicating whether $g_i$ is online, and let $y_i \\geq 0$ be a decision variable indicating how much power does $g_i$ produce. The problem is then given by:\n",
"\n",
"$$\n",
"\\begin{align}\n",
"\\text{minimize } \\quad & \\sum_{i=1}^n \\left( c^\\text{fix}_i x_i + c^\\text{var}_i y_i \\right) \\\\\n",
"\\text{subject to } \\quad & y_i \\leq p^\\text{max}_i x_i & i=1,\\ldots,n \\\\\n",
"& y_i \\geq p^\\text{min}_i x_i & i=1,\\ldots,n \\\\\n",
"& \\sum_{i=1}^n y_i = d \\\\\n",
"& x_i \\in \\{0,1\\} & i=1,\\ldots,n \\\\\n",
"& y_i \\geq 0 & i=1,\\ldots,n\n",
"\\end{align}\n",
"$$\n",
"\n",
"<div class=\"alert alert-info\">\n",
" \n",
"Note\n",
" \n",
"We use a simplified version of the unit commitment problem in this tutorial just to make it easier to follow. MIPLearn can also handle realistic, large-scale versions of this problem. See benchmarks for more details.\n",
" \n",
"</div>\n",
"\n",
"Next, let us convert this abstract mathematical formulation into a concrete optimization model, using Python and Pyomo. We start by defining a data class `UnitCommitmentData`, which holds all the input data."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "22a67170-10b4-43d3-8708-014d91141e73",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"from dataclasses import dataclass\n",
"import numpy as np\n",
"\n",
"@dataclass\n",
"class UnitCommitmentData:\n",
" demand: float\n",
" pmin: np.ndarray\n",
" pmax: np.ndarray\n",
" cfix: np.ndarray\n",
" cvar: np.ndarray"
]
},
{
"cell_type": "markdown",
"id": "29f55efa-0751-465a-9b0a-a821d46a3d40",
"metadata": {},
"source": [
"Next, we write a `build_uc_model` function, which converts the input data into a concrete Pyomo model."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "2f67032f-0d74-4317-b45c-19da0ec859e9",
"metadata": {},
"outputs": [],
"source": [
"import pyomo.environ as pe\n",
"\n",
"def build_uc_model(data: UnitCommitmentData) -> pe.ConcreteModel:\n",
" model = pe.ConcreteModel()\n",
" n = len(data.pmin)\n",
" model.x = pe.Var(range(n), domain=pe.Binary)\n",
" model.y = pe.Var(range(n), domain=pe.NonNegativeReals)\n",
" model.obj = pe.Objective(\n",
" expr=sum(\n",
" data.cfix[i] * model.x[i] +\n",
" data.cvar[i] * model.y[i]\n",
" for i in range(n)\n",
" )\n",
" )\n",
" model.eq_max_power = pe.ConstraintList()\n",
" model.eq_min_power = pe.ConstraintList()\n",
" for i in range(n):\n",
" model.eq_max_power.add(model.y[i] <= data.pmax[i] * model.x[i])\n",
" model.eq_min_power.add(model.y[i] >= data.pmin[i] * model.x[i])\n",
" model.eq_demand = pe.Constraint(\n",
" expr=sum(model.y[i] for i in range(n)) == data.demand,\n",
" )\n",
" return model"
]
},
{
"cell_type": "markdown",
"id": "c22714a3",
"metadata": {},
"source": [
"At this point, we can already use Pyomo and any mixed-integer linear programming solver to find optimal solutions to any instance of this problem. To illustrate this, let us solve a small instance with three generators:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "2a896f47",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter Threads to value 1\n",
"Set parameter Seed to value 42\n",
"Restricted license - for non-production use only - expires 2023-10-25\n",
"obj = 1320.0\n",
"x = [-0.0, 1.0, 1.0]\n",
"y = [0.0, 60.0, 40.0]\n"
]
}
],
"source": [
"model = build_uc_model(\n",
" UnitCommitmentData(\n",
" demand = 100.0,\n",
" pmin = [10, 20, 30],\n",
" pmax = [50, 60, 70],\n",
" cfix = [700, 600, 500],\n",
" cvar = [1.5, 2.0, 2.5],\n",
" )\n",
")\n",
"\n",
"solver = pe.SolverFactory(\"gurobi_persistent\")\n",
"solver.set_instance(model)\n",
"solver.solve()\n",
"print(\"obj =\", model.obj())\n",
"print(\"x =\", [model.x[i].value for i in range(3)])\n",
"print(\"y =\", [model.y[i].value for i in range(3)])"
]
},
{
"cell_type": "markdown",
"id": "41b03bbc",
"metadata": {},
"source": [
"Running the code above, we found that the optimal solution for our small problem instance costs \\$1320. It is achieve by keeping generators 2 and 3 online and producing, respectively, 60 MW and 40 MW of power."
]
},
{
"cell_type": "markdown",
"id": "cf60c1dd",
"metadata": {},
"source": [
"## Generating training data\n",
"\n",
"Although Gurobi could solve the small example above in a fraction of a second, it gets slower for larger and more complex versions of the problem. If this is a problem that needs to be solved frequently, as it is often the case in practice, it could make sense to spend some time upfront generating a **trained** version of Gurobi, which can solve new instances (similar to the ones it was trained on) faster.\n",
"\n",
"In the following, we will use MIPLearn to train machine learning models that is able to predict the optimal solution for instances that follow a given probability distribution, then it will provide this predicted solution to Gurobi as a warm start. Before we can train the model, we need to collect training data by solving a large number of instances. In real-world situations, we may construct these training instances based on historical data. In this tutorial, we will construct them using a random instance generator:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "5eb09fab",
"metadata": {},
"outputs": [],
"source": [
"from scipy.stats import uniform\n",
"from typing import List\n",
"import random\n",
"\n",
"def random_uc_data(samples: int, n: int, seed: int = 42) -> List[UnitCommitmentData]:\n",
" random.seed(seed)\n",
" np.random.seed(seed)\n",
" pmin = uniform(loc=100_000.0, scale=400_000.0).rvs(n)\n",
" pmax = pmin * uniform(loc=2.0, scale=2.5).rvs(n)\n",
" cfix = pmin * uniform(loc=100.0, scale=25.0).rvs(n)\n",
" cvar = uniform(loc=1.25, scale=0.25).rvs(n)\n",
" return [\n",
" UnitCommitmentData(\n",
" demand = pmax.sum() * uniform(loc=0.5, scale=0.25).rvs(),\n",
" pmin = pmin,\n",
" pmax = pmax,\n",
" cfix = cfix,\n",
" cvar = cvar,\n",
" )\n",
" for i in range(samples)\n",
" ]"
]
},
{
"cell_type": "markdown",
"id": "3a03a7ac",
"metadata": {},
"source": [
"In this example, for simplicity, only the demands change from one instance to the next. We could also have randomized the costs, production limits or even the number of units. The more randomization we have in the training data, however, the more challenging it is for the machine learning models to learn solution patterns.\n",
"\n",
"Now we generate 500 instances of this problem, each one with 50 generators, and we use 450 of these instances for training. After generating the instances, we write them to individual files. MIPLearn uses files during the training process because, for large-scale optimization problems, it is often impractical to hold in memory the entire training data, as well as the concrete Pyomo models. Files also make it much easier to solve multiple instances simultaneously, potentially even on multiple machines. We will cover parallel and distributed computing in a future tutorial. The code below generates the files `uc/train/00000.pkl.gz`, `uc/train/00001.pkl.gz`, etc., which contain the input data in compressed (gzipped) pickle format."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "6156752c",
"metadata": {},
"outputs": [],
"source": [
"from miplearn import save\n",
"data = random_uc_data(samples=500, n=50)\n",
"train_files = save(data[0:450], \"uc/train/\")\n",
"test_files = save(data[450:500], \"uc/test/\")"
]
},
{
"cell_type": "markdown",
"id": "b17af877",
"metadata": {},
"source": [
"Finally, we use `LearningSolver` to solve all the training instances. `LearningSolver` is the main component provided by MIPLearn, which integrates MIP solvers and ML. The optimal solutions, along with other useful training data, are stored in HDF5 files `uc/train/00000.h5`, `uc/train/00001.h5`, etc."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "7623f002",
"metadata": {},
"outputs": [],
"source": [
"from miplearn import LearningSolver\n",
"solver = LearningSolver()\n",
"solver.solve(train_files, build_uc_model);"
]
},
{
"cell_type": "markdown",
"id": "2f24ee83",
"metadata": {},
"source": [
"## Solving test instances\n",
"\n",
"With training data in hand, we can now fit the ML models, using the `LearningSolver.fit` method, then solve the test instances with `LearningSolver.solve`, as shown below. The `tee=True` parameter asks MIPLearn to print the solver log to the screen."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "c8385030",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter LogFile to value \"/tmp/tmpvbaqbyty.log\"\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
"Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
"Model fingerprint: 0x8de73876\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 2e+06]\n",
" Objective range [1e+00, 6e+07]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+07, 2e+07]\n",
"Presolve removed 100 rows and 50 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1 rows, 50 columns, 50 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 5.7349081e+08 1.044003e+04 0.000000e+00 0s\n",
" 1 6.8268465e+08 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.00 seconds (0.00 work units)\n",
"Optimal objective 6.826846503e+08\n",
"Set parameter LogFile to value \"\"\n",
"Set parameter LogFile to value \"/tmp/tmp48j6n35b.log\"\n",
"Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
"Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
"Model fingerprint: 0x200d64ba\n",
"Variable types: 50 continuous, 50 integer (50 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 2e+06]\n",
" Objective range [1e+00, 6e+07]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+07, 2e+07]\n",
"\n",
"User MIP start produced solution with objective 6.84841e+08 (0.00s)\n",
"Loaded user MIP start with objective 6.84841e+08\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 101 rows, 100 columns, 250 nonzeros\n",
"Variable types: 50 continuous, 50 integer (50 binary)\n",
"\n",
"Root relaxation: objective 6.826847e+08, 56 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 6.8268e+08 0 1 6.8484e+08 6.8268e+08 0.31% - 0s\n",
" 0 0 6.8315e+08 0 3 6.8484e+08 6.8315e+08 0.25% - 0s\n",
" 0 0 6.8315e+08 0 1 6.8484e+08 6.8315e+08 0.25% - 0s\n",
" 0 0 6.8315e+08 0 3 6.8484e+08 6.8315e+08 0.25% - 0s\n",
" 0 0 6.8315e+08 0 4 6.8484e+08 6.8315e+08 0.25% - 0s\n",
" 0 0 6.8315e+08 0 4 6.8484e+08 6.8315e+08 0.25% - 0s\n",
" 0 2 6.8327e+08 0 4 6.8484e+08 6.8327e+08 0.23% - 0s\n",
"\n",
"Cutting planes:\n",
" Flow cover: 3\n",
"\n",
"Explored 32 nodes (155 simplex iterations) in 0.02 seconds (0.00 work units)\n",
"Thread count was 1 (of 32 available processors)\n",
"\n",
"Solution count 1: 6.84841e+08 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 6.848411655488e+08, best bound 6.848411655488e+08, gap 0.0000%\n",
"Set parameter LogFile to value \"\"\n",
"WARNING: Cannot get reduced costs for MIP.\n",
"WARNING: Cannot get duals for MIP.\n"
]
}
],
"source": [
"solver_ml = LearningSolver()\n",
"solver_ml.fit(train_files, build_uc_model)\n",
"solver_ml.solve(test_files[0:1], build_uc_model, tee=True);"
]
},
{
"cell_type": "markdown",
"id": "61da6dad-7f56-4edb-aa26-c00eb5f946c0",
"metadata": {},
"source": [
"By examining the solve log above, specifically the line `Loaded user MIP start with objective...`, we can see that MIPLearn was able to construct an initial solution which turned out to be the optimal solution to the problem. Now let us repeat the code above, but using an untrained solver. Note that the `fit` line is omitted."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "33d15d6c-6db4-477f-bd4b-fe8e84e5f023",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter LogFile to value \"/tmp/tmp3uhhdurw.log\"\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
"Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
"Model fingerprint: 0x8de73876\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 2e+06]\n",
" Objective range [1e+00, 6e+07]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+07, 2e+07]\n",
"Presolve removed 100 rows and 50 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1 rows, 50 columns, 50 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 5.7349081e+08 1.044003e+04 0.000000e+00 0s\n",
" 1 6.8268465e+08 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 6.826846503e+08\n",
"Set parameter LogFile to value \"\"\n",
"Set parameter LogFile to value \"/tmp/tmp18aqg2ic.log\"\n",
"Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)\n",
"Thread count: 16 physical cores, 32 logical processors, using up to 1 threads\n",
"Optimize a model with 101 rows, 100 columns and 250 nonzeros\n",
"Model fingerprint: 0xb90d1075\n",
"Variable types: 50 continuous, 50 integer (50 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 2e+06]\n",
" Objective range [1e+00, 6e+07]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+07, 2e+07]\n",
"Found heuristic solution: objective 8.056576e+08\n",
"Presolve time: 0.00s\n",
"Presolved: 101 rows, 100 columns, 250 nonzeros\n",
"Variable types: 50 continuous, 50 integer (50 binary)\n",
"\n",
"Root relaxation: objective 6.826847e+08, 56 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 6.8268e+08 0 1 8.0566e+08 6.8268e+08 15.3% - 0s\n",
"H 0 0 7.099498e+08 6.8268e+08 3.84% - 0s\n",
" 0 0 6.8315e+08 0 3 7.0995e+08 6.8315e+08 3.78% - 0s\n",
"H 0 0 6.883227e+08 6.8315e+08 0.75% - 0s\n",
" 0 0 6.8352e+08 0 4 6.8832e+08 6.8352e+08 0.70% - 0s\n",
" 0 0 6.8352e+08 0 4 6.8832e+08 6.8352e+08 0.70% - 0s\n",
" 0 0 6.8352e+08 0 1 6.8832e+08 6.8352e+08 0.70% - 0s\n",
"H 0 0 6.862582e+08 6.8352e+08 0.40% - 0s\n",
" 0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s\n",
" 0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s\n",
" 0 0 6.8352e+08 0 1 6.8626e+08 6.8352e+08 0.40% - 0s\n",
" 0 0 6.8352e+08 0 3 6.8626e+08 6.8352e+08 0.40% - 0s\n",
" 0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s\n",
" 0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s\n",
" 0 2 6.8354e+08 0 4 6.8626e+08 6.8354e+08 0.40% - 0s\n",
"* 18 5 6 6.849018e+08 6.8413e+08 0.11% 3.1 0s\n",
"H 24 1 6.848412e+08 6.8426e+08 0.09% 3.2 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 1\n",
" Flow cover: 2\n",
"\n",
"Explored 30 nodes (217 simplex iterations) in 0.02 seconds (0.00 work units)\n",
"Thread count was 1 (of 32 available processors)\n",
"\n",
"Solution count 6: 6.84841e+08 6.84902e+08 6.86258e+08 ... 8.05658e+08\n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 6.848411655488e+08, best bound 6.848411655488e+08, gap 0.0000%\n",
"Set parameter LogFile to value \"\"\n",
"WARNING: Cannot get reduced costs for MIP.\n",
"WARNING: Cannot get duals for MIP.\n"
]
}
],
"source": [
"solver_baseline = LearningSolver()\n",
"solver_baseline.solve(test_files[0:1], build_uc_model, tee=True);"
]
},
{
"cell_type": "markdown",
"id": "b6d37b88-9fcc-43ee-ac1e-2a7b1e51a266",
"metadata": {},
"source": [
"In the log above, the `MIP start` line is missing, and Gurobi had to start with a significantly inferior initial solution. The solver was still able to find the optimal solution at the end, but it required using its own internal heuristic procedures. In this example, because we solve very small optimization problems, there was almost no difference in terms of running time. For larger problems, however, the difference can be significant. See benchmarks for more details.\n",
"\n",
"<div class=\"alert alert-info\">\n",
"Note\n",
" \n",
"In addition to partial initial solutions, MIPLearn is also able to predict lazy constraints, cutting planes and branching priorities. See the next tutorials for more details.\n",
"</div>\n",
"\n",
"<div class=\"alert alert-info\">\n",
"Note\n",
" \n",
"It is not necessary to specify what ML models to use. MIPLearn, by default, will try a number of classical ML models and will choose the one that performs the best, based on k-fold cross validation. MIPLearn is also able to automatically collect features based on the MIP formulation of the problem and the solution to the LP relaxation, among other things, so it does not require handcrafted features. If you do want to customize the models and features, however, that is also possible, as we will see in a later tutorial.\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "eec97f06",
"metadata": {
"tags": []
},
"source": [
"## Accessing the solution\n",
"\n",
"In the example above, we used `LearningSolver.solve` together with data files to solve both the training and the test instances. The optimal solutions were saved to HDF5 files in the train/test folders, and could be retrieved by reading theses files, but that is not very convenient. In the following example, we show how to build and solve a Pyomo model entirely in-memory, using our trained solver."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "67a6cd18",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"obj = 903865807.3536932\n",
" x = [1.0, 1.0, 1.0, 1.0, 1.0]\n",
" y = [1105176.593734543, 1891284.5155055337, 1708177.4224033852, 1438329.610189608, 535496.3347187206]\n"
]
}
],
"source": [
"# Construct model using previously defined functions\n",
"data = random_uc_data(samples=1, n=50)[0]\n",
"model = build_uc_model(data)\n",
"\n",
"# Solve model using ML + Gurobi\n",
"solver_ml.solve(model)\n",
"\n",
"# Print part of the optimal solution\n",
"print(\"obj =\", model.obj())\n",
"print(\" x =\", [model.x[i].value for i in range(5)])\n",
"print(\" y =\", [model.y[i].value for i in range(5)])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5593d23a-83bd-4e16-8253-6300f5e3f63b",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -1,982 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Getting started (JuMP) &#8212; MIPLearn 0.3</title>
<link href="../../_static/css/theme.css" rel="stylesheet" />
<link href="../../_static/css/index.c5995385ac14fb8791e8eb36b4908be2.css" rel="stylesheet" />
<link rel="stylesheet"
href="../../_static/vendor/fontawesome/5.13.0/css/all.min.css">
<link rel="preload" as="font" type="font/woff2" crossorigin
href="../../_static/vendor/fontawesome/5.13.0/webfonts/fa-solid-900.woff2">
<link rel="preload" as="font" type="font/woff2" crossorigin
href="../../_static/vendor/fontawesome/5.13.0/webfonts/fa-brands-400.woff2">
<link rel="stylesheet" href="../../_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css" type="text/css" />
<link rel="stylesheet" type="text/css" href="../../_static/custom.css" />
<link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js">
<script id="documentation_options" data-url_root="../../" src="../../_static/documentation_options.js"></script>
<script src="../../_static/jquery.js"></script>
<script src="../../_static/underscore.js"></script>
<script src="../../_static/doctools.js"></script>
<script crossorigin="anonymous" integrity="sha256-Ae2Vz/4ePdIu6ZyI/5ZGsYnb+m0JlOmKPjt6XZ9JJkA=" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js"></script>
<script src="../../_static/sphinx-book-theme.12a9622fbb08dcb3a2a40b2c02b83a57.js"></script>
<script async="async" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script>
<link rel="index" title="Index" href="../../genindex/" />
<link rel="search" title="Search" href="../../search/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="docsearch:language" content="en" />
</head>
<body data-spy="scroll" data-target="#bd-toc-nav" data-offset="80">
<div class="container-fluid" id="banner"></div>
<div class="container-xl">
<div class="row">
<div class="col-12 col-md-3 bd-sidebar site-navigation show" id="site-navigation">
<div class="navbar-brand-box">
<a class="navbar-brand text-wrap" href="../../">
<h1 class="site-logo" id="site-title">MIPLearn 0.3</h1>
</a>
</div><form class="bd-search d-flex align-items-center" action="../../search/" method="get">
<i class="icon fas fa-search"></i>
<input type="search" class="form-control" name="q" id="search-input" placeholder="Search the docs ..." aria-label="Search the docs ..." autocomplete="off" >
</form><nav class="bd-links" id="bd-docs-nav" aria-label="Main navigation">
<div class="bd-toc-item active">
<p class="caption">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../guide/problems/">
1. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/collectors/">
2. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/features/">
3. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/primal/">
4. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/solvers/">
5. Solvers
</a>
</li>
</ul>
<p class="caption">
<span class="caption-text">
API Reference
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../api/problems/">
6. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/collectors/">
7. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/components/">
8. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/solvers/">
9. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/helpers/">
10. Helpers
</a>
</li>
</ul>
</div>
</nav> <!-- To handle the deprecated key -->
</div>
<main class="col py-md-3 pl-md-4 bd-content overflow-auto" role="main">
<div class="topbar container-xl fixed-top">
<div class="topbar-contents row">
<div class="col-12 col-md-3 bd-topbar-whitespace site-navigation show"></div>
<div class="col pl-md-4 topbar-main">
<button id="navbar-toggler" class="navbar-toggler ml-0" type="button" data-toggle="collapse"
data-toggle="tooltip" data-placement="bottom" data-target=".site-navigation" aria-controls="navbar-menu"
aria-expanded="true" aria-label="Toggle navigation" aria-controls="site-navigation"
title="Toggle navigation" data-toggle="tooltip" data-placement="left">
<i class="fas fa-bars"></i>
<i class="fas fa-arrow-left"></i>
<i class="fas fa-arrow-up"></i>
</button>
<div class="dropdown-buttons-trigger">
<button id="dropdown-buttons-trigger" class="btn btn-secondary topbarbtn" aria-label="Download this page"><i
class="fas fa-download"></i></button>
<div class="dropdown-buttons">
<!-- ipynb file if we had a myst markdown file -->
<!-- Download raw file -->
<a class="dropdown-buttons" href="../../_sources/tutorials/getting-started-jl.ipynb.txt"><button type="button"
class="btn btn-secondary topbarbtn" title="Download source file" data-toggle="tooltip"
data-placement="left">.ipynb</button></a>
<!-- Download PDF via print -->
<button type="button" id="download-print" class="btn btn-secondary topbarbtn" title="Print to PDF"
onClick="window.print()" data-toggle="tooltip" data-placement="left">.pdf</button>
</div>
</div>
<!-- Source interaction buttons -->
<!-- Full screen (wrap in <a> to have style consistency -->
<a class="full-screen-button"><button type="button" class="btn btn-secondary topbarbtn" data-toggle="tooltip"
data-placement="bottom" onclick="toggleFullScreen()" aria-label="Fullscreen mode"
title="Fullscreen mode"><i
class="fas fa-expand"></i></button></a>
<!-- Launch buttons -->
</div>
<!-- Table of contents -->
<div class="d-none d-md-block col-md-2 bd-toc show">
<div class="tocsection onthispage pt-5 pb-3">
<i class="fas fa-list"></i> Contents
</div>
<nav id="bd-toc-nav">
<ul class="visible nav section-nav flex-column">
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Introduction">
Introduction
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Installation">
Installation
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Modeling-a-simple-optimization-problem">
Modeling a simple optimization problem
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Generating-training-data">
Generating training data
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Solving-test-instances">
Solving test instances
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Accessing-the-solution">
Accessing the solution
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div id="main-content" class="row">
<div class="col-12 col-md-9 pl-md-3 pr-md-0">
<div>
<style>
/* CSS for nbsphinx extension */
/* remove conflicting styling from Sphinx themes */
div.nbinput.container div.prompt *,
div.nboutput.container div.prompt *,
div.nbinput.container div.input_area pre,
div.nboutput.container div.output_area pre,
div.nbinput.container div.input_area .highlight,
div.nboutput.container div.output_area .highlight {
border: none;
padding: 0;
margin: 0;
box-shadow: none;
}
div.nbinput.container > div[class*=highlight],
div.nboutput.container > div[class*=highlight] {
margin: 0;
}
div.nbinput.container div.prompt *,
div.nboutput.container div.prompt * {
background: none;
}
div.nboutput.container div.output_area .highlight,
div.nboutput.container div.output_area pre {
background: unset;
}
div.nboutput.container div.output_area div.highlight {
color: unset; /* override Pygments text color */
}
/* avoid gaps between output lines */
div.nboutput.container div[class*=highlight] pre {
line-height: normal;
}
/* input/output containers */
div.nbinput.container,
div.nboutput.container {
display: -webkit-flex;
display: flex;
align-items: flex-start;
margin: 0;
width: 100%;
}
@media (max-width: 540px) {
div.nbinput.container,
div.nboutput.container {
flex-direction: column;
}
}
/* input container */
div.nbinput.container {
padding-top: 5px;
}
/* last container */
div.nblast.container {
padding-bottom: 5px;
}
/* input prompt */
div.nbinput.container div.prompt pre {
color: #307FC1;
}
/* output prompt */
div.nboutput.container div.prompt pre {
color: #BF5B3D;
}
/* all prompts */
div.nbinput.container div.prompt,
div.nboutput.container div.prompt {
width: 4.5ex;
padding-top: 5px;
position: relative;
user-select: none;
}
div.nbinput.container div.prompt > div,
div.nboutput.container div.prompt > div {
position: absolute;
right: 0;
margin-right: 0.3ex;
}
@media (max-width: 540px) {
div.nbinput.container div.prompt,
div.nboutput.container div.prompt {
width: unset;
text-align: left;
padding: 0.4em;
}
div.nboutput.container div.prompt.empty {
padding: 0;
}
div.nbinput.container div.prompt > div,
div.nboutput.container div.prompt > div {
position: unset;
}
}
/* disable scrollbars and line breaks on prompts */
div.nbinput.container div.prompt pre,
div.nboutput.container div.prompt pre {
overflow: hidden;
white-space: pre;
}
/* input/output area */
div.nbinput.container div.input_area,
div.nboutput.container div.output_area {
-webkit-flex: 1;
flex: 1;
overflow: auto;
}
@media (max-width: 540px) {
div.nbinput.container div.input_area,
div.nboutput.container div.output_area {
width: 100%;
}
}
/* input area */
div.nbinput.container div.input_area {
border: 1px solid #e0e0e0;
border-radius: 2px;
/*background: #f5f5f5;*/
}
/* override MathJax center alignment in output cells */
div.nboutput.container div[class*=MathJax] {
text-align: left !important;
}
/* override sphinx.ext.imgmath center alignment in output cells */
div.nboutput.container div.math p {
text-align: left;
}
/* standard error */
div.nboutput.container div.output_area.stderr {
background: #fdd;
}
/* ANSI colors */
.ansi-black-fg { color: #3E424D; }
.ansi-black-bg { background-color: #3E424D; }
.ansi-black-intense-fg { color: #282C36; }
.ansi-black-intense-bg { background-color: #282C36; }
.ansi-red-fg { color: #E75C58; }
.ansi-red-bg { background-color: #E75C58; }
.ansi-red-intense-fg { color: #B22B31; }
.ansi-red-intense-bg { background-color: #B22B31; }
.ansi-green-fg { color: #00A250; }
.ansi-green-bg { background-color: #00A250; }
.ansi-green-intense-fg { color: #007427; }
.ansi-green-intense-bg { background-color: #007427; }
.ansi-yellow-fg { color: #DDB62B; }
.ansi-yellow-bg { background-color: #DDB62B; }
.ansi-yellow-intense-fg { color: #B27D12; }
.ansi-yellow-intense-bg { background-color: #B27D12; }
.ansi-blue-fg { color: #208FFB; }
.ansi-blue-bg { background-color: #208FFB; }
.ansi-blue-intense-fg { color: #0065CA; }
.ansi-blue-intense-bg { background-color: #0065CA; }
.ansi-magenta-fg { color: #D160C4; }
.ansi-magenta-bg { background-color: #D160C4; }
.ansi-magenta-intense-fg { color: #A03196; }
.ansi-magenta-intense-bg { background-color: #A03196; }
.ansi-cyan-fg { color: #60C6C8; }
.ansi-cyan-bg { background-color: #60C6C8; }
.ansi-cyan-intense-fg { color: #258F8F; }
.ansi-cyan-intense-bg { background-color: #258F8F; }
.ansi-white-fg { color: #C5C1B4; }
.ansi-white-bg { background-color: #C5C1B4; }
.ansi-white-intense-fg { color: #A1A6B2; }
.ansi-white-intense-bg { background-color: #A1A6B2; }
.ansi-default-inverse-fg { color: #FFFFFF; }
.ansi-default-inverse-bg { background-color: #000000; }
.ansi-bold { font-weight: bold; }
.ansi-underline { text-decoration: underline; }
div.nbinput.container div.input_area div[class*=highlight] > pre,
div.nboutput.container div.output_area div[class*=highlight] > pre,
div.nboutput.container div.output_area div[class*=highlight].math,
div.nboutput.container div.output_area.rendered_html,
div.nboutput.container div.output_area > div.output_javascript,
div.nboutput.container div.output_area:not(.rendered_html) > img{
padding: 5px;
margin: 0;
}
/* fix copybtn overflow problem in chromium (needed for 'sphinx_copybutton') */
div.nbinput.container div.input_area > div[class^='highlight'],
div.nboutput.container div.output_area > div[class^='highlight']{
overflow-y: hidden;
}
/* hide copybtn icon on prompts (needed for 'sphinx_copybutton') */
.prompt .copybtn {
display: none;
}
/* Some additional styling taken form the Jupyter notebook CSS */
.jp-RenderedHTMLCommon table,
div.rendered_html table {
border: none;
border-collapse: collapse;
border-spacing: 0;
color: black;
font-size: 12px;
table-layout: fixed;
}
.jp-RenderedHTMLCommon thead,
div.rendered_html thead {
border-bottom: 1px solid black;
vertical-align: bottom;
}
.jp-RenderedHTMLCommon tr,
.jp-RenderedHTMLCommon th,
.jp-RenderedHTMLCommon td,
div.rendered_html tr,
div.rendered_html th,
div.rendered_html td {
text-align: right;
vertical-align: middle;
padding: 0.5em 0.5em;
line-height: normal;
white-space: normal;
max-width: none;
border: none;
}
.jp-RenderedHTMLCommon th,
div.rendered_html th {
font-weight: bold;
}
.jp-RenderedHTMLCommon tbody tr:nth-child(odd),
div.rendered_html tbody tr:nth-child(odd) {
background: #f5f5f5;
}
.jp-RenderedHTMLCommon tbody tr:hover,
div.rendered_html tbody tr:hover {
background: rgba(66, 165, 245, 0.2);
}
</style>
<div class="section" id="Getting-started-(JuMP)">
<h1>Getting started (JuMP)<a class="headerlink" href="#Getting-started-(JuMP)" title="Permalink to this headline"></a></h1>
<div class="section" id="Introduction">
<h2>Introduction<a class="headerlink" href="#Introduction" title="Permalink to this headline"></a></h2>
<p><strong>MIPLearn</strong> is an open source framework that uses machine learning (ML) to accelerate the performance of both commercial and open source mixed-integer programming solvers (e.g. Gurobi, CPLEX, XPRESS, Cbc or SCIP). In this tutorial, we will:</p>
<ol class="arabic simple">
<li><p>Install the Python/Pyomo version of MIPLearn</p></li>
<li><p>Model a simple optimization problem using JuMP</p></li>
<li><p>Generate training data and train the ML models</p></li>
<li><p>Use the ML models together Gurobi to solve new instances</p></li>
</ol>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>The Python/Pyomo version of MIPLearn is currently only compatible with with Gurobi, CPLEX and XPRESS. For broader solver compatibility, see the Julia/JuMP version of the package.</p>
</div>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>MIPLearn is still in early development stage. If run into any bugs or issues, please submit a bug report in our GitHub repository. Comments, suggestions and pull requests are also very welcome!</p>
</div>
</div>
<div class="section" id="Installation">
<h2>Installation<a class="headerlink" href="#Installation" title="Permalink to this headline"></a></h2>
<p>MIPLearn is available in two versions:</p>
<ul class="simple">
<li><p>Python version, compatible with the Pyomo modeling language,</p></li>
<li><p>Julia version, compatible with the JuMP modeling language.</p></li>
</ul>
<p>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 <a class="reference external" href="https://www.python.org/downloads/">official Python website for more instructions</a>. After Python is installed, we proceed to install MIPLearn using <code class="docutils literal notranslate"><span class="pre">pip</span></code>:</p>
<div class="nbinput nblast docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[1]:
</pre></div>
</div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="c1"># !pip install MIPLearn==0.2.0.dev13</span>
</pre></div>
</div>
</div>
<p>In addition to MIPLearn itself, we will also install Gurobi 9.5, a state-of-the-art commercial MILP solver. This step also install a demo license for Gurobi, which should able to solve the small optimization problems in this tutorial. A paid license is required for solving large-scale problems.</p>
<div class="nbinput docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[2]:
</pre></div>
</div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="o">!</span>pip install --upgrade -i https://pypi.gurobi.com <span class="s1">&#39;gurobipy&gt;=9.5,&lt;9.6&#39;</span>
</pre></div>
</div>
</div>
<div class="nboutput nblast docutils container">
<div class="prompt empty docutils container">
</div>
<div class="output_area docutils container">
<div class="highlight"><pre>
Looking in indexes: https://pypi.gurobi.com
Requirement already satisfied: gurobipy&lt;9.6,&gt;=9.5 in /opt/anaconda3/envs/miplearn/lib/python3.8/site-packages (9.5.1)
</pre></div></div>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>In the code above, we install specific version of all packages to ensure that this tutorial keeps running in the future, even when newer (and possibly incompatible) versions of the packages are released. This is usually a recommended practice for all Python projects.</p>
</div>
</div>
<div class="section" id="Modeling-a-simple-optimization-problem">
<h2>Modeling a simple optimization problem<a class="headerlink" href="#Modeling-a-simple-optimization-problem" title="Permalink to this headline"></a></h2>
<p>To illustrate how can MIPLearn be used, we will model and solve a small optimization problem related to power systems optimization. The problem we discuss below is a simplification of the <strong>unit commitment problem,</strong> a practical optimization problem solved daily by electric grid operators around the world.</p>
<p>Suppose that you work at a utility company, and that it is your job to decide which electrical generators should be online at a certain hour of the day, as well as how much power should each generator produce. More specifically, assume that your company owns <span class="math notranslate nohighlight">\(n\)</span> generators, denoted by <span class="math notranslate nohighlight">\(g_1, \ldots, g_n\)</span>. Each generator can either be online or offline. An online generator <span class="math notranslate nohighlight">\(g_i\)</span> can produce between <span class="math notranslate nohighlight">\(p^\text{min}_i\)</span> to <span class="math notranslate nohighlight">\(p^\text{max}_i\)</span> megawatts of power, and it costs
your company <span class="math notranslate nohighlight">\(c^\text{fix}_i + c^\text{var}_i y_i\)</span>, where <span class="math notranslate nohighlight">\(y_i\)</span> is the amount of power produced. An offline generator produces nothing and costs nothing. You also know that the total amount of power to be produced needs to be exactly equal to the total demand <span class="math notranslate nohighlight">\(d\)</span> (in megawatts). To minimize the costs to your company, which generators should be online, and how much power should they produce?</p>
<p>This simple problem can be modeled as a <em>mixed-integer linear optimization</em> problem as follows. For each generator <span class="math notranslate nohighlight">\(g_i\)</span>, let <span class="math notranslate nohighlight">\(x_i \in \{0,1\}\)</span> be a decision variable indicating whether <span class="math notranslate nohighlight">\(g_i\)</span> is online, and let <span class="math notranslate nohighlight">\(y_i \geq 0\)</span> be a decision variable indicating how much power does <span class="math notranslate nohighlight">\(g_i\)</span> produce. The problem is then given by:</p>
<div class="math notranslate nohighlight">
\[\begin{split}\begin{align}
\text{minimize } \quad &amp; \sum_{i=1}^n \left( c^\text{fix}_i x_i + c^\text{var}_i y_i \right) \\
\text{subject to } \quad &amp; y_i \leq p^\text{max}_i x_i &amp; i=1,\ldots,n \\
&amp; y_i \geq p^\text{min}_i x_i &amp; i=1,\ldots,n \\
&amp; \sum_{i=1}^n y_i = d \\
&amp; x_i \in \{0,1\} &amp; i=1,\ldots,n \\
&amp; y_i \geq 0 &amp; i=1,\ldots,n
\end{align}\end{split}\]</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>We use a simplified version of the unit commitment problem in this tutorial just to make it easier to follow. MIPLearn can also handle realistic, large-scale versions of this problem. See benchmarks for more details.</p>
</div>
<p>Next, let us convert this abstract mathematical formulation into a concrete optimization model, using Python and Pyomo. We start by defining a data class <code class="docutils literal notranslate"><span class="pre">UnitCommitmentData</span></code>, which holds all the input data.</p>
<div class="nbinput nblast docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[3]:
</pre></div>
</div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
<span class="nd">@dataclass</span>
<span class="k">class</span> <span class="nc">UnitCommitmentData</span><span class="p">:</span>
<span class="n">demand</span><span class="p">:</span> <span class="nb">float</span>
<span class="n">pmin</span><span class="p">:</span> <span class="n">np</span><span class="o">.</span><span class="n">ndarray</span>
<span class="n">pmax</span><span class="p">:</span> <span class="n">np</span><span class="o">.</span><span class="n">ndarray</span>
<span class="n">cfix</span><span class="p">:</span> <span class="n">np</span><span class="o">.</span><span class="n">ndarray</span>
<span class="n">cvar</span><span class="p">:</span> <span class="n">np</span><span class="o">.</span><span class="n">ndarray</span>
</pre></div>
</div>
</div>
<p>Next, we write a <code class="docutils literal notranslate"><span class="pre">build_uc_model</span></code> function, which converts the input data into a concrete Pyomo model.</p>
<div class="nbinput nblast docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[4]:
</pre></div>
</div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pyomo.environ</span> <span class="k">as</span> <span class="nn">pe</span>
<span class="k">def</span> <span class="nf">build_uc_model</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="n">UnitCommitmentData</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">pe</span><span class="o">.</span><span class="n">ConcreteModel</span><span class="p">:</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">pe</span><span class="o">.</span><span class="n">ConcreteModel</span><span class="p">()</span>
<span class="n">n</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">pmin</span><span class="p">)</span>
<span class="n">model</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">pe</span><span class="o">.</span><span class="n">Var</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">),</span> <span class="n">domain</span><span class="o">=</span><span class="n">pe</span><span class="o">.</span><span class="n">Binary</span><span class="p">)</span>
<span class="n">model</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">pe</span><span class="o">.</span><span class="n">Var</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">),</span> <span class="n">domain</span><span class="o">=</span><span class="n">pe</span><span class="o">.</span><span class="n">NonNegativeReals</span><span class="p">)</span>
<span class="n">model</span><span class="o">.</span><span class="n">obj</span> <span class="o">=</span> <span class="n">pe</span><span class="o">.</span><span class="n">Objective</span><span class="p">(</span>
<span class="n">expr</span><span class="o">=</span><span class="nb">sum</span><span class="p">(</span>
<span class="n">data</span><span class="o">.</span><span class="n">cfix</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">model</span><span class="o">.</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span>
<span class="n">data</span><span class="o">.</span><span class="n">cvar</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">model</span><span class="o">.</span><span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
<span class="p">)</span>
<span class="p">)</span>
<span class="n">model</span><span class="o">.</span><span class="n">eq_max_power</span> <span class="o">=</span> <span class="n">pe</span><span class="o">.</span><span class="n">ConstraintList</span><span class="p">()</span>
<span class="n">model</span><span class="o">.</span><span class="n">eq_min_power</span> <span class="o">=</span> <span class="n">pe</span><span class="o">.</span><span class="n">ConstraintList</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
<span class="n">model</span><span class="o">.</span><span class="n">eq_max_power</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">data</span><span class="o">.</span><span class="n">pmax</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">model</span><span class="o">.</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
<span class="n">model</span><span class="o">.</span><span class="n">eq_min_power</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="n">data</span><span class="o">.</span><span class="n">pmin</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">model</span><span class="o">.</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
<span class="n">model</span><span class="o">.</span><span class="n">eq_demand</span> <span class="o">=</span> <span class="n">pe</span><span class="o">.</span><span class="n">Constraint</span><span class="p">(</span>
<span class="n">expr</span><span class="o">=</span><span class="nb">sum</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">))</span> <span class="o">==</span> <span class="n">data</span><span class="o">.</span><span class="n">demand</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">return</span> <span class="n">model</span>
</pre></div>
</div>
</div>
<p>At this point, we can already use Pyomo and any mixed-integer linear programming solver to find optimal solutions to any instance of this problem. To illustrate this, let us solve a small instance with three generators:</p>
<div class="nbinput docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[5]:
</pre></div>
</div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">model</span> <span class="o">=</span> <span class="n">build_uc_model</span><span class="p">(</span>
<span class="n">UnitCommitmentData</span><span class="p">(</span>
<span class="n">demand</span> <span class="o">=</span> <span class="mf">100.0</span><span class="p">,</span>
<span class="n">pmin</span> <span class="o">=</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">30</span><span class="p">],</span>
<span class="n">pmax</span> <span class="o">=</span> <span class="p">[</span><span class="mi">50</span><span class="p">,</span> <span class="mi">60</span><span class="p">,</span> <span class="mi">70</span><span class="p">],</span>
<span class="n">cfix</span> <span class="o">=</span> <span class="p">[</span><span class="mi">700</span><span class="p">,</span> <span class="mi">600</span><span class="p">,</span> <span class="mi">500</span><span class="p">],</span>
<span class="n">cvar</span> <span class="o">=</span> <span class="p">[</span><span class="mf">1.5</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">,</span> <span class="mf">2.5</span><span class="p">],</span>
<span class="p">)</span>
<span class="p">)</span>
<span class="n">solver</span> <span class="o">=</span> <span class="n">pe</span><span class="o">.</span><span class="n">SolverFactory</span><span class="p">(</span><span class="s2">&quot;gurobi_persistent&quot;</span><span class="p">)</span>
<span class="n">solver</span><span class="o">.</span><span class="n">set_instance</span><span class="p">(</span><span class="n">model</span><span class="p">)</span>
<span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;obj =&quot;</span><span class="p">,</span> <span class="n">model</span><span class="o">.</span><span class="n">obj</span><span class="p">())</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;x =&quot;</span><span class="p">,</span> <span class="p">[</span><span class="n">model</span><span class="o">.</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">value</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">)])</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;y =&quot;</span><span class="p">,</span> <span class="p">[</span><span class="n">model</span><span class="o">.</span><span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">value</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">)])</span>
</pre></div>
</div>
</div>
<div class="nboutput nblast docutils container">
<div class="prompt empty docutils container">
</div>
<div class="output_area docutils container">
<div class="highlight"><pre>
Set parameter Threads to value 1
Set parameter Seed to value 42
Restricted license - for non-production use only - expires 2023-10-25
obj = 1320.0
x = [-0.0, 1.0, 1.0]
y = [0.0, 60.0, 40.0]
</pre></div></div>
</div>
<p>Running the code above, we found that the optimal solution for our small problem instance costs $1320. It is achieve by keeping generators 2 and 3 online and producing, respectively, 60 MW and 40 MW of power.</p>
</div>
<div class="section" id="Generating-training-data">
<h2>Generating training data<a class="headerlink" href="#Generating-training-data" title="Permalink to this headline"></a></h2>
<p>Although Gurobi could solve the small example above in a fraction of a second, it gets slower for larger and more complex versions of the problem. If this is a problem that needs to be solved frequently, as it is often the case in practice, it could make sense to spend some time upfront generating a <strong>trained</strong> version of Gurobi, which can solve new instances (similar to the ones it was trained on) faster.</p>
<p>In the following, we will use MIPLearn to train machine learning models that is able to predict the optimal solution for instances that follow a given probability distribution, then it will provide this predicted solution to Gurobi as a warm start. Before we can train the model, we need to collect training data by solving a large number of instances. In real-world situations, we may construct these training instances based on historical data. In this tutorial, we will construct them using a
random instance generator:</p>
<div class="nbinput nblast docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[6]:
</pre></div>
</div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">scipy.stats</span> <span class="kn">import</span> <span class="n">uniform</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="k">def</span> <span class="nf">random_uc_data</span><span class="p">(</span><span class="n">samples</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">seed</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">42</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">UnitCommitmentData</span><span class="p">]:</span>
<span class="n">random</span><span class="o">.</span><span class="n">seed</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
<span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">seed</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
<span class="n">pmin</span> <span class="o">=</span> <span class="n">uniform</span><span class="p">(</span><span class="n">loc</span><span class="o">=</span><span class="mf">100_000.0</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mf">400_000.0</span><span class="p">)</span><span class="o">.</span><span class="n">rvs</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
<span class="n">pmax</span> <span class="o">=</span> <span class="n">pmin</span> <span class="o">*</span> <span class="n">uniform</span><span class="p">(</span><span class="n">loc</span><span class="o">=</span><span class="mf">2.0</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mf">2.5</span><span class="p">)</span><span class="o">.</span><span class="n">rvs</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
<span class="n">cfix</span> <span class="o">=</span> <span class="n">pmin</span> <span class="o">*</span> <span class="n">uniform</span><span class="p">(</span><span class="n">loc</span><span class="o">=</span><span class="mf">100.0</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mf">25.0</span><span class="p">)</span><span class="o">.</span><span class="n">rvs</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
<span class="n">cvar</span> <span class="o">=</span> <span class="n">uniform</span><span class="p">(</span><span class="n">loc</span><span class="o">=</span><span class="mf">1.25</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mf">0.25</span><span class="p">)</span><span class="o">.</span><span class="n">rvs</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[</span>
<span class="n">UnitCommitmentData</span><span class="p">(</span>
<span class="n">demand</span> <span class="o">=</span> <span class="n">pmax</span><span class="o">.</span><span class="n">sum</span><span class="p">()</span> <span class="o">*</span> <span class="n">uniform</span><span class="p">(</span><span class="n">loc</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mf">0.25</span><span class="p">)</span><span class="o">.</span><span class="n">rvs</span><span class="p">(),</span>
<span class="n">pmin</span> <span class="o">=</span> <span class="n">pmin</span><span class="p">,</span>
<span class="n">pmax</span> <span class="o">=</span> <span class="n">pmax</span><span class="p">,</span>
<span class="n">cfix</span> <span class="o">=</span> <span class="n">cfix</span><span class="p">,</span>
<span class="n">cvar</span> <span class="o">=</span> <span class="n">cvar</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">samples</span><span class="p">)</span>
<span class="p">]</span>
</pre></div>
</div>
</div>
<p>In this example, for simplicity, only the demands change from one instance to the next. We could also have randomized the costs, production limits or even the number of units. The more randomization we have in the training data, however, the more challenging it is for the machine learning models to learn solution patterns.</p>
<p>Now we generate 500 instances of this problem, each one with 50 generators, and we use 450 of these instances for training. After generating the instances, we write them to individual files. MIPLearn uses files during the training process because, for large-scale optimization problems, it is often impractical to hold in memory the entire training data, as well as the concrete Pyomo models. Files also make it much easier to solve multiple instances simultaneously, potentially even on multiple
machines. We will cover parallel and distributed computing in a future tutorial. The code below generates the files <code class="docutils literal notranslate"><span class="pre">uc/train/00000.pkl.gz</span></code>, <code class="docutils literal notranslate"><span class="pre">uc/train/00001.pkl.gz</span></code>, etc., which contain the input data in compressed (gzipped) pickle format.</p>
<div class="nbinput nblast docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[7]:
</pre></div>
</div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">miplearn</span> <span class="kn">import</span> <span class="n">save</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">random_uc_data</span><span class="p">(</span><span class="n">samples</span><span class="o">=</span><span class="mi">500</span><span class="p">,</span> <span class="n">n</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
<span class="n">train_files</span> <span class="o">=</span> <span class="n">save</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">450</span><span class="p">],</span> <span class="s2">&quot;uc/train/&quot;</span><span class="p">)</span>
<span class="n">test_files</span> <span class="o">=</span> <span class="n">save</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">450</span><span class="p">:</span><span class="mi">500</span><span class="p">],</span> <span class="s2">&quot;uc/test/&quot;</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>Finally, we use <code class="docutils literal notranslate"><span class="pre">LearningSolver</span></code> to solve all the training instances. <code class="docutils literal notranslate"><span class="pre">LearningSolver</span></code> is the main component provided by MIPLearn, which integrates MIP solvers and ML. The optimal solutions, along with other useful training data, are stored in HDF5 files <code class="docutils literal notranslate"><span class="pre">uc/train/00000.h5</span></code>, <code class="docutils literal notranslate"><span class="pre">uc/train/00001.h5</span></code>, etc.</p>
<div class="nbinput nblast docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[12]:
</pre></div>
</div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">miplearn</span> <span class="kn">import</span> <span class="n">LearningSolver</span>
<span class="n">solver</span> <span class="o">=</span> <span class="n">LearningSolver</span><span class="p">()</span>
<span class="n">solver</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">train_files</span><span class="p">,</span> <span class="n">build_uc_model</span><span class="p">);</span>
</pre></div>
</div>
</div>
</div>
<div class="section" id="Solving-test-instances">
<h2>Solving test instances<a class="headerlink" href="#Solving-test-instances" title="Permalink to this headline"></a></h2>
<p>With training data in hand, we can now fit the ML models, using the <code class="docutils literal notranslate"><span class="pre">LearningSolver.fit</span></code> method, then solve the test instances with <code class="docutils literal notranslate"><span class="pre">LearningSolver.solve</span></code>, as shown below. The <code class="docutils literal notranslate"><span class="pre">tee=True</span></code> parameter asks MIPLearn to print the solver log to the screen.</p>
<div class="nbinput docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[9]:
</pre></div>
</div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">solver_ml</span> <span class="o">=</span> <span class="n">LearningSolver</span><span class="p">()</span>
<span class="n">solver_ml</span><span class="o">.</span><span class="n">fit</span><span class="p">(</span><span class="n">train_files</span><span class="p">,</span> <span class="n">build_uc_model</span><span class="p">)</span>
<span class="n">solver_ml</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">test_files</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">1</span><span class="p">],</span> <span class="n">build_uc_model</span><span class="p">,</span> <span class="n">tee</span><span class="o">=</span><span class="kc">True</span><span class="p">);</span>
</pre></div>
</div>
</div>
<div class="nboutput nblast docutils container">
<div class="prompt empty docutils container">
</div>
<div class="output_area docutils container">
<div class="highlight"><pre>
Set parameter LogFile to value &#34;/tmp/tmpvbaqbyty.log&#34;
Set parameter QCPDual to value 1
Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)
Thread count: 16 physical cores, 32 logical processors, using up to 1 threads
Optimize a model with 101 rows, 100 columns and 250 nonzeros
Model fingerprint: 0x8de73876
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [2e+07, 2e+07]
Presolve removed 100 rows and 50 columns
Presolve time: 0.00s
Presolved: 1 rows, 50 columns, 50 nonzeros
Iteration Objective Primal Inf. Dual Inf. Time
0 5.7349081e+08 1.044003e+04 0.000000e+00 0s
1 6.8268465e+08 0.000000e+00 0.000000e+00 0s
Solved in 1 iterations and 0.00 seconds (0.00 work units)
Optimal objective 6.826846503e+08
Set parameter LogFile to value &#34;&#34;
Set parameter LogFile to value &#34;/tmp/tmp48j6n35b.log&#34;
Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)
Thread count: 16 physical cores, 32 logical processors, using up to 1 threads
Optimize a model with 101 rows, 100 columns and 250 nonzeros
Model fingerprint: 0x200d64ba
Variable types: 50 continuous, 50 integer (50 binary)
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [2e+07, 2e+07]
User MIP start produced solution with objective 6.84841e+08 (0.00s)
Loaded user MIP start with objective 6.84841e+08
Presolve time: 0.00s
Presolved: 101 rows, 100 columns, 250 nonzeros
Variable types: 50 continuous, 50 integer (50 binary)
Root relaxation: objective 6.826847e+08, 56 iterations, 0.00 seconds (0.00 work units)
Nodes | Current Node | Objective Bounds | Work
Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
0 0 6.8268e+08 0 1 6.8484e+08 6.8268e+08 0.31% - 0s
0 0 6.8315e+08 0 3 6.8484e+08 6.8315e+08 0.25% - 0s
0 0 6.8315e+08 0 1 6.8484e+08 6.8315e+08 0.25% - 0s
0 0 6.8315e+08 0 3 6.8484e+08 6.8315e+08 0.25% - 0s
0 0 6.8315e+08 0 4 6.8484e+08 6.8315e+08 0.25% - 0s
0 0 6.8315e+08 0 4 6.8484e+08 6.8315e+08 0.25% - 0s
0 2 6.8327e+08 0 4 6.8484e+08 6.8327e+08 0.23% - 0s
Cutting planes:
Flow cover: 3
Explored 32 nodes (155 simplex iterations) in 0.02 seconds (0.00 work units)
Thread count was 1 (of 32 available processors)
Solution count 1: 6.84841e+08
Optimal solution found (tolerance 1.00e-04)
Best objective 6.848411655488e+08, best bound 6.848411655488e+08, gap 0.0000%
Set parameter LogFile to value &#34;&#34;
WARNING: Cannot get reduced costs for MIP.
WARNING: Cannot get duals for MIP.
</pre></div></div>
</div>
<p>By examining the solve log above, specifically the line <code class="docutils literal notranslate"><span class="pre">Loaded</span> <span class="pre">user</span> <span class="pre">MIP</span> <span class="pre">start</span> <span class="pre">with</span> <span class="pre">objective...</span></code>, we can see that MIPLearn was able to construct an initial solution which turned out to be the optimal solution to the problem. Now let us repeat the code above, but using an untrained solver. Note that the <code class="docutils literal notranslate"><span class="pre">fit</span></code> line is omitted.</p>
<div class="nbinput docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[10]:
</pre></div>
</div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">solver_baseline</span> <span class="o">=</span> <span class="n">LearningSolver</span><span class="p">()</span>
<span class="n">solver_baseline</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">test_files</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">1</span><span class="p">],</span> <span class="n">build_uc_model</span><span class="p">,</span> <span class="n">tee</span><span class="o">=</span><span class="kc">True</span><span class="p">);</span>
</pre></div>
</div>
</div>
<div class="nboutput nblast docutils container">
<div class="prompt empty docutils container">
</div>
<div class="output_area docutils container">
<div class="highlight"><pre>
Set parameter LogFile to value &#34;/tmp/tmp3uhhdurw.log&#34;
Set parameter QCPDual to value 1
Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)
Thread count: 16 physical cores, 32 logical processors, using up to 1 threads
Optimize a model with 101 rows, 100 columns and 250 nonzeros
Model fingerprint: 0x8de73876
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [2e+07, 2e+07]
Presolve removed 100 rows and 50 columns
Presolve time: 0.00s
Presolved: 1 rows, 50 columns, 50 nonzeros
Iteration Objective Primal Inf. Dual Inf. Time
0 5.7349081e+08 1.044003e+04 0.000000e+00 0s
1 6.8268465e+08 0.000000e+00 0.000000e+00 0s
Solved in 1 iterations and 0.01 seconds (0.00 work units)
Optimal objective 6.826846503e+08
Set parameter LogFile to value &#34;&#34;
Set parameter LogFile to value &#34;/tmp/tmp18aqg2ic.log&#34;
Gurobi Optimizer version 9.5.1 build v9.5.1rc2 (linux64)
Thread count: 16 physical cores, 32 logical processors, using up to 1 threads
Optimize a model with 101 rows, 100 columns and 250 nonzeros
Model fingerprint: 0xb90d1075
Variable types: 50 continuous, 50 integer (50 binary)
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [2e+07, 2e+07]
Found heuristic solution: objective 8.056576e+08
Presolve time: 0.00s
Presolved: 101 rows, 100 columns, 250 nonzeros
Variable types: 50 continuous, 50 integer (50 binary)
Root relaxation: objective 6.826847e+08, 56 iterations, 0.00 seconds (0.00 work units)
Nodes | Current Node | Objective Bounds | Work
Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
0 0 6.8268e+08 0 1 8.0566e+08 6.8268e+08 15.3% - 0s
H 0 0 7.099498e+08 6.8268e+08 3.84% - 0s
0 0 6.8315e+08 0 3 7.0995e+08 6.8315e+08 3.78% - 0s
H 0 0 6.883227e+08 6.8315e+08 0.75% - 0s
0 0 6.8352e+08 0 4 6.8832e+08 6.8352e+08 0.70% - 0s
0 0 6.8352e+08 0 4 6.8832e+08 6.8352e+08 0.70% - 0s
0 0 6.8352e+08 0 1 6.8832e+08 6.8352e+08 0.70% - 0s
H 0 0 6.862582e+08 6.8352e+08 0.40% - 0s
0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s
0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s
0 0 6.8352e+08 0 1 6.8626e+08 6.8352e+08 0.40% - 0s
0 0 6.8352e+08 0 3 6.8626e+08 6.8352e+08 0.40% - 0s
0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s
0 0 6.8352e+08 0 4 6.8626e+08 6.8352e+08 0.40% - 0s
0 2 6.8354e+08 0 4 6.8626e+08 6.8354e+08 0.40% - 0s
* 18 5 6 6.849018e+08 6.8413e+08 0.11% 3.1 0s
H 24 1 6.848412e+08 6.8426e+08 0.09% 3.2 0s
Cutting planes:
Gomory: 1
Flow cover: 2
Explored 30 nodes (217 simplex iterations) in 0.02 seconds (0.00 work units)
Thread count was 1 (of 32 available processors)
Solution count 6: 6.84841e+08 6.84902e+08 6.86258e+08 ... 8.05658e+08
Optimal solution found (tolerance 1.00e-04)
Best objective 6.848411655488e+08, best bound 6.848411655488e+08, gap 0.0000%
Set parameter LogFile to value &#34;&#34;
WARNING: Cannot get reduced costs for MIP.
WARNING: Cannot get duals for MIP.
</pre></div></div>
</div>
<p>In the log above, the <code class="docutils literal notranslate"><span class="pre">MIP</span> <span class="pre">start</span></code> line is missing, and Gurobi had to start with a significantly inferior initial solution. The solver was still able to find the optimal solution at the end, but it required using its own internal heuristic procedures. In this example, because we solve very small optimization problems, there was almost no difference in terms of running time. For larger problems, however, the difference can be significant. See benchmarks for more details.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>In addition to partial initial solutions, MIPLearn is also able to predict lazy constraints, cutting planes and branching priorities. See the next tutorials for more details.</p>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>It is not necessary to specify what ML models to use. MIPLearn, by default, will try a number of classical ML models and will choose the one that performs the best, based on k-fold cross validation. MIPLearn is also able to automatically collect features based on the MIP formulation of the problem and the solution to the LP relaxation, among other things, so it does not require handcrafted features. If you do want to customize the models and features, however, that is also possible, as we will
see in a later tutorial.</p>
</div>
</div>
<div class="section" id="Accessing-the-solution">
<h2>Accessing the solution<a class="headerlink" href="#Accessing-the-solution" title="Permalink to this headline"></a></h2>
<p>In the example above, we used <code class="docutils literal notranslate"><span class="pre">LearningSolver.solve</span></code> together with data files to solve both the training and the test instances. The optimal solutions were saved to HDF5 files in the train/test folders, and could be retrieved by reading theses files, but that is not very convenient. In the following example, we show how to build and solve a Pyomo model entirely in-memory, using our trained solver.</p>
<div class="nbinput docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[11]:
</pre></div>
</div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="c1"># Construct model using previously defined functions</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">random_uc_data</span><span class="p">(</span><span class="n">samples</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">n</span><span class="o">=</span><span class="mi">50</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">build_uc_model</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="c1"># Solve model using ML + Gurobi</span>
<span class="n">solver_ml</span><span class="o">.</span><span class="n">solve</span><span class="p">(</span><span class="n">model</span><span class="p">)</span>
<span class="c1"># Print part of the optimal solution</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;obj =&quot;</span><span class="p">,</span> <span class="n">model</span><span class="o">.</span><span class="n">obj</span><span class="p">())</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot; x =&quot;</span><span class="p">,</span> <span class="p">[</span><span class="n">model</span><span class="o">.</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">value</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)])</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot; y =&quot;</span><span class="p">,</span> <span class="p">[</span><span class="n">model</span><span class="o">.</span><span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">value</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)])</span>
</pre></div>
</div>
</div>
<div class="nboutput nblast docutils container">
<div class="prompt empty docutils container">
</div>
<div class="output_area docutils container">
<div class="highlight"><pre>
obj = 903865807.3536932
x = [1.0, 1.0, 1.0, 1.0, 1.0]
y = [1105176.593734543, 1891284.5155055337, 1708177.4224033852, 1438329.610189608, 535496.3347187206]
</pre></div></div>
</div>
<div class="nbinput nblast docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[ ]:
</pre></div>
</div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>
</pre></div>
</div>
</div>
</div>
</div>
</div>
<div class='prev-next-bottom'>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2022, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>
Loading…
Cancel
Save