Add v0.4 docs

docs
Alinson S. Xavier 2 years ago
parent 35c7820048
commit b3d887d38d

@ -0,0 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 5d2632d2fbe792265ca773e6a51b93f0
tags: d77d1c0d9ca2f4c8421862c7c5a0d620

@ -0,0 +1,42 @@
Collectors & Extractors
=======================
miplearn.classifiers.minprob
----------------------------
.. automodule:: miplearn.classifiers.minprob
:members:
:undoc-members:
:show-inheritance:
miplearn.classifiers.singleclass
--------------------------------
.. automodule:: miplearn.classifiers.singleclass
:members:
:undoc-members:
:show-inheritance:
miplearn.collectors.basic
-------------------------
.. automodule:: miplearn.collectors.basic
:members:
:undoc-members:
:show-inheritance:
miplearn.extractors.fields
--------------------------
.. automodule:: miplearn.extractors.fields
:members:
:undoc-members:
:show-inheritance:
miplearn.extractors.AlvLouWeh2017
---------------------------------
.. automodule:: miplearn.extractors.AlvLouWeh2017
:members:
:undoc-members:
:show-inheritance:

@ -0,0 +1,44 @@
Components
==========
miplearn.components.primal.actions
----------------------------------
.. automodule:: miplearn.components.primal.actions
:members:
:undoc-members:
:show-inheritance:
miplearn.components.primal.expert
----------------------------------
.. automodule:: miplearn.components.primal.expert
:members:
:undoc-members:
:show-inheritance:
miplearn.components.primal.indep
----------------------------------
.. automodule:: miplearn.components.primal.indep
:members:
:undoc-members:
:show-inheritance:
miplearn.components.primal.joint
----------------------------------
.. automodule:: miplearn.components.primal.joint
:members:
:undoc-members:
:show-inheritance:
miplearn.components.primal.mem
----------------------------------
.. automodule:: miplearn.components.primal.mem
:members:
:undoc-members:
:show-inheritance:

@ -0,0 +1,18 @@
Helpers
=======
miplearn.io
-----------
.. automodule:: miplearn.io
:members:
:undoc-members:
:show-inheritance:
miplearn.h5
-----------
.. automodule:: miplearn.h5
:members:
:undoc-members:
:show-inheritance:

@ -0,0 +1,57 @@
Benchmark Problems
==================
miplearn.problems.binpack
-------------------------
.. automodule:: miplearn.problems.binpack
:members:
miplearn.problems.multiknapsack
-------------------------------
.. automodule:: miplearn.problems.multiknapsack
:members:
miplearn.problems.pmedian
-------------------------
.. automodule:: miplearn.problems.pmedian
:members:
miplearn.problems.setcover
--------------------------
.. automodule:: miplearn.problems.setcover
:members:
miplearn.problems.setpack
-------------------------
.. automodule:: miplearn.problems.setpack
:members:
miplearn.problems.stab
----------------------
.. automodule:: miplearn.problems.stab
:members:
miplearn.problems.tsp
---------------------
.. automodule:: miplearn.problems.tsp
:members:
miplearn.problems.uc
--------------------
.. automodule:: miplearn.problems.uc
:members:
miplearn.problems.vertexcover
-----------------------------
.. automodule:: miplearn.problems.vertexcover
:members:

@ -0,0 +1,26 @@
Solvers
=======
miplearn.solvers.abstract
-------------------------
.. automodule:: miplearn.solvers.abstract
:members:
:undoc-members:
:show-inheritance:
miplearn.solvers.gurobi
-------------------------
.. automodule:: miplearn.solvers.gurobi
:members:
:undoc-members:
:show-inheritance:
miplearn.solvers.learning
-------------------------
.. automodule:: miplearn.solvers.learning
:members:
:undoc-members:
:show-inheritance:

@ -0,0 +1,288 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "505cea0b-5f5d-478a-9107-42bb5515937d",
"metadata": {},
"source": [
"# Training Data Collectors\n",
"The first step in solving mixed-integer optimization problems with the assistance of supervised machine learning methods is solving a large set of training instances and collecting the raw training data. In this section, we describe the various training data collectors included in MIPLearn. Additionally, the framework follows the convention of storing all training data in files with a specific data format (namely, HDF5). In this section, we briefly describe this format and the rationale for choosing it.\n",
"\n",
"## Overview\n",
"\n",
"In MIPLearn, a **collector** is a class that solves or analyzes the problem and collects raw data which may be later useful for machine learning methods. Collectors, by convention, take as input: (i) a list of problem data filenames, in gzipped pickle format, ending with `.pkl.gz`; (ii) a function that builds the optimization model, such as `build_tsp_model`. After processing is done, collectors store the training data in a HDF5 file located alongside with the problem data. For example, if the problem data is stored in file `problem.pkl.gz`, then the collector writes to `problem.h5`. Collectors are, in general, very time consuming, as they may need to solve the problem to optimality, potentially multiple times.\n",
"\n",
"## HDF5 Format\n",
"\n",
"MIPLearn stores all training data in [HDF5](HDF5) (Hierarchical Data Format, Version 5) files. The HDF format was originally developed by the [National Center for Supercomputing Applications][NCSA] (NCSA) for storing and organizing large amounts of data, and supports a variety of data types, including integers, floating-point numbers, strings, and arrays. Compared to other formats, such as CSV, JSON or SQLite, the HDF5 format provides several advantages for MIPLearn, including:\n",
"\n",
"- *Storage of multiple scalars, vectors and matrices in a single file* --- This allows MIPLearn to store all training data related to a given problem instance in a single file, which makes training data easier to store, organize and transfer.\n",
"- *High-performance partial I/O* --- Partial I/O allows MIPLearn to read a single element from the training data (e.g. value of the optimal solution) without loading the entire file to memory or reading it from beginning to end, which dramatically improves performance and reduces memory requirements. This is especially important when processing a large number of training data files.\n",
"- *On-the-fly compression* --- HDF5 files can be transparently compressed, using the gzip method, which reduces storage requirements and accelerates network transfers.\n",
"- *Stable, portable and well-supported data format* --- Training data files are typically expensive to generate. Having a stable and well supported data format ensures that these files remain usable in the future, potentially even by other non-Python MIP/ML frameworks.\n",
"\n",
"MIPLearn currently uses HDF5 as simple key-value storage for numerical data; more advanced features of the format, such as metadata, are not currently used. Although files generated by MIPLearn can be read with any HDF5 library, such as [h5py][h5py], some convenience functions are provided to make the access more simple and less error-prone. Specifically, the class [H5File][H5File], which is built on top of h5py, provides the methods [put_scalar][put_scalar], [put_array][put_array], [put_sparse][put_sparse], [put_bytes][put_bytes] to store, respectively, scalar values, dense multi-dimensional arrays, sparse multi-dimensional arrays and arbitrary binary data. The corresponding *get* methods are also provided. Compared to pure h5py methods, these methods automatically perform type-checking and gzip compression. The example below shows their usage.\n",
"\n",
"[HDF5]: https://en.wikipedia.org/wiki/Hierarchical_Data_Format\n",
"[NCSA]: https://en.wikipedia.org/wiki/National_Center_for_Supercomputing_Applications\n",
"[h5py]: https://www.h5py.org/\n",
"[H5File]: ../../api/helpers/#miplearn.h5.H5File\n",
"[put_scalar]: ../../api/helpers/#miplearn.h5.H5File.put_scalar\n",
"[put_array]: ../../api/helpers/#miplearn.h5.H5File.put_scalar\n",
"[put_sparse]: ../../api/helpers/#miplearn.h5.H5File.put_scalar\n",
"[put_bytes]: ../../api/helpers/#miplearn.h5.H5File.put_scalar\n",
"\n",
"\n",
"### Example"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "f906fe9c",
"metadata": {
"ExecuteTime": {
"end_time": "2024-01-30T22:19:30.826123021Z",
"start_time": "2024-01-30T22:19:30.766066926Z"
},
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"x1 = 1\n",
"x2 = hello world\n",
"x3 = [1 2 3]\n",
"x4 = [[0.37454012 0.9507143 0.7319939 ]\n",
" [0.5986585 0.15601864 0.15599452]\n",
" [0.05808361 0.8661761 0.601115 ]]\n",
"x5 = (3, 2)\t0.6803075671195984\n",
" (2, 3)\t0.4504992663860321\n",
" (0, 4)\t0.013264961540699005\n",
" (2, 0)\t0.9422017335891724\n",
" (2, 4)\t0.5632882118225098\n",
" (1, 2)\t0.38541650772094727\n",
" (1, 1)\t0.015966251492500305\n",
" (0, 3)\t0.2308938205242157\n",
" (4, 4)\t0.24102546274662018\n",
" (3, 1)\t0.6832635402679443\n",
" (1, 3)\t0.6099966764450073\n",
" (3, 0)\t0.83319491147995\n"
]
}
],
"source": [
"import numpy as np\n",
"import scipy.sparse\n",
"\n",
"from miplearn.h5 import H5File\n",
"\n",
"# Set random seed to make example reproducible\n",
"np.random.seed(42)\n",
"\n",
"# Create a new empty HDF5 file\n",
"with H5File(\"test.h5\", \"w\") as h5:\n",
" # Store a scalar\n",
" h5.put_scalar(\"x1\", 1)\n",
" h5.put_scalar(\"x2\", \"hello world\")\n",
"\n",
" # Store a dense array and a dense matrix\n",
" h5.put_array(\"x3\", np.array([1, 2, 3]))\n",
" h5.put_array(\"x4\", np.random.rand(3, 3))\n",
"\n",
" # Store a sparse matrix\n",
" h5.put_sparse(\"x5\", scipy.sparse.random(5, 5, 0.5))\n",
"\n",
"# Re-open the file we just created and print\n",
"# previously-stored data\n",
"with H5File(\"test.h5\", \"r\") as h5:\n",
" print(\"x1 =\", h5.get_scalar(\"x1\"))\n",
" print(\"x2 =\", h5.get_scalar(\"x2\"))\n",
" print(\"x3 =\", h5.get_array(\"x3\"))\n",
" print(\"x4 =\", h5.get_array(\"x4\"))\n",
" print(\"x5 =\", h5.get_sparse(\"x5\"))"
]
},
{
"cell_type": "markdown",
"id": "50441907",
"metadata": {},
"source": []
},
{
"cell_type": "markdown",
"id": "d0000c8d",
"metadata": {},
"source": [
"## Basic collector\n",
"\n",
"[BasicCollector][BasicCollector] is the most fundamental collector, and performs the following steps:\n",
"\n",
"1. Extracts all model data, such as objective function and constraint right-hand sides into numpy arrays, which can later be easily and efficiently accessed without rebuilding the model or invoking the solver;\n",
"2. Solves the linear relaxation of the problem and stores its optimal solution, basis status and sensitivity information, among other information;\n",
"3. Solves the original mixed-integer optimization problem to optimality and stores its optimal solution, along with solve statistics, such as number of explored nodes and wallclock time.\n",
"\n",
"Data extracted in Phases 1, 2 and 3 above are prefixed, respectively as `static_`, `lp_` and `mip_`. The entire set of fields is shown in the table below.\n",
"\n",
"[BasicCollector]: ../../api/collectors/#miplearn.collectors.basic.BasicCollector\n"
]
},
{
"cell_type": "markdown",
"id": "6529f667",
"metadata": {},
"source": [
"### Data fields\n",
"\n",
"| Field | Type | Description |\n",
"|-----------------------------------|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------|\n",
"| `static_constr_lhs` | `(nconstrs, nvars)` | Constraint left-hand sides, in sparse matrix format |\n",
"| `static_constr_names` | `(nconstrs,)` | Constraint names |\n",
"| `static_constr_rhs` | `(nconstrs,)` | Constraint right-hand sides |\n",
"| `static_constr_sense` | `(nconstrs,)` | Constraint senses (`\"<\"`, `\">\"` or `\"=\"`) |\n",
"| `static_obj_offset` | `float` | Constant value added to the objective function |\n",
"| `static_sense` | `str` | `\"min\"` if minimization problem or `\"max\"` otherwise |\n",
"| `static_var_lower_bounds` | `(nvars,)` | Variable lower bounds |\n",
"| `static_var_names` | `(nvars,)` | Variable names |\n",
"| `static_var_obj_coeffs` | `(nvars,)` | Objective coefficients |\n",
"| `static_var_types` | `(nvars,)` | Types of the decision variables (`\"C\"`, `\"B\"` and `\"I\"` for continuous, binary and integer, respectively) |\n",
"| `static_var_upper_bounds` | `(nvars,)` | Variable upper bounds |\n",
"| `lp_constr_basis_status` | `(nconstr,)` | Constraint basis status (`0` for basic, `-1` for non-basic) |\n",
"| `lp_constr_dual_values` | `(nconstr,)` | Constraint dual value (or shadow price) |\n",
"| `lp_constr_sa_rhs_{up,down}` | `(nconstr,)` | Sensitivity information for the constraint RHS |\n",
"| `lp_constr_slacks` | `(nconstr,)` | Constraint slack in the solution to the LP relaxation |\n",
"| `lp_obj_value` | `float` | Optimal value of the LP relaxation |\n",
"| `lp_var_basis_status` | `(nvars,)` | Variable basis status (`0`, `-1`, `-2` or `-3` for basic, non-basic at lower bound, non-basic at upper bound, and superbasic, respectively) |\n",
"| `lp_var_reduced_costs` | `(nvars,)` | Variable reduced costs |\n",
"| `lp_var_sa_{obj,ub,lb}_{up,down}` | `(nvars,)` | Sensitivity information for the variable objective coefficient, lower and upper bound. |\n",
"| `lp_var_values` | `(nvars,)` | Optimal solution to the LP relaxation |\n",
"| `lp_wallclock_time` | `float` | Time taken to solve the LP relaxation (in seconds) |\n",
"| `mip_constr_slacks` | `(nconstrs,)` | Constraint slacks in the best MIP solution |\n",
"| `mip_gap` | `float` | Relative MIP optimality gap |\n",
"| `mip_node_count` | `float` | Number of explored branch-and-bound nodes |\n",
"| `mip_obj_bound` | `float` | Dual bound |\n",
"| `mip_obj_value` | `float` | Value of the best MIP solution |\n",
"| `mip_var_values` | `(nvars,)` | Best MIP solution |\n",
"| `mip_wallclock_time` | `float` | Time taken to solve the MIP (in seconds) |"
]
},
{
"cell_type": "markdown",
"id": "f2894594",
"metadata": {},
"source": [
"### Example\n",
"\n",
"The example below shows how to generate a few random instances of the traveling salesman problem, store its problem data, run the collector and print some of the training data to screen."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "ac6f8c6f",
"metadata": {
"ExecuteTime": {
"end_time": "2024-01-30T22:19:30.826707866Z",
"start_time": "2024-01-30T22:19:30.825940503Z"
},
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"lp_obj_value = 2909.0\n",
"mip_obj_value = 2921.0\n"
]
}
],
"source": [
"import random\n",
"import numpy as np\n",
"from scipy.stats import uniform, randint\n",
"from glob import glob\n",
"\n",
"from miplearn.problems.tsp import (\n",
" TravelingSalesmanGenerator,\n",
" build_tsp_model_gurobipy,\n",
")\n",
"from miplearn.io import write_pkl_gz\n",
"from miplearn.h5 import H5File\n",
"from miplearn.collectors.basic import BasicCollector\n",
"\n",
"# Set random seed to make example reproducible.\n",
"random.seed(42)\n",
"np.random.seed(42)\n",
"\n",
"# Generate a few instances of the traveling salesman problem.\n",
"data = TravelingSalesmanGenerator(\n",
" n=randint(low=10, high=11),\n",
" x=uniform(loc=0.0, scale=1000.0),\n",
" y=uniform(loc=0.0, scale=1000.0),\n",
" gamma=uniform(loc=0.90, scale=0.20),\n",
" fix_cities=True,\n",
" round=True,\n",
").generate(10)\n",
"\n",
"# Save instance data to data/tsp/00000.pkl.gz, data/tsp/00001.pkl.gz, ...\n",
"write_pkl_gz(data, \"data/tsp\")\n",
"\n",
"# Solve all instances and collect basic solution information.\n",
"# Process at most four instances in parallel.\n",
"bc = BasicCollector()\n",
"bc.collect(glob(\"data/tsp/*.pkl.gz\"), build_tsp_model_gurobipy, n_jobs=4)\n",
"\n",
"# Read and print some training data for the first instance.\n",
"with H5File(\"data/tsp/00000.h5\", \"r\") as h5:\n",
" print(\"lp_obj_value = \", h5.get_scalar(\"lp_obj_value\"))\n",
" print(\"mip_obj_value = \", h5.get_scalar(\"mip_obj_value\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "78f0b07a",
"metadata": {
"ExecuteTime": {
"start_time": "2024-01-30T22:19:30.826179789Z"
},
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,334 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "cdc6ebe9-d1d4-4de1-9b5a-4fc8ef57b11b",
"metadata": {},
"source": [
"# Feature Extractors\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."
]
},
{
"cell_type": "markdown",
"id": "b4026de5",
"metadata": {},
"source": [
"\n",
"## Overview\n",
"\n",
"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.\n",
"\n",
"In the framework, we treat data collection and feature extraction as two separate steps to accelerate the model development cycle. Specifically, collectors are typically time-consuming, as they often need to solve the problem to optimality, and therefore focus on collecting and storing all data that may or may not be relevant, in its raw format. Feature extractors, on the other hand, focus entirely on filtering the data and improving its representation, and are therefore much faster to run. Experimenting with new data representations, therefore, can be done without resolving the instances.\n",
"\n",
"In MIPLearn, extractors implement the abstract class [FeatureExtractor][FeatureExtractor], which has methods that take as input an [H5File][H5File] and produce either: (i) instance features, which describe the entire instances; (ii) variable features, which describe a particular decision variables; or (iii) constraint features, which describe a particular constraint. The extractor is free to implement only a subset of these methods, if it is known that it will not be used with a machine learning component that requires the other types of features.\n",
"\n",
"[FeatureExtractor]: ../../api/collectors/#miplearn.features.fields.FeaturesExtractor\n",
"[H5File]: ../../api/helpers/#miplearn.h5.H5File"
]
},
{
"cell_type": "markdown",
"id": "b2d9736c",
"metadata": {},
"source": [
"\n",
"## H5FieldsExtractor\n",
"\n",
"[H5FieldsExtractor][H5FieldsExtractor], the most simple extractor in MIPLearn, simple extracts data that is already available in the HDF5 file, assembles it into a matrix and returns it as-is. The fields used to build instance, variable and constraint features are user-specified. The class also performs checks to ensure that the shapes of the returned matrices make sense."
]
},
{
"cell_type": "markdown",
"id": "e8184dff",
"metadata": {},
"source": [
"### Example\n",
"\n",
"The example below demonstrates the usage of H5FieldsExtractor in a randomly generated instance of the multi-dimensional knapsack problem."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "ed9a18c8",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"instance features (11,) \n",
" [-1531.24308771 -350. -692. -454.\n",
" -709. -605. -543. -321.\n",
" -674. -571. -341. ]\n",
"variable features (10, 4) \n",
" [[-1.53124309e+03 -3.50000000e+02 0.00000000e+00 9.43468018e+01]\n",
" [-1.53124309e+03 -6.92000000e+02 2.51703322e-01 0.00000000e+00]\n",
" [-1.53124309e+03 -4.54000000e+02 0.00000000e+00 8.25504150e+01]\n",
" [-1.53124309e+03 -7.09000000e+02 1.11373022e-01 0.00000000e+00]\n",
" [-1.53124309e+03 -6.05000000e+02 1.00000000e+00 -1.26055283e+02]\n",
" [-1.53124309e+03 -5.43000000e+02 0.00000000e+00 1.68693771e+02]\n",
" [-1.53124309e+03 -3.21000000e+02 1.07488781e-01 0.00000000e+00]\n",
" [-1.53124309e+03 -6.74000000e+02 8.82293701e-01 0.00000000e+00]\n",
" [-1.53124309e+03 -5.71000000e+02 0.00000000e+00 1.41129074e+02]\n",
" [-1.53124309e+03 -3.41000000e+02 1.28830120e-01 0.00000000e+00]]\n",
"constraint features (5, 3) \n",
" [[ 1.3100000e+03 -1.5978307e-01 0.0000000e+00]\n",
" [ 9.8800000e+02 -3.2881632e-01 0.0000000e+00]\n",
" [ 1.0040000e+03 -4.0601316e-01 0.0000000e+00]\n",
" [ 1.2690000e+03 -1.3659772e-01 0.0000000e+00]\n",
" [ 1.0070000e+03 -2.8800571e-01 0.0000000e+00]]\n"
]
}
],
"source": [
"from glob import glob\n",
"from shutil import rmtree\n",
"\n",
"import numpy as np\n",
"from scipy.stats import uniform, randint\n",
"\n",
"from miplearn.collectors.basic import BasicCollector\n",
"from miplearn.extractors.fields import H5FieldsExtractor\n",
"from miplearn.h5 import H5File\n",
"from miplearn.io import write_pkl_gz\n",
"from miplearn.problems.multiknapsack import (\n",
" MultiKnapsackGenerator,\n",
" build_multiknapsack_model_gurobipy,\n",
")\n",
"\n",
"# Set random seed to make example reproducible\n",
"np.random.seed(42)\n",
"\n",
"# Generate some random multiknapsack instances\n",
"rmtree(\"data/multiknapsack/\", ignore_errors=True)\n",
"write_pkl_gz(\n",
" MultiKnapsackGenerator(\n",
" n=randint(low=10, high=11),\n",
" m=randint(low=5, high=6),\n",
" w=uniform(loc=0, scale=1000),\n",
" K=uniform(loc=100, scale=0),\n",
" u=uniform(loc=1, scale=0),\n",
" alpha=uniform(loc=0.25, scale=0),\n",
" w_jitter=uniform(loc=0.95, scale=0.1),\n",
" p_jitter=uniform(loc=0.75, scale=0.5),\n",
" fix_w=True,\n",
" ).generate(10),\n",
" \"data/multiknapsack\",\n",
")\n",
"\n",
"# Run the basic collector\n",
"BasicCollector().collect(\n",
" glob(\"data/multiknapsack/*\"),\n",
" build_multiknapsack_model_gurobipy,\n",
" n_jobs=4,\n",
")\n",
"\n",
"ext = H5FieldsExtractor(\n",
" # Use as instance features the value of the LP relaxation and the\n",
" # vector of objective coefficients.\n",
" instance_fields=[\n",
" \"lp_obj_value\",\n",
" \"static_var_obj_coeffs\",\n",
" ],\n",
" # For each variable, use as features the optimal value of the LP\n",
" # relaxation, the variable objective coefficient, the variable's\n",
" # value its reduced cost.\n",
" var_fields=[\n",
" \"lp_obj_value\",\n",
" \"static_var_obj_coeffs\",\n",
" \"lp_var_values\",\n",
" \"lp_var_reduced_costs\",\n",
" ],\n",
" # For each constraint, use as features the RHS, dual value and slack.\n",
" constr_fields=[\n",
" \"static_constr_rhs\",\n",
" \"lp_constr_dual_values\",\n",
" \"lp_constr_slacks\",\n",
" ],\n",
")\n",
"\n",
"with H5File(\"data/multiknapsack/00000.h5\") as h5:\n",
" # Extract and print instance features\n",
" x1 = ext.get_instance_features(h5)\n",
" print(\"instance features\", x1.shape, \"\\n\", x1)\n",
"\n",
" # Extract and print variable features\n",
" x2 = ext.get_var_features(h5)\n",
" print(\"variable features\", x2.shape, \"\\n\", x2)\n",
"\n",
" # Extract and print constraint features\n",
" x3 = ext.get_constr_features(h5)\n",
" print(\"constraint features\", x3.shape, \"\\n\", x3)"
]
},
{
"cell_type": "markdown",
"id": "2da2e74e",
"metadata": {},
"source": [
"\n",
"[H5FieldsExtractor]: ../../api/collectors/#miplearn.features.fields.H5FieldsExtractor"
]
},
{
"cell_type": "markdown",
"id": "d879c0d3",
"metadata": {},
"source": [
"<div class=\"alert alert-warning\">\n",
"Warning\n",
"\n",
"You should ensure that the number of features remains the same for all relevant HDF5 files. In the previous example, to illustrate this issue, we used variable objective coefficients as instance features. While this is allowed, note that this requires all problem instances to have the same number of variables; otherwise the number of features would vary from instance to instance and MIPLearn would be unable to concatenate the matrices.\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "cd0ba071",
"metadata": {},
"source": [
"## AlvLouWeh2017Extractor\n",
"\n",
"Alvarez, Louveaux and Wehenkel (2017) proposed a set features to describe a particular decision variable in a given node of the branch-and-bound tree, and applied it to the problem of mimicking strong branching decisions. The class [AlvLouWeh2017Extractor][] implements a subset of these features (40 out of 64), which are available outside of the branch-and-bound tree. Some features are derived from the static defintion of the problem (i.e. from objective function and constraint data), while some features are derived from the solution to the LP relaxation. The features have been designed to be: (i) independent of the size of the problem; (ii) invariant with respect to irrelevant problem transformations, such as row and column permutation; and (iii) independent of the scale of the problem. We refer to the paper for a more complete description.\n",
"\n",
"### Example"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "a1bc38fe",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"x1 (10, 40) \n",
" [[-1.00e+00 1.00e+20 1.00e-01 1.00e+00 0.00e+00 1.00e+00 6.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 6.00e-01 1.00e+00 1.75e+01 1.00e+00 2.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 -1.00e+00 0.00e+00 1.00e+20]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 1.00e-01 1.00e+00 1.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 7.00e-01 1.00e+00 5.10e+00 1.00e+00 2.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 3.00e-01 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 0.00e+00 1.00e+00 9.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 5.00e-01 1.00e+00 1.30e+01 1.00e+00 2.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 -1.00e+00 0.00e+00 1.00e+20]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 2.00e-01 1.00e+00 9.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 8.00e-01 1.00e+00 3.40e+00 1.00e+00 2.00e-01\n",
" 1.00e+00 1.00e-01 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 1.00e-01 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 1.00e-01 1.00e+00 7.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 6.00e-01 1.00e+00 3.80e+00 1.00e+00 2.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 1.00e-01 1.00e+00 8.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 7.00e-01 1.00e+00 3.30e+00 1.00e+00 2.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 -1.00e+00 0.00e+00 1.00e+20]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 0.00e+00 1.00e+00 3.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 1.00e+00 1.00e+00 5.70e+00 1.00e+00 1.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 1.00e-01 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 1.00e-01 1.00e+00 6.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 8.00e-01 1.00e+00 6.80e+00 1.00e+00 2.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 1.00e-01 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 4.00e-01 1.00e+00 6.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 8.00e-01 1.00e+00 1.40e+00 1.00e+00 1.00e-01\n",
" 1.00e+00 1.00e-01 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 -1.00e+00 0.00e+00 1.00e+20]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 0.00e+00 1.00e+00 5.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 5.00e-01 1.00e+00 7.60e+00 1.00e+00 1.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 1.00e-01 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]]\n"
]
}
],
"source": [
"from miplearn.extractors.AlvLouWeh2017 import AlvLouWeh2017Extractor\n",
"from miplearn.h5 import H5File\n",
"\n",
"# Build the extractor\n",
"ext = AlvLouWeh2017Extractor()\n",
"\n",
"# Open previously-created multiknapsack training data\n",
"with H5File(\"data/multiknapsack/00000.h5\") as h5:\n",
" # Extract and print variable features\n",
" x1 = ext.get_var_features(h5)\n",
" print(\"x1\", x1.shape, \"\\n\", x1.round(1))"
]
},
{
"cell_type": "markdown",
"id": "286c9927",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
"References\n",
"\n",
"* **Alvarez, Alejandro Marcos.** *Computational and theoretical synergies between linear optimization and supervised machine learning.* (2016). University of Liège.\n",
"* **Alvarez, Alejandro Marcos, Quentin Louveaux, and Louis Wehenkel.** *A machine learning-based approximation of strong branching.* INFORMS Journal on Computing 29.1 (2017): 185-195.\n",
"\n",
"</div>"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,291 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "880cf4c7-d3c4-4b92-85c7-04a32264cdae",
"metadata": {},
"source": [
"# Primal Components\n",
"\n",
"In MIPLearn, a **primal component** is class that uses machine learning to predict a (potentially partial) assignment of values to the decision variables of the problem. Predicting high-quality primal solutions may be beneficial, as they allow the MIP solver to prune potentially large portions of the search space. Alternatively, if proof of optimality is not required, the MIP solver can be used to complete the partial solution generated by the machine learning model and and double-check its feasibility. MIPLearn allows both of these usage patterns.\n",
"\n",
"In this page, we describe the four primal components currently included in MIPLearn, which employ machine learning in different ways. Each component is highly configurable, and accepts an user-provided machine learning model, which it uses for all predictions. Each component can also be configured to provide the solution to the solver in multiple ways, depending on whether proof of optimality is required.\n",
"\n",
"## Primal component actions\n",
"\n",
"Before presenting the primal components themselves, we briefly discuss the three ways a solution may be provided to the solver. Each approach has benefits and limitations, which we also discuss in this section. All primal components can be configured to use any of the following approaches.\n",
"\n",
"The first approach is to provide the solution to the solver as a **warm start**. This is implemented by the class [SetWarmStart](SetWarmStart). The main advantage is that this method maintains all optimality and feasibility guarantees of the MIP solver, while still providing significant performance benefits for various classes of problems. If the machine learning model is able to predict multiple solutions, it is also possible to set multiple warm starts. In this case, the solver evaluates each warm start, discards the infeasible ones, then proceeds with the one that has the best objective value. The main disadvantage of this approach, compared to the next two, is that it provides relatively modest speedups for most problem classes, and no speedup at all for many others, even when the machine learning predictions are 100% accurate.\n",
"\n",
"[SetWarmStart]: ../../api/components/#miplearn.components.primal.actions.SetWarmStart\n",
"\n",
"The second approach is to **fix the decision variables** to their predicted values, then solve a restricted optimization problem on the remaining variables. This approach is implemented by the class `FixVariables`. The main advantage is its potential speedup: if machine learning can accurately predict values for a significant portion of the decision variables, then the MIP solver can typically complete the solution in a small fraction of the time it would take to find the same solution from scratch. The main disadvantage of this approach is that it loses optimality guarantees; that is, the complete solution found by the MIP solver may no longer be globally optimal. Also, if the machine learning predictions are not sufficiently accurate, there might not even be a feasible assignment for the variables that were left free.\n",
"\n",
"Finally, the third approach, which tries to strike a balance between the two previous ones, is to **enforce proximity** to a given solution. This strategy is implemented by the class `EnforceProximity`. More precisely, given values $\\bar{x}_1,\\ldots,\\bar{x}_n$ for a subset of binary decision variables $x_1,\\ldots,x_n$, this approach adds the constraint\n",
"\n",
"$$\n",
"\\sum_{i : \\bar{x}_i=0} x_i + \\sum_{i : \\bar{x}_i=1} \\left(1 - x_i\\right) \\leq k,\n",
"$$\n",
"to the problem, where $k$ is a user-defined parameter, which indicates how many of the predicted variables are allowed to deviate from the machine learning suggestion. The main advantage of this approach, compared to fixing variables, is its tolerance to lower-quality machine learning predictions. Its main disadvantage is that it typically leads to smaller speedups, especially for larger values of $k$. This approach also loses optimality guarantees.\n",
"\n",
"## Memorizing primal component\n",
"\n",
"A simple machine learning strategy for the prediction of primal solutions is to memorize all distinct solutions seen during training, then try to predict, during inference time, which of those memorized solutions are most likely to be feasible and to provide a good objective value for the current instance. The most promising solutions may alternatively be combined into a single partial solution, which is then provided to the MIP solver. Both variations of this strategy are implemented by the `MemorizingPrimalComponent` class. Note that it is only applicable if the problem size, and in fact if the meaning of the decision variables, remains the same across problem instances.\n",
"\n",
"More precisely, let $I_1,\\ldots,I_n$ be the training instances, and let $\\bar{x}^1,\\ldots,\\bar{x}^n$ be their respective optimal solutions. Given a new instance $I_{n+1}$, `MemorizingPrimalComponent` expects a user-provided binary classifier that assigns (through the `predict_proba` method, following scikit-learn's conventions) a score $\\delta_i$ to each solution $\\bar{x}^i$, such that solutions with higher score are more likely to be good solutions for $I_{n+1}$. The features provided to the classifier are the instance features computed by an user-provided extractor. Given these scores, the component then performs one of the following to actions, as decided by the user:\n",
"\n",
"1. Selects the top $k$ solutions with the highest scores and provides them to the solver; this is implemented by `SelectTopSolutions`, and it is typically used with the `SetWarmStart` action.\n",
"\n",
"2. Merges the top $k$ solutions into a single partial solution, then provides it to the solver. This is implemented by `MergeTopSolutions`. More precisely, suppose that the machine learning regressor ordered the solutions in the sequence $\\bar{x}^{i_1},\\ldots,\\bar{x}^{i_n}$, with the most promising solutions appearing first, and with ties being broken arbitrarily. The component starts by keeping only the $k$ most promising solutions $\\bar{x}^{i_1},\\ldots,\\bar{x}^{i_k}$. Then it computes, for each binary decision variable $x_l$, its average assigned value $\\tilde{x}_l$:\n",
"$$\n",
" \\tilde{x}_l = \\frac{1}{k} \\sum_{j=1}^k \\bar{x}^{i_j}_l.\n",
"$$\n",
" Finally, the component constructs a merged solution $y$, defined as:\n",
"$$\n",
" y_j = \\begin{cases}\n",
" 0 & \\text{ if } \\tilde{x}_l \\le \\theta_0 \\\\\n",
" 1 & \\text{ if } \\tilde{x}_l \\ge \\theta_1 \\\\\n",
" \\square & \\text{otherwise,}\n",
" \\end{cases}\n",
"$$\n",
" where $\\theta_0$ and $\\theta_1$ are user-specified parameters, and where $\\square$ indicates that the variable is left undefined. The solution $y$ is then provided by the solver using any of the three approaches defined in the previous section.\n",
"\n",
"The above specification of `MemorizingPrimalComponent` is meant to be as general as possible. Simpler strategies can be implemented by configuring this component in specific ways. For example, a simpler approach employed in the literature is to collect all optimal solutions, then provide the entire list of solutions to the solver as warm starts, without any filtering or post-processing. This strategy can be implemented with `MemorizingPrimalComponent` by using a model that returns a constant value for all solutions (e.g. [scikit-learn's DummyClassifier][DummyClassifier]), then selecting the top $n$ (instead of $k$) solutions. See example below. Another simple approach is taking the solution to the most similar instance, and using it, by itself, as a warm start. This can be implemented by using a model that computes distances between the current instance and the training ones (e.g. [scikit-learn's KNeighborsClassifier][KNeighborsClassifier]), then select the solution to the nearest one. See also example below. More complex strategies, of course, can also be configured.\n",
"\n",
"[DummyClassifier]: https://scikit-learn.org/stable/modules/generated/sklearn.dummy.DummyClassifier.html\n",
"[KNeighborsClassifier]: https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html\n",
"\n",
"### Examples"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "253adbf4",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"from sklearn.dummy import DummyClassifier\n",
"from sklearn.neighbors import KNeighborsClassifier\n",
"\n",
"from miplearn.components.primal.actions import (\n",
" SetWarmStart,\n",
" FixVariables,\n",
" EnforceProximity,\n",
")\n",
"from miplearn.components.primal.mem import (\n",
" MemorizingPrimalComponent,\n",
" SelectTopSolutions,\n",
" MergeTopSolutions,\n",
")\n",
"from miplearn.extractors.dummy import DummyExtractor\n",
"from miplearn.extractors.fields import H5FieldsExtractor\n",
"\n",
"# Configures a memorizing primal component that collects\n",
"# all distinct solutions seen during training and provides\n",
"# them to the solver without any filtering or post-processing.\n",
"comp1 = MemorizingPrimalComponent(\n",
" clf=DummyClassifier(),\n",
" extractor=DummyExtractor(),\n",
" constructor=SelectTopSolutions(1_000_000),\n",
" action=SetWarmStart(),\n",
")\n",
"\n",
"# Configures a memorizing primal component that finds the\n",
"# training instance with the closest objective function, then\n",
"# fixes the decision variables to the values they assumed\n",
"# at the optimal solution for that instance.\n",
"comp2 = MemorizingPrimalComponent(\n",
" clf=KNeighborsClassifier(n_neighbors=1),\n",
" extractor=H5FieldsExtractor(\n",
" instance_fields=[\"static_var_obj_coeffs\"],\n",
" ),\n",
" constructor=SelectTopSolutions(1),\n",
" action=FixVariables(),\n",
")\n",
"\n",
"# Configures a memorizing primal component that finds the distinct\n",
"# solutions to the 10 most similar training problem instances,\n",
"# selects the 3 solutions that were most often optimal to these\n",
"# training instances, combines them into a single partial solution,\n",
"# then enforces proximity, allowing at most 3 variables to deviate\n",
"# from the machine learning suggestion.\n",
"comp3 = MemorizingPrimalComponent(\n",
" clf=KNeighborsClassifier(n_neighbors=10),\n",
" extractor=H5FieldsExtractor(instance_fields=[\"static_var_obj_coeffs\"]),\n",
" constructor=MergeTopSolutions(k=3, thresholds=[0.25, 0.75]),\n",
" action=EnforceProximity(3),\n",
")"
]
},
{
"cell_type": "markdown",
"id": "f194a793",
"metadata": {},
"source": [
"## Independent vars primal component\n",
"\n",
"Instead of memorizing previously-seen primal solutions, it is also natural to use machine learning models to directly predict the values of the decision variables, constructing a solution from scratch. This approach has the benefit of potentially constructing novel high-quality solutions, never observed in the training data. Two variations of this strategy are supported by MIPLearn: (i) predicting the values of the decision variables independently, using multiple ML models; or (ii) predicting the values jointly, with a single model. We describe the first variation in this section, and the second variation in the next section.\n",
"\n",
"Let $I_1,\\ldots,I_n$ be the training instances, and let $\\bar{x}^1,\\ldots,\\bar{x}^n$ be their respective optimal solutions. For each binary decision variable $x_j$, the component `IndependentVarsPrimalComponent` creates a copy of a user-provided binary classifier and trains it to predict the optimal value of $x_j$, given $\\bar{x}^1_j,\\ldots,\\bar{x}^n_j$ as training labels. The features provided to the model are the variable features computed by an user-provided extractor. During inference time, the component uses these $n$ binary classifiers to construct a solution and provides it to the solver using one of the available actions.\n",
"\n",
"Three issues often arise in practice when using this approach:\n",
"\n",
" 1. For certain binary variables $x_j$, it is frequently the case that its optimal value is either always zero or always one in the training dataset, which poses problems to some standard scikit-learn classifiers, since they do not expect a single class. The wrapper `SingleClassFix` can be used to fix this issue (see example below).\n",
"2. It is also frequently the case that machine learning classifier can only reliably predict the values of some variables with high accuracy, not all of them. In this situation, instead of computing a complete primal solution, it may be more beneficial to construct a partial solution containing values only for the variables for which the ML made a high-confidence prediction. The meta-classifier `MinProbabilityClassifier` can be used for this purpose. It asks the base classifier for the probability of the value being zero or one (using the `predict_proba` method) and erases from the primal solution all values whose probabilities are below a given threshold.\n",
"3. To make multiple copies of the provided ML classifier, MIPLearn uses the standard `sklearn.base.clone` method, which may not be suitable for classifiers from other frameworks. To handle this, it is possible to override the clone function using the `clone_fn` constructor argument.\n",
"\n",
"### Examples"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "3fc0b5d1",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"from sklearn.linear_model import LogisticRegression\n",
"from miplearn.classifiers.minprob import MinProbabilityClassifier\n",
"from miplearn.classifiers.singleclass import SingleClassFix\n",
"from miplearn.components.primal.indep import IndependentVarsPrimalComponent\n",
"from miplearn.extractors.AlvLouWeh2017 import AlvLouWeh2017Extractor\n",
"from miplearn.components.primal.actions import SetWarmStart\n",
"\n",
"# Configures a primal component that independently predicts the value of each\n",
"# binary variable using logistic regression and provides it to the solver as\n",
"# warm start. Erases predictions with probability less than 99%; applies\n",
"# single-class fix; and uses AlvLouWeh2017 features.\n",
"comp = IndependentVarsPrimalComponent(\n",
" base_clf=SingleClassFix(\n",
" MinProbabilityClassifier(\n",
" base_clf=LogisticRegression(),\n",
" thresholds=[0.99, 0.99],\n",
" ),\n",
" ),\n",
" extractor=AlvLouWeh2017Extractor(),\n",
" action=SetWarmStart(),\n",
")"
]
},
{
"cell_type": "markdown",
"id": "45107a0c",
"metadata": {},
"source": [
"## Joint vars primal component\n",
"In the previous subsection, we used multiple machine learning models to independently predict the values of the binary decision variables. When these values are correlated, an alternative approach is to jointly predict the values of all binary variables using a single machine learning model. This strategy is implemented by `JointVarsPrimalComponent`. Compared to the previous ones, this component is much more straightforwad. It simply extracts instance features, using the user-provided feature extractor, then directly trains the user-provided binary classifier (using the `fit` method), without making any copies. The trained classifier is then used to predict entire solutions (using the `predict` method), which are given to the solver using one of the previously discussed methods. In the example below, we illustrate the usage of this component with a simple feed-forward neural network.\n",
"\n",
"`JointVarsPrimalComponent` can also be used to implement strategies that use multiple machine learning models, but not indepedently. For example, a common strategy in multioutput prediction is building a *classifier chain*. In this approach, the first decision variable is predicted using the instance features alone; but the $n$-th decision variable is predicted using the instance features plus the predicted values of the $n-1$ previous variables. This can be easily implemented using scikit-learn's `ClassifierChain` estimator, as shown in the example below.\n",
"\n",
"### Examples"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "cf9b52dd",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"from sklearn.multioutput import ClassifierChain\n",
"from sklearn.neural_network import MLPClassifier\n",
"from miplearn.components.primal.joint import JointVarsPrimalComponent\n",
"from miplearn.extractors.fields import H5FieldsExtractor\n",
"from miplearn.components.primal.actions import SetWarmStart\n",
"\n",
"# Configures a primal component that uses a feedforward neural network\n",
"# to jointly predict the values of the binary variables, based on the\n",
"# objective cost function, and provides the solution to the solver as\n",
"# a warm start.\n",
"comp = JointVarsPrimalComponent(\n",
" clf=MLPClassifier(),\n",
" extractor=H5FieldsExtractor(\n",
" instance_fields=[\"static_var_obj_coeffs\"],\n",
" ),\n",
" action=SetWarmStart(),\n",
")\n",
"\n",
"# Configures a primal component that uses a chain of logistic regression\n",
"# models to jointly predict the values of the binary variables, based on\n",
"# the objective function.\n",
"comp = JointVarsPrimalComponent(\n",
" clf=ClassifierChain(SingleClassFix(LogisticRegression())),\n",
" extractor=H5FieldsExtractor(\n",
" instance_fields=[\"static_var_obj_coeffs\"],\n",
" ),\n",
" action=SetWarmStart(),\n",
")"
]
},
{
"cell_type": "markdown",
"id": "dddf7be4",
"metadata": {},
"source": [
"## Expert primal component\n",
"\n",
"Before spending time and effort choosing a machine learning strategy and tweaking its parameters, it is usually a good idea to evaluate what would be the performance impact of the model if its predictions were 100% accurate. This is especially important for the prediction of warm starts, since they are not always very beneficial. To simplify this task, MIPLearn provides `ExpertPrimalComponent`, a component which simply loads the optimal solution from the HDF5 file, assuming that it has already been computed, then directly provides it to the solver using one of the available methods. This component is useful in benchmarks, to evaluate how close to the best theoretical performance the machine learning components are.\n",
"\n",
"### Example"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "9e2e81b9",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"from miplearn.components.primal.expert import ExpertPrimalComponent\n",
"from miplearn.components.primal.actions import SetWarmStart\n",
"\n",
"# Configures an expert primal component, which reads a pre-computed\n",
"# optimal solution from the HDF5 file and provides it to the solver\n",
"# as warm start.\n",
"comp = ExpertPrimalComponent(action=SetWarmStart())"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,251 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"id": "9ec1907b-db93-4840-9439-c9005902b968",
"metadata": {},
"source": [
"# Learning Solver\n",
"\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",
"### 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",
"execution_count": 1,
"id": "92b09b98",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Restricted license - for non-production use only - expires 2024-10-28\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 10 rows, 45 columns and 90 nonzeros\n",
"Model fingerprint: 0x6ddcd141\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",
"Presolve time: 0.00s\n",
"Presolved: 10 rows, 45 columns, 90 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\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",
"\n",
"Solved in 15 iterations and 0.00 seconds (0.00 work units)\n",
"Optimal objective 2.761000000e+03\n",
"\n",
"User-callback calls 56, time in user-callback 0.00 sec\n",
"Set parameter PreCrush to value 1\n",
"Set parameter LazyConstraints to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"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",
"\n",
"User MIP start produced solution with objective 2796 (0.00s)\n",
"Loaded user MIP start with objective 2796\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 10 rows, 45 columns, 90 nonzeros\n",
"Variable types: 0 continuous, 45 integer (45 binary)\n",
"\n",
"Root relaxation: objective 2.761000e+03, 14 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 2761.00000 0 - 2796.00000 2761.00000 1.25% - 0s\n",
" 0 0 cutoff 0 2796.00000 2796.00000 0.00% - 0s\n",
"\n",
"Cutting planes:\n",
" Lazy constraints: 3\n",
"\n",
"Explored 1 nodes (16 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 20 (of 20 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 110, time in user-callback 0.00 sec\n"
]
},
{
"data": {
"text/plain": [
"{'WS: Count': 1, 'WS: Number of variables set': 41.0}"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import random\n",
"\n",
"import numpy as np\n",
"from scipy.stats import uniform, randint\n",
"from sklearn.linear_model import LogisticRegression\n",
"\n",
"from miplearn.classifiers.minprob import MinProbabilityClassifier\n",
"from miplearn.classifiers.singleclass import SingleClassFix\n",
"from miplearn.collectors.basic import BasicCollector\n",
"from miplearn.components.primal.actions import SetWarmStart\n",
"from miplearn.components.primal.indep import IndependentVarsPrimalComponent\n",
"from miplearn.extractors.AlvLouWeh2017 import AlvLouWeh2017Extractor\n",
"from miplearn.io import write_pkl_gz\n",
"from miplearn.problems.tsp import (\n",
" TravelingSalesmanGenerator,\n",
" build_tsp_model_gurobipy,\n",
")\n",
"from miplearn.solvers.learning import LearningSolver\n",
"\n",
"# Set random seed to make example reproducible.\n",
"random.seed(42)\n",
"np.random.seed(42)\n",
"\n",
"# Generate a few instances of the traveling salesman problem.\n",
"data = TravelingSalesmanGenerator(\n",
" n=randint(low=10, high=11),\n",
" x=uniform(loc=0.0, scale=1000.0),\n",
" y=uniform(loc=0.0, scale=1000.0),\n",
" gamma=uniform(loc=0.90, scale=0.20),\n",
" fix_cities=True,\n",
" round=True,\n",
").generate(50)\n",
"\n",
"# Save instance data to data/tsp/00000.pkl.gz, data/tsp/00001.pkl.gz, ...\n",
"all_data = write_pkl_gz(data, \"data/tsp\")\n",
"\n",
"# Split train/test data\n",
"train_data = all_data[:40]\n",
"test_data = all_data[40:]\n",
"\n",
"# Collect training data\n",
"bc = BasicCollector()\n",
"bc.collect(train_data, build_tsp_model_gurobipy, n_jobs=4)\n",
"\n",
"# Build learning solver\n",
"solver = LearningSolver(\n",
" components=[\n",
" IndependentVarsPrimalComponent(\n",
" base_clf=SingleClassFix(\n",
" MinProbabilityClassifier(\n",
" base_clf=LogisticRegression(),\n",
" thresholds=[0.95, 0.95],\n",
" ),\n",
" ),\n",
" extractor=AlvLouWeh2017Extractor(),\n",
" action=SetWarmStart(),\n",
" )\n",
" ]\n",
")\n",
"\n",
"# Train ML models\n",
"solver.fit(train_data)\n",
"\n",
"# Solve a test instance\n",
"solver.optimize(test_data[0], build_tsp_model_gurobipy)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e27d2cbd-5341-461d-bbc1-8131aee8d949",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,68 @@
MIPLearn
========
**MIPLearn** is an extensible framework for solving discrete optimization problems using a combination of Mixed-Integer Linear Programming (MIP) and Machine Learning (ML). MIPLearn uses ML methods to automatically identify patterns in previously solved instances of the problem, then uses these patterns to accelerate the performance of conventional state-of-the-art MIP solvers such as CPLEX, Gurobi or XPRESS.
Unlike pure ML methods, MIPLearn is not only able to find high-quality solutions to discrete optimization problems, but it can also prove the optimality and feasibility of these solutions. Unlike conventional MIP solvers, MIPLearn can take full advantage of very specific observations that happen to be true in a particular family of instances (such as the observation that a particular constraint is typically redundant, or that a particular variable typically assumes a certain value). For certain classes of problems, this approach may provide significant performance benefits.
Contents
--------
.. toctree::
:maxdepth: 1
:caption: Tutorials
:numbered: 2
tutorials/getting-started-pyomo
tutorials/getting-started-gurobipy
tutorials/getting-started-jump
tutorials/cuts-gurobipy
.. toctree::
:maxdepth: 2
:caption: User Guide
:numbered: 2
guide/problems
guide/collectors
guide/features
guide/primal
guide/solvers
.. toctree::
:maxdepth: 1
:caption: Python API Reference
:numbered: 2
api/problems
api/collectors
api/components
api/solvers
api/helpers
Authors
-------
- **Alinson S. Xavier** (Argonne National Laboratory)
- **Feng Qiu** (Argonne National Laboratory)
- **Xiaoyi Gu** (Georgia Institute of Technology)
- **Berkay Becu** (Georgia Institute of Technology)
- **Santanu S. Dey** (Georgia Institute of Technology)
Acknowledgments
---------------
* Based upon work supported by **Laboratory Directed Research and Development** (LDRD) funding from Argonne National Laboratory, provided by the Director, Office of Science, of the U.S. Department of Energy.
* Based upon work supported by the **U.S. Department of Energy Advanced Grid Modeling Program**.
Citing MIPLearn
---------------
If you use MIPLearn in your research (either the solver or the included problem generators), we kindly request that you cite the package as follows:
* **Alinson S. Xavier, Feng Qiu, Xiaoyi Gu, Berkay Becu, Santanu S. Dey.** *MIPLearn: An Extensible Framework for Learning-Enhanced Optimization (Version 0.3)*. Zenodo (2023). DOI: https://doi.org/10.5281/zenodo.4287567
If you use MIPLearn in the field of power systems optimization, we kindly request that you cite the reference below, in which the main techniques implemented in MIPLearn were first developed:
* **Alinson S. Xavier, Feng Qiu, Shabbir Ahmed.** *Learning to Solve Large-Scale Unit Commitment Problems.* INFORMS Journal on Computing (2020). DOI: https://doi.org/10.1287/ijoc.2020.0976

@ -0,0 +1,541 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "b4bd8bd6-3ce9-4932-852f-f98a44120a3e",
"metadata": {},
"source": [
"# User cuts and lazy constraints\n",
"\n",
"User cuts and lazy constraints are two advanced mixed-integer programming techniques that can accelerate solver performance. User cuts are additional constraints, derived from the constraints already in the model, that can tighten the feasible region and eliminate fractional solutions, thus reducing the size of the branch-and-bound tree. Lazy constraints, on the other hand, are constraints that are potentially part of the problem formulation but are omitted from the initial model to reduce its size; these constraints are added to the formulation only once the solver finds a solution that violates them. While both techniques have been successful, significant computational effort may still be required to generate strong user cuts and to identify violated lazy constraints, which can reduce their effectiveness.\n",
"\n",
"MIPLearn is able to predict which user cuts and which lazy constraints to enforce at the beginning of the optimization process, using machine learning. In this tutorial, we will use the framework to predict subtour elimination constraints for the **traveling salesman problem** using Gurobipy. We assume that MIPLearn has already been correctly installed.\n",
"\n",
"<div class=\"alert alert-info\">\n",
"\n",
"Solver Compatibility\n",
"\n",
"User cuts and lazy constraints are also supported in the Python/Pyomo and Julia/JuMP versions of the package. See the source code of <code>build_tsp_model_pyomo</code> and <code>build_tsp_model_jump</code> for more details. Note, however, the following limitations:\n",
"\n",
"- Python/Pyomo: Only `gurobi_persistent` is currently supported. PRs implementing callbacks for other persistent solvers are welcome.\n",
"- Julia/JuMP: Only solvers supporting solver-independent callbacks are supported. As of JuMP 1.19, this includes Gurobi, CPLEX, XPRESS, SCIP and GLPK. Note that HiGHS and Cbc are not supported. As newer versions of JuMP implement further callback support, MIPLearn should become automatically compatible with these solvers.\n",
"\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "72229e1f-cbd8-43f0-82ee-17d6ec9c3b7d",
"metadata": {},
"source": [
"## Modeling the traveling salesman problem\n",
"\n",
"Given a list of cities and the distances between them, the **traveling salesman problem (TSP)** asks for the shortest route starting at the first city, visiting each other city exactly once, then returning to the first city. This problem is a generalization of the Hamiltonian path problem, one of Karp's 21 NP-complete problems, and has many practical applications, including routing delivery trucks and scheduling airline routes.\n",
"\n",
"To describe an instance of TSP, we need to specify the number of cities $n$, and an $n \\times n$ matrix of distances. The class `TravelingSalesmanData`, in the `miplearn.problems.tsp` package, can hold this data:"
]
},
{
"cell_type": "markdown",
"id": "4598a1bc-55b6-48cc-a050-2262786c203a",
"metadata": {},
"source": [
"```python\n",
"@dataclass\r\n",
"class TravelingSalesmanData:\r\n",
" n_cities: int\r\n",
" distances: np.ndarray\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "3a43cc12-1207-4247-bdb2-69a6a2910738",
"metadata": {},
"source": [
"MIPLearn also provides `TravelingSalesmandGenerator`, a random generator for TSP instances, and `build_tsp_model_gurobipy`, a function which converts `TravelingSalesmanData` into an actual gurobipy optimization model, and which uses lazy constraints to enforce subtour elimination.\n",
"\n",
"The example below is a simplified and annotated version of `build_tsp_model_gurobipy`, illustrating the usage of callbacks with MIPLearn. Compared the the previous tutorial examples, note that, in addition to defining the variables, objective function and constraints of our problem, we also define two callback functions `lazy_separate` and `lazy_enforce`."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "e4712a85-0327-439c-8889-933e1ff714e7",
"metadata": {},
"outputs": [],
"source": [
"import gurobipy as gp\n",
"from gurobipy import quicksum, GRB, tuplelist\n",
"from miplearn.solvers.gurobi import GurobiModel\n",
"import networkx as nx\n",
"import numpy as np\n",
"from miplearn.problems.tsp import (\n",
" TravelingSalesmanData,\n",
" TravelingSalesmanGenerator,\n",
")\n",
"from scipy.stats import uniform, randint\n",
"from miplearn.io import write_pkl_gz, read_pkl_gz\n",
"from miplearn.collectors.basic import BasicCollector\n",
"from miplearn.solvers.learning import LearningSolver\n",
"from miplearn.components.lazy.mem import MemorizingLazyComponent\n",
"from miplearn.extractors.fields import H5FieldsExtractor\n",
"from sklearn.neighbors import KNeighborsClassifier\n",
"\n",
"# Set up random seed to make example more reproducible\n",
"np.random.seed(42)\n",
"\n",
"# Set up Python logging\n",
"import logging\n",
"\n",
"logging.basicConfig(level=logging.WARNING)\n",
"\n",
"\n",
"def build_tsp_model_gurobipy_simplified(data):\n",
" # Read data from file if a filename is provided\n",
" if isinstance(data, str):\n",
" data = read_pkl_gz(data)\n",
"\n",
" # Create empty gurobipy model\n",
" model = gp.Model()\n",
"\n",
" # Create set of edges between every pair of cities, for convenience\n",
" edges = tuplelist(\n",
" (i, j) for i in range(data.n_cities) for j in range(i + 1, data.n_cities)\n",
" )\n",
"\n",
" # Add binary variable x[e] for each edge e\n",
" x = model.addVars(edges, vtype=GRB.BINARY, name=\"x\")\n",
"\n",
" # Add objective function\n",
" model.setObjective(quicksum(x[(i, j)] * data.distances[i, j] for (i, j) in edges))\n",
"\n",
" # Add constraint: must choose two edges adjacent to each city\n",
" model.addConstrs(\n",
" (\n",
" quicksum(x[min(i, j), max(i, j)] for j in range(data.n_cities) if i != j)\n",
" == 2\n",
" for i in range(data.n_cities)\n",
" ),\n",
" name=\"eq_degree\",\n",
" )\n",
"\n",
" def lazy_separate(m: GurobiModel):\n",
" \"\"\"\n",
" Callback function that finds subtours in the current solution.\n",
" \"\"\"\n",
" # Query current value of the x variables\n",
" x_val = m.inner.cbGetSolution(x)\n",
"\n",
" # Initialize empty set of violations\n",
" violations = []\n",
"\n",
" # Build set of edges we have currently selected\n",
" selected_edges = [e for e in edges if x_val[e] > 0.5]\n",
"\n",
" # Build a graph containing the selected edges, using networkx\n",
" graph = nx.Graph()\n",
" graph.add_edges_from(selected_edges)\n",
"\n",
" # For each component of the graph\n",
" for component in list(nx.connected_components(graph)):\n",
"\n",
" # If the component is not the entire graph, we found a\n",
" # subtour. Add the edge cut to the list of violations.\n",
" if len(component) < data.n_cities:\n",
" cut_edges = [\n",
" [e[0], e[1]]\n",
" for e in edges\n",
" if (e[0] in component and e[1] not in component)\n",
" or (e[0] not in component and e[1] in component)\n",
" ]\n",
" violations.append(cut_edges)\n",
"\n",
" # Return the list of violations\n",
" return violations\n",
"\n",
" def lazy_enforce(m: GurobiModel, violations) -> None:\n",
" \"\"\"\n",
" Callback function that, given a list of subtours, adds lazy\n",
" constraints to remove them from the feasible region.\n",
" \"\"\"\n",
" print(f\"Enforcing {len(violations)} subtour elimination constraints\")\n",
" for violation in violations:\n",
" m.add_constr(quicksum(x[e[0], e[1]] for e in violation) >= 2)\n",
"\n",
" return GurobiModel(\n",
" model,\n",
" lazy_separate=lazy_separate,\n",
" lazy_enforce=lazy_enforce,\n",
" )"
]
},
{
"cell_type": "markdown",
"id": "58875042-d6ac-4f93-b3cc-9a5822b11dad",
"metadata": {},
"source": [
"The `lazy_separate` function starts by querying the current fractional solution value through `m.inner.cbGetSolution` (recall that `m.inner` is a regular gurobipy model), then finds the set of violated lazy constraints. Unlike a regular lazy constraint solver callback, note that `lazy_separate` does not add the violated constraints to the model; it simply returns a list of objects that uniquely identifies the set of lazy constraints that should be generated. Enforcing the constraints is the responsbility of the second callback function, `lazy_enforce`. This function takes as input the model and the list of violations found by `lazy_separate`, converts them into actual constraints, and adds them to the model through `m.add_constr`.\n",
"\n",
"During training data generation, MIPLearn calls `lazy_separate` and `lazy_enforce` in sequence, inside a regular solver callback. However, once the machine learning models are trained, MIPLearn calls `lazy_enforce` directly, before the optimization process starts, with a list of **predicted** violations, as we will see in the example below."
]
},
{
"cell_type": "markdown",
"id": "5839728e-406c-4be2-ba81-83f2b873d4b2",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
"\n",
"Constraint Representation\n",
"\n",
"How should user cuts and lazy constraints be represented is a decision that the user can make; MIPLearn is representation agnostic. The objects returned by `lazy_separate`, however, are serialized as JSON and stored in the HDF5 training data files. Therefore, it is recommended to use only simple objects, such as lists, tuples and dictionaries.\n",
"\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "847ae32e-fad7-406a-8797-0d79065a07fd",
"metadata": {},
"source": [
"## Generating training data\n",
"\n",
"To test the callback defined above, we generate a small set of TSP instances, using the provided random instance generator. As in the previous tutorial, we generate some test instances and some training instances, then solve them using `BasicCollector`. Input problem data is stored in `tsp/train/00000.pkl.gz, ...`, whereas solver training data (including list of required lazy constraints) is stored in `tsp/train/00000.h5, ...`."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "eb63154a-1fa6-4eac-aa46-6838b9c201f6",
"metadata": {},
"outputs": [],
"source": [
"# Configure generator to produce instances with 50 cities located\n",
"# in the 1000 x 1000 square, and with slightly perturbed distances.\n",
"gen = TravelingSalesmanGenerator(\n",
" x=uniform(loc=0.0, scale=1000.0),\n",
" y=uniform(loc=0.0, scale=1000.0),\n",
" n=randint(low=50, high=51),\n",
" gamma=uniform(loc=1.0, scale=0.25),\n",
" fix_cities=True,\n",
" round=True,\n",
")\n",
"\n",
"# Generate 500 instances and store input data file to .pkl.gz files\n",
"data = gen.generate(500)\n",
"train_data = write_pkl_gz(data[0:450], \"tsp/train\")\n",
"test_data = write_pkl_gz(data[450:500], \"tsp/test\")\n",
"\n",
"# Solve the training instances in parallel, collecting the required lazy\n",
"# constraints, in addition to other information, such as optimal solution.\n",
"bc = BasicCollector()\n",
"bc.collect(train_data, build_tsp_model_gurobipy_simplified, n_jobs=10)"
]
},
{
"cell_type": "markdown",
"id": "6903c26c-dbe0-4a2e-bced-fdbf93513dde",
"metadata": {},
"source": [
"## Training and solving new instances"
]
},
{
"cell_type": "markdown",
"id": "57cd724a-2d27-4698-a1e6-9ab8345ef31f",
"metadata": {},
"source": [
"After producing the training dataset, we can train the machine learning models to predict which lazy constraints are necessary. In this tutorial, we use the following ML strategy: given a new instance, find the 50 most similar ones in the training dataset and verify how often each lazy constraint was required. If a lazy constraint was required for the majority of the 50 most-similar instances, enforce it ahead-of-time for the current instance. To measure instance similarity, use the objective function only. This ML strategy can be implemented using `MemorizingLazyComponent` with `H5FieldsExtractor` and `KNeighborsClassifier`, as shown below."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "43779e3d-4174-4189-bc75-9f564910e212",
"metadata": {},
"outputs": [],
"source": [
"solver = LearningSolver(\n",
" components=[\n",
" MemorizingLazyComponent(\n",
" extractor=H5FieldsExtractor(instance_fields=[\"static_var_obj_coeffs\"]),\n",
" clf=KNeighborsClassifier(n_neighbors=100),\n",
" ),\n",
" ],\n",
")\n",
"solver.fit(train_data)"
]
},
{
"cell_type": "markdown",
"id": "12480712-9d3d-4cbc-a6d7-d6c1e2f950f4",
"metadata": {},
"source": [
"Next, we solve one of the test instances using the trained solver. In the run below, we can see that MIPLearn adds many lazy constraints ahead-of-time, before the optimization starts. During the optimization process itself, some additional lazy constraints are required, but very few."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "23f904ad-f1a8-4b5a-81ae-c0b9e813a4b2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter Threads to value 1\n",
"Restricted license - for non-production use only - expires 2024-10-28\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Optimize a model with 50 rows, 1225 columns and 2450 nonzeros\n",
"Model fingerprint: 0x04d7bec1\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+00]\n",
" Objective range [1e+01, 1e+03]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+00, 2e+00]\n",
"Presolve time: 0.00s\n",
"Presolved: 50 rows, 1225 columns, 2450 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 4.0600000e+02 9.700000e+01 0.000000e+00 0s\n",
" 66 5.5880000e+03 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 66 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 5.588000000e+03\n",
"\n",
"User-callback calls 107, time in user-callback 0.00 sec\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:miplearn.components.cuts.mem:Predicting violated lazy constraints...\n",
"INFO:miplearn.components.lazy.mem:Enforcing 19 constraints ahead-of-time...\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Enforcing 19 subtour elimination constraints\n",
"Set parameter PreCrush to value 1\n",
"Set parameter LazyConstraints to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Optimize a model with 69 rows, 1225 columns and 6091 nonzeros\n",
"Model fingerprint: 0x09bd34d6\n",
"Variable types: 0 continuous, 1225 integer (1225 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+00]\n",
" Objective range [1e+01, 1e+03]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+00, 2e+00]\n",
"Found heuristic solution: objective 29853.000000\n",
"Presolve time: 0.00s\n",
"Presolved: 69 rows, 1225 columns, 6091 nonzeros\n",
"Variable types: 0 continuous, 1225 integer (1225 binary)\n",
"\n",
"Root relaxation: objective 6.139000e+03, 93 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 6139.00000 0 6 29853.0000 6139.00000 79.4% - 0s\n",
"H 0 0 6390.0000000 6139.00000 3.93% - 0s\n",
" 0 0 6165.50000 0 10 6390.00000 6165.50000 3.51% - 0s\n",
"Enforcing 3 subtour elimination constraints\n",
" 0 0 6165.50000 0 6 6390.00000 6165.50000 3.51% - 0s\n",
" 0 0 6198.50000 0 16 6390.00000 6198.50000 3.00% - 0s\n",
"* 0 0 0 6219.0000000 6219.00000 0.00% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 11\n",
" MIR: 1\n",
" Zero half: 4\n",
" Lazy constraints: 3\n",
"\n",
"Explored 1 nodes (222 simplex iterations) in 0.03 seconds (0.02 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"\n",
"Solution count 3: 6219 6390 29853 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 6.219000000000e+03, best bound 6.219000000000e+03, gap 0.0000%\n",
"\n",
"User-callback calls 141, time in user-callback 0.00 sec\n"
]
}
],
"source": [
"# Increase log verbosity, so that we can see what is MIPLearn doing\n",
"logging.getLogger(\"miplearn\").setLevel(logging.INFO)\n",
"\n",
"# Solve a new test instance\n",
"solver.optimize(test_data[0], build_tsp_model_gurobipy_simplified);"
]
},
{
"cell_type": "markdown",
"id": "79cc3e61-ee2b-4f18-82cb-373d55d67de6",
"metadata": {},
"source": [
"Finally, we solve the same instance, but using a regular solver, without ML prediction. We can see that a much larger number of lazy constraints are added during the optimization process itself. Additionally, the solver requires a larger number of iterations to find the optimal solution. There is not a significant difference in running time because of the small size of these instances."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "a015c51c-091a-43b6-b761-9f3577fc083e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Optimize a model with 50 rows, 1225 columns and 2450 nonzeros\n",
"Model fingerprint: 0x04d7bec1\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+00]\n",
" Objective range [1e+01, 1e+03]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+00, 2e+00]\n",
"Presolve time: 0.00s\n",
"Presolved: 50 rows, 1225 columns, 2450 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 4.0600000e+02 9.700000e+01 0.000000e+00 0s\n",
" 66 5.5880000e+03 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 66 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 5.588000000e+03\n",
"\n",
"User-callback calls 107, time in user-callback 0.00 sec\n",
"Set parameter PreCrush to value 1\n",
"Set parameter LazyConstraints to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Optimize a model with 50 rows, 1225 columns and 2450 nonzeros\n",
"Model fingerprint: 0x77a94572\n",
"Variable types: 0 continuous, 1225 integer (1225 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+00]\n",
" Objective range [1e+01, 1e+03]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+00, 2e+00]\n",
"Found heuristic solution: objective 29695.000000\n",
"Presolve time: 0.00s\n",
"Presolved: 50 rows, 1225 columns, 2450 nonzeros\n",
"Variable types: 0 continuous, 1225 integer (1225 binary)\n",
"\n",
"Root relaxation: objective 5.588000e+03, 68 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 5588.00000 0 12 29695.0000 5588.00000 81.2% - 0s\n",
"Enforcing 9 subtour elimination constraints\n",
"Enforcing 11 subtour elimination constraints\n",
"H 0 0 27241.000000 5588.00000 79.5% - 0s\n",
" 0 0 5898.00000 0 8 27241.0000 5898.00000 78.3% - 0s\n",
"Enforcing 4 subtour elimination constraints\n",
"Enforcing 3 subtour elimination constraints\n",
" 0 0 6066.00000 0 - 27241.0000 6066.00000 77.7% - 0s\n",
"Enforcing 2 subtour elimination constraints\n",
" 0 0 6128.00000 0 - 27241.0000 6128.00000 77.5% - 0s\n",
" 0 0 6139.00000 0 6 27241.0000 6139.00000 77.5% - 0s\n",
"H 0 0 6368.0000000 6139.00000 3.60% - 0s\n",
" 0 0 6154.75000 0 15 6368.00000 6154.75000 3.35% - 0s\n",
"Enforcing 2 subtour elimination constraints\n",
" 0 0 6154.75000 0 6 6368.00000 6154.75000 3.35% - 0s\n",
" 0 0 6165.75000 0 11 6368.00000 6165.75000 3.18% - 0s\n",
"Enforcing 3 subtour elimination constraints\n",
" 0 0 6204.00000 0 6 6368.00000 6204.00000 2.58% - 0s\n",
"* 0 0 0 6219.0000000 6219.00000 0.00% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 5\n",
" MIR: 1\n",
" Zero half: 4\n",
" Lazy constraints: 4\n",
"\n",
"Explored 1 nodes (224 simplex iterations) in 0.10 seconds (0.03 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"\n",
"Solution count 4: 6219 6368 27241 29695 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 6.219000000000e+03, best bound 6.219000000000e+03, gap 0.0000%\n",
"\n",
"User-callback calls 170, time in user-callback 0.01 sec\n"
]
}
],
"source": [
"solver = LearningSolver(components=[]) # empty set of ML components\n",
"solver.optimize(test_data[0], build_tsp_model_gurobipy_simplified);"
]
},
{
"cell_type": "markdown",
"id": "432c99b2-67fe-409b-8224-ccef91de96d1",
"metadata": {},
"source": [
"## Learning user cuts\n",
"\n",
"The example above focused on lazy constraints. To enforce user cuts instead, the procedure is very similar, with the following changes:\n",
"\n",
"- Instead of `lazy_separate` and `lazy_enforce`, use `cuts_separate` and `cuts_enforce`\n",
"- Instead of `m.inner.cbGetSolution`, use `m.inner.cbGetNodeRel`\n",
"\n",
"For a complete example, see `build_stab_model_gurobipy`, `build_stab_model_pyomo` and `build_stab_model_jump`, which solves the maximum-weight stable set problem using user cut callbacks."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e6cb694d-8c43-410f-9a13-01bf9e0763b7",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,837 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "6b8983b1",
"metadata": {
"tags": []
},
"source": [
"# Getting started (Gurobipy)\n",
"\n",
"## Introduction\n",
"\n",
"**MIPLearn** is an open source framework that uses machine learning (ML) to accelerate the performance of mixed-integer programming solvers (e.g. Gurobi, CPLEX, XPRESS). In this tutorial, we will:\n",
"\n",
"1. Install the Python/Gurobipy version of MIPLearn\n",
"2. Model a simple optimization problem using Gurobipy\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/Gurobipy version of MIPLearn is only compatible with the Gurobi Optimizer. For broader solver compatibility, see the Python/Pyomo and Julia/JuMP versions 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"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "02f0a927",
"metadata": {},
"source": [
"## Installation\n",
"\n",
"MIPLearn is available in two versions:\n",
"\n",
"- Python version, compatible with the Pyomo and Gurobipy modeling languages,\n",
"- Julia version, compatible with the JuMP modeling language.\n",
"\n",
"In this tutorial, we will demonstrate how to use and install the Python/Gurobipy version of the package. The first step is to install Python 3.8+ in your computer. See the [official Python website for more instructions](https://www.python.org/downloads/). After Python is installed, we proceed to install MIPLearn using `pip`:\n",
"\n",
"```\n",
"$ pip install MIPLearn==0.3\n",
"```\n",
"\n",
"In addition to MIPLearn itself, we will also install Gurobi 10.0, a state-of-the-art commercial MILP solver. This step also install a demo license for Gurobi, which should able to solve the small optimization problems in this tutorial. A license is required for solving larger-scale problems.\n",
"\n",
"```\n",
"$ pip install 'gurobipy>=10,<10.1'\n",
"```"
]
},
{
"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 a utility company needs to decide which electrical generators should be online at each hour of the day, as well as how much power should each generator produce. More specifically, assume that the 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 the 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. The total amount of power to be produced needs to be exactly equal to the total demand $d$ (in megawatts).\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:"
]
},
{
"cell_type": "markdown",
"id": "f12c3702",
"metadata": {},
"source": [
"$$\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",
"$$"
]
},
{
"cell_type": "markdown",
"id": "be3989ed",
"metadata": {},
"source": [
"<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.\n",
"\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "a5fd33f6",
"metadata": {},
"source": [
"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": 1,
"id": "22a67170-10b4-43d3-8708-014d91141e73",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:18:25.442346786Z",
"start_time": "2023-06-06T20:18:25.329017476Z"
},
"tags": []
},
"outputs": [],
"source": [
"from dataclasses import dataclass\n",
"from typing import List\n",
"\n",
"import numpy as np\n",
"\n",
"\n",
"@dataclass\n",
"class UnitCommitmentData:\n",
" demand: float\n",
" pmin: List[float]\n",
" pmax: List[float]\n",
" cfix: List[float]\n",
" cvar: List[float]"
]
},
{
"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. The function accepts `UnitCommitmentData`, the data structure we previously defined, or the path to a compressed pickle file containing this data."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "2f67032f-0d74-4317-b45c-19da0ec859e9",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:48:05.953902842Z",
"start_time": "2023-06-06T20:48:05.909747925Z"
}
},
"outputs": [],
"source": [
"import gurobipy as gp\n",
"from gurobipy import GRB, quicksum\n",
"from typing import Union\n",
"from miplearn.io import read_pkl_gz\n",
"from miplearn.solvers.gurobi import GurobiModel\n",
"\n",
"\n",
"def build_uc_model(data: Union[str, UnitCommitmentData]) -> GurobiModel:\n",
" if isinstance(data, str):\n",
" data = read_pkl_gz(data)\n",
"\n",
" model = gp.Model()\n",
" n = len(data.pmin)\n",
" x = model._x = model.addVars(n, vtype=GRB.BINARY, name=\"x\")\n",
" y = model._y = model.addVars(n, name=\"y\")\n",
" model.setObjective(\n",
" quicksum(data.cfix[i] * x[i] + data.cvar[i] * y[i] for i in range(n))\n",
" )\n",
" model.addConstrs(y[i] <= data.pmax[i] * x[i] for i in range(n))\n",
" model.addConstrs(y[i] >= data.pmin[i] * x[i] for i in range(n))\n",
" model.addConstr(quicksum(y[i] for i in range(n)) == data.demand)\n",
" return GurobiModel(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": 3,
"id": "2a896f47",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:49:14.266758244Z",
"start_time": "2023-06-06T20:49:14.223514806Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Restricted license - for non-production use only - expires 2024-10-28\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 7 rows, 6 columns and 15 nonzeros\n",
"Model fingerprint: 0x58dfdd53\n",
"Variable types: 3 continuous, 3 integer (3 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 7e+01]\n",
" Objective range [2e+00, 7e+02]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [1e+02, 1e+02]\n",
"Presolve removed 2 rows and 1 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 5 rows, 5 columns, 13 nonzeros\n",
"Variable types: 0 continuous, 5 integer (3 binary)\n",
"Found heuristic solution: objective 1400.0000000\n",
"\n",
"Root relaxation: objective 1.035000e+03, 3 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 1035.00000 0 1 1400.00000 1035.00000 26.1% - 0s\n",
" 0 0 1105.71429 0 1 1400.00000 1105.71429 21.0% - 0s\n",
"* 0 0 0 1320.0000000 1320.00000 0.00% - 0s\n",
"\n",
"Explored 1 nodes (5 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 2: 1320 1400 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 1.320000000000e+03, best bound 1.320000000000e+03, gap 0.0000%\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",
"model.optimize()\n",
"print(\"obj =\", model.inner.objVal)\n",
"print(\"x =\", [model.inner._x[i].x for i in range(3)])\n",
"print(\"y =\", [model.inner._y[i].x 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": "01f576e1-1790-425e-9e5c-9fa07b6f4c26",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
" \n",
"Note\n",
"\n",
"- In the example above, `GurobiModel` is just a thin wrapper around a standard Gurobi model. This wrapper allows MIPLearn to be solver- and modeling-language-agnostic. The wrapper provides only a few basic methods, such as `optimize`. For more control, and to query the solution, the original Gurobi model can be accessed through `model.inner`, as illustrated above.\n",
"- To ensure training data consistency, MIPLearn requires all decision variables to have names.\n",
"</div>"
]
},
{
"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** solver, which can optimize 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": 4,
"id": "5eb09fab",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:49:22.758192368Z",
"start_time": "2023-06-06T20:49:22.724784572Z"
}
},
"outputs": [],
"source": [
"from scipy.stats import uniform\n",
"from typing import List\n",
"import random\n",
"\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 _ 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 on multiple machines. 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": 5,
"id": "6156752c",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:49:24.811192929Z",
"start_time": "2023-06-06T20:49:24.575639142Z"
}
},
"outputs": [],
"source": [
"from miplearn.io import write_pkl_gz\n",
"\n",
"data = random_uc_data(samples=500, n=500)\n",
"train_data = write_pkl_gz(data[0:450], \"uc/train\")\n",
"test_data = write_pkl_gz(data[450:500], \"uc/test\")"
]
},
{
"cell_type": "markdown",
"id": "b17af877",
"metadata": {},
"source": [
"Finally, we use `BasicCollector` to collect the optimal solutions and other useful training data for all training instances. The data is stored in HDF5 files `uc/train/00000.h5`, `uc/train/00001.h5`, etc. The optimization models are also exported to compressed MPS files `uc/train/00000.mps.gz`, `uc/train/00001.mps.gz`, etc."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "7623f002",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:49:34.936729253Z",
"start_time": "2023-06-06T20:49:25.936126612Z"
}
},
"outputs": [],
"source": [
"from miplearn.collectors.basic import BasicCollector\n",
"\n",
"bc = BasicCollector()\n",
"bc.collect(train_data, build_uc_model, n_jobs=4)"
]
},
{
"cell_type": "markdown",
"id": "c42b1be1-9723-4827-82d8-974afa51ef9f",
"metadata": {},
"source": [
"## Training and solving test instances"
]
},
{
"cell_type": "markdown",
"id": "a33c6aa4-f0b8-4ccb-9935-01f7d7de2a1c",
"metadata": {},
"source": [
"With training data in hand, we can now design and train a machine learning model to accelerate solver performance. In this tutorial, for illustration purposes, we will use ML to generate a good warm start using $k$-nearest neighbors. More specifically, the strategy is to:\n",
"\n",
"1. Memorize the optimal solutions of all training instances;\n",
"2. Given a test instance, find the 25 most similar training instances, based on constraint right-hand sides;\n",
"3. Merge their optimal solutions into a single partial solution; specifically, only assign values to the binary variables that agree unanimously.\n",
"4. Provide this partial solution to the solver as a warm start.\n",
"\n",
"This simple strategy can be implemented as shown below, using `MemorizingPrimalComponent`. For more advanced strategies, and for the usage of more advanced classifiers, see the user guide."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "435f7bf8-4b09-4889-b1ec-b7b56e7d8ed2",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:49:38.997939600Z",
"start_time": "2023-06-06T20:49:38.968261432Z"
}
},
"outputs": [],
"source": [
"from sklearn.neighbors import KNeighborsClassifier\n",
"from miplearn.components.primal.actions import SetWarmStart\n",
"from miplearn.components.primal.mem import (\n",
" MemorizingPrimalComponent,\n",
" MergeTopSolutions,\n",
")\n",
"from miplearn.extractors.fields import H5FieldsExtractor\n",
"\n",
"comp = MemorizingPrimalComponent(\n",
" clf=KNeighborsClassifier(n_neighbors=25),\n",
" extractor=H5FieldsExtractor(\n",
" instance_fields=[\"static_constr_rhs\"],\n",
" ),\n",
" constructor=MergeTopSolutions(25, [0.0, 1.0]),\n",
" action=SetWarmStart(),\n",
")"
]
},
{
"cell_type": "markdown",
"id": "9536e7e4-0b0d-49b0-bebd-4a848f839e94",
"metadata": {},
"source": [
"Having defined the ML strategy, we next construct `LearningSolver`, train the ML component and optimize one of the test instances."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "9d13dd50-3dcf-4673-a757-6f44dcc0dedf",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:49:42.072345411Z",
"start_time": "2023-06-06T20:49:41.294040974Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0xa8b70287\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 [3e+08, 3e+08]\n",
"Presolve removed 1000 rows and 500 columns\n",
"Presolve time: 0.01s\n",
"Presolved: 1 rows, 500 columns, 500 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 6.6166537e+09 5.648803e+04 0.000000e+00 0s\n",
" 1 8.2906219e+09 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.290621916e+09\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0xcf27855a\n",
"Variable types: 500 continuous, 500 integer (500 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 [3e+08, 3e+08]\n",
"\n",
"User MIP start produced solution with objective 8.29153e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.29153e+09 (0.01s)\n",
"Loaded user MIP start with objective 8.29153e+09\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"\n",
"Root relaxation: objective 8.290622e+09, 512 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 8.2906e+09 0 1 8.2915e+09 8.2906e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 3 8.2915e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2915e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 2 8.2915e+09 8.2907e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 1\n",
" Flow cover: 2\n",
"\n",
"Explored 1 nodes (565 simplex iterations) in 0.03 seconds (0.01 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 1: 8.29153e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.291528276179e+09, best bound 8.290733258025e+09, gap 0.0096%\n"
]
},
{
"data": {
"text/plain": [
"{'WS: Count': 1, 'WS: Number of variables set': 482.0}"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from miplearn.solvers.learning import LearningSolver\n",
"\n",
"solver_ml = LearningSolver(components=[comp])\n",
"solver_ml.fit(train_data)\n",
"solver_ml.optimize(test_data[0], build_uc_model)"
]
},
{
"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 very close to the optimal solution to the problem. Now let us repeat the code above, but a solver which does not apply any ML strategies. Note that our previously-defined component is not provided."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "2ff391ed-e855-4228-aa09-a7641d8c2893",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:49:44.012782276Z",
"start_time": "2023-06-06T20:49:43.813974362Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0xa8b70287\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 [3e+08, 3e+08]\n",
"Presolve removed 1000 rows and 500 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1 rows, 500 columns, 500 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 6.6166537e+09 5.648803e+04 0.000000e+00 0s\n",
" 1 8.2906219e+09 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.290621916e+09\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x4cbbf7c7\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"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 [3e+08, 3e+08]\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"Found heuristic solution: objective 9.757128e+09\n",
"\n",
"Root relaxation: objective 8.290622e+09, 512 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 8.2906e+09 0 1 9.7571e+09 8.2906e+09 15.0% - 0s\n",
"H 0 0 8.298273e+09 8.2906e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 4 8.2983e+09 8.2907e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2983e+09 8.2907e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 4 8.2983e+09 8.2907e+09 0.09% - 0s\n",
"H 0 0 8.293980e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 5 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 2 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 1 8.2940e+09 8.2908e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 4 8.2940e+09 8.2908e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 4 8.2940e+09 8.2908e+09 0.04% - 0s\n",
"H 0 0 8.291465e+09 8.2908e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 2\n",
" MIR: 1\n",
"\n",
"Explored 1 nodes (1031 simplex iterations) in 0.15 seconds (0.03 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 4: 8.29147e+09 8.29398e+09 8.29827e+09 9.75713e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.291465302389e+09, best bound 8.290781665333e+09, gap 0.0082%\n"
]
},
{
"data": {
"text/plain": [
"{}"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"solver_baseline = LearningSolver(components=[])\n",
"solver_baseline.fit(train_data)\n",
"solver_baseline.optimize(test_data[0], build_uc_model)"
]
},
{
"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, but the difference can be significant for larger problems."
]
},
{
"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. 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": 10,
"id": "67a6cd18",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:50:12.869892930Z",
"start_time": "2023-06-06T20:50:12.509410473Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x19042f12\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 [3e+08, 3e+08]\n",
"Presolve removed 1000 rows and 500 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1 rows, 500 columns, 500 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 6.5917580e+09 5.627453e+04 0.000000e+00 0s\n",
" 1 8.2535968e+09 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.253596777e+09\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0xf97cde91\n",
"Variable types: 500 continuous, 500 integer (500 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 [3e+08, 3e+08]\n",
"\n",
"User MIP start produced solution with objective 8.25814e+09 (0.00s)\n",
"User MIP start produced solution with objective 8.25512e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25483e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25483e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25483e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25459e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25459e+09 (0.01s)\n",
"Loaded user MIP start with objective 8.25459e+09\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"\n",
"Root relaxation: objective 8.253597e+09, 512 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 8.2536e+09 0 1 8.2546e+09 8.2536e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 3 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 1 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 4 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 4 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2538e+09 0 4 8.2546e+09 8.2538e+09 0.01% - 0s\n",
" 0 0 8.2538e+09 0 5 8.2546e+09 8.2538e+09 0.01% - 0s\n",
" 0 0 8.2538e+09 0 6 8.2546e+09 8.2538e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Cover: 1\n",
" MIR: 2\n",
" StrongCG: 1\n",
" Flow cover: 1\n",
"\n",
"Explored 1 nodes (575 simplex iterations) in 0.05 seconds (0.01 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 4: 8.25459e+09 8.25483e+09 8.25512e+09 8.25814e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.254590409970e+09, best bound 8.253768093811e+09, gap 0.0100%\n",
"obj = 8254590409.969726\n",
"x = [1.0, 1.0, 0.0]\n",
"y = [935662.0949262811, 1604270.0218116897, 0.0]\n"
]
}
],
"source": [
"data = random_uc_data(samples=1, n=500)[0]\n",
"model = build_uc_model(data)\n",
"solver_ml.optimize(model)\n",
"print(\"obj =\", model.inner.objVal)\n",
"print(\"x =\", [model.inner._x[i].x for i in range(3)])\n",
"print(\"y =\", [model.inner._y[i].x for i in range(3)])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5593d23a-83bd-4e16-8253-6300f5e3f63b",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,680 @@
{
"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 mixed-integer programming solvers (e.g. Gurobi, CPLEX, XPRESS). In this tutorial, we will:\n",
"\n",
"1. Install the Julia/JuMP version of MIPLearn\n",
"2. Model a simple optimization problem using JuMP\n",
"3. Generate training data and train the ML models\n",
"4. Use the ML models together Gurobi to solve new instances\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 and Gurobipy modeling languages,\n",
"- Julia version, compatible with the JuMP modeling language.\n",
"\n",
"In this tutorial, we will demonstrate how to use and install the Python/Pyomo version of the package. The first step is to install Julia in your machine. See the [official Julia website for more instructions](https://julialang.org/downloads/). After Julia is installed, launch the Julia REPL, type `]` to enter package mode, then install MIPLearn:\n",
"\n",
"```\n",
"pkg> add MIPLearn@0.3\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "e8274543",
"metadata": {},
"source": [
"In addition to MIPLearn itself, we will also install:\n",
"\n",
"- the JuMP modeling language\n",
"- Gurobi, a state-of-the-art commercial MILP solver\n",
"- Distributions, to generate random data\n",
"- PyCall, to access ML model from Scikit-Learn\n",
"- Suppressor, to make the output cleaner\n",
"\n",
"```\n",
"pkg> add JuMP@1, Gurobi@1, Distributions@0.25, PyCall@1, Suppressor@0.2\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "a14e4550",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
" \n",
"Note\n",
"\n",
"- If you do not have a Gurobi license available, you can also follow the tutorial by installing an open-source solver, such as `HiGHS`, and replacing `Gurobi.Optimizer` by `HiGHS.Optimizer` in all the code examples.\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 Julia 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 a utility company needs to decide which electrical generators should be online at each hour of the day, as well as how much power should each generator produce. More specifically, assume that the 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 the 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. The total amount of power to be produced needs to be exactly equal to the total demand $d$ (in megawatts).\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:"
]
},
{
"cell_type": "markdown",
"id": "f12c3702",
"metadata": {},
"source": [
"$$\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",
"$$"
]
},
{
"cell_type": "markdown",
"id": "be3989ed",
"metadata": {},
"source": [
"<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.\n",
"\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "a5fd33f6",
"metadata": {},
"source": [
"Next, let us convert this abstract mathematical formulation into a concrete optimization model, using Julia and JuMP. We start by defining a data class `UnitCommitmentData`, which holds all the input data."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "c62ebff1-db40-45a1-9997-d121837f067b",
"metadata": {},
"outputs": [],
"source": [
"struct UnitCommitmentData\n",
" demand::Float64\n",
" pmin::Vector{Float64}\n",
" pmax::Vector{Float64}\n",
" cfix::Vector{Float64}\n",
" cvar::Vector{Float64}\n",
"end;"
]
},
{
"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 JuMP model. The function accepts `UnitCommitmentData`, the data structure we previously defined, or the path to a JLD2 file containing this data."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "79ef7775-18ca-4dfa-b438-49860f762ad0",
"metadata": {},
"outputs": [],
"source": [
"using MIPLearn\n",
"using JuMP\n",
"using Gurobi\n",
"\n",
"function build_uc_model(data)\n",
" if data isa String\n",
" data = read_jld2(data)\n",
" end\n",
" model = Model(Gurobi.Optimizer)\n",
" G = 1:length(data.pmin)\n",
" @variable(model, x[G], Bin)\n",
" @variable(model, y[G] >= 0)\n",
" @objective(model, Min, sum(data.cfix[g] * x[g] + data.cvar[g] * y[g] for g in G))\n",
" @constraint(model, eq_max_power[g in G], y[g] <= data.pmax[g] * x[g])\n",
" @constraint(model, eq_min_power[g in G], y[g] >= data.pmin[g] * x[g])\n",
" @constraint(model, eq_demand, sum(y[g] for g in G) == data.demand)\n",
" return JumpModel(model)\n",
"end;"
]
},
{
"cell_type": "markdown",
"id": "c22714a3",
"metadata": {},
"source": [
"At this point, we can already use Gurobi 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": 3,
"id": "dd828d68-fd43-4d2a-a058-3e2628d99d9e",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:01:10.993801745Z",
"start_time": "2023-06-06T20:01:10.887580927Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)\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 7 rows, 6 columns and 15 nonzeros\n",
"Model fingerprint: 0x55e33a07\n",
"Variable types: 3 continuous, 3 integer (3 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 7e+01]\n",
" Objective range [2e+00, 7e+02]\n",
" Bounds range [0e+00, 0e+00]\n",
" RHS range [1e+02, 1e+02]\n",
"Presolve removed 2 rows and 1 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 5 rows, 5 columns, 13 nonzeros\n",
"Variable types: 0 continuous, 5 integer (3 binary)\n",
"Found heuristic solution: objective 1400.0000000\n",
"\n",
"Root relaxation: objective 1.035000e+03, 3 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 1035.00000 0 1 1400.00000 1035.00000 26.1% - 0s\n",
" 0 0 1105.71429 0 1 1400.00000 1105.71429 21.0% - 0s\n",
"* 0 0 0 1320.0000000 1320.00000 0.00% - 0s\n",
"\n",
"Explored 1 nodes (5 simplex iterations) in 0.00 seconds (0.00 work units)\n",
"Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 2: 1320 1400 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 1.320000000000e+03, best bound 1.320000000000e+03, gap 0.0000%\n",
"\n",
"User-callback calls 371, time in user-callback 0.00 sec\n",
"objective_value(model.inner) = 1320.0\n",
"Vector(value.(model.inner[:x])) = [-0.0, 1.0, 1.0]\n",
"Vector(value.(model.inner[:y])) = [0.0, 60.0, 40.0]\n"
]
}
],
"source": [
"model = build_uc_model(\n",
" UnitCommitmentData(\n",
" 100.0, # demand\n",
" [10, 20, 30], # pmin\n",
" [50, 60, 70], # pmax\n",
" [700, 600, 500], # cfix\n",
" [1.5, 2.0, 2.5], # cvar\n",
" )\n",
")\n",
"model.optimize()\n",
"@show objective_value(model.inner)\n",
"@show Vector(value.(model.inner[:x]))\n",
"@show Vector(value.(model.inner[:y]));"
]
},
{
"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": "01f576e1-1790-425e-9e5c-9fa07b6f4c26",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
" \n",
"Notes\n",
" \n",
"- In the example above, `JumpModel` is just a thin wrapper around a standard JuMP model. This wrapper allows MIPLearn to be solver- and modeling-language-agnostic. The wrapper provides only a few basic methods, such as `optimize`. For more control, and to query the solution, the original JuMP model can be accessed through `model.inner`, as illustrated above.\n",
"</div>"
]
},
{
"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** solver, which can optimize 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": 4,
"id": "1326efd7-3869-4137-ab6b-df9cb609a7e0",
"metadata": {},
"outputs": [],
"source": [
"using Distributions\n",
"using Random\n",
"\n",
"function random_uc_data(; samples::Int, n::Int, seed::Int=42)::Vector\n",
" Random.seed!(seed)\n",
" pmin = rand(Uniform(100_000, 500_000), n)\n",
" pmax = pmin .* rand(Uniform(2, 2.5), n)\n",
" cfix = pmin .* rand(Uniform(100, 125), n)\n",
" cvar = rand(Uniform(1.25, 1.50), n)\n",
" return [\n",
" UnitCommitmentData(\n",
" sum(pmax) * rand(Uniform(0.5, 0.75)),\n",
" pmin,\n",
" pmax,\n",
" cfix,\n",
" cvar,\n",
" )\n",
" for _ in 1:samples\n",
" ]\n",
"end;"
]
},
{
"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 on multiple machines. The code below generates the files `uc/train/00001.jld2`, `uc/train/00002.jld2`, etc., which contain the input data in JLD2 format."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "6156752c",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:03:04.782830561Z",
"start_time": "2023-06-06T20:03:04.530421396Z"
}
},
"outputs": [],
"source": [
"data = random_uc_data(samples=500, n=500)\n",
"train_data = write_jld2(data[1:450], \"uc/train\")\n",
"test_data = write_jld2(data[451:500], \"uc/test\");"
]
},
{
"cell_type": "markdown",
"id": "b17af877",
"metadata": {},
"source": [
"Finally, we use `BasicCollector` to collect the optimal solutions and other useful training data for all training instances. The data is stored in HDF5 files `uc/train/00001.h5`, `uc/train/00002.h5`, etc. The optimization models are also exported to compressed MPS files `uc/train/00001.mps.gz`, `uc/train/00002.mps.gz`, etc."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "7623f002",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:03:35.571497019Z",
"start_time": "2023-06-06T20:03:25.804104036Z"
}
},
"outputs": [],
"source": [
"using Suppressor\n",
"@suppress_out begin\n",
" bc = BasicCollector()\n",
" bc.collect(train_data, build_uc_model)\n",
"end"
]
},
{
"cell_type": "markdown",
"id": "c42b1be1-9723-4827-82d8-974afa51ef9f",
"metadata": {},
"source": [
"## Training and solving test instances"
]
},
{
"cell_type": "markdown",
"id": "a33c6aa4-f0b8-4ccb-9935-01f7d7de2a1c",
"metadata": {},
"source": [
"With training data in hand, we can now design and train a machine learning model to accelerate solver performance. In this tutorial, for illustration purposes, we will use ML to generate a good warm start using $k$-nearest neighbors. More specifically, the strategy is to:\n",
"\n",
"1. Memorize the optimal solutions of all training instances;\n",
"2. Given a test instance, find the 25 most similar training instances, based on constraint right-hand sides;\n",
"3. Merge their optimal solutions into a single partial solution; specifically, only assign values to the binary variables that agree unanimously.\n",
"4. Provide this partial solution to the solver as a warm start.\n",
"\n",
"This simple strategy can be implemented as shown below, using `MemorizingPrimalComponent`. For more advanced strategies, and for the usage of more advanced classifiers, see the user guide."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "435f7bf8-4b09-4889-b1ec-b7b56e7d8ed2",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:05:20.497772794Z",
"start_time": "2023-06-06T20:05:20.484821405Z"
}
},
"outputs": [],
"source": [
"# Load kNN classifier from Scikit-Learn\n",
"using PyCall\n",
"KNeighborsClassifier = pyimport(\"sklearn.neighbors\").KNeighborsClassifier\n",
"\n",
"# Build the MIPLearn component\n",
"comp = MemorizingPrimalComponent(\n",
" clf=KNeighborsClassifier(n_neighbors=25),\n",
" extractor=H5FieldsExtractor(\n",
" instance_fields=[\"static_constr_rhs\"],\n",
" ),\n",
" constructor=MergeTopSolutions(25, [0.0, 1.0]),\n",
" action=SetWarmStart(),\n",
");"
]
},
{
"cell_type": "markdown",
"id": "9536e7e4-0b0d-49b0-bebd-4a848f839e94",
"metadata": {},
"source": [
"Having defined the ML strategy, we next construct `LearningSolver`, train the ML component and optimize one of the test instances."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "9d13dd50-3dcf-4673-a757-6f44dcc0dedf",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:05:22.672002339Z",
"start_time": "2023-06-06T20:05:21.447466634Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)\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 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0xd2378195\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+06]\n",
" Objective range [1e+00, 6e+07]\n",
" Bounds range [0e+00, 0e+00]\n",
" RHS range [2e+08, 2e+08]\n",
"\n",
"User MIP start produced solution with objective 1.02165e+10 (0.00s)\n",
"Loaded user MIP start with objective 1.02165e+10\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"\n",
"Root relaxation: objective 1.021568e+10, 510 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 1.0216e+10 0 1 1.0217e+10 1.0216e+10 0.01% - 0s\n",
"\n",
"Explored 1 nodes (510 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 1: 1.02165e+10 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 1.021651058978e+10, best bound 1.021567971257e+10, gap 0.0081%\n",
"\n",
"User-callback calls 169, time in user-callback 0.00 sec\n"
]
}
],
"source": [
"solver_ml = LearningSolver(components=[comp])\n",
"solver_ml.fit(train_data)\n",
"solver_ml.optimize(test_data[1], build_uc_model);"
]
},
{
"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 very close to the optimal solution to the problem. Now let us repeat the code above, but a solver which does not apply any ML strategies. Note that our previously-defined component is not provided."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "2ff391ed-e855-4228-aa09-a7641d8c2893",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:05:46.969575966Z",
"start_time": "2023-06-06T20:05:46.420803286Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)\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 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0xb45c0594\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+06]\n",
" Objective range [1e+00, 6e+07]\n",
" Bounds range [0e+00, 0e+00]\n",
" RHS range [2e+08, 2e+08]\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"Found heuristic solution: objective 1.071463e+10\n",
"\n",
"Root relaxation: objective 1.021568e+10, 510 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 1.0216e+10 0 1 1.0715e+10 1.0216e+10 4.66% - 0s\n",
"H 0 0 1.025162e+10 1.0216e+10 0.35% - 0s\n",
" 0 0 1.0216e+10 0 1 1.0252e+10 1.0216e+10 0.35% - 0s\n",
"H 0 0 1.023090e+10 1.0216e+10 0.15% - 0s\n",
"H 0 0 1.022335e+10 1.0216e+10 0.07% - 0s\n",
"H 0 0 1.022281e+10 1.0216e+10 0.07% - 0s\n",
"H 0 0 1.021753e+10 1.0216e+10 0.02% - 0s\n",
"H 0 0 1.021752e+10 1.0216e+10 0.02% - 0s\n",
" 0 0 1.0216e+10 0 3 1.0218e+10 1.0216e+10 0.02% - 0s\n",
" 0 0 1.0216e+10 0 1 1.0218e+10 1.0216e+10 0.02% - 0s\n",
"H 0 0 1.021651e+10 1.0216e+10 0.01% - 0s\n",
"\n",
"Explored 1 nodes (764 simplex iterations) in 0.03 seconds (0.02 work units)\n",
"Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 7: 1.02165e+10 1.02175e+10 1.02228e+10 ... 1.07146e+10\n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 1.021651058978e+10, best bound 1.021573363741e+10, gap 0.0076%\n",
"\n",
"User-callback calls 204, time in user-callback 0.00 sec\n"
]
}
],
"source": [
"solver_baseline = LearningSolver(components=[])\n",
"solver_baseline.fit(train_data)\n",
"solver_baseline.optimize(test_data[1], build_uc_model);"
]
},
{
"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, but the difference can be significant for larger problems."
]
},
{
"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. In the following example, we show how to build and solve a JuMP model entirely in-memory, using our trained solver."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "67a6cd18",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:06:26.913448568Z",
"start_time": "2023-06-06T20:06:26.169047914Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)\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 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x974a7fba\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+06]\n",
" Objective range [1e+00, 6e+07]\n",
" Bounds range [0e+00, 0e+00]\n",
" RHS range [2e+08, 2e+08]\n",
"\n",
"User MIP start produced solution with objective 9.86729e+09 (0.00s)\n",
"User MIP start produced solution with objective 9.86675e+09 (0.00s)\n",
"User MIP start produced solution with objective 9.86654e+09 (0.01s)\n",
"User MIP start produced solution with objective 9.8661e+09 (0.01s)\n",
"Loaded user MIP start with objective 9.8661e+09\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"\n",
"Root relaxation: objective 9.865344e+09, 510 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 9.8653e+09 0 1 9.8661e+09 9.8653e+09 0.01% - 0s\n",
"\n",
"Explored 1 nodes (510 simplex iterations) in 0.02 seconds (0.01 work units)\n",
"Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 4: 9.8661e+09 9.86654e+09 9.86675e+09 9.86729e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 9.866096485614e+09, best bound 9.865343669936e+09, gap 0.0076%\n",
"\n",
"User-callback calls 182, time in user-callback 0.00 sec\n",
"objective_value(model.inner) = 9.866096485613789e9\n"
]
}
],
"source": [
"data = random_uc_data(samples=1, n=500)[1]\n",
"model = build_uc_model(data)\n",
"solver_ml.optimize(model)\n",
"@show objective_value(model.inner);"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Julia 1.9.0",
"language": "julia",
"name": "julia-1.9"
},
"language_info": {
"file_extension": ".jl",
"mimetype": "application/julia",
"name": "julia",
"version": "1.9.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,858 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "6b8983b1",
"metadata": {
"tags": []
},
"source": [
"# Getting started (Pyomo)\n",
"\n",
"## Introduction\n",
"\n",
"**MIPLearn** is an open source framework that uses machine learning (ML) to accelerate the performance of mixed-integer programming solvers (e.g. Gurobi, CPLEX, XPRESS). In this tutorial, we will:\n",
"\n",
"1. Install the Python/Pyomo version of MIPLearn\n",
"2. Model a simple optimization problem using Pyomo\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 Pyomo persistent solvers (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"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "02f0a927",
"metadata": {},
"source": [
"## Installation\n",
"\n",
"MIPLearn is available in two versions:\n",
"\n",
"- Python version, compatible with the Pyomo and Gurobipy modeling languages,\n",
"- Julia version, compatible with the JuMP modeling language.\n",
"\n",
"In this tutorial, we will demonstrate how to use and install the Python/Pyomo version of the package. The first step is to install Python 3.8+ in your computer. See the [official Python website for more instructions](https://www.python.org/downloads/). After Python is installed, we proceed to install MIPLearn using `pip`:\n",
"\n",
"```\n",
"$ pip install MIPLearn==0.3\n",
"```\n",
"\n",
"In addition to MIPLearn itself, we will also install Gurobi 10.0, a state-of-the-art commercial MILP solver. This step also install a demo license for Gurobi, which should able to solve the small optimization problems in this tutorial. A license is required for solving larger-scale problems.\n",
"\n",
"```\n",
"$ pip install 'gurobipy>=10,<10.1'\n",
"```"
]
},
{
"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 a utility company needs to decide which electrical generators should be online at each hour of the day, as well as how much power should each generator produce. More specifically, assume that the 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 the 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. The total amount of power to be produced needs to be exactly equal to the total demand $d$ (in megawatts).\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:"
]
},
{
"cell_type": "markdown",
"id": "f12c3702",
"metadata": {},
"source": [
"$$\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",
"$$"
]
},
{
"cell_type": "markdown",
"id": "be3989ed",
"metadata": {},
"source": [
"<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.\n",
"\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "a5fd33f6",
"metadata": {},
"source": [
"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": 1,
"id": "22a67170-10b4-43d3-8708-014d91141e73",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:00:03.278853343Z",
"start_time": "2023-06-06T20:00:03.123324067Z"
},
"tags": []
},
"outputs": [],
"source": [
"from dataclasses import dataclass\n",
"from typing import List\n",
"\n",
"import numpy as np\n",
"\n",
"\n",
"@dataclass\n",
"class UnitCommitmentData:\n",
" demand: float\n",
" pmin: List[float]\n",
" pmax: List[float]\n",
" cfix: List[float]\n",
" cvar: List[float]"
]
},
{
"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. The function accepts `UnitCommitmentData`, the data structure we previously defined, or the path to a compressed pickle file containing this data."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "2f67032f-0d74-4317-b45c-19da0ec859e9",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:00:45.890126754Z",
"start_time": "2023-06-06T20:00:45.637044282Z"
}
},
"outputs": [],
"source": [
"import pyomo.environ as pe\n",
"from typing import Union\n",
"from miplearn.io import read_pkl_gz\n",
"from miplearn.solvers.pyomo import PyomoModel\n",
"\n",
"\n",
"def build_uc_model(data: Union[str, UnitCommitmentData]) -> PyomoModel:\n",
" if isinstance(data, str):\n",
" data = read_pkl_gz(data)\n",
"\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] + data.cvar[i] * model.y[i] 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 PyomoModel(model, \"gurobi_persistent\")"
]
},
{
"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": 3,
"id": "2a896f47",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:01:10.993801745Z",
"start_time": "2023-06-06T20:01:10.887580927Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Restricted license - for non-production use only - expires 2024-10-28\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 7 rows, 6 columns and 15 nonzeros\n",
"Model fingerprint: 0x15c7a953\n",
"Variable types: 3 continuous, 3 integer (3 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 7e+01]\n",
" Objective range [2e+00, 7e+02]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [1e+02, 1e+02]\n",
"Presolve removed 2 rows and 1 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 5 rows, 5 columns, 13 nonzeros\n",
"Variable types: 0 continuous, 5 integer (3 binary)\n",
"Found heuristic solution: objective 1400.0000000\n",
"\n",
"Root relaxation: objective 1.035000e+03, 3 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 1035.00000 0 1 1400.00000 1035.00000 26.1% - 0s\n",
" 0 0 1105.71429 0 1 1400.00000 1105.71429 21.0% - 0s\n",
"* 0 0 0 1320.0000000 1320.00000 0.00% - 0s\n",
"\n",
"Explored 1 nodes (5 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 2: 1320 1400 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 1.320000000000e+03, best bound 1.320000000000e+03, gap 0.0000%\n",
"WARNING: Cannot get reduced costs for MIP.\n",
"WARNING: Cannot get duals for MIP.\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",
"model.optimize()\n",
"print(\"obj =\", model.inner.obj())\n",
"print(\"x =\", [model.inner.x[i].value for i in range(3)])\n",
"print(\"y =\", [model.inner.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": "01f576e1-1790-425e-9e5c-9fa07b6f4c26",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
" \n",
"Notes\n",
" \n",
"- In the example above, `PyomoModel` is just a thin wrapper around a standard Pyomo model. This wrapper allows MIPLearn to be solver- and modeling-language-agnostic. The wrapper provides only a few basic methods, such as `optimize`. For more control, and to query the solution, the original Pyomo model can be accessed through `model.inner`, as illustrated above. \n",
"- To use CPLEX or XPRESS, instead of Gurobi, replace `gurobi_persistent` by `cplex_persistent` or `xpress_persistent` in the `build_uc_model`. Note that only persistent Pyomo solvers are currently supported. Pull requests adding support for other types of solver are very welcome.\n",
"</div>"
]
},
{
"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** solver, which can optimize 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": 4,
"id": "5eb09fab",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:02:27.324208900Z",
"start_time": "2023-06-06T20:02:26.990044230Z"
}
},
"outputs": [],
"source": [
"from scipy.stats import uniform\n",
"from typing import List\n",
"import random\n",
"\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 _ 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 on multiple machines. 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": 5,
"id": "6156752c",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:03:04.782830561Z",
"start_time": "2023-06-06T20:03:04.530421396Z"
}
},
"outputs": [],
"source": [
"from miplearn.io import write_pkl_gz\n",
"\n",
"data = random_uc_data(samples=500, n=500)\n",
"train_data = write_pkl_gz(data[0:450], \"uc/train\")\n",
"test_data = write_pkl_gz(data[450:500], \"uc/test\")"
]
},
{
"cell_type": "markdown",
"id": "b17af877",
"metadata": {},
"source": [
"Finally, we use `BasicCollector` to collect the optimal solutions and other useful training data for all training instances. The data is stored in HDF5 files `uc/train/00000.h5`, `uc/train/00001.h5`, etc. The optimization models are also exported to compressed MPS files `uc/train/00000.mps.gz`, `uc/train/00001.mps.gz`, etc."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "7623f002",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:03:35.571497019Z",
"start_time": "2023-06-06T20:03:25.804104036Z"
}
},
"outputs": [],
"source": [
"from miplearn.collectors.basic import BasicCollector\n",
"\n",
"bc = BasicCollector()\n",
"bc.collect(train_data, build_uc_model, n_jobs=4)"
]
},
{
"cell_type": "markdown",
"id": "c42b1be1-9723-4827-82d8-974afa51ef9f",
"metadata": {},
"source": [
"## Training and solving test instances"
]
},
{
"cell_type": "markdown",
"id": "a33c6aa4-f0b8-4ccb-9935-01f7d7de2a1c",
"metadata": {},
"source": [
"With training data in hand, we can now design and train a machine learning model to accelerate solver performance. In this tutorial, for illustration purposes, we will use ML to generate a good warm start using $k$-nearest neighbors. More specifically, the strategy is to:\n",
"\n",
"1. Memorize the optimal solutions of all training instances;\n",
"2. Given a test instance, find the 25 most similar training instances, based on constraint right-hand sides;\n",
"3. Merge their optimal solutions into a single partial solution; specifically, only assign values to the binary variables that agree unanimously.\n",
"4. Provide this partial solution to the solver as a warm start.\n",
"\n",
"This simple strategy can be implemented as shown below, using `MemorizingPrimalComponent`. For more advanced strategies, and for the usage of more advanced classifiers, see the user guide."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "435f7bf8-4b09-4889-b1ec-b7b56e7d8ed2",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:05:20.497772794Z",
"start_time": "2023-06-06T20:05:20.484821405Z"
}
},
"outputs": [],
"source": [
"from sklearn.neighbors import KNeighborsClassifier\n",
"from miplearn.components.primal.actions import SetWarmStart\n",
"from miplearn.components.primal.mem import (\n",
" MemorizingPrimalComponent,\n",
" MergeTopSolutions,\n",
")\n",
"from miplearn.extractors.fields import H5FieldsExtractor\n",
"\n",
"comp = MemorizingPrimalComponent(\n",
" clf=KNeighborsClassifier(n_neighbors=25),\n",
" extractor=H5FieldsExtractor(\n",
" instance_fields=[\"static_constr_rhs\"],\n",
" ),\n",
" constructor=MergeTopSolutions(25, [0.0, 1.0]),\n",
" action=SetWarmStart(),\n",
")"
]
},
{
"cell_type": "markdown",
"id": "9536e7e4-0b0d-49b0-bebd-4a848f839e94",
"metadata": {},
"source": [
"Having defined the ML strategy, we next construct `LearningSolver`, train the ML component and optimize one of the test instances."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "9d13dd50-3dcf-4673-a757-6f44dcc0dedf",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:05:22.672002339Z",
"start_time": "2023-06-06T20:05:21.447466634Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x5e67c6ee\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 [3e+08, 3e+08]\n",
"Presolve removed 1000 rows and 500 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1 rows, 500 columns, 500 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 6.6166537e+09 5.648803e+04 0.000000e+00 0s\n",
" 1 8.2906219e+09 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.290621916e+09\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x4a7cfe2b\n",
"Variable types: 500 continuous, 500 integer (500 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 [3e+08, 3e+08]\n",
"\n",
"User MIP start produced solution with objective 8.29153e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.29153e+09 (0.01s)\n",
"Loaded user MIP start with objective 8.29153e+09\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"\n",
"Root relaxation: objective 8.290622e+09, 512 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 8.2906e+09 0 1 8.2915e+09 8.2906e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 3 8.2915e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2915e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 2 8.2915e+09 8.2907e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 1\n",
" Flow cover: 2\n",
"\n",
"Explored 1 nodes (565 simplex iterations) in 0.04 seconds (0.01 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 1: 8.29153e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.291528276179e+09, best bound 8.290733258025e+09, gap 0.0096%\n",
"WARNING: Cannot get reduced costs for MIP.\n",
"WARNING: Cannot get duals for MIP.\n"
]
},
{
"data": {
"text/plain": [
"{}"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from miplearn.solvers.learning import LearningSolver\n",
"\n",
"solver_ml = LearningSolver(components=[comp])\n",
"solver_ml.fit(train_data)\n",
"solver_ml.optimize(test_data[0], build_uc_model)"
]
},
{
"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 very close to the optimal solution to the problem. Now let us repeat the code above, but a solver which does not apply any ML strategies. Note that our previously-defined component is not provided."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "2ff391ed-e855-4228-aa09-a7641d8c2893",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:05:46.969575966Z",
"start_time": "2023-06-06T20:05:46.420803286Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x5e67c6ee\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 [3e+08, 3e+08]\n",
"Presolve removed 1000 rows and 500 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1 rows, 500 columns, 500 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 6.6166537e+09 5.648803e+04 0.000000e+00 0s\n",
" 1 8.2906219e+09 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.290621916e+09\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x8a0f9587\n",
"Variable types: 500 continuous, 500 integer (500 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 [3e+08, 3e+08]\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"Found heuristic solution: objective 9.757128e+09\n",
"\n",
"Root relaxation: objective 8.290622e+09, 512 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 8.2906e+09 0 1 9.7571e+09 8.2906e+09 15.0% - 0s\n",
"H 0 0 8.298273e+09 8.2906e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 4 8.2983e+09 8.2907e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2983e+09 8.2907e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 4 8.2983e+09 8.2907e+09 0.09% - 0s\n",
"H 0 0 8.293980e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 5 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 2 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 1 8.2940e+09 8.2908e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 4 8.2940e+09 8.2908e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 4 8.2940e+09 8.2908e+09 0.04% - 0s\n",
"H 0 0 8.291465e+09 8.2908e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 2\n",
" MIR: 1\n",
"\n",
"Explored 1 nodes (1025 simplex iterations) in 0.12 seconds (0.03 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 4: 8.29147e+09 8.29398e+09 8.29827e+09 9.75713e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.291465302389e+09, best bound 8.290781665333e+09, gap 0.0082%\n",
"WARNING: Cannot get reduced costs for MIP.\n",
"WARNING: Cannot get duals for MIP.\n"
]
},
{
"data": {
"text/plain": [
"{}"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"solver_baseline = LearningSolver(components=[])\n",
"solver_baseline.fit(train_data)\n",
"solver_baseline.optimize(test_data[0], build_uc_model)"
]
},
{
"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, but the difference can be significant for larger problems."
]
},
{
"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. 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": 10,
"id": "67a6cd18",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:06:26.913448568Z",
"start_time": "2023-06-06T20:06:26.169047914Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x2dfe4e1c\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 [3e+08, 3e+08]\n",
"Presolve removed 1000 rows and 500 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1 rows, 500 columns, 500 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 6.5917580e+09 5.627453e+04 0.000000e+00 0s\n",
" 1 8.2535968e+09 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.253596777e+09\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x0f0924a1\n",
"Variable types: 500 continuous, 500 integer (500 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 [3e+08, 3e+08]\n",
"\n",
"User MIP start produced solution with objective 8.25814e+09 (0.00s)\n",
"User MIP start produced solution with objective 8.25512e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25483e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25483e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25483e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25459e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25459e+09 (0.01s)\n",
"Loaded user MIP start with objective 8.25459e+09\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"\n",
"Root relaxation: objective 8.253597e+09, 512 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 8.2536e+09 0 1 8.2546e+09 8.2536e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 3 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 1 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 4 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 4 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2538e+09 0 4 8.2546e+09 8.2538e+09 0.01% - 0s\n",
" 0 0 8.2538e+09 0 5 8.2546e+09 8.2538e+09 0.01% - 0s\n",
" 0 0 8.2538e+09 0 6 8.2546e+09 8.2538e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Cover: 1\n",
" MIR: 2\n",
" StrongCG: 1\n",
" Flow cover: 1\n",
"\n",
"Explored 1 nodes (575 simplex iterations) in 0.09 seconds (0.01 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 4: 8.25459e+09 8.25483e+09 8.25512e+09 8.25814e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.254590409970e+09, best bound 8.253768093811e+09, gap 0.0100%\n",
"WARNING: Cannot get reduced costs for MIP.\n",
"WARNING: Cannot get duals for MIP.\n",
"obj = 8254590409.96973\n",
" x = [1.0, 1.0, 0.0, 1.0, 1.0]\n",
" y = [935662.0949262811, 1604270.0218116897, 0.0, 1369560.835229226, 602828.5321028307]\n"
]
}
],
"source": [
"data = random_uc_data(samples=1, n=500)[0]\n",
"model = build_uc_model(data)\n",
"solver_ml.optimize(model)\n",
"print(\"obj =\", model.inner.obj())\n",
"print(\" x =\", [model.inner.x[i].value for i in range(5)])\n",
"print(\" y =\", [model.inner.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 (ipykernel)",
"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.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,925 @@
/*
* basic.css
* ~~~~~~~~~
*
* Sphinx stylesheet -- basic theme.
*
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
/* -- main layout ----------------------------------------------------------- */
div.clearer {
clear: both;
}
div.section::after {
display: block;
content: '';
clear: left;
}
/* -- relbar ---------------------------------------------------------------- */
div.related {
width: 100%;
font-size: 90%;
}
div.related h3 {
display: none;
}
div.related ul {
margin: 0;
padding: 0 0 0 10px;
list-style: none;
}
div.related li {
display: inline;
}
div.related li.right {
float: right;
margin-right: 5px;
}
/* -- sidebar --------------------------------------------------------------- */
div.sphinxsidebarwrapper {
padding: 10px 5px 0 10px;
}
div.sphinxsidebar {
float: left;
width: 270px;
margin-left: -100%;
font-size: 90%;
word-wrap: break-word;
overflow-wrap : break-word;
}
div.sphinxsidebar ul {
list-style: none;
}
div.sphinxsidebar ul ul,
div.sphinxsidebar ul.want-points {
margin-left: 20px;
list-style: square;
}
div.sphinxsidebar ul ul {
margin-top: 0;
margin-bottom: 0;
}
div.sphinxsidebar form {
margin-top: 10px;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
div.sphinxsidebar #searchbox form.search {
overflow: hidden;
}
div.sphinxsidebar #searchbox input[type="text"] {
float: left;
width: 80%;
padding: 0.25em;
box-sizing: border-box;
}
div.sphinxsidebar #searchbox input[type="submit"] {
float: left;
width: 20%;
border-left: none;
padding: 0.25em;
box-sizing: border-box;
}
img {
border: 0;
max-width: 100%;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin: 10px 0 0 20px;
padding: 0;
}
ul.search li {
padding: 5px 0 5px 20px;
background-image: url(file.png);
background-repeat: no-repeat;
background-position: 0 7px;
}
ul.search li a {
font-weight: bold;
}
ul.search li p.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- index page ------------------------------------------------------------ */
table.contentstable {
width: 90%;
margin-left: auto;
margin-right: auto;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}
/* -- general index --------------------------------------------------------- */
table.indextable {
width: 100%;
}
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable ul {
margin-top: 0;
margin-bottom: 0;
list-style-type: none;
}
table.indextable > tbody > tr > td > ul {
padding-left: 0em;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
div.modindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
div.genindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
/* -- domain module index --------------------------------------------------- */
table.modindextable td {
padding: 2px;
border-collapse: collapse;
}
/* -- general body styles --------------------------------------------------- */
div.body {
min-width: 360px;
max-width: 800px;
}
div.body p, div.body dd, div.body li, div.body blockquote {
-moz-hyphens: auto;
-ms-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
a.headerlink {
visibility: hidden;
}
a:visited {
color: #551A8B;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink,
caption:hover > a.headerlink,
p.caption:hover > a.headerlink,
div.code-block-caption:hover > a.headerlink {
visibility: visible;
}
div.body p.caption {
text-align: inherit;
}
div.body td {
text-align: left;
}
.first {
margin-top: 0 !important;
}
p.rubric {
margin-top: 30px;
font-weight: bold;
}
img.align-left, figure.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
img.align-right, figure.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
img.align-center, figure.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
img.align-default, figure.align-default, .figure.align-default {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left;
}
.align-center {
text-align: center;
}
.align-default {
text-align: center;
}
.align-right {
text-align: right;
}
/* -- sidebars -------------------------------------------------------------- */
div.sidebar,
aside.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
padding: 7px;
background-color: #ffe;
width: 40%;
float: right;
clear: right;
overflow-x: auto;
}
p.sidebar-title {
font-weight: bold;
}
nav.contents,
aside.topic,
div.admonition, div.topic, blockquote {
clear: left;
}
/* -- topics ---------------------------------------------------------------- */
nav.contents,
aside.topic,
div.topic {
border: 1px solid #ccc;
padding: 7px;
margin: 10px 0 10px 0;
}
p.topic-title {
font-size: 1.1em;
font-weight: bold;
margin-top: 10px;
}
/* -- admonitions ----------------------------------------------------------- */
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 7px;
}
div.admonition dt {
font-weight: bold;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
div.body p.centered {
text-align: center;
margin-top: 25px;
}
/* -- content of sidebars/topics/admonitions -------------------------------- */
div.sidebar > :last-child,
aside.sidebar > :last-child,
nav.contents > :last-child,
aside.topic > :last-child,
div.topic > :last-child,
div.admonition > :last-child {
margin-bottom: 0;
}
div.sidebar::after,
aside.sidebar::after,
nav.contents::after,
aside.topic::after,
div.topic::after,
div.admonition::after,
blockquote::after {
display: block;
content: '';
clear: both;
}
/* -- tables ---------------------------------------------------------------- */
table.docutils {
margin-top: 10px;
margin-bottom: 10px;
border: 0;
border-collapse: collapse;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
table.align-default {
margin-left: auto;
margin-right: auto;
}
table caption span.caption-number {
font-style: italic;
}
table caption span.caption-text {
}
table.docutils td, table.docutils th {
padding: 1px 8px 1px 5px;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #aaa;
}
th {
text-align: left;
padding-right: 5px;
}
table.citation {
border-left: solid 1px gray;
margin-left: 1px;
}
table.citation td {
border-bottom: none;
}
th > :first-child,
td > :first-child {
margin-top: 0px;
}
th > :last-child,
td > :last-child {
margin-bottom: 0px;
}
/* -- figures --------------------------------------------------------------- */
div.figure, figure {
margin: 0.5em;
padding: 0.5em;
}
div.figure p.caption, figcaption {
padding: 0.3em;
}
div.figure p.caption span.caption-number,
figcaption span.caption-number {
font-style: italic;
}
div.figure p.caption span.caption-text,
figcaption span.caption-text {
}
/* -- field list styles ----------------------------------------------------- */
table.field-list td, table.field-list th {
border: 0 !important;
}
.field-list ul {
margin: 0;
padding-left: 1em;
}
.field-list p {
margin: 0;
}
.field-name {
-moz-hyphens: manual;
-ms-hyphens: manual;
-webkit-hyphens: manual;
hyphens: manual;
}
/* -- hlist styles ---------------------------------------------------------- */
table.hlist {
margin: 1em 0;
}
table.hlist td {
vertical-align: top;
}
/* -- object description styles --------------------------------------------- */
.sig {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
}
.sig-name, code.descname {
background-color: transparent;
font-weight: bold;
}
.sig-name {
font-size: 1.1em;
}
code.descname {
font-size: 1.2em;
}
.sig-prename, code.descclassname {
background-color: transparent;
}
.optional {
font-size: 1.3em;
}
.sig-paren {
font-size: larger;
}
.sig-param.n {
font-style: italic;
}
/* C++ specific styling */
.sig-inline.c-texpr,
.sig-inline.cpp-texpr {
font-family: unset;
}
.sig.c .k, .sig.c .kt,
.sig.cpp .k, .sig.cpp .kt {
color: #0033B3;
}
.sig.c .m,
.sig.cpp .m {
color: #1750EB;
}
.sig.c .s, .sig.c .sc,
.sig.cpp .s, .sig.cpp .sc {
color: #067D17;
}
/* -- other body styles ----------------------------------------------------- */
ol.arabic {
list-style: decimal;
}
ol.loweralpha {
list-style: lower-alpha;
}
ol.upperalpha {
list-style: upper-alpha;
}
ol.lowerroman {
list-style: lower-roman;
}
ol.upperroman {
list-style: upper-roman;
}
:not(li) > ol > li:first-child > :first-child,
:not(li) > ul > li:first-child > :first-child {
margin-top: 0px;
}
:not(li) > ol > li:last-child > :last-child,
:not(li) > ul > li:last-child > :last-child {
margin-bottom: 0px;
}
ol.simple ol p,
ol.simple ul p,
ul.simple ol p,
ul.simple ul p {
margin-top: 0;
}
ol.simple > li:not(:first-child) > p,
ul.simple > li:not(:first-child) > p {
margin-top: 0;
}
ol.simple p,
ul.simple p {
margin-bottom: 0;
}
aside.footnote > span,
div.citation > span {
float: left;
}
aside.footnote > span:last-of-type,
div.citation > span:last-of-type {
padding-right: 0.5em;
}
aside.footnote > p {
margin-left: 2em;
}
div.citation > p {
margin-left: 4em;
}
aside.footnote > p:last-of-type,
div.citation > p:last-of-type {
margin-bottom: 0em;
}
aside.footnote > p:last-of-type:after,
div.citation > p:last-of-type:after {
content: "";
clear: both;
}
dl.field-list {
display: grid;
grid-template-columns: fit-content(30%) auto;
}
dl.field-list > dt {
font-weight: bold;
word-break: break-word;
padding-left: 0.5em;
padding-right: 5px;
}
dl.field-list > dd {
padding-left: 0.5em;
margin-top: 0em;
margin-left: 0em;
margin-bottom: 0em;
}
dl {
margin-bottom: 15px;
}
dd > :first-child {
margin-top: 0px;
}
dd ul, dd table {
margin-bottom: 10px;
}
dd {
margin-top: 3px;
margin-bottom: 10px;
margin-left: 30px;
}
.sig dd {
margin-top: 0px;
margin-bottom: 0px;
}
.sig dl {
margin-top: 0px;
margin-bottom: 0px;
}
dl > dd:last-child,
dl > dd:last-child > :last-child {
margin-bottom: 0;
}
dt:target, span.highlighted {
background-color: #fbe54e;
}
rect.highlighted {
fill: #fbe54e;
}
dl.glossary dt {
font-weight: bold;
font-size: 1.1em;
}
.versionmodified {
font-style: italic;
}
.system-message {
background-color: #fda;
padding: 5px;
border: 3px solid red;
}
.footnote:target {
background-color: #ffa;
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
.guilabel, .menuselection {
font-family: sans-serif;
}
.accelerator {
text-decoration: underline;
}
.classifier {
font-style: oblique;
}
.classifier:before {
font-style: normal;
margin: 0 0.5em;
content: ":";
display: inline-block;
}
abbr, acronym {
border-bottom: dotted 1px;
cursor: help;
}
.translated {
background-color: rgba(207, 255, 207, 0.2)
}
.untranslated {
background-color: rgba(255, 207, 207, 0.2)
}
/* -- code displays --------------------------------------------------------- */
pre {
overflow: auto;
overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
pre, div[class*="highlight-"] {
clear: both;
}
span.pre {
-moz-hyphens: none;
-ms-hyphens: none;
-webkit-hyphens: none;
hyphens: none;
white-space: nowrap;
}
div[class*="highlight-"] {
margin: 1em 0;
}
td.linenos pre {
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
display: block;
}
table.highlighttable tbody {
display: block;
}
table.highlighttable tr {
display: flex;
}
table.highlighttable td {
margin: 0;
padding: 0;
}
table.highlighttable td.linenos {
padding-right: 0.5em;
}
table.highlighttable td.code {
flex: 1;
overflow: hidden;
}
.highlight .hll {
display: block;
}
div.highlight pre,
table.highlighttable pre {
margin: 0;
}
div.code-block-caption + div {
margin-top: 0;
}
div.code-block-caption {
margin-top: 1em;
padding: 2px 5px;
font-size: small;
}
div.code-block-caption code {
background-color: transparent;
}
table.highlighttable td.linenos,
span.linenos,
div.highlight span.gp { /* gp: Generic.Prompt */
user-select: none;
-webkit-user-select: text; /* Safari fallback only */
-webkit-user-select: none; /* Chrome/Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+ */
}
div.code-block-caption span.caption-number {
padding: 0.1em 0.3em;
font-style: italic;
}
div.code-block-caption span.caption-text {
}
div.literal-block-wrapper {
margin: 1em 0;
}
code.xref, a code {
background-color: transparent;
font-weight: bold;
}
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
background-color: transparent;
}
.viewcode-link {
float: right;
}
.viewcode-back {
float: right;
font-family: sans-serif;
}
div.viewcode-block:target {
margin: -1px -10px;
padding: 0 10px;
}
/* -- math display ---------------------------------------------------------- */
img.math {
vertical-align: middle;
}
div.body div.math p {
text-align: center;
}
span.eqno {
float: right;
}
span.eqno a.headerlink {
position: absolute;
z-index: 1;
}
div.math:hover a.headerlink {
visibility: visible;
}
/* -- printout stylesheet --------------------------------------------------- */
@media print {
div.document,
div.documentwrapper,
div.bodywrapper {
margin: 0 !important;
width: 100%;
}
div.sphinxsidebar,
div.related,
div.footer,
#top-link {
display: none;
}
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,117 @@
:root {
/*****************************************************************************
* Theme config
**/
--pst-header-height: 60px;
/*****************************************************************************
* Font size
**/
--pst-font-size-base: 15px; /* base font size - applied at body / html level */
/* heading font sizes */
--pst-font-size-h1: 36px;
--pst-font-size-h2: 32px;
--pst-font-size-h3: 26px;
--pst-font-size-h4: 21px;
--pst-font-size-h5: 18px;
--pst-font-size-h6: 16px;
/* smaller then heading font sizes*/
--pst-font-size-milli: 12px;
--pst-sidebar-font-size: .9em;
--pst-sidebar-caption-font-size: .9em;
/*****************************************************************************
* Font family
**/
/* These are adapted from https://systemfontstack.com/ */
--pst-font-family-base-system: -apple-system, BlinkMacSystemFont, Segoe UI, "Helvetica Neue",
Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
--pst-font-family-monospace-system: "SFMono-Regular", Menlo, Consolas, Monaco,
Liberation Mono, Lucida Console, monospace;
--pst-font-family-base: var(--pst-font-family-base-system);
--pst-font-family-heading: var(--pst-font-family-base);
--pst-font-family-monospace: var(--pst-font-family-monospace-system);
/*****************************************************************************
* Color
*
* Colors are defined in rgb string way, "red, green, blue"
**/
--pst-color-primary: 19, 6, 84;
--pst-color-success: 40, 167, 69;
--pst-color-info: 0, 123, 255; /*23, 162, 184;*/
--pst-color-warning: 255, 193, 7;
--pst-color-danger: 220, 53, 69;
--pst-color-text-base: 51, 51, 51;
--pst-color-h1: var(--pst-color-primary);
--pst-color-h2: var(--pst-color-primary);
--pst-color-h3: var(--pst-color-text-base);
--pst-color-h4: var(--pst-color-text-base);
--pst-color-h5: var(--pst-color-text-base);
--pst-color-h6: var(--pst-color-text-base);
--pst-color-paragraph: var(--pst-color-text-base);
--pst-color-link: 0, 91, 129;
--pst-color-link-hover: 227, 46, 0;
--pst-color-headerlink: 198, 15, 15;
--pst-color-headerlink-hover: 255, 255, 255;
--pst-color-preformatted-text: 34, 34, 34;
--pst-color-preformatted-background: 250, 250, 250;
--pst-color-inline-code: 232, 62, 140;
--pst-color-active-navigation: 19, 6, 84;
--pst-color-navbar-link: 77, 77, 77;
--pst-color-navbar-link-hover: var(--pst-color-active-navigation);
--pst-color-navbar-link-active: var(--pst-color-active-navigation);
--pst-color-sidebar-link: 77, 77, 77;
--pst-color-sidebar-link-hover: var(--pst-color-active-navigation);
--pst-color-sidebar-link-active: var(--pst-color-active-navigation);
--pst-color-sidebar-expander-background-hover: 244, 244, 244;
--pst-color-sidebar-caption: 77, 77, 77;
--pst-color-toc-link: 119, 117, 122;
--pst-color-toc-link-hover: var(--pst-color-active-navigation);
--pst-color-toc-link-active: var(--pst-color-active-navigation);
/*****************************************************************************
* Icon
**/
/* font awesome icons*/
--pst-icon-check-circle: '\f058';
--pst-icon-info-circle: '\f05a';
--pst-icon-exclamation-triangle: '\f071';
--pst-icon-exclamation-circle: '\f06a';
--pst-icon-times-circle: '\f057';
--pst-icon-lightbulb: '\f0eb';
/*****************************************************************************
* Admonitions
**/
--pst-color-admonition-default: var(--pst-color-info);
--pst-color-admonition-note: var(--pst-color-info);
--pst-color-admonition-attention: var(--pst-color-warning);
--pst-color-admonition-caution: var(--pst-color-warning);
--pst-color-admonition-warning: var(--pst-color-warning);
--pst-color-admonition-danger: var(--pst-color-danger);
--pst-color-admonition-error: var(--pst-color-danger);
--pst-color-admonition-hint: var(--pst-color-success);
--pst-color-admonition-tip: var(--pst-color-success);
--pst-color-admonition-important: var(--pst-color-success);
--pst-icon-admonition-default: var(--pst-icon-info-circle);
--pst-icon-admonition-note: var(--pst-icon-info-circle);
--pst-icon-admonition-attention: var(--pst-icon-exclamation-circle);
--pst-icon-admonition-caution: var(--pst-icon-exclamation-triangle);
--pst-icon-admonition-warning: var(--pst-icon-exclamation-triangle);
--pst-icon-admonition-danger: var(--pst-icon-exclamation-triangle);
--pst-icon-admonition-error: var(--pst-icon-times-circle);
--pst-icon-admonition-hint: var(--pst-icon-lightbulb);
--pst-icon-admonition-tip: var(--pst-icon-lightbulb);
--pst-icon-admonition-important: var(--pst-icon-exclamation-circle);
}

@ -0,0 +1,120 @@
h1.site-logo {
font-size: 30px !important;
}
h1.site-logo small {
font-size: 20px !important;
}
code {
display: inline-block;
border-radius: 4px;
padding: 0 4px;
background-color: #eee;
color: rgb(232, 62, 140);
}
.right-next, .left-prev {
border-radius: 8px;
border-width: 0px !important;
box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.2);
}
.right-next:hover, .left-prev:hover {
text-decoration: none;
}
.admonition {
border-radius: 8px;
border-width: 0;
box-shadow: 0 0 0 !important;
}
.note { background-color: rgba(0, 123, 255, 0.1); }
.note * { color: rgb(69 94 121); }
.warning { background-color: rgb(220 150 40 / 10%); }
.warning * { color: rgb(105 72 28); }
.input_area, .output_area, .output_area img {
border-radius: 8px !important;
border-width: 0 !important;
margin: 8px 0 8px 0;
}
.output_area {
padding: 4px;
background-color: hsl(227 60% 11% / 0.7) !important;
}
.output_area pre {
color: #fff;
line-height: 20px !important;
}
.input_area pre {
background-color: rgba(0 0 0 / 3%) !important;
padding: 12px !important;
line-height: 20px;
}
.ansi-green-intense-fg {
color: #64d88b !important;
}
#site-navigation {
background-color: #fafafa;
}
.container, .container-lg, .container-md, .container-sm, .container-xl {
max-width: inherit !important;
}
h1, h2 {
font-weight: bold !important;
}
#main-content .section {
max-width: 900px !important;
margin: 0 auto !important;
font-size: 16px;
}
p.caption {
font-weight: bold;
}
h2 {
padding-bottom: 5px;
border-bottom: 1px solid #ccc;
}
h3 {
margin-top: 1.5rem;
}
tbody, thead, pre {
border: 1px solid rgba(0, 0, 0, 0.25);
}
table td, th {
padding: 8px;
}
table p {
margin-bottom: 0;
}
table td code {
white-space: nowrap;
}
table tr,
table th {
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
table tr:last-child {
border-bottom: 0;
}

@ -0,0 +1,156 @@
/*
* doctools.js
* ~~~~~~~~~~~
*
* Base JavaScript utilities for all Sphinx HTML documentation.
*
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
"use strict";
const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([
"TEXTAREA",
"INPUT",
"SELECT",
"BUTTON",
]);
const _ready = (callback) => {
if (document.readyState !== "loading") {
callback();
} else {
document.addEventListener("DOMContentLoaded", callback);
}
};
/**
* Small JavaScript module for the documentation.
*/
const Documentation = {
init: () => {
Documentation.initDomainIndexTable();
Documentation.initOnKeyListeners();
},
/**
* i18n support
*/
TRANSLATIONS: {},
PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
LOCALE: "unknown",
// gettext and ngettext don't access this so that the functions
// can safely bound to a different name (_ = Documentation.gettext)
gettext: (string) => {
const translated = Documentation.TRANSLATIONS[string];
switch (typeof translated) {
case "undefined":
return string; // no translation
case "string":
return translated; // translation exists
default:
return translated[0]; // (singular, plural) translation tuple exists
}
},
ngettext: (singular, plural, n) => {
const translated = Documentation.TRANSLATIONS[singular];
if (typeof translated !== "undefined")
return translated[Documentation.PLURAL_EXPR(n)];
return n === 1 ? singular : plural;
},
addTranslations: (catalog) => {
Object.assign(Documentation.TRANSLATIONS, catalog.messages);
Documentation.PLURAL_EXPR = new Function(
"n",
`return (${catalog.plural_expr})`
);
Documentation.LOCALE = catalog.locale;
},
/**
* helper function to focus on search bar
*/
focusSearchBar: () => {
document.querySelectorAll("input[name=q]")[0]?.focus();
},
/**
* Initialise the domain index toggle buttons
*/
initDomainIndexTable: () => {
const toggler = (el) => {
const idNumber = el.id.substr(7);
const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
if (el.src.substr(-9) === "minus.png") {
el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
toggledRows.forEach((el) => (el.style.display = "none"));
} else {
el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
toggledRows.forEach((el) => (el.style.display = ""));
}
};
const togglerElements = document.querySelectorAll("img.toggler");
togglerElements.forEach((el) =>
el.addEventListener("click", (event) => toggler(event.currentTarget))
);
togglerElements.forEach((el) => (el.style.display = ""));
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
},
initOnKeyListeners: () => {
// only install a listener if it is really needed
if (
!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS &&
!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
)
return;
document.addEventListener("keydown", (event) => {
// bail for input elements
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
// bail with special keys
if (event.altKey || event.ctrlKey || event.metaKey) return;
if (!event.shiftKey) {
switch (event.key) {
case "ArrowLeft":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const prevLink = document.querySelector('link[rel="prev"]');
if (prevLink && prevLink.href) {
window.location.href = prevLink.href;
event.preventDefault();
}
break;
case "ArrowRight":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const nextLink = document.querySelector('link[rel="next"]');
if (nextLink && nextLink.href) {
window.location.href = nextLink.href;
event.preventDefault();
}
break;
}
}
// some keyboard layouts may need Shift to get /
switch (event.key) {
case "/":
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
Documentation.focusSearchBar();
event.preventDefault();
}
});
},
};
// quick alias for translations
const _ = Documentation.gettext;
_ready(Documentation.init);

@ -0,0 +1,13 @@
const DOCUMENTATION_OPTIONS = {
VERSION: '0.4',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'dirhtml',
FILE_SUFFIX: '.html',
LINK_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt',
NAVIGATION_WITH_KEYS: true,
SHOW_SEARCH_SUMMARY: true,
ENABLE_SEARCH_SHORTCUTS: true,
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 44.4 44.4" style="enable-background:new 0 0 44.4 44.4;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#F5A252;stroke-width:5;stroke-miterlimit:10;}
.st1{fill:none;stroke:#579ACA;stroke-width:5;stroke-miterlimit:10;}
.st2{fill:none;stroke:#E66581;stroke-width:5;stroke-miterlimit:10;}
</style>
<title>logo</title>
<g>
<path class="st0" d="M33.9,6.4c3.6,3.9,3.4,9.9-0.5,13.5s-9.9,3.4-13.5-0.5s-3.4-9.9,0.5-13.5l0,0C24.2,2.4,30.2,2.6,33.9,6.4z"/>
<path class="st1" d="M35.1,27.3c2.6,4.6,1.1,10.4-3.5,13c-4.6,2.6-10.4,1.1-13-3.5s-1.1-10.4,3.5-13l0,0
C26.6,21.2,32.4,22.7,35.1,27.3z"/>
<path class="st2" d="M25.9,17.8c2.6,4.6,1.1,10.4-3.5,13s-10.4,1.1-13-3.5s-1.1-10.4,3.5-13l0,0C17.5,11.7,23.3,13.2,25.9,17.8z"/>
<path class="st1" d="M19.2,26.4c3.1-4.3,9.1-5.2,13.3-2.1c1.1,0.8,2,1.8,2.7,3"/>
<path class="st0" d="M19.9,19.4c-3.6-3.9-3.4-9.9,0.5-13.5s9.9-3.4,13.5,0.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="38.73" height="50" viewBox="0 0 38.73 50"><defs><style>.cls-1{fill:#767677;}.cls-2{fill:#f37726;}.cls-3{fill:#9e9e9e;}.cls-4{fill:#616262;}.cls-5{font-size:17.07px;fill:#fff;font-family:Roboto-Regular, Roboto;}</style></defs><title>logo_jupyterhub</title><g id="Canvas"><path id="path7_fill" data-name="path7 fill" class="cls-1" d="M39.51,3.53a3,3,0,0,1-1.7,2.9A3,3,0,0,1,34.48,6a3,3,0,0,1-.82-3.26,3,3,0,0,1,1.05-1.41A3,3,0,0,1,37.52.86a2.88,2.88,0,0,1,1,.6,3,3,0,0,1,.7.93,3.18,3.18,0,0,1,.28,1.14Z" transform="translate(-1.87 -0.69)"/><path id="path8_fill" data-name="path8 fill" class="cls-2" d="M21.91,38.39c-8,0-15.06-2.87-18.7-7.12a19.93,19.93,0,0,0,37.39,0C37,35.52,30,38.39,21.91,38.39Z" transform="translate(-1.87 -0.69)"/><path id="path9_fill" data-name="path9 fill" class="cls-2" d="M21.91,10.78c8,0,15.05,2.87,18.69,7.12a19.93,19.93,0,0,0-37.39,0C6.85,13.64,13.86,10.78,21.91,10.78Z" transform="translate(-1.87 -0.69)"/><path id="path10_fill" data-name="path10 fill" class="cls-3" d="M10.88,46.66a3.86,3.86,0,0,1-.52,2.15,3.81,3.81,0,0,1-1.62,1.51,3.93,3.93,0,0,1-2.19.34,3.79,3.79,0,0,1-2-.94,3.73,3.73,0,0,1-1.14-1.9,3.79,3.79,0,0,1,.1-2.21,3.86,3.86,0,0,1,1.33-1.78,3.92,3.92,0,0,1,3.54-.53,3.85,3.85,0,0,1,2.14,1.93,3.74,3.74,0,0,1,.37,1.43Z" transform="translate(-1.87 -0.69)"/><path id="path11_fill" data-name="path11 fill" class="cls-4" d="M4.12,9.81A2.18,2.18,0,0,1,2.9,9.48a2.23,2.23,0,0,1-.84-1A2.26,2.26,0,0,1,1.9,7.26a2.13,2.13,0,0,1,.56-1.13,2.18,2.18,0,0,1,2.36-.56,2.13,2.13,0,0,1,1,.76,2.18,2.18,0,0,1,.42,1.2A2.22,2.22,0,0,1,4.12,9.81Z" transform="translate(-1.87 -0.69)"/></g><text class="cls-5" transform="translate(5.24 30.01)">Hub</text></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

File diff suppressed because one or more lines are too long

@ -0,0 +1,199 @@
/*
* language_data.js
* ~~~~~~~~~~~~~~~~
*
* This script contains the language-specific data used by searchtools.js,
* namely the list of stopwords, stemmer, scorer and splitter.
*
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"];
/* Non-minified version is copied as a separate JS file, is available */
/**
* Porter Stemmer
*/
var Stemmer = function() {
var step2list = {
ational: 'ate',
tional: 'tion',
enci: 'ence',
anci: 'ance',
izer: 'ize',
bli: 'ble',
alli: 'al',
entli: 'ent',
eli: 'e',
ousli: 'ous',
ization: 'ize',
ation: 'ate',
ator: 'ate',
alism: 'al',
iveness: 'ive',
fulness: 'ful',
ousness: 'ous',
aliti: 'al',
iviti: 'ive',
biliti: 'ble',
logi: 'log'
};
var step3list = {
icate: 'ic',
ative: '',
alize: 'al',
iciti: 'ic',
ical: 'ic',
ful: '',
ness: ''
};
var c = "[^aeiou]"; // consonant
var v = "[aeiouy]"; // vowel
var C = c + "[^aeiouy]*"; // consonant sequence
var V = v + "[aeiou]*"; // vowel sequence
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
var s_v = "^(" + C + ")?" + v; // vowel in stem
this.stemWord = function (w) {
var stem;
var suffix;
var firstch;
var origword = w;
if (w.length < 3)
return w;
var re;
var re2;
var re3;
var re4;
firstch = w.substr(0,1);
if (firstch == "y")
w = firstch.toUpperCase() + w.substr(1);
// Step 1a
re = /^(.+?)(ss|i)es$/;
re2 = /^(.+?)([^s])s$/;
if (re.test(w))
w = w.replace(re,"$1$2");
else if (re2.test(w))
w = w.replace(re2,"$1$2");
// Step 1b
re = /^(.+?)eed$/;
re2 = /^(.+?)(ed|ing)$/;
if (re.test(w)) {
var fp = re.exec(w);
re = new RegExp(mgr0);
if (re.test(fp[1])) {
re = /.$/;
w = w.replace(re,"");
}
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1];
re2 = new RegExp(s_v);
if (re2.test(stem)) {
w = stem;
re2 = /(at|bl|iz)$/;
re3 = new RegExp("([^aeiouylsz])\\1$");
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re2.test(w))
w = w + "e";
else if (re3.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
else if (re4.test(w))
w = w + "e";
}
}
// Step 1c
re = /^(.+?)y$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(s_v);
if (re.test(stem))
w = stem + "i";
}
// Step 2
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step2list[suffix];
}
// Step 3
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step3list[suffix];
}
// Step 4
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
re2 = /^(.+?)(s|t)(ion)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
if (re.test(stem))
w = stem;
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1] + fp[2];
re2 = new RegExp(mgr1);
if (re2.test(stem))
w = stem;
}
// Step 5
re = /^(.+?)e$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
re2 = new RegExp(meq1);
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
w = stem;
}
re = /ll$/;
re2 = new RegExp(mgr1);
if (re.test(w) && re2.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
// and turn initial Y back to y
if (firstch == "y")
w = firstch.toLowerCase() + w.substr(1);
return w;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<style>
svg { fill: lightcoral; }
@media (prefers-color-scheme: dark) {
svg { fill: crimson; }
}
</style>
<path d="m 80.856887,38.34474 h 2.903414 l 7.066397,13.332221 V 38.34474 h 2.092166 V 54.281494 H 90.01545 L 82.949053,40.949273 v 13.332221 h -2.092166 z m -13.812565,0 h 10.076555 v 1.814633 h -7.920343 v 4.718048 h 7.589439 v 1.814634 h -7.589439 v 5.774805 h 8.11248 v 1.814634 H 67.044322 Z m -14.335606,0 h 2.156212 v 6.735493 L 62.01672,38.34474 h 2.775323 l -7.909669,7.429324 8.475407,8.50743 h -2.839368 l -7.653485,-7.674833 v 7.674833 h -2.156212 z m -10.738365,1.462381 q -2.348349,0 -3.73601,1.750588 -1.376987,1.750588 -1.376987,4.771419 0,3.010158 1.376987,4.760746 1.387661,1.750587 3.73601,1.750587 2.34835,0 3.714663,-1.750587 1.376986,-1.750588 1.376986,-4.760746 0,-3.020831 -1.376986,-4.771419 -1.366313,-1.750588 -3.714663,-1.750588 z m 0,-1.750588 q 3.351736,0 5.358507,2.252281 2.006772,2.241606 2.006772,6.020314 0,3.768034 -2.006772,6.020315 -2.006771,2.241606 -5.358507,2.241606 -3.362409,0 -5.379855,-2.241606 -2.006772,-2.241607 -2.006772,-6.020315 0,-3.778708 2.006772,-6.020314 2.017446,-2.252281 5.379855,-2.252281 z m -14.100771,8.75294 q 0.693831,0.234835 1.344964,1.003385 0.661808,0.768551 1.323615,2.113515 l 2.188235,4.355121 H 30.410068 L 28.371273,50.193231 Q 27.581374,48.592083 26.834172,48.069042 26.097644,47.546 24.816726,47.546 h -2.34835 v 6.735494 H 20.312164 V 38.34474 h 4.867489 q 2.732625,0 4.077588,1.142152 1.344964,1.142151 1.344964,3.447804 0,1.505078 -0.704505,2.49779 -0.69383,0.992711 -2.02812,1.376987 z m -5.401204,-6.692797 v 5.657388 h 2.711277 q 1.55845,0 2.348349,-0.715179 0.800574,-0.725854 0.800574,-2.124189 0,-1.398336 -0.800574,-2.102841 -0.789899,-0.715179 -2.348349,-0.715179 z M 7.4709616,46.670707 v 5.838851 H 10.92944 q 1.739914,0 2.57251,-0.71518 0.843271,-0.725853 0.843271,-2.209583 0,-1.494404 -0.843271,-2.198909 -0.832596,-0.715179 -2.57251,-0.715179 z m 0,-6.554031 v 4.803443 h 3.1916204 q 1.579799,0 2.34835,-0.587088 0.779225,-0.597761 0.779225,-1.814634 0,-1.206197 -0.779225,-1.803959 -0.768551,-0.597762 -2.34835,-0.597762 z M 5.3147497,38.34474 h 5.5079473 q 2.465767,0 3.800057,1.024734 1.334289,1.024734 1.334289,2.914088 0,1.462382 -0.683156,2.327001 -0.683156,0.86462 -2.006772,1.078106 1.590474,0.341579 2.465768,1.430359 0.885968,1.078106 0.885968,2.700602 0,2.134863 -1.451707,3.298364 -1.451707,1.1635 -4.130961,1.1635 H 5.3147497 Z M 65.365328,74.550032 H 67.52154 V 88.67217 h 7.760238 v 1.814634 h -9.91645 z m -6.447298,0 h 2.156214 V 90.486804 H 58.91803 Z m -9.955303,2.124192 -2.924765,7.931025 h 5.860208 z m -1.216872,-2.124192 h 2.444423 l 6.073692,15.936772 h -2.241606 l -1.451709,-4.088268 h -7.183824 l -1.451707,4.088268 H 41.66149 Z m -21.264514,0 h 2.903417 L 36.45116,87.882271 V 74.550032 h 2.092171 V 90.486804 H 35.639914 L 28.573506,77.154569 v 13.332235 h -2.092165 z m 53.454813,-9.781663 v 5.838858 h 3.458479 q 1.739919,0 2.572513,-0.71518 0.843272,-0.725854 0.843272,-2.209586 0,-1.494406 -0.843272,-2.198912 -0.832594,-0.71518 -2.572513,-0.71518 z m 0,-6.554037 v 4.803447 h 3.191622 q 1.579803,0 2.348355,-0.587087 0.779225,-0.597763 0.779225,-1.814636 0,-1.206199 -0.779225,-1.803962 -0.768552,-0.597762 -2.348355,-0.597762 z m -2.156212,-1.771939 h 5.507949 q 2.465772,0 3.800065,1.024736 1.334288,1.024735 1.334288,2.914092 0,1.462383 -0.683157,2.327003 -0.683157,0.864621 -2.006771,1.078107 1.590471,0.341579 2.46577,1.43036 0.885967,1.078107 0.885967,2.700605 0,2.134866 -1.451708,3.298368 -1.451709,1.163502 -4.130963,1.163502 h -5.72144 z m -18.86154,0 h 3.212969 l 4.066921,10.845118 4.088269,-10.845118 H 73.49953 V 72.379166 H 71.396692 V 58.385121 L 67.287075,69.315634 H 65.120183 L 61.010567,58.385121 v 13.994045 h -2.092165 z m -16.246333,0 h 2.166892 v 9.681616 q 0,2.561839 0.928668,3.693318 0.928667,1.120804 3.010159,1.120804 2.070819,0 2.999486,-1.120804 0.928667,-1.131479 0.928667,-3.693318 v -9.681616 h 2.166886 v 9.948474 q 0,3.116904 -1.547776,4.70738 -1.537104,1.590474 -4.547263,1.590474 -3.020833,0 -4.568615,-1.590474 -1.537104,-1.590476 -1.537104,-4.70738 z m -16.192953,0 h 2.156213 v 6.532689 h 7.83496 v -6.532689 h 2.156213 v 15.936773 h -2.156213 v -7.589448 h -7.83496 v 7.589448 h -2.156213 z m -15.563168,0 h 13.481671 v 1.814636 H 18.740226 V 72.379166 H 16.573339 V 58.257029 h -5.657391 z" />
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

@ -0,0 +1,259 @@
/* 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,
/* for sphinx_immaterial theme: */
div.nbinput.container div.prompt pre > code {
color: #307FC1;
}
/* output prompt */
div.nboutput.container div.prompt pre,
/* for sphinx_immaterial theme: */
div.nboutput.container div.prompt pre > code {
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 copy button on prompts for 'sphinx_copybutton' extension ... */
.prompt .copybtn,
/* ... and 'sphinx_immaterial' theme */
.prompt .md-clipboard.md-icon {
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);
}

@ -0,0 +1,31 @@
.nbsphinx-gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 5px;
margin-top: 1em;
margin-bottom: 1em;
}
.nbsphinx-gallery > a {
padding: 5px;
border: 1px dotted currentColor;
border-radius: 2px;
text-align: center;
}
.nbsphinx-gallery > a:hover {
border-style: solid;
}
.nbsphinx-gallery img {
max-width: 100%;
max-height: 100%;
}
.nbsphinx-gallery > a > div:first-child {
display: flex;
align-items: start;
justify-content: center;
height: 120px;
margin-bottom: 5px;
}

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<style>
svg { fill: #ccc; }
@media (prefers-color-scheme: dark) {
svg { fill: #999; }
}
</style>
<path d="M 65.365328,74.550032 H 67.52154 V 88.67217 h 7.760238 v 1.814634 h -9.91645 z m -6.447298,0 h 2.156214 V 90.486804 H 58.91803 Z m -9.955303,2.124192 -2.924765,7.931025 h 5.860208 z m -1.216872,-2.124192 h 2.444423 l 6.073692,15.936772 h -2.241606 l -1.451709,-4.088268 h -7.183824 l -1.451707,4.088268 H 41.66149 Z m -21.264514,0 h 2.903417 L 36.45116,87.882271 V 74.550032 h 2.092171 V 90.486804 H 35.639914 L 28.573506,77.154569 v 13.332235 h -2.092165 z m 53.454813,-9.781663 v 5.838858 h 3.458479 q 1.739919,0 2.572513,-0.71518 0.843272,-0.725854 0.843272,-2.209586 0,-1.494406 -0.843272,-2.198912 -0.832594,-0.71518 -2.572513,-0.71518 z m 0,-6.554037 v 4.803447 h 3.191622 q 1.579803,0 2.348355,-0.587087 0.779225,-0.597763 0.779225,-1.814636 0,-1.206199 -0.779225,-1.803962 -0.768552,-0.597762 -2.348355,-0.597762 z m -2.156212,-1.771939 h 5.507949 q 2.465772,0 3.800065,1.024736 1.334288,1.024735 1.334288,2.914092 0,1.462383 -0.683157,2.327003 -0.683157,0.864621 -2.006771,1.078107 1.590471,0.341579 2.46577,1.43036 0.885967,1.078107 0.885967,2.700605 0,2.134866 -1.451708,3.298368 -1.451709,1.163502 -4.130963,1.163502 h -5.72144 z m -18.86154,0 h 3.212969 l 4.066921,10.845118 4.088269,-10.845118 H 73.49953 V 72.379166 H 71.396692 V 58.385121 L 67.287075,69.315634 H 65.120183 L 61.010567,58.385121 v 13.994045 h -2.092165 z m -16.246333,0 h 2.166892 v 9.681616 q 0,2.561839 0.928668,3.693318 0.928667,1.120804 3.010159,1.120804 2.070819,0 2.999486,-1.120804 0.928667,-1.131479 0.928667,-3.693318 v -9.681616 h 2.166886 v 9.948474 q 0,3.116904 -1.547776,4.70738 -1.537104,1.590474 -4.547263,1.590474 -3.020833,0 -4.568615,-1.590474 -1.537104,-1.590476 -1.537104,-4.70738 z m -16.192953,0 h 2.156213 v 6.532689 h 7.83496 v -6.532689 h 2.156213 v 15.936773 h -2.156213 v -7.589448 h -7.83496 v 7.589448 h -2.156213 z m -15.563168,0 h 13.481671 v 1.814636 H 18.740226 V 72.379166 H 16.573339 V 58.257029 H 10.915948 Z M 65.497127,39.809967 q -2.34835,0 -3.736011,1.750588 -1.376987,1.750588 -1.376987,4.771419 0,3.010157 1.376987,4.760745 1.387661,1.750588 3.736011,1.750588 2.348349,0 3.714662,-1.750588 1.376986,-1.750588 1.376986,-4.760745 0,-3.020831 -1.376986,-4.771419 -1.366313,-1.750588 -3.714662,-1.750588 z m 0,-1.750588 q 3.351735,0 5.358506,2.252281 2.006772,2.241606 2.006772,6.020314 0,3.768034 -2.006772,6.020315 -2.006771,2.241606 -5.358506,2.241606 -3.36241,0 -5.379856,-2.241606 -2.006771,-2.241607 -2.006771,-6.020315 0,-3.778708 2.006771,-6.020314 2.017446,-2.252281 5.379856,-2.252281 z m -22.821688,0.288206 h 2.903414 L 52.64525,51.679807 V 38.347585 h 2.092166 V 54.28434 H 51.834001 L 44.767605,40.952119 V 54.28434 h -2.092166 z" />
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

@ -0,0 +1,83 @@
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #f8f8f8; }
.highlight .c { color: #8f5902; font-style: italic } /* Comment */
.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */
.highlight .g { color: #000000 } /* Generic */
.highlight .k { color: #204a87; font-weight: bold } /* Keyword */
.highlight .l { color: #000000 } /* Literal */
.highlight .n { color: #000000 } /* Name */
.highlight .o { color: #ce5c00; font-weight: bold } /* Operator */
.highlight .x { color: #000000 } /* Other */
.highlight .p { color: #000000; font-weight: bold } /* Punctuation */
.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */
.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #8f5902; font-style: italic } /* Comment.Preproc */
.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */
.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */
.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */
.highlight .gd { color: #a40000 } /* Generic.Deleted */
.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */
.highlight .gr { color: #ef2929 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #00A000 } /* Generic.Inserted */
.highlight .go { color: #000000; font-style: italic } /* Generic.Output */
.highlight .gp { color: #8f5902 } /* Generic.Prompt */
.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */
.highlight .kc { color: #204a87; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #204a87; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #204a87; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #204a87; font-weight: bold } /* Keyword.Pseudo */
.highlight .kr { color: #204a87; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #204a87; font-weight: bold } /* Keyword.Type */
.highlight .ld { color: #000000 } /* Literal.Date */
.highlight .m { color: #0000cf; font-weight: bold } /* Literal.Number */
.highlight .s { color: #4e9a06 } /* Literal.String */
.highlight .na { color: #c4a000 } /* Name.Attribute */
.highlight .nb { color: #204a87 } /* Name.Builtin */
.highlight .nc { color: #000000 } /* Name.Class */
.highlight .no { color: #000000 } /* Name.Constant */
.highlight .nd { color: #5c35cc; font-weight: bold } /* Name.Decorator */
.highlight .ni { color: #ce5c00 } /* Name.Entity */
.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #000000 } /* Name.Function */
.highlight .nl { color: #f57900 } /* Name.Label */
.highlight .nn { color: #000000 } /* Name.Namespace */
.highlight .nx { color: #000000 } /* Name.Other */
.highlight .py { color: #000000 } /* Name.Property */
.highlight .nt { color: #204a87; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #000000 } /* Name.Variable */
.highlight .ow { color: #204a87; font-weight: bold } /* Operator.Word */
.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */
.highlight .w { color: #f8f8f8 } /* Text.Whitespace */
.highlight .mb { color: #0000cf; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000cf; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000cf; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000cf; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000cf; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */
.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */
.highlight .sc { color: #4e9a06 } /* Literal.String.Char */
.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */
.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */
.highlight .se { color: #4e9a06 } /* Literal.String.Escape */
.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */
.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */
.highlight .sx { color: #4e9a06 } /* Literal.String.Other */
.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */
.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */
.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */
.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #000000 } /* Name.Function.Magic */
.highlight .vc { color: #000000 } /* Name.Variable.Class */
.highlight .vg { color: #000000 } /* Name.Variable.Global */
.highlight .vi { color: #000000 } /* Name.Variable.Instance */
.highlight .vm { color: #000000 } /* Name.Variable.Magic */
.highlight .il { color: #0000cf; font-weight: bold } /* Literal.Number.Integer.Long */

@ -0,0 +1,574 @@
/*
* searchtools.js
* ~~~~~~~~~~~~~~~~
*
* Sphinx JavaScript utilities for the full-text search.
*
* :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
"use strict";
/**
* Simple result scoring code.
*/
if (typeof Scorer === "undefined") {
var Scorer = {
// Implement the following function to further tweak the score for each result
// The function takes a result array [docname, title, anchor, descr, score, filename]
// and returns the new score.
/*
score: result => {
const [docname, title, anchor, descr, score, filename] = result
return score
},
*/
// query matches the full name of an object
objNameMatch: 11,
// or matches in the last dotted part of the object name
objPartialMatch: 6,
// Additive scores depending on the priority of the object
objPrio: {
0: 15, // used to be importantResults
1: 5, // used to be objectResults
2: -5, // used to be unimportantResults
},
// Used when the priority is not in the mapping.
objPrioDefault: 0,
// query found in title
title: 15,
partialTitle: 7,
// query found in terms
term: 5,
partialTerm: 2,
};
}
const _removeChildren = (element) => {
while (element && element.lastChild) element.removeChild(element.lastChild);
};
/**
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
*/
const _escapeRegExp = (string) =>
string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
const _displayItem = (item, searchTerms, highlightTerms) => {
const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX;
const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY;
const contentRoot = document.documentElement.dataset.content_root;
const [docName, title, anchor, descr, score, _filename] = item;
let listItem = document.createElement("li");
let requestUrl;
let linkUrl;
if (docBuilder === "dirhtml") {
// dirhtml builder
let dirname = docName + "/";
if (dirname.match(/\/index\/$/))
dirname = dirname.substring(0, dirname.length - 6);
else if (dirname === "index/") dirname = "";
requestUrl = contentRoot + dirname;
linkUrl = requestUrl;
} else {
// normal html builders
requestUrl = contentRoot + docName + docFileSuffix;
linkUrl = docName + docLinkSuffix;
}
let linkEl = listItem.appendChild(document.createElement("a"));
linkEl.href = linkUrl + anchor;
linkEl.dataset.score = score;
linkEl.innerHTML = title;
if (descr) {
listItem.appendChild(document.createElement("span")).innerHTML =
" (" + descr + ")";
// highlight search terms in the description
if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted"));
}
else if (showSearchSummary)
fetch(requestUrl)
.then((responseData) => responseData.text())
.then((data) => {
if (data)
listItem.appendChild(
Search.makeSearchSummary(data, searchTerms)
);
// highlight search terms in the summary
if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted"));
});
Search.output.appendChild(listItem);
};
const _finishSearch = (resultCount) => {
Search.stopPulse();
Search.title.innerText = _("Search Results");
if (!resultCount)
Search.status.innerText = Documentation.gettext(
"Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
);
else
Search.status.innerText = _(
`Search finished, found ${resultCount} page(s) matching the search query.`
);
};
const _displayNextItem = (
results,
resultCount,
searchTerms,
highlightTerms,
) => {
// results left, load the summary and display it
// this is intended to be dynamic (don't sub resultsCount)
if (results.length) {
_displayItem(results.pop(), searchTerms, highlightTerms);
setTimeout(
() => _displayNextItem(results, resultCount, searchTerms, highlightTerms),
5
);
}
// search finished, update title and status message
else _finishSearch(resultCount);
};
/**
* Default splitQuery function. Can be overridden in ``sphinx.search`` with a
* custom function per language.
*
* The regular expression works by splitting the string on consecutive characters
* that are not Unicode letters, numbers, underscores, or emoji characters.
* This is the same as ``\W+`` in Python, preserving the surrogate pair area.
*/
if (typeof splitQuery === "undefined") {
var splitQuery = (query) => query
.split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu)
.filter(term => term) // remove remaining empty strings
}
/**
* Search Module
*/
const Search = {
_index: null,
_queued_query: null,
_pulse_status: -1,
htmlToText: (htmlString) => {
const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html');
htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() });
const docContent = htmlElement.querySelector('[role="main"]');
if (docContent !== undefined) return docContent.textContent;
console.warn(
"Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template."
);
return "";
},
init: () => {
const query = new URLSearchParams(window.location.search).get("q");
document
.querySelectorAll('input[name="q"]')
.forEach((el) => (el.value = query));
if (query) Search.performSearch(query);
},
loadIndex: (url) =>
(document.body.appendChild(document.createElement("script")).src = url),
setIndex: (index) => {
Search._index = index;
if (Search._queued_query !== null) {
const query = Search._queued_query;
Search._queued_query = null;
Search.query(query);
}
},
hasIndex: () => Search._index !== null,
deferQuery: (query) => (Search._queued_query = query),
stopPulse: () => (Search._pulse_status = -1),
startPulse: () => {
if (Search._pulse_status >= 0) return;
const pulse = () => {
Search._pulse_status = (Search._pulse_status + 1) % 4;
Search.dots.innerText = ".".repeat(Search._pulse_status);
if (Search._pulse_status >= 0) window.setTimeout(pulse, 500);
};
pulse();
},
/**
* perform a search for something (or wait until index is loaded)
*/
performSearch: (query) => {
// create the required interface elements
const searchText = document.createElement("h2");
searchText.textContent = _("Searching");
const searchSummary = document.createElement("p");
searchSummary.classList.add("search-summary");
searchSummary.innerText = "";
const searchList = document.createElement("ul");
searchList.classList.add("search");
const out = document.getElementById("search-results");
Search.title = out.appendChild(searchText);
Search.dots = Search.title.appendChild(document.createElement("span"));
Search.status = out.appendChild(searchSummary);
Search.output = out.appendChild(searchList);
const searchProgress = document.getElementById("search-progress");
// Some themes don't use the search progress node
if (searchProgress) {
searchProgress.innerText = _("Preparing search...");
}
Search.startPulse();
// index already loaded, the browser was quick!
if (Search.hasIndex()) Search.query(query);
else Search.deferQuery(query);
},
/**
* execute search (requires search index to be loaded)
*/
query: (query) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const titles = Search._index.titles;
const allTitles = Search._index.alltitles;
const indexEntries = Search._index.indexentries;
// stem the search terms and add them to the correct list
const stemmer = new Stemmer();
const searchTerms = new Set();
const excludedTerms = new Set();
const highlightTerms = new Set();
const objectTerms = new Set(splitQuery(query.toLowerCase().trim()));
splitQuery(query.trim()).forEach((queryTerm) => {
const queryTermLower = queryTerm.toLowerCase();
// maybe skip this "word"
// stopwords array is from language_data.js
if (
stopwords.indexOf(queryTermLower) !== -1 ||
queryTerm.match(/^\d+$/)
)
return;
// stem the word
let word = stemmer.stemWord(queryTermLower);
// select the correct list
if (word[0] === "-") excludedTerms.add(word.substr(1));
else {
searchTerms.add(word);
highlightTerms.add(queryTermLower);
}
});
if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js
localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" "))
}
// console.debug("SEARCH: searching for:");
// console.info("required: ", [...searchTerms]);
// console.info("excluded: ", [...excludedTerms]);
// array of [docname, title, anchor, descr, score, filename]
let results = [];
_removeChildren(document.getElementById("search-progress"));
const queryLower = query.toLowerCase();
for (const [title, foundTitles] of Object.entries(allTitles)) {
if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) {
for (const [file, id] of foundTitles) {
let score = Math.round(100 * queryLower.length / title.length)
results.push([
docNames[file],
titles[file] !== title ? `${titles[file]} > ${title}` : title,
id !== null ? "#" + id : "",
null,
score,
filenames[file],
]);
}
}
}
// search for explicit entries in index directives
for (const [entry, foundEntries] of Object.entries(indexEntries)) {
if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) {
for (const [file, id] of foundEntries) {
let score = Math.round(100 * queryLower.length / entry.length)
results.push([
docNames[file],
titles[file],
id ? "#" + id : "",
null,
score,
filenames[file],
]);
}
}
}
// lookup as object
objectTerms.forEach((term) =>
results.push(...Search.performObjectSearch(term, objectTerms))
);
// lookup as search terms in fulltext
results.push(...Search.performTermsSearch(searchTerms, excludedTerms));
// let the scorer override scores with a custom scoring function
if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item)));
// now sort the results by score (in opposite order of appearance, since the
// display function below uses pop() to retrieve items) and then
// alphabetically
results.sort((a, b) => {
const leftScore = a[4];
const rightScore = b[4];
if (leftScore === rightScore) {
// same score: sort alphabetically
const leftTitle = a[1].toLowerCase();
const rightTitle = b[1].toLowerCase();
if (leftTitle === rightTitle) return 0;
return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
}
return leftScore > rightScore ? 1 : -1;
});
// remove duplicate search results
// note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
let seen = new Set();
results = results.reverse().reduce((acc, result) => {
let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(',');
if (!seen.has(resultStr)) {
acc.push(result);
seen.add(resultStr);
}
return acc;
}, []);
results = results.reverse();
// for debugging
//Search.lastresults = results.slice(); // a copy
// console.info("search results:", Search.lastresults);
// print the results
_displayNextItem(results, results.length, searchTerms, highlightTerms);
},
/**
* search for object names
*/
performObjectSearch: (object, objectTerms) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const objects = Search._index.objects;
const objNames = Search._index.objnames;
const titles = Search._index.titles;
const results = [];
const objectSearchCallback = (prefix, match) => {
const name = match[4]
const fullname = (prefix ? prefix + "." : "") + name;
const fullnameLower = fullname.toLowerCase();
if (fullnameLower.indexOf(object) < 0) return;
let score = 0;
const parts = fullnameLower.split(".");
// check for different match types: exact matches of full name or
// "last name" (i.e. last dotted part)
if (fullnameLower === object || parts.slice(-1)[0] === object)
score += Scorer.objNameMatch;
else if (parts.slice(-1)[0].indexOf(object) > -1)
score += Scorer.objPartialMatch; // matches in last name
const objName = objNames[match[1]][2];
const title = titles[match[0]];
// If more than one term searched for, we require other words to be
// found in the name/title/description
const otherTerms = new Set(objectTerms);
otherTerms.delete(object);
if (otherTerms.size > 0) {
const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase();
if (
[...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0)
)
return;
}
let anchor = match[3];
if (anchor === "") anchor = fullname;
else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname;
const descr = objName + _(", in ") + title;
// add custom score for some objects according to scorer
if (Scorer.objPrio.hasOwnProperty(match[2]))
score += Scorer.objPrio[match[2]];
else score += Scorer.objPrioDefault;
results.push([
docNames[match[0]],
fullname,
"#" + anchor,
descr,
score,
filenames[match[0]],
]);
};
Object.keys(objects).forEach((prefix) =>
objects[prefix].forEach((array) =>
objectSearchCallback(prefix, array)
)
);
return results;
},
/**
* search for full-text terms in the index
*/
performTermsSearch: (searchTerms, excludedTerms) => {
// prepare search
const terms = Search._index.terms;
const titleTerms = Search._index.titleterms;
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const titles = Search._index.titles;
const scoreMap = new Map();
const fileMap = new Map();
// perform the search on the required terms
searchTerms.forEach((word) => {
const files = [];
const arr = [
{ files: terms[word], score: Scorer.term },
{ files: titleTerms[word], score: Scorer.title },
];
// add support for partial matches
if (word.length > 2) {
const escapedWord = _escapeRegExp(word);
Object.keys(terms).forEach((term) => {
if (term.match(escapedWord) && !terms[word])
arr.push({ files: terms[term], score: Scorer.partialTerm });
});
Object.keys(titleTerms).forEach((term) => {
if (term.match(escapedWord) && !titleTerms[word])
arr.push({ files: titleTerms[word], score: Scorer.partialTitle });
});
}
// no match but word was a required one
if (arr.every((record) => record.files === undefined)) return;
// found search word in contents
arr.forEach((record) => {
if (record.files === undefined) return;
let recordFiles = record.files;
if (recordFiles.length === undefined) recordFiles = [recordFiles];
files.push(...recordFiles);
// set score for the word in each file
recordFiles.forEach((file) => {
if (!scoreMap.has(file)) scoreMap.set(file, {});
scoreMap.get(file)[word] = record.score;
});
});
// create the mapping
files.forEach((file) => {
if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1)
fileMap.get(file).push(word);
else fileMap.set(file, [word]);
});
});
// now check if the files don't contain excluded terms
const results = [];
for (const [file, wordList] of fileMap) {
// check if all requirements are matched
// as search terms with length < 3 are discarded
const filteredTermCount = [...searchTerms].filter(
(term) => term.length > 2
).length;
if (
wordList.length !== searchTerms.size &&
wordList.length !== filteredTermCount
)
continue;
// ensure that none of the excluded terms is in the search result
if (
[...excludedTerms].some(
(term) =>
terms[term] === file ||
titleTerms[term] === file ||
(terms[term] || []).includes(file) ||
(titleTerms[term] || []).includes(file)
)
)
break;
// select one (max) score for the file.
const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w]));
// add result to the result list
results.push([
docNames[file],
titles[file],
"",
null,
score,
filenames[file],
]);
}
return results;
},
/**
* helper function to return a node containing the
* search summary for a given text. keywords is a list
* of stemmed words.
*/
makeSearchSummary: (htmlText, keywords) => {
const text = Search.htmlToText(htmlText);
if (text === "") return null;
const textLower = text.toLowerCase();
const actualStartPosition = [...keywords]
.map((k) => textLower.indexOf(k.toLowerCase()))
.filter((i) => i > -1)
.slice(-1)[0];
const startWithContext = Math.max(actualStartPosition - 120, 0);
const top = startWithContext === 0 ? "" : "...";
const tail = startWithContext + 240 < text.length ? "..." : "";
let summary = document.createElement("p");
summary.classList.add("context");
summary.textContent = top + text.substr(startWithContext, 240).trim() + tail;
return summary;
},
};
_ready(Search.init);

@ -0,0 +1,18 @@
var initTriggerNavBar=()=>{if($(window).width()<768){$("#navbar-toggler").trigger("click")}}
var scrollToActive=()=>{var navbar=document.getElementById('site-navigation')
var active_pages=navbar.querySelectorAll(".active")
var active_page=active_pages[active_pages.length-1]
if(active_page!==undefined&&active_page.offsetTop>($(window).height()*.5)){navbar.scrollTop=active_page.offsetTop-($(window).height()*.2)}}
var sbRunWhenDOMLoaded=cb=>{if(document.readyState!='loading'){cb()}else if(document.addEventListener){document.addEventListener('DOMContentLoaded',cb)}else{document.attachEvent('onreadystatechange',function(){if(document.readyState=='complete')cb()})}}
function toggleFullScreen(){var navToggler=$("#navbar-toggler");if(!document.fullscreenElement){document.documentElement.requestFullscreen();if(!navToggler.hasClass("collapsed")){navToggler.click();}}else{if(document.exitFullscreen){document.exitFullscreen();if(navToggler.hasClass("collapsed")){navToggler.click();}}}}
var initTooltips=()=>{$(document).ready(function(){$('[data-toggle="tooltip"]').tooltip();});}
var initTocHide=()=>{var scrollTimeout;var throttle=200;var tocHeight=$("#bd-toc-nav").outerHeight(true)+$(".bd-toc").outerHeight(true);var hideTocAfter=tocHeight+200;var checkTocScroll=function(){var margin_content=$(".margin, .tag_margin, .full-width, .full_width, .tag_full-width, .tag_full_width, .sidebar, .tag_sidebar, .popout, .tag_popout");margin_content.each((index,item)=>{var topOffset=$(item).offset().top-$(window).scrollTop();var bottomOffset=topOffset+$(item).outerHeight(true);var topOverlaps=((topOffset>=0)&&(topOffset<hideTocAfter));var bottomOverlaps=((bottomOffset>=0)&&(bottomOffset<hideTocAfter));var removeToc=(topOverlaps||bottomOverlaps);if(removeToc&&window.pageYOffset>20){$("div.bd-toc").removeClass("show")
return false}else{$("div.bd-toc").addClass("show")};})};var manageScrolledClassOnBody=function(){if(window.scrollY>0){document.body.classList.add("scrolled");}else{document.body.classList.remove("scrolled");}}
$(window).on('scroll',function(){if(!scrollTimeout){scrollTimeout=setTimeout(function(){checkTocScroll();manageScrolledClassOnBody();scrollTimeout=null;},throttle);}});}
var initThebeSBT=()=>{var title=$("div.section h1")[0]
if(!$(title).next().hasClass("thebe-launch-button")){$("<button class='thebe-launch-button'></button>").insertAfter($(title))}
initThebe();}
sbRunWhenDOMLoaded(initTooltips)
sbRunWhenDOMLoaded(initTriggerNavBar)
sbRunWhenDOMLoaded(scrollToActive)
sbRunWhenDOMLoaded(initTocHide)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,154 @@
/* Highlighting utilities for Sphinx HTML documentation. */
"use strict";
const SPHINX_HIGHLIGHT_ENABLED = true
/**
* highlight a given string on a node by wrapping it in
* span elements with the given class name.
*/
const _highlight = (node, addItems, text, className) => {
if (node.nodeType === Node.TEXT_NODE) {
const val = node.nodeValue;
const parent = node.parentNode;
const pos = val.toLowerCase().indexOf(text);
if (
pos >= 0 &&
!parent.classList.contains(className) &&
!parent.classList.contains("nohighlight")
) {
let span;
const closestNode = parent.closest("body, svg, foreignObject");
const isInSVG = closestNode && closestNode.matches("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.classList.add(className);
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
const rest = document.createTextNode(val.substr(pos + text.length));
parent.insertBefore(
span,
parent.insertBefore(
rest,
node.nextSibling
)
);
node.nodeValue = val.substr(0, pos);
/* There may be more occurrences of search term in this node. So call this
* function recursively on the remaining fragment.
*/
_highlight(rest, addItems, text, className);
if (isInSVG) {
const rect = document.createElementNS(
"http://www.w3.org/2000/svg",
"rect"
);
const bbox = parent.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute("class", className);
addItems.push({ parent: parent, target: rect });
}
}
} else if (node.matches && !node.matches("button, select, textarea")) {
node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
}
};
const _highlightText = (thisNode, text, className) => {
let addItems = [];
_highlight(thisNode, addItems, text, className);
addItems.forEach((obj) =>
obj.parent.insertAdjacentElement("beforebegin", obj.target)
);
};
/**
* Small JavaScript module for the documentation.
*/
const SphinxHighlight = {
/**
* highlight the search words provided in localstorage in the text
*/
highlightSearchWords: () => {
if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight
// get and clear terms from localstorage
const url = new URL(window.location);
const highlight =
localStorage.getItem("sphinx_highlight_terms")
|| url.searchParams.get("highlight")
|| "";
localStorage.removeItem("sphinx_highlight_terms")
url.searchParams.delete("highlight");
window.history.replaceState({}, "", url);
// get individual terms from highlight string
const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
if (terms.length === 0) return; // nothing to do
// There should never be more than one element matching "div.body"
const divBody = document.querySelectorAll("div.body");
const body = divBody.length ? divBody[0] : document.querySelector("body");
window.setTimeout(() => {
terms.forEach((term) => _highlightText(body, term, "highlighted"));
}, 10);
const searchBox = document.getElementById("searchbox");
if (searchBox === null) return;
searchBox.appendChild(
document
.createRange()
.createContextualFragment(
'<p class="highlight-link">' +
'<a href="javascript:SphinxHighlight.hideSearchWords()">' +
_("Hide Search Matches") +
"</a></p>"
)
);
},
/**
* helper function to hide the search marks again
*/
hideSearchWords: () => {
document
.querySelectorAll("#searchbox .highlight-link")
.forEach((el) => el.remove());
document
.querySelectorAll("span.highlighted")
.forEach((el) => el.classList.remove("highlighted"));
localStorage.removeItem("sphinx_highlight_terms")
},
initEscapeListener: () => {
// only install a listener if it is really needed
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return;
document.addEventListener("keydown", (event) => {
// bail for input elements
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
// bail with special keys
if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return;
if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) {
SphinxHighlight.hideSearchWords();
event.preventDefault();
}
});
},
};
_ready(() => {
/* Do not call highlightSearchWords() when we are on the search page.
* It will highlight words from the *previous* search query.
*/
if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords();
SphinxHighlight.initEscapeListener();
});

@ -0,0 +1,34 @@
Font Awesome Free License
-------------------------
Font Awesome Free is free, open source, and GPL friendly. You can use it for
commercial projects, open source projects, or really almost whatever you want.
Full Font Awesome Free license: https://fontawesome.com/license/free.
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
In the Font Awesome Free download, the CC BY 4.0 license applies to all icons
packaged as SVG and JS file types.
# Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL)
In the Font Awesome Free download, the SIL OFL license applies to all icons
packaged as web and desktop font files.
# Code: MIT License (https://opensource.org/licenses/MIT)
In the Font Awesome Free download, the MIT license applies to all non-font and
non-icon files.
# Attribution
Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
Awesome Free files already contain embedded comments with sufficient
attribution, so you shouldn't need to do anything additional when using these
files normally.
We've kept attribution comments terse, so we ask that you do not actively work
to remove them from files, especially code. They're a great way for folks to
learn about Font Awesome.
# Brand Icons
All brand icons are trademarks of their respective owners. The use of these
trademarks does not indicate endorsement of the trademark holder by Font
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
to represent the company, product, or service to which they refer.**

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 699 KiB

@ -0,0 +1,803 @@
<?xml version="1.0" standalone="no"?>
<!--
Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
-->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<metadata>
Created by FontForge 20190801 at Mon Mar 23 10:45:51 2020
By Robert Madole
Copyright (c) Font Awesome
</metadata>
<defs>
<font id="FontAwesome5Free-Regular" horiz-adv-x="512" >
<font-face
font-family="Font Awesome 5 Free Regular"
font-weight="400"
font-stretch="normal"
units-per-em="512"
panose-1="2 0 5 3 0 0 0 0 0 0"
ascent="448"
descent="-64"
bbox="-0.0663408 -64.0662 640.01 448.1"
underline-thickness="25"
underline-position="-50"
unicode-range="U+0020-F5C8"
/>
<missing-glyph />
<glyph glyph-name="heart" unicode="&#xf004;"
d="M458.4 383.7c75.2998 -63.4004 64.0996 -166.601 10.5996 -221.3l-175.4 -178.7c-10 -10.2002 -23.2998 -15.7998 -37.5996 -15.7998c-14.2002 0 -27.5996 5.69922 -37.5996 15.8994l-175.4 178.7c-53.5996 54.7002 -64.5996 157.9 10.5996 221.2
c57.8008 48.7002 147.101 41.2998 202.4 -15c55.2998 56.2998 144.6 63.5996 202.4 15zM434.8 196.2c36.2002 36.8994 43.7998 107.7 -7.2998 150.8c-38.7002 32.5996 -98.7002 27.9004 -136.5 -10.5996l-35 -35.7002l-35 35.7002
c-37.5996 38.2998 -97.5996 43.1992 -136.5 10.5c-51.2002 -43.1006 -43.7998 -113.5 -7.2998 -150.7l175.399 -178.7c2.40039 -2.40039 4.40039 -2.40039 6.80078 0z" />
<glyph glyph-name="star" unicode="&#xf005;" horiz-adv-x="576"
d="M528.1 276.5c26.2002 -3.7998 36.7002 -36.0996 17.7002 -54.5996l-105.7 -103l25 -145.5c4.5 -26.3008 -23.1992 -45.9004 -46.3994 -33.7002l-130.7 68.7002l-130.7 -68.7002c-23.2002 -12.2998 -50.8994 7.39941 -46.3994 33.7002l25 145.5l-105.7 103
c-19 18.5 -8.5 50.7998 17.7002 54.5996l146.1 21.2998l65.2998 132.4c11.7998 23.8994 45.7002 23.5996 57.4004 0l65.2998 -132.4zM388.6 135.7l100.601 98l-139 20.2002l-62.2002 126l-62.2002 -126l-139 -20.2002l100.601 -98l-23.7002 -138.4l124.3 65.2998
l124.3 -65.2998z" />
<glyph glyph-name="user" unicode="&#xf007;" horiz-adv-x="448"
d="M313.6 144c74.2002 0 134.4 -60.2002 134.4 -134.4v-25.5996c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v25.5996c0 74.2002 60.2002 134.4 134.4 134.4c28.7998 0 42.5 -16 89.5996 -16s60.9004 16 89.5996 16zM400 -16v25.5996
c0 47.6006 -38.7998 86.4004 -86.4004 86.4004c-14.6992 0 -37.8994 -16 -89.5996 -16c-51.2998 0 -75 16 -89.5996 16c-47.6006 0 -86.4004 -38.7998 -86.4004 -86.4004v-25.5996h352zM224 160c-79.5 0 -144 64.5 -144 144s64.5 144 144 144s144 -64.5 144 -144
s-64.5 -144 -144 -144zM224 400c-52.9004 0 -96 -43.0996 -96 -96s43.0996 -96 96 -96s96 43.0996 96 96s-43.0996 96 -96 96z" />
<glyph glyph-name="clock" unicode="&#xf017;"
d="M256 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM256 -8c110.5 0 200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200s89.5 -200 200 -200zM317.8 96.4004l-84.8994 61.6992
c-3.10059 2.30078 -4.90039 5.90039 -4.90039 9.7002v164.2c0 6.59961 5.40039 12 12 12h32c6.59961 0 12 -5.40039 12 -12v-141.7l66.7998 -48.5996c5.40039 -3.90039 6.5 -11.4004 2.60059 -16.7998l-18.8008 -25.9004c-3.89941 -5.2998 -11.3994 -6.5 -16.7998 -2.59961z
" />
<glyph glyph-name="list-alt" unicode="&#xf022;"
d="M464 416c26.5098 0 48 -21.4902 48 -48v-352c0 -26.5098 -21.4902 -48 -48 -48h-416c-26.5098 0 -48 21.4902 -48 48v352c0 26.5098 21.4902 48 48 48h416zM458 16c3.31152 0 6 2.68848 6 6v340c0 3.31152 -2.68848 6 -6 6h-404c-3.31152 0 -6 -2.68848 -6 -6v-340
c0 -3.31152 2.68848 -6 6 -6h404zM416 108v-24c0 -6.62695 -5.37305 -12 -12 -12h-200c-6.62695 0 -12 5.37305 -12 12v24c0 6.62695 5.37305 12 12 12h200c6.62695 0 12 -5.37305 12 -12zM416 204v-24c0 -6.62695 -5.37305 -12 -12 -12h-200c-6.62695 0 -12 5.37305 -12 12
v24c0 6.62695 5.37305 12 12 12h200c6.62695 0 12 -5.37305 12 -12zM416 300v-24c0 -6.62695 -5.37305 -12 -12 -12h-200c-6.62695 0 -12 5.37305 -12 12v24c0 6.62695 5.37305 12 12 12h200c6.62695 0 12 -5.37305 12 -12zM164 288c0 -19.8818 -16.1182 -36 -36 -36
s-36 16.1182 -36 36s16.1182 36 36 36s36 -16.1182 36 -36zM164 192c0 -19.8818 -16.1182 -36 -36 -36s-36 16.1182 -36 36s16.1182 36 36 36s36 -16.1182 36 -36zM164 96c0 -19.8818 -16.1182 -36 -36 -36s-36 16.1182 -36 36s16.1182 36 36 36s36 -16.1182 36 -36z" />
<glyph glyph-name="flag" unicode="&#xf024;"
d="M336.174 368c35.4668 0 73.0195 12.6914 108.922 28.1797c31.6406 13.6514 66.9043 -9.65723 66.9043 -44.1162v-239.919c0 -16.1953 -8.1543 -31.3057 -21.7129 -40.1631c-26.5762 -17.3643 -70.0693 -39.9814 -128.548 -39.9814c-68.6084 0 -112.781 32 -161.913 32
c-56.5674 0 -89.957 -11.2803 -127.826 -28.5566v-83.4434c0 -8.83691 -7.16309 -16 -16 -16h-16c-8.83691 0 -16 7.16309 -16 16v406.438c-14.3428 8.2998 -24 23.7979 -24 41.5615c0 27.5693 23.2422 49.71 51.2012 47.8965
c22.9658 -1.49023 41.8662 -19.4717 44.4805 -42.3379c0.177734 -1.52441 0.321289 -4.00781 0.321289 -5.54199c0 -4.30176 -1.10352 -11.1035 -2.46289 -15.1846c22.418 8.68555 49.4199 15.168 80.7207 15.168c68.6084 0 112.781 -32 161.913 -32zM464 112v240
c-31.5059 -14.6338 -84.5547 -32 -127.826 -32c-59.9111 0 -101.968 32 -161.913 32c-41.4365 0 -80.4766 -16.5879 -102.261 -32v-232c31.4473 14.5967 84.4648 24 127.826 24c59.9111 0 101.968 -32 161.913 -32c41.4365 0 80.4775 16.5879 102.261 32z" />
<glyph glyph-name="bookmark" unicode="&#xf02e;" horiz-adv-x="384"
d="M336 448c26.5098 0 48 -21.4902 48 -48v-464l-192 112l-192 -112v464c0 26.5098 21.4902 48 48 48h288zM336 19.5703v374.434c0 3.31348 -2.68555 5.99609 -6 5.99609h-276c-3.31152 0 -6 -2.68848 -6 -6v-374.43l144 84z" />
<glyph glyph-name="image" unicode="&#xf03e;"
d="M464 384c26.5098 0 48 -21.4902 48 -48v-288c0 -26.5098 -21.4902 -48 -48 -48h-416c-26.5098 0 -48 21.4902 -48 48v288c0 26.5098 21.4902 48 48 48h416zM458 48c3.31152 0 6 2.68848 6 6v276c0 3.31152 -2.68848 6 -6 6h-404c-3.31152 0 -6 -2.68848 -6 -6v-276
c0 -3.31152 2.68848 -6 6 -6h404zM128 296c22.0908 0 40 -17.9092 40 -40s-17.9092 -40 -40 -40s-40 17.9092 -40 40s17.9092 40 40 40zM96 96v48l39.5137 39.5146c4.6875 4.68652 12.2852 4.68652 16.9717 0l39.5146 -39.5146l119.514 119.515
c4.6875 4.68652 12.2852 4.68652 16.9717 0l87.5146 -87.5146v-80h-320z" />
<glyph glyph-name="edit" unicode="&#xf044;" horiz-adv-x="576"
d="M402.3 103.1l32 32c5 5 13.7002 1.5 13.7002 -5.69922v-145.4c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h273.5c7.09961 0 10.7002 -8.59961 5.7002 -13.7002l-32 -32c-1.5 -1.5 -3.5 -2.2998 -5.7002 -2.2998h-241.5v-352h352
v113.5c0 2.09961 0.799805 4.09961 2.2998 5.59961zM558.9 304.9l-262.601 -262.601l-90.3994 -10c-26.2002 -2.89941 -48.5 19.2002 -45.6006 45.6006l10 90.3994l262.601 262.601c22.8994 22.8994 59.8994 22.8994 82.6992 0l43.2002 -43.2002
c22.9004 -22.9004 22.9004 -60 0.100586 -82.7998zM460.1 274l-58.0996 58.0996l-185.8 -185.899l-7.2998 -65.2998l65.2998 7.2998zM524.9 353.7l-43.2002 43.2002c-4.10059 4.09961 -10.7998 4.09961 -14.7998 0l-30.9004 -30.9004l58.0996 -58.0996l30.9004 30.8994
c4 4.2002 4 10.7998 -0.0996094 14.9004z" />
<glyph glyph-name="times-circle" unicode="&#xf057;"
d="M256 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM256 -8c110.5 0 200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200s89.5 -200 200 -200zM357.8 254.2l-62.2002 -62.2002l62.2002 -62.2002
c4.7002 -4.7002 4.7002 -12.2998 0 -17l-22.5996 -22.5996c-4.7002 -4.7002 -12.2998 -4.7002 -17 0l-62.2002 62.2002l-62.2002 -62.2002c-4.7002 -4.7002 -12.2998 -4.7002 -17 0l-22.5996 22.5996c-4.7002 4.7002 -4.7002 12.2998 0 17l62.2002 62.2002l-62.2002 62.2002
c-4.7002 4.7002 -4.7002 12.2998 0 17l22.5996 22.5996c4.7002 4.7002 12.2998 4.7002 17 0l62.2002 -62.2002l62.2002 62.2002c4.7002 4.7002 12.2998 4.7002 17 0l22.5996 -22.5996c4.7002 -4.7002 4.7002 -12.2998 0 -17z" />
<glyph glyph-name="check-circle" unicode="&#xf058;"
d="M256 440c136.967 0 248 -111.033 248 -248s-111.033 -248 -248 -248s-248 111.033 -248 248s111.033 248 248 248zM256 392c-110.549 0 -200 -89.4678 -200 -200c0 -110.549 89.4678 -200 200 -200c110.549 0 200 89.4678 200 200c0 110.549 -89.4678 200 -200 200z
M396.204 261.733c4.66699 -4.70508 4.63672 -12.3037 -0.0673828 -16.9717l-172.589 -171.204c-4.70508 -4.66797 -12.3027 -4.63672 -16.9697 0.0683594l-90.7812 91.5156c-4.66797 4.70605 -4.63672 12.3047 0.0683594 16.9717l22.7188 22.5361
c4.70508 4.66699 12.3027 4.63574 16.9697 -0.0693359l59.792 -60.2773l141.353 140.216c4.70508 4.66797 12.3027 4.6377 16.9697 -0.0673828z" />
<glyph glyph-name="question-circle" unicode="&#xf059;"
d="M256 440c136.957 0 248 -111.083 248 -248c0 -136.997 -111.043 -248 -248 -248s-248 111.003 -248 248c0 136.917 111.043 248 248 248zM256 -8c110.569 0 200 89.4697 200 200c0 110.529 -89.5088 200 -200 200c-110.528 0 -200 -89.5049 -200 -200
c0 -110.569 89.4678 -200 200 -200zM363.244 247.2c0 -67.0518 -72.4209 -68.084 -72.4209 -92.8633v-6.33691c0 -6.62695 -5.37305 -12 -12 -12h-45.6475c-6.62695 0 -12 5.37305 -12 12v8.65918c0 35.7451 27.1006 50.0342 47.5791 61.5156
c17.5615 9.84473 28.3242 16.541 28.3242 29.5791c0 17.2461 -21.999 28.6934 -39.7842 28.6934c-23.1885 0 -33.8936 -10.9775 -48.9424 -29.9697c-4.05664 -5.11914 -11.46 -6.07031 -16.666 -2.12402l-27.8232 21.0986
c-5.10742 3.87207 -6.25098 11.0654 -2.64453 16.3633c23.627 34.6934 53.7217 54.1846 100.575 54.1846c49.0713 0 101.45 -38.3037 101.45 -88.7998zM298 80c0 -23.1592 -18.8408 -42 -42 -42s-42 18.8408 -42 42s18.8408 42 42 42s42 -18.8408 42 -42z" />
<glyph glyph-name="eye" unicode="&#xf06e;" horiz-adv-x="576"
d="M288 304c0.0927734 0 0.244141 0.000976562 0.336914 0.000976562c61.6641 0 111.71 -50.0469 111.71 -111.711c0 -61.6631 -50.0459 -111.71 -111.71 -111.71s-111.71 50.0469 -111.71 111.71c0 8.71289 1.95898 22.5781 4.37305 30.9502
c6.93066 -3.94141 19.0273 -7.18457 27 -7.24023c30.9121 0 56 25.0879 56 56c-0.0556641 7.97266 -3.29883 20.0693 -7.24023 27c8.42383 2.62207 22.4189 4.8623 31.2402 5zM572.52 206.6c1.9209 -3.79883 3.47949 -10.3379 3.47949 -14.5947
s-1.55859 -10.7959 -3.47949 -14.5947c-54.1992 -105.771 -161.59 -177.41 -284.52 -177.41s-230.29 71.5898 -284.52 177.4c-1.9209 3.79883 -3.47949 10.3379 -3.47949 14.5947s1.55859 10.7959 3.47949 14.5947c54.1992 105.771 161.59 177.41 284.52 177.41
s230.29 -71.5898 284.52 -177.4zM288 48c98.6602 0 189.1 55 237.93 144c-48.8398 89 -139.27 144 -237.93 144s-189.09 -55 -237.93 -144c48.8398 -89 139.279 -144 237.93 -144z" />
<glyph glyph-name="eye-slash" unicode="&#xf070;" horiz-adv-x="640"
d="M634 -23c3.31738 -2.65137 6.00977 -8.25098 6.00977 -12.498c0 -3.10449 -1.57715 -7.58984 -3.51953 -10.0117l-10 -12.4902c-2.65234 -3.31152 -8.24707 -6 -12.4902 -6c-3.09961 0 -7.58008 1.57227 -10 3.50977l-598 467.49
c-3.31738 2.65137 -6.00977 8.25098 -6.00977 12.498c0 3.10449 1.57715 7.58984 3.51953 10.0117l10 12.4902c2.65234 3.31152 8.24707 6 12.4902 6c3.09961 0 7.58008 -1.57227 10 -3.50977zM296.79 301.53c6.33496 1.35059 16.7324 2.45801 23.21 2.46973
c60.4805 0 109.36 -47.9102 111.58 -107.85zM343.21 82.46c-6.33496 -1.34375 -16.7334 -2.44629 -23.21 -2.45996c-60.4697 0 -109.35 47.9102 -111.58 107.84zM320 336c-19.8799 0 -39.2803 -2.7998 -58.2197 -7.09961l-46.4102 36.29
c32.9199 11.8096 67.9297 18.8096 104.63 18.8096c122.93 0 230.29 -71.5898 284.57 -177.4c1.91992 -3.79883 3.47949 -10.3379 3.47949 -14.5947s-1.55957 -10.7959 -3.47949 -14.5947c-11.7197 -22.7598 -35.4189 -56.4092 -52.9004 -75.1104l-37.7402 29.5
c14.333 15.0156 34.0449 41.9854 44 60.2002c-48.8398 89 -139.279 144 -237.93 144zM320 48c19.8896 0 39.2803 2.7998 58.2197 7.08984l46.4102 -36.2803c-32.9199 -11.7598 -67.9297 -18.8096 -104.63 -18.8096c-122.92 0 -230.28 71.5898 -284.51 177.4
c-1.9209 3.79883 -3.47949 10.3379 -3.47949 14.5947s1.55859 10.7959 3.47949 14.5947c11.7168 22.7568 35.4111 56.4014 52.8896 75.1006l37.7402 -29.5c-14.3467 -15.0107 -34.0811 -41.9756 -44.0498 -60.1904c48.8496 -89 139.279 -144 237.93 -144z" />
<glyph glyph-name="calendar-alt" unicode="&#xf073;" horiz-adv-x="448"
d="M148 160h-40c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12zM256 172c0 -6.59961 -5.40039 -12 -12 -12h-40c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40
c6.59961 0 12 -5.40039 12 -12v-40zM352 172c0 -6.59961 -5.40039 -12 -12 -12h-40c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40zM256 76c0 -6.59961 -5.40039 -12 -12 -12h-40c-6.59961 0 -12 5.40039 -12 12v40
c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40zM160 76c0 -6.59961 -5.40039 -12 -12 -12h-40c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40zM352 76c0 -6.59961 -5.40039 -12 -12 -12h-40
c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40zM448 336v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h48v52c0 6.59961 5.40039 12 12 12h40
c6.59961 0 12 -5.40039 12 -12v-52h128v52c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-52h48c26.5 0 48 -21.5 48 -48zM400 -10v298h-352v-298c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="comment" unicode="&#xf075;"
d="M256 416c141.4 0 256 -93.0996 256 -208s-114.6 -208 -256 -208c-32.7998 0 -64 5.2002 -92.9004 14.2998c-29.0996 -20.5996 -77.5996 -46.2998 -139.1 -46.2998c-9.59961 0 -18.2998 5.7002 -22.0996 14.5c-3.80078 8.7998 -2 19 4.59961 26
c0.5 0.400391 31.5 33.7998 46.4004 73.2002c-33 35.0996 -52.9004 78.7002 -52.9004 126.3c0 114.9 114.6 208 256 208zM256 48c114.7 0 208 71.7998 208 160s-93.2998 160 -208 160s-208 -71.7998 -208 -160c0 -42.2002 21.7002 -74.0996 39.7998 -93.4004
l20.6006 -21.7998l-10.6006 -28.0996c-5.5 -14.5 -12.5996 -28.1006 -19.8994 -40.2002c23.5996 7.59961 43.1992 18.9004 57.5 29l19.5 13.7998l22.6992 -7.2002c25.3008 -8 51.7002 -12.0996 78.4004 -12.0996z" />
<glyph glyph-name="folder" unicode="&#xf07b;"
d="M464 320c26.5098 0 48 -21.4902 48 -48v-224c0 -26.5098 -21.4902 -48 -48 -48h-416c-26.5098 0 -48 21.4902 -48 48v288c0 26.5098 21.4902 48 48 48h146.74c8.49023 0 16.6299 -3.37012 22.6299 -9.37012l54.6299 -54.6299h192zM464 48v224h-198.62
c-8.49023 0 -16.6299 3.37012 -22.6299 9.37012l-54.6299 54.6299h-140.12v-288h416z" />
<glyph glyph-name="folder-open" unicode="&#xf07c;" horiz-adv-x="576"
d="M527.9 224c37.6992 0 60.6992 -41.5 40.6992 -73.4004l-79.8994 -128c-8.7998 -14.0996 -24.2002 -22.5996 -40.7002 -22.5996h-400c-26.5 0 -48 21.5 -48 48v288c0 26.5 21.5 48 48 48h160l64 -64h160c26.5 0 48 -21.5 48 -48v-48h47.9004zM48 330v-233.4l62.9004 104.2
c8.69922 14.4004 24.2998 23.2002 41.0996 23.2002h280v42c0 3.2998 -2.7002 6 -6 6h-173.9l-64 64h-134.1c-3.2998 0 -6 -2.7002 -6 -6zM448 48l80 128h-378.8l-77.2002 -128h376z" />
<glyph glyph-name="chart-bar" unicode="&#xf080;"
d="M396.8 96c-6.39941 0 -12.7998 6.40039 -12.7998 12.7998v230.4c0 6.39941 6.40039 12.7998 12.7998 12.7998h22.4004c6.39941 0 12.7998 -6.40039 12.7998 -12.7998v-230.4c0 -6.39941 -6.40039 -12.7998 -12.7998 -12.7998h-22.4004zM204.8 96
c-6.39941 0 -12.7998 6.40039 -12.7998 12.7998v198.4c0 6.39941 6.40039 12.7998 12.7998 12.7998h22.4004c6.39941 0 12.7998 -6.40039 12.7998 -12.7998v-198.4c0 -6.39941 -6.40039 -12.7998 -12.7998 -12.7998h-22.4004zM300.8 96
c-6.39941 0 -12.7998 6.40039 -12.7998 12.7998v134.4c0 6.39941 6.40039 12.7998 12.7998 12.7998h22.4004c6.39941 0 12.7998 -6.40039 12.7998 -12.7998v-134.4c0 -6.39941 -6.40039 -12.7998 -12.7998 -12.7998h-22.4004zM496 48c8.83984 0 16 -7.16016 16 -16v-16
c0 -8.83984 -7.16016 -16 -16 -16h-464c-17.6699 0 -32 14.3301 -32 32v336c0 8.83984 7.16016 16 16 16h16c8.83984 0 16 -7.16016 16 -16v-320h448zM108.8 96c-6.39941 0 -12.7998 6.40039 -12.7998 12.7998v70.4004c0 6.39941 6.40039 12.7998 12.7998 12.7998h22.4004
c6.39941 0 12.7998 -6.40039 12.7998 -12.7998v-70.4004c0 -6.39941 -6.40039 -12.7998 -12.7998 -12.7998h-22.4004z" />
<glyph glyph-name="comments" unicode="&#xf086;" horiz-adv-x="576"
d="M532 61.7998c15.2998 -30.7002 37.4004 -54.5 37.7998 -54.7998c6.2998 -6.7002 8 -16.5 4.40039 -25c-3.7002 -8.5 -12 -14 -21.2002 -14c-53.5996 0 -96.7002 20.2998 -125.2 38.7998c-19 -4.39941 -39 -6.7998 -59.7998 -6.7998
c-86.2002 0 -159.9 40.4004 -191.3 97.7998c-9.7002 1.2002 -19.2002 2.7998 -28.4004 4.90039c-28.5 -18.6006 -71.7002 -38.7998 -125.2 -38.7998c-9.19922 0 -17.5996 5.5 -21.1992 14c-3.7002 8.5 -1.90039 18.2998 4.39941 25
c0.400391 0.399414 22.4004 24.1992 37.7002 54.8994c-27.5 27.2002 -44 61.2002 -44 98.2002c0 88.4004 93.0996 160 208 160c86.2998 0 160.3 -40.5 191.8 -98.0996c99.7002 -11.8008 176.2 -77.9004 176.2 -157.9c0 -37.0996 -16.5 -71.0996 -44 -98.2002zM139.2 154.1
l19.7998 -4.5c16 -3.69922 32.5 -5.59961 49 -5.59961c86.7002 0 160 51.2998 160 112s-73.2998 112 -160 112s-160 -51.2998 -160 -112c0 -28.7002 16.2002 -50.5996 29.7002 -64l24.7998 -24.5l-15.5 -31.0996c-2.59961 -5.10059 -5.2998 -10.1006 -8 -14.8008
c14.5996 5.10059 29 12.3008 43.0996 21.4004zM498.3 96c13.5 13.4004 29.7002 35.2998 29.7002 64c0 49.2002 -48.2998 91.5 -112.7 106c0.299805 -3.2998 0.700195 -6.59961 0.700195 -10c0 -80.9004 -78 -147.5 -179.3 -158.3
c29.0996 -29.6006 77.2998 -49.7002 131.3 -49.7002c16.5 0 33 1.90039 49 5.59961l19.9004 4.60059l17.0996 -11.1006c14.0996 -9.09961 28.5 -16.2998 43.0996 -21.3994c-2.69922 4.7002 -5.39941 9.7002 -8 14.7998l-15.5 31.0996z" />
<glyph glyph-name="star-half" unicode="&#xf089;" horiz-adv-x="576"
d="M288 62.7002v-54.2998l-130.7 -68.6006c-23.3994 -12.2998 -50.8994 7.60059 -46.3994 33.7002l25 145.5l-105.7 103c-19 18.5 -8.5 50.7998 17.7002 54.5996l146.1 21.2002l65.2998 132.4c5.90039 11.8994 17.2998 17.7998 28.7002 17.7998v-68.0996l-62.2002 -126
l-139 -20.2002l100.601 -98l-23.7002 -138.4z" />
<glyph glyph-name="lemon" unicode="&#xf094;"
d="M484.112 420.111c28.1221 -28.123 35.9434 -68.0039 19.0215 -97.0547c-23.0576 -39.584 50.1436 -163.384 -82.3311 -295.86c-132.301 -132.298 -256.435 -59.3594 -295.857 -82.3291c-29.0459 -16.917 -68.9219 -9.11426 -97.0576 19.0205
c-28.1221 28.1221 -35.9434 68.0029 -19.0215 97.0547c23.0566 39.5859 -50.1436 163.386 82.3301 295.86c132.308 132.309 256.407 59.3496 295.862 82.332c29.0498 16.9219 68.9307 9.09863 97.0537 -19.0234zM461.707 347.217
c13.5166 23.2031 -27.7578 63.7314 -50.4883 50.4912c-66.6025 -38.7939 -165.646 45.5898 -286.081 -74.8457c-120.444 -120.445 -36.0449 -219.472 -74.8447 -286.08c-13.542 -23.2471 27.8145 -63.6953 50.4932 -50.4883
c66.6006 38.7949 165.636 -45.5996 286.076 74.8428c120.444 120.445 36.0449 219.472 74.8447 286.08zM291.846 338.481c1.37012 -10.96 -6.40332 -20.957 -17.3643 -22.3271c-54.8467 -6.85547 -135.779 -87.7871 -142.636 -142.636
c-1.37305 -10.9883 -11.3984 -18.7334 -22.3262 -17.3643c-10.9609 1.37012 -18.7344 11.3652 -17.3643 22.3262c9.16211 73.2852 104.167 168.215 177.364 177.364c10.9531 1.36816 20.9561 -6.40234 22.3262 -17.3633z" />
<glyph glyph-name="credit-card" unicode="&#xf09d;" horiz-adv-x="576"
d="M527.9 416c26.5996 0 48.0996 -21.5 48.0996 -48v-352c0 -26.5 -21.5 -48 -48.0996 -48h-479.801c-26.5996 0 -48.0996 21.5 -48.0996 48v352c0 26.5 21.5 48 48.0996 48h479.801zM54.0996 368c-3.2998 0 -6 -2.7002 -6 -6v-42h479.801v42c0 3.2998 -2.7002 6 -6 6
h-467.801zM521.9 16c3.2998 0 6 2.7002 6 6v170h-479.801v-170c0 -3.2998 2.7002 -6 6 -6h467.801zM192 116v-40c0 -6.59961 -5.40039 -12 -12 -12h-72c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h72c6.59961 0 12 -5.40039 12 -12zM384 116v-40
c0 -6.59961 -5.40039 -12 -12 -12h-136c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h136c6.59961 0 12 -5.40039 12 -12z" />
<glyph glyph-name="hdd" unicode="&#xf0a0;" horiz-adv-x="576"
d="M567.403 212.358c5.59668 -8.04688 8.59668 -17.6113 8.59668 -27.4121v-136.946c0 -26.5098 -21.4902 -48 -48 -48h-480c-26.5098 0 -48 21.4902 -48 48v136.946c0 8.30957 3.85156 20.5898 8.59668 27.4121l105.08 151.053
c7.90625 11.3652 25.5596 20.5889 39.4033 20.5889h0.000976562h269.838h0.000976562c13.8438 0 31.4971 -9.22363 39.4033 -20.5889zM153.081 336l-77.9131 -112h425.664l-77.9131 112h-269.838zM528 48v128h-480v-128h480zM496 112c0 -17.6729 -14.3271 -32 -32 -32
s-32 14.3271 -32 32s14.3271 32 32 32s32 -14.3271 32 -32zM400 112c0 -17.6729 -14.3271 -32 -32 -32s-32 14.3271 -32 32s14.3271 32 32 32s32 -14.3271 32 -32z" />
<glyph glyph-name="hand-point-right" unicode="&#xf0a4;"
d="M428.8 310.4c45.0996 0 83.2002 -38.1016 83.2002 -83.2002c0 -45.6162 -37.7646 -83.2002 -83.2002 -83.2002h-35.6475c-1.41602 -6.36719 -4.96875 -16.252 -7.92969 -22.0645c2.50586 -22.0059 -3.50293 -44.9775 -15.9844 -62.791
c-1.14062 -52.4863 -37.3984 -91.1445 -99.9404 -91.1445h-21.2988c-60.0635 0 -98.5117 40 -127.2 40h-2.67871c-5.74707 -4.95215 -13.5361 -8 -22.1201 -8h-64c-17.6729 0 -32 12.8936 -32 28.7998v230.4c0 15.9062 14.3271 28.7998 32 28.7998h64.001
c8.58398 0 16.373 -3.04785 22.1201 -8h2.67871c6.96387 0 14.8623 6.19336 30.1816 23.6689l0.128906 0.148438l0.130859 0.145508c8.85645 9.93652 18.1162 20.8398 25.8506 33.2529c18.7051 30.2471 30.3936 78.7842 75.707 78.7842c56.9277 0 92 -35.2861 92 -83.2002
v-0.0839844c0 -6.21777 -0.974609 -16.2148 -2.17578 -22.3154h86.1768zM428.8 192c18.9756 0 35.2002 16.2246 35.2002 35.2002c0 18.7002 -16.7754 35.2002 -35.2002 35.2002h-158.399c0 17.3242 26.3994 35.1992 26.3994 70.3994c0 26.4004 -20.625 35.2002 -44 35.2002
c-8.79395 0 -20.4443 -32.7119 -34.9258 -56.0996c-9.07422 -14.5752 -19.5244 -27.2256 -30.7988 -39.875c-16.1094 -18.374 -33.8359 -36.6328 -59.0752 -39.5967v-176.753c42.79 -3.7627 74.5088 -39.6758 120 -39.6758h21.2988
c40.5244 0 57.124 22.1973 50.6006 61.3252c14.6113 8.00098 24.1514 33.9785 12.9248 53.625c19.3652 18.2246 17.7871 46.3809 4.9502 61.0498h91.0254zM88 64c0 13.2549 -10.7451 24 -24 24s-24 -10.7451 -24 -24s10.7451 -24 24 -24s24 10.7451 24 24z" />
<glyph glyph-name="hand-point-left" unicode="&#xf0a5;"
d="M0 227.2c0 45.0986 38.1006 83.2002 83.2002 83.2002h86.1758c-1.3623 6.91016 -2.17578 14.374 -2.17578 22.3994c0 47.9141 35.0723 83.2002 92 83.2002c45.3135 0 57.002 -48.5371 75.7061 -78.7852c7.73438 -12.4121 16.9951 -23.3154 25.8506 -33.2529
l0.130859 -0.145508l0.128906 -0.148438c15.3213 -17.4746 23.2197 -23.668 30.1836 -23.668h2.67871c5.74707 4.95215 13.5361 8 22.1201 8h64c17.6729 0 32 -12.8936 32 -28.7998v-230.4c0 -15.9062 -14.3271 -28.7998 -32 -28.7998h-64
c-8.58398 0 -16.373 3.04785 -22.1201 8h-2.67871c-28.6885 0 -67.1367 -40 -127.2 -40h-21.2988c-62.542 0 -98.8008 38.6582 -99.9404 91.1445c-12.4814 17.8135 -18.4922 40.7852 -15.9844 62.791c-2.96094 5.8125 -6.51367 15.6973 -7.92969 22.0645h-35.6465
c-45.4355 0 -83.2002 37.584 -83.2002 83.2002zM48 227.2c0 -18.9756 16.2246 -35.2002 35.2002 -35.2002h91.0244c-12.8369 -14.6689 -14.415 -42.8252 4.9502 -61.0498c-11.2256 -19.6465 -1.68652 -45.624 12.9248 -53.625
c-6.52246 -39.1279 10.0771 -61.3252 50.6016 -61.3252h21.2988c45.4912 0 77.21 35.9131 120 39.6768v176.752c-25.2393 2.96289 -42.9658 21.2227 -59.0752 39.5967c-11.2744 12.6494 -21.7246 25.2998 -30.7988 39.875
c-14.4814 23.3877 -26.1318 56.0996 -34.9258 56.0996c-23.375 0 -44 -8.7998 -44 -35.2002c0 -35.2002 26.3994 -53.0752 26.3994 -70.3994h-158.399c-18.4248 0 -35.2002 -16.5 -35.2002 -35.2002zM448 88c-13.2549 0 -24 -10.7451 -24 -24s10.7451 -24 24 -24
s24 10.7451 24 24s-10.7451 24 -24 24z" />
<glyph glyph-name="hand-point-up" unicode="&#xf0a6;" horiz-adv-x="448"
d="M105.6 364.8c0 45.0996 38.1016 83.2002 83.2002 83.2002c45.6162 0 83.2002 -37.7646 83.2002 -83.2002v-35.6465c6.36719 -1.41602 16.252 -4.96875 22.0645 -7.92969c22.0059 2.50684 44.9775 -3.50293 62.791 -15.9844
c52.4863 -1.14062 91.1445 -37.3984 91.1445 -99.9404v-21.2988c0 -60.0635 -40 -98.5117 -40 -127.2v-2.67871c4.95215 -5.74707 8 -13.5361 8 -22.1201v-64c0 -17.6729 -12.8936 -32 -28.7998 -32h-230.4c-15.9062 0 -28.7998 14.3271 -28.7998 32v64
c0 8.58398 3.04785 16.373 8 22.1201v2.67871c0 6.96387 -6.19336 14.8623 -23.6689 30.1816l-0.148438 0.128906l-0.145508 0.130859c-9.93652 8.85645 -20.8398 18.1162 -33.2529 25.8506c-30.2471 18.7051 -78.7842 30.3936 -78.7842 75.707
c0 56.9277 35.2861 92 83.2002 92h0.0839844c6.21777 0 16.2148 -0.974609 22.3154 -2.17578v86.1768zM224 364.8c0 18.9756 -16.2246 35.2002 -35.2002 35.2002c-18.7002 0 -35.2002 -16.7754 -35.2002 -35.2002v-158.399c-17.3242 0 -35.1992 26.3994 -70.3994 26.3994
c-26.4004 0 -35.2002 -20.625 -35.2002 -44c0 -8.79395 32.7119 -20.4443 56.0996 -34.9258c14.5752 -9.07422 27.2256 -19.5244 39.875 -30.7988c18.374 -16.1094 36.6328 -33.8359 39.5967 -59.0752h176.753c3.7627 42.79 39.6758 74.5088 39.6758 120v21.2988
c0 40.5244 -22.1973 57.124 -61.3252 50.6006c-8.00098 14.6113 -33.9785 24.1514 -53.625 12.9248c-18.2246 19.3652 -46.3809 17.7871 -61.0498 4.9502v91.0254zM352 24c-13.2549 0 -24 -10.7451 -24 -24s10.7451 -24 24 -24s24 10.7451 24 24s-10.7451 24 -24 24z" />
<glyph glyph-name="hand-point-down" unicode="&#xf0a7;" horiz-adv-x="448"
d="M188.8 -64c-45.0986 0 -83.2002 38.1006 -83.2002 83.2002v86.1758c-6.91016 -1.3623 -14.374 -2.17578 -22.3994 -2.17578c-47.9141 0 -83.2002 35.0723 -83.2002 92c0 45.3135 48.5371 57.002 78.7852 75.707c12.4121 7.73438 23.3154 16.9951 33.2529 25.8506
l0.145508 0.130859l0.148438 0.128906c17.4746 15.3213 23.668 23.2197 23.668 30.1836v2.67871c-4.95215 5.74707 -8 13.5361 -8 22.1201v64c0 17.6729 12.8936 32 28.7998 32h230.4c15.9062 0 28.7998 -14.3271 28.7998 -32v-64.001
c0 -8.58398 -3.04785 -16.373 -8 -22.1201v-2.67871c0 -28.6885 40 -67.1367 40 -127.2v-21.2988c0 -62.542 -38.6582 -98.8008 -91.1445 -99.9404c-17.8135 -12.4814 -40.7852 -18.4922 -62.791 -15.9844c-5.8125 -2.96094 -15.6973 -6.51367 -22.0645 -7.92969v-35.6465
c0 -45.4355 -37.584 -83.2002 -83.2002 -83.2002zM188.8 -16c18.9756 0 35.2002 16.2246 35.2002 35.2002v91.0244c14.6689 -12.8369 42.8252 -14.415 61.0498 4.9502c19.6465 -11.2256 45.624 -1.68652 53.625 12.9248c39.1279 -6.52246 61.3252 10.0771 61.3252 50.6016
v21.2988c0 45.4912 -35.9131 77.21 -39.6768 120h-176.752c-2.96289 -25.2393 -21.2227 -42.9658 -39.5967 -59.0752c-12.6494 -11.2744 -25.2998 -21.7246 -39.875 -30.7988c-23.3877 -14.4814 -56.0996 -26.1318 -56.0996 -34.9258c0 -23.375 8.7998 -44 35.2002 -44
c35.2002 0 53.0752 26.3994 70.3994 26.3994v-158.399c0 -18.4248 16.5 -35.2002 35.2002 -35.2002zM328 384c0 -13.2549 10.7451 -24 24 -24s24 10.7451 24 24s-10.7451 24 -24 24s-24 -10.7451 -24 -24z" />
<glyph glyph-name="copy" unicode="&#xf0c5;" horiz-adv-x="448"
d="M433.941 382.059c7.75977 -7.75977 14.0586 -22.9658 14.0586 -33.9404v-268.118c0 -26.5098 -21.4902 -48 -48 -48h-80v-48c0 -26.5098 -21.4902 -48 -48 -48h-224c-26.5098 0 -48 21.4902 -48 48v320c0 26.5098 21.4902 48 48 48h80v48c0 26.5098 21.4902 48 48 48
h172.118c10.9746 0 26.1807 -6.29883 33.9404 -14.0586zM266 -16c3.31152 0 6 2.68848 6 6v42h-96c-26.5098 0 -48 21.4902 -48 48v224h-74c-3.31152 0 -6 -2.68848 -6 -6v-308c0 -3.31152 2.68848 -6 6 -6h212zM394 80c3.31152 0 6 2.68848 6 6v202h-88
c-13.2549 0 -24 10.7451 -24 24v88h-106c-3.31152 0 -6 -2.68848 -6 -6v-308c0 -3.31152 2.68848 -6 6 -6h212zM400 336v9.63184v0.000976562c0 1.37207 -0.787109 3.27246 -1.75684 4.24219l-48.3682 48.3682c-1.12598 1.125 -2.65234 1.75684 -4.24316 1.75684h-9.63184
v-64h64z" />
<glyph glyph-name="save" unicode="&#xf0c7;" horiz-adv-x="448"
d="M433.941 318.059c7.75977 -7.75977 14.0586 -22.9658 14.0586 -33.9404v-268.118c0 -26.5098 -21.4902 -48 -48 -48h-352c-26.5098 0 -48 21.4902 -48 48v352c0 26.5098 21.4902 48 48 48h268.118c10.9746 0 26.1807 -6.29883 33.9404 -14.0586zM272 368h-128v-80h128v80
zM394 16c3.31152 0 6 2.68848 6 6v259.632v0.000976562c0 1.37207 -0.787109 3.27246 -1.75684 4.24219l-78.2432 78.2432v-100.118c0 -13.2549 -10.7451 -24 -24 -24h-176c-13.2549 0 -24 10.7451 -24 24v104h-42c-3.31152 0 -6 -2.68848 -6 -6v-340
c0 -3.31152 2.68848 -6 6 -6h340zM224 216c48.5234 0 88 -39.4766 88 -88s-39.4766 -88 -88 -88s-88 39.4766 -88 88s39.4766 88 88 88zM224 88c22.0557 0 40 17.9443 40 40s-17.9443 40 -40 40s-40 -17.9443 -40 -40s17.9443 -40 40 -40z" />
<glyph glyph-name="square" unicode="&#xf0c8;" horiz-adv-x="448"
d="M400 416c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h352zM394 16c3.2998 0 6 2.7002 6 6v340c0 3.2998 -2.7002 6 -6 6h-340c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h340z" />
<glyph glyph-name="envelope" unicode="&#xf0e0;"
d="M464 384c26.5098 0 48 -21.4902 48 -48v-288c0 -26.5098 -21.4902 -48 -48 -48h-416c-26.5098 0 -48 21.4902 -48 48v288c0 26.5098 21.4902 48 48 48h416zM464 336h-416v-40.8047c22.4248 -18.2627 58.1797 -46.6602 134.587 -106.49
c16.834 -13.2422 50.2051 -45.0762 73.4131 -44.7012c23.2119 -0.371094 56.5723 31.4541 73.4131 44.7012c76.4189 59.8389 112.165 88.2305 134.587 106.49v40.8047zM48 48h416v185.601c-22.915 -18.252 -55.4189 -43.8691 -104.947 -82.6523
c-22.5439 -17.748 -60.3359 -55.1787 -103.053 -54.9473c-42.9277 -0.231445 -81.2051 37.75 -103.062 54.9551c-49.5293 38.7842 -82.0244 64.3945 -104.938 82.6455v-185.602z" />
<glyph glyph-name="lightbulb" unicode="&#xf0eb;" horiz-adv-x="352"
d="M176 368c8.83984 0 16 -7.16016 16 -16s-7.16016 -16 -16 -16c-35.2803 0 -64 -28.7002 -64 -64c0 -8.83984 -7.16016 -16 -16 -16s-16 7.16016 -16 16c0 52.9404 43.0596 96 96 96zM96.0596 -11.1699l-0.0400391 43.1797h159.961l-0.0507812 -43.1797
c-0.00976562 -3.13965 -0.939453 -6.21973 -2.67969 -8.83984l-24.5098 -36.8398c-2.95996 -4.45996 -7.95996 -7.14062 -13.3203 -7.14062h-78.8496c-5.35059 0 -10.3506 2.68066 -13.3203 7.14062l-24.5098 36.8398c-1.75 2.62012 -2.68066 5.68945 -2.68066 8.83984z
M176 448c97.2002 0 176 -78.7998 176 -176c0 -44.3701 -16.4502 -84.8496 -43.5498 -115.79c-16.6406 -18.9795 -42.7402 -58.79 -52.4199 -92.1602v-0.0498047h-48v0.0996094c0.00390625 4.04199 0.999023 10.4482 2.21973 14.3008
c5.67969 17.9893 22.9902 64.8496 62.0996 109.46c20.4102 23.29 31.6504 53.1699 31.6504 84.1396c0 70.5801 -57.4199 128 -128 128c-68.2803 0 -128.15 -54.3604 -127.95 -128c0.0898438 -30.9902 11.0703 -60.71 31.6104 -84.1396
c39.3496 -44.9004 56.5801 -91.8604 62.1699 -109.67c1.42969 -4.56055 2.13965 -9.30078 2.15039 -14.0703v-0.120117h-48v0.0595703c-9.68066 33.3604 -35.7803 73.1709 -52.4209 92.1602c-27.1094 30.9307 -43.5596 71.4102 -43.5596 115.78
c0 93.0303 73.7197 176 176 176z" />
<glyph glyph-name="bell" unicode="&#xf0f3;" horiz-adv-x="448"
d="M439.39 85.71c6 -6.44043 8.66016 -14.1602 8.61035 -21.71c-0.0996094 -16.4004 -12.9805 -32 -32.0996 -32h-383.801c-19.1191 0 -31.9893 15.5996 -32.0996 32c-0.0498047 7.5498 2.61035 15.2598 8.61035 21.71c19.3193 20.7598 55.4697 51.9902 55.4697 154.29
c0 77.7002 54.4795 139.9 127.939 155.16v20.8398c0 17.6699 14.3203 32 31.9805 32s31.9805 -14.3301 31.9805 -32v-20.8398c73.46 -15.2598 127.939 -77.46 127.939 -155.16c0 -102.3 36.1504 -133.53 55.4697 -154.29zM67.5303 80h312.939
c-21.2197 27.96 -44.4199 74.3203 -44.5293 159.42c0 0.200195 0.0595703 0.379883 0.0595703 0.580078c0 61.8604 -50.1396 112 -112 112s-112 -50.1396 -112 -112c0 -0.200195 0.0595703 -0.379883 0.0595703 -0.580078
c-0.109375 -85.0898 -23.3096 -131.45 -44.5293 -159.42zM224 -64c-35.3203 0 -63.9697 28.6504 -63.9697 64h127.939c0 -35.3496 -28.6494 -64 -63.9697 -64z" />
<glyph glyph-name="hospital" unicode="&#xf0f8;" horiz-adv-x="448"
d="M128 204v40c0 6.62695 5.37305 12 12 12h40c6.62695 0 12 -5.37305 12 -12v-40c0 -6.62695 -5.37305 -12 -12 -12h-40c-6.62695 0 -12 5.37305 -12 12zM268 192c-6.62695 0 -12 5.37305 -12 12v40c0 6.62695 5.37305 12 12 12h40c6.62695 0 12 -5.37305 12 -12v-40
c0 -6.62695 -5.37305 -12 -12 -12h-40zM192 108c0 -6.62695 -5.37305 -12 -12 -12h-40c-6.62695 0 -12 5.37305 -12 12v40c0 6.62695 5.37305 12 12 12h40c6.62695 0 12 -5.37305 12 -12v-40zM268 96c-6.62695 0 -12 5.37305 -12 12v40c0 6.62695 5.37305 12 12 12h40
c6.62695 0 12 -5.37305 12 -12v-40c0 -6.62695 -5.37305 -12 -12 -12h-40zM448 -28v-36h-448v36c0 6.62695 5.37305 12 12 12h19.5v378.965c0 11.6172 10.7451 21.0352 24 21.0352h88.5v40c0 13.2549 10.7451 24 24 24h112c13.2549 0 24 -10.7451 24 -24v-40h88.5
c13.2549 0 24 -9.41797 24 -21.0352v-378.965h19.5c6.62695 0 12 -5.37305 12 -12zM79.5 -15h112.5v67c0 6.62695 5.37305 12 12 12h40c6.62695 0 12 -5.37305 12 -12v-67h112.5v351h-64.5v-24c0 -13.2549 -10.7451 -24 -24 -24h-112c-13.2549 0 -24 10.7451 -24 24v24
h-64.5v-351zM266 384h-26v26c0 3.31152 -2.68848 6 -6 6h-20c-3.31152 0 -6 -2.68848 -6 -6v-26h-26c-3.31152 0 -6 -2.68848 -6 -6v-20c0 -3.31152 2.68848 -6 6 -6h26v-26c0 -3.31152 2.68848 -6 6 -6h20c3.31152 0 6 2.68848 6 6v26h26c3.31152 0 6 2.68848 6 6v20
c0 3.31152 -2.68848 6 -6 6z" />
<glyph glyph-name="plus-square" unicode="&#xf0fe;" horiz-adv-x="448"
d="M352 208v-32c0 -6.59961 -5.40039 -12 -12 -12h-88v-88c0 -6.59961 -5.40039 -12 -12 -12h-32c-6.59961 0 -12 5.40039 -12 12v88h-88c-6.59961 0 -12 5.40039 -12 12v32c0 6.59961 5.40039 12 12 12h88v88c0 6.59961 5.40039 12 12 12h32c6.59961 0 12 -5.40039 12 -12
v-88h88c6.59961 0 12 -5.40039 12 -12zM448 368v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h352c26.5 0 48 -21.5 48 -48zM400 22v340c0 3.2998 -2.7002 6 -6 6h-340c-3.2998 0 -6 -2.7002 -6 -6v-340
c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="circle" unicode="&#xf111;"
d="M256 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM256 -8c110.5 0 200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200s89.5 -200 200 -200z" />
<glyph glyph-name="smile" unicode="&#xf118;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM168 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32
s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM328 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM332 135.4c8.5 10.1992 23.7002 11.5 33.7998 3.09961c10.2002 -8.5 11.6006 -23.5996 3.10059 -33.7998
c-30 -36 -74.1006 -56.6006 -120.9 -56.6006s-90.9004 20.6006 -120.9 56.6006c-8.39941 10.2002 -7.09961 25.2998 3.10059 33.7998c10.0996 8.40039 25.2998 7.09961 33.7998 -3.09961c20.7998 -25.1006 51.5 -39.4004 84 -39.4004s63.2002 14.4004 84 39.4004z" />
<glyph glyph-name="frown" unicode="&#xf119;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM168 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32
s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32zM248 144c40.2002 0 78 -17.7002 103.8 -48.5996c8.40039 -10.2002 7.10059 -25.3008 -3.09961 -33.8008
c-10.7002 -8.7998 -25.7002 -6.59961 -33.7998 3.10059c-16.6006 20 -41 31.3994 -66.9004 31.3994s-50.2998 -11.5 -66.9004 -31.3994c-8.5 -10.2002 -23.5996 -11.5 -33.7998 -3.10059c-10.2002 8.5 -11.5996 23.6006 -3.09961 33.8008
c25.7998 30.8994 63.5996 48.5996 103.8 48.5996z" />
<glyph glyph-name="meh" unicode="&#xf11a;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM168 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32
s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32zM336 128c13.2002 0 24 -10.7998 24 -24s-10.7998 -24 -24 -24h-176c-13.2002 0 -24 10.7998 -24 24s10.7998 24 24 24h176z
" />
<glyph glyph-name="keyboard" unicode="&#xf11c;" horiz-adv-x="576"
d="M528 384c26.5098 0 48 -21.4902 48 -48v-288c0 -26.5098 -21.4902 -48 -48 -48h-480c-26.5098 0 -48 21.4902 -48 48v288c0 26.5098 21.4902 48 48 48h480zM536 48v288c0 4.41113 -3.58887 8 -8 8h-480c-4.41113 0 -8 -3.58887 -8 -8v-288c0 -4.41113 3.58887 -8 8 -8
h480c4.41113 0 8 3.58887 8 8zM170 178c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM266 178c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28
c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM362 178c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM458 178c0 -6.62695 -5.37305 -12 -12 -12h-28
c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM122 96c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM506 96
c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM122 260c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28
c6.62695 0 12 -5.37305 12 -12v-28zM218 260c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM314 260c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28
c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM410 260c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM506 260c0 -6.62695 -5.37305 -12 -12 -12h-28
c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM408 102c0 -6.62695 -5.37305 -12 -12 -12h-216c-6.62695 0 -12 5.37305 -12 12v16c0 6.62695 5.37305 12 12 12h216c6.62695 0 12 -5.37305 12 -12v-16z" />
<glyph glyph-name="calendar" unicode="&#xf133;" horiz-adv-x="448"
d="M400 384c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h48v52c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-52h128v52c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12
v-52h48zM394 -16c3.2998 0 6 2.7002 6 6v298h-352v-298c0 -3.2998 2.7002 -6 6 -6h340z" />
<glyph glyph-name="play-circle" unicode="&#xf144;"
d="M371.7 210c16.3994 -9.2002 16.3994 -32.9004 0 -42l-176 -101c-15.9004 -8.7998 -35.7002 2.59961 -35.7002 21v208c0 18.5 19.9004 29.7998 35.7002 21zM504 192c0 -137 -111 -248 -248 -248s-248 111 -248 248s111 248 248 248s248 -111 248 -248zM56 192
c0 -110.5 89.5 -200 200 -200s200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200z" />
<glyph glyph-name="minus-square" unicode="&#xf146;" horiz-adv-x="448"
d="M108 164c-6.59961 0 -12 5.40039 -12 12v32c0 6.59961 5.40039 12 12 12h232c6.59961 0 12 -5.40039 12 -12v-32c0 -6.59961 -5.40039 -12 -12 -12h-232zM448 368v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h352
c26.5 0 48 -21.5 48 -48zM400 22v340c0 3.2998 -2.7002 6 -6 6h-340c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="check-square" unicode="&#xf14a;" horiz-adv-x="448"
d="M400 416c26.5098 0 48 -21.4902 48 -48v-352c0 -26.5098 -21.4902 -48 -48 -48h-352c-26.5098 0 -48 21.4902 -48 48v352c0 26.5098 21.4902 48 48 48h352zM400 16v352h-352v-352h352zM364.136 257.724l-172.589 -171.204
c-4.70508 -4.66699 -12.3027 -4.63672 -16.9697 0.0683594l-90.7812 91.5156c-4.66699 4.70508 -4.63672 12.3037 0.0693359 16.9717l22.7188 22.5361c4.70508 4.66699 12.3027 4.63672 16.9697 -0.0693359l59.792 -60.2773l141.353 140.217
c4.70508 4.66699 12.3027 4.63672 16.9697 -0.0683594l22.5361 -22.7178c4.66699 -4.70605 4.63672 -12.3047 -0.0683594 -16.9717z" />
<glyph glyph-name="share-square" unicode="&#xf14d;" horiz-adv-x="576"
d="M561.938 289.94c18.75 -18.7402 18.75 -49.1406 0 -67.8809l-143.998 -144c-29.9727 -29.9727 -81.9404 -9.05273 -81.9404 33.9404v53.7998c-101.266 -7.83691 -99.625 -31.6406 -84.1104 -78.7598c14.2285 -43.0889 -33.4736 -79.248 -71.0195 -55.7402
c-51.6924 32.3057 -84.8701 83.0635 -84.8701 144.76c0 39.3408 12.2197 72.7402 36.3301 99.3008c19.8398 21.8398 47.7402 38.4697 82.9102 49.4199c36.7295 11.4395 78.3096 16.1094 120.76 17.9893v57.1982c0 42.9355 51.9258 63.9541 81.9404 33.9404zM384 112l144 144
l-144 144v-104.09c-110.86 -0.90332 -240 -10.5166 -240 -119.851c0 -52.1396 32.79 -85.6094 62.3096 -104.06c-39.8174 120.65 48.999 141.918 177.69 143.84v-103.84zM408.74 27.5068c6.14844 1.75684 15.5449 5.92383 20.9736 9.30273
c7.97656 4.95215 18.2861 -0.825195 18.2861 -10.2139v-42.5957c0 -26.5098 -21.4902 -48 -48 -48h-352c-26.5098 0 -48 21.4902 -48 48v352c0 26.5098 21.4902 48 48 48h132c6.62695 0 12 -5.37305 12 -12v-4.48633c0 -4.91699 -2.9873 -9.36914 -7.56934 -11.1514
c-13.7021 -5.33105 -26.3955 -11.5371 -38.0498 -18.585c-1.59668 -0.974609 -4.41016 -1.77051 -6.28027 -1.77734h-86.1006c-3.31152 0 -6 -2.68848 -6 -6v-340c0 -3.31152 2.68848 -6 6 -6h340c3.31152 0 6 2.68848 6 6v25.9658c0 5.37012 3.5791 10.0596 8.74023 11.541
z" />
<glyph glyph-name="compass" unicode="&#xf14e;" horiz-adv-x="496"
d="M347.94 318.14c16.6592 7.61035 33.8096 -9.54004 26.1992 -26.1992l-65.9697 -144.341c-2.73047 -5.97363 -9.7959 -13.0391 -15.7695 -15.7695l-144.341 -65.9697c-16.6592 -7.61035 -33.8096 9.5498 -26.1992 26.1992l65.9697 144.341
c2.73047 5.97363 9.7959 13.0391 15.7695 15.7695zM270.58 169.42c12.4697 12.4697 12.4697 32.6904 0 45.1602s-32.6904 12.4697 -45.1602 0s-12.4697 -32.6904 0 -45.1602s32.6904 -12.4697 45.1602 0zM248 440c136.97 0 248 -111.03 248 -248s-111.03 -248 -248 -248
s-248 111.03 -248 248s111.03 248 248 248zM248 -8c110.28 0 200 89.7197 200 200s-89.7197 200 -200 200s-200 -89.7197 -200 -200s89.7197 -200 200 -200z" />
<glyph glyph-name="caret-square-down" unicode="&#xf150;" horiz-adv-x="448"
d="M125.1 240h197.801c10.6992 0 16.0996 -13 8.5 -20.5l-98.9004 -98.2998c-4.7002 -4.7002 -12.2002 -4.7002 -16.9004 0l-98.8994 98.2998c-7.7002 7.5 -2.2998 20.5 8.39941 20.5zM448 368v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352
c0 26.5 21.5 48 48 48h352c26.5 0 48 -21.5 48 -48zM400 22v340c0 3.2998 -2.7002 6 -6 6h-340c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="caret-square-up" unicode="&#xf151;" horiz-adv-x="448"
d="M322.9 144h-197.801c-10.6992 0 -16.0996 13 -8.5 20.5l98.9004 98.2998c4.7002 4.7002 12.2002 4.7002 16.9004 0l98.8994 -98.2998c7.7002 -7.5 2.2998 -20.5 -8.39941 -20.5zM448 368v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352
c0 26.5 21.5 48 48 48h352c26.5 0 48 -21.5 48 -48zM400 22v340c0 3.2998 -2.7002 6 -6 6h-340c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="caret-square-right" unicode="&#xf152;" horiz-adv-x="448"
d="M176 93.0996v197.801c0 10.6992 13 16.0996 20.5 8.5l98.2998 -98.9004c4.7002 -4.7002 4.7002 -12.2002 0 -16.9004l-98.2998 -98.8994c-7.5 -7.7002 -20.5 -2.2998 -20.5 8.39941zM448 368v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352
c0 26.5 21.5 48 48 48h352c26.5 0 48 -21.5 48 -48zM400 22v340c0 3.2998 -2.7002 6 -6 6h-340c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="file" unicode="&#xf15b;" horiz-adv-x="384"
d="M369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM332.1 320l-76.0996 76.0996v-76.0996h76.0996zM48 -16h288v288
h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416z" />
<glyph glyph-name="file-alt" unicode="&#xf15c;" horiz-adv-x="384"
d="M288 200v-28c0 -6.59961 -5.40039 -12 -12 -12h-168c-6.59961 0 -12 5.40039 -12 12v28c0 6.59961 5.40039 12 12 12h168c6.59961 0 12 -5.40039 12 -12zM276 128c6.59961 0 12 -5.40039 12 -12v-28c0 -6.59961 -5.40039 -12 -12 -12h-168c-6.59961 0 -12 5.40039 -12 12
v28c0 6.59961 5.40039 12 12 12h168zM384 316.1v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996l83.9004 -83.9004c9 -8.90039 14.0996 -21.2002 14.0996 -33.9004z
M256 396.1v-76.0996h76.0996zM336 -16v288h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416h288z" />
<glyph glyph-name="thumbs-up" unicode="&#xf164;"
d="M466.27 161.31c4.6748 -22.6465 0.864258 -44.5371 -8.98926 -62.9893c2.95898 -23.8682 -4.02148 -48.5654 -17.3398 -66.9902c-0.954102 -55.9072 -35.8232 -95.3301 -112.94 -95.3301c-7 0 -15 0.00976562 -22.2197 0.00976562
c-102.742 0 -133.293 38.9395 -177.803 39.9404c-3.56934 -13.7764 -16.085 -23.9502 -30.9775 -23.9502h-64c-17.6729 0 -32 14.3271 -32 32v240c0 17.6729 14.3271 32 32 32h98.7598c19.1455 16.9531 46.0137 60.6533 68.7598 83.4004
c13.667 13.667 10.1533 108.6 71.7607 108.6c57.5801 0 95.2695 -31.9355 95.2695 -104.73c0 -18.4092 -3.92969 -33.7295 -8.84961 -46.5391h36.4795c48.6025 0 85.8203 -41.5654 85.8203 -85.5801c0 -19.1504 -4.95996 -34.9902 -13.7305 -49.8408zM404.52 107.48
c21.5811 20.3838 18.6992 51.0645 5.21094 65.6191c9.44922 0 22.3594 18.9102 22.2695 37.8105c-0.0898438 18.9102 -16.71 37.8203 -37.8203 37.8203h-103.989c0 37.8193 28.3594 55.3691 28.3594 94.5391c0 23.75 0 56.7305 -47.2695 56.7305
c-18.9102 -18.9102 -9.45996 -66.1797 -37.8203 -94.54c-26.5596 -26.5703 -66.1797 -97.46 -94.54 -97.46h-10.9199v-186.17c53.6113 0 100.001 -37.8203 171.64 -37.8203h37.8203c35.5117 0 60.8203 17.1201 53.1201 65.9004
c15.2002 8.16016 26.5 36.4395 13.9395 57.5703zM88 16c0 13.2549 -10.7451 24 -24 24s-24 -10.7451 -24 -24s10.7451 -24 24 -24s24 10.7451 24 24z" />
<glyph glyph-name="thumbs-down" unicode="&#xf165;"
d="M466.27 222.69c8.77051 -14.8506 13.7305 -30.6904 13.7305 -49.8408c0 -44.0146 -37.2178 -85.5801 -85.8203 -85.5801h-36.4795c4.91992 -12.8096 8.84961 -28.1299 8.84961 -46.5391c0 -72.7949 -37.6895 -104.73 -95.2695 -104.73
c-61.6074 0 -58.0938 94.9326 -71.7607 108.6c-22.7461 22.7471 -49.6133 66.4473 -68.7598 83.4004h-7.05176c-5.5332 -9.56152 -15.8662 -16 -27.708 -16h-64c-17.6729 0 -32 14.3271 -32 32v240c0 17.6729 14.3271 32 32 32h64c8.11328 0 15.5146 -3.02539 21.1553 -8
h10.8447c40.9971 0 73.1953 39.9902 176.78 39.9902c7.21973 0 15.2197 0.00976562 22.2197 0.00976562c77.1172 0 111.986 -39.4229 112.94 -95.3301c13.3184 -18.4248 20.2979 -43.1221 17.3398 -66.9902c9.85352 -18.4521 13.6641 -40.3428 8.98926 -62.9893zM64 152
c13.2549 0 24 10.7451 24 24s-10.7451 24 -24 24s-24 -10.7451 -24 -24s10.7451 -24 24 -24zM394.18 135.27c21.1104 0 37.7305 18.9102 37.8203 37.8203c0.0898438 18.9004 -12.8203 37.8105 -22.2695 37.8105c13.4883 14.5547 16.3701 45.2354 -5.21094 65.6191
c12.5605 21.1309 1.26074 49.4102 -13.9395 57.5703c7.7002 48.7803 -17.6084 65.9004 -53.1201 65.9004h-37.8203c-71.6387 0 -118.028 -37.8203 -171.64 -37.8203v-186.17h10.9199c28.3604 0 67.9805 -70.8896 94.54 -97.46
c28.3604 -28.3604 18.9102 -75.6299 37.8203 -94.54c47.2695 0 47.2695 32.9805 47.2695 56.7305c0 39.1699 -28.3594 56.7197 -28.3594 94.5391h103.989z" />
<glyph glyph-name="sun" unicode="&#xf185;"
d="M494.2 226.1c11.2002 -7.59961 17.7998 -20.0996 17.8994 -33.6992c0 -13.4004 -6.69922 -26 -17.7998 -33.5l-59.7998 -40.5l13.7002 -71c2.5 -13.2002 -1.60059 -26.8008 -11.1006 -36.3008s-22.8994 -13.7998 -36.2998 -11.0996l-70.8994 13.7002l-40.4004 -59.9004
c-7.5 -11.0996 -20.0996 -17.7998 -33.5 -17.7998s-26 6.7002 -33.5 17.9004l-40.4004 59.8994l-70.7998 -13.7002c-13.3994 -2.59961 -26.7998 1.60059 -36.2998 11.1006s-13.7002 23.0996 -11.0996 36.2998l13.6992 71l-59.7998 40.5
c-11.0996 7.5 -17.7998 20 -17.7998 33.5s6.59961 26 17.7998 33.5996l59.7998 40.5l-13.6992 71c-2.60059 13.2002 1.59961 26.7002 11.0996 36.3008c9.5 9.59961 23 13.6992 36.2998 11.1992l70.7998 -13.6992l40.4004 59.8994c15.0996 22.2998 51.9004 22.2998 67 0
l40.4004 -59.8994l70.8994 13.6992c13 2.60059 26.6006 -1.59961 36.2002 -11.0996c9.5 -9.59961 13.7002 -23.2002 11.0996 -36.4004l-13.6992 -71zM381.3 140.5l76.7998 52.0996l-76.7998 52l17.6006 91.1006l-91 -17.6006l-51.9004 76.9004l-51.7998 -76.7998
l-91 17.5996l17.5996 -91.2002l-76.7998 -52l76.7998 -52l-17.5996 -91.1992l90.8994 17.5996l51.9004 -77l51.9004 76.9004l91 -17.6006zM256 296c57.2998 0 104 -46.7002 104 -104s-46.7002 -104 -104 -104s-104 46.7002 -104 104s46.7002 104 104 104zM256 136
c30.9004 0 56 25.0996 56 56s-25.0996 56 -56 56s-56 -25.0996 -56 -56s25.0996 -56 56 -56z" />
<glyph glyph-name="moon" unicode="&#xf186;"
d="M279.135 -64c-141.424 0 -256 114.64 -256 256c0 141.425 114.641 256 256 256c13.0068 -0.00195312 33.9443 -1.91797 46.7354 -4.27734c44.0205 -8.13086 53.7666 -66.8691 15.0215 -88.9189c-41.374 -23.5439 -67.4336 -67.4121 -67.4336 -115.836
c0 -83.5234 75.9238 -146.475 158.272 -130.792c43.6904 8.32129 74.5186 -42.5693 46.248 -77.4004c-47.8613 -58.9717 -120.088 -94.7754 -198.844 -94.7754zM279.135 400c-114.875 0 -208 -93.125 -208 -208s93.125 -208 208 -208
c65.2314 0 123.439 30.0361 161.575 77.0244c-111.611 -21.2568 -215.252 64.0957 -215.252 177.943c0 67.5127 36.9326 126.392 91.6934 157.555c-12.3271 2.27637 -25.0312 3.47754 -38.0166 3.47754z" />
<glyph glyph-name="caret-square-left" unicode="&#xf191;" horiz-adv-x="448"
d="M272 290.9v-197.801c0 -10.6992 -13 -16.0996 -20.5 -8.5l-98.2998 98.9004c-4.7002 4.7002 -4.7002 12.2002 0 16.9004l98.2998 98.8994c7.5 7.7002 20.5 2.2998 20.5 -8.39941zM448 368v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352
c0 26.5 21.5 48 48 48h352c26.5 0 48 -21.5 48 -48zM400 22v340c0 3.2998 -2.7002 6 -6 6h-340c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="dot-circle" unicode="&#xf192;"
d="M256 392c-110.549 0 -200 -89.4678 -200 -200c0 -110.549 89.4678 -200 200 -200c110.549 0 200 89.4678 200 200c0 110.549 -89.4678 200 -200 200zM256 440c136.967 0 248 -111.033 248 -248s-111.033 -248 -248 -248s-248 111.033 -248 248s111.033 248 248 248z
M256 272c44.1826 0 80 -35.8174 80 -80s-35.8174 -80 -80 -80s-80 35.8174 -80 80s35.8174 80 80 80z" />
<glyph glyph-name="building" unicode="&#xf1ad;" horiz-adv-x="448"
d="M128 300v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12h-40c-6.59961 0 -12 5.40039 -12 12zM268 288c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40
c0 -6.59961 -5.40039 -12 -12 -12h-40zM140 192c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12h-40zM268 192c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40
c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12h-40zM192 108c0 -6.59961 -5.40039 -12 -12 -12h-40c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40zM268 96c-6.59961 0 -12 5.40039 -12 12v40
c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12h-40zM448 -28v-36h-448v36c0 6.59961 5.40039 12 12 12h19.5v440c0 13.2998 10.7002 24 24 24h337c13.2998 0 24 -10.7002 24 -24v-440h19.5
c6.59961 0 12 -5.40039 12 -12zM79.5 -15h112.5v67c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-67h112.5v414l-288.5 1z" />
<glyph glyph-name="file-pdf" unicode="&#xf1c1;" horiz-adv-x="384"
d="M369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM332.1 320l-76.0996 76.0996v-76.0996h76.0996zM48 -16h288v288
h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416zM298.2 127.7c10.5 -10.5 8 -38.7002 -17.5 -38.7002c-14.7998 0 -36.9004 6.7998 -55.7998 17c-21.6006 -3.59961 -46 -12.7002 -68.4004 -20.0996c-50.0996 -86.4004 -79.4004 -47 -76.0996 -31.2002
c4 20 31 35.8994 51 46.2002c10.5 18.3994 25.3994 50.5 35.3994 74.3994c-7.39941 28.6006 -11.3994 51 -7 67.1006c4.7998 17.6992 38.4004 20.2998 42.6006 -5.90039c4.69922 -15.4004 -1.5 -39.9004 -5.40039 -56c8.09961 -21.2998 19.5996 -35.7998 36.7998 -46.2998
c17.4004 2.2002 52.2002 5.5 64.4004 -6.5zM100.1 49.9004c0 -0.700195 11.4004 4.69922 30.4004 35c-5.90039 -5.5 -25.2998 -21.3008 -30.4004 -35zM181.7 240.5c-2.5 0 -2.60059 -26.9004 1.7998 -40.7998c4.90039 8.7002 5.59961 40.7998 -1.7998 40.7998zM157.3 103.9
c15.9004 6.09961 34 14.8994 54.7998 19.1992c-11.1992 8.30078 -21.7998 20.4004 -30.0996 35.5c-6.7002 -17.6992 -15 -37.7998 -24.7002 -54.6992zM288.9 108.9c3.59961 2.39941 -2.2002 10.3994 -37.3008 7.7998c32.3008 -13.7998 37.3008 -7.7998 37.3008 -7.7998z" />
<glyph glyph-name="file-word" unicode="&#xf1c2;" horiz-adv-x="384"
d="M369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM332.1 320l-76.0996 76.0996v-76.0996h76.0996zM48 -16h288v288
h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416zM268.1 192v0.200195h15.8008c7.7998 0 13.5 -7.2998 11.5996 -14.9004c-4.2998 -17 -13.7002 -54.0996 -34.5 -136c-1.2998 -5.39941 -6.09961 -9.09961 -11.5996 -9.09961h-24.7002
c-5.5 0 -10.2998 3.7998 -11.6006 9.09961c-5.2998 20.9004 -17.7998 71 -17.8994 71.4004l-2.90039 17.2998c-0.5 -5.2998 -1.5 -11.0996 -3 -17.2998l-17.8994 -71.4004c-1.30078 -5.39941 -6.10059 -9.09961 -11.6006 -9.09961h-25.2002
c-5.59961 0 -10.3994 3.7002 -11.6992 9.09961c-6.5 26.5 -25.2002 103.4 -33.2002 136c-1.7998 7.5 3.89941 14.7998 11.7002 14.7998h16.7998c5.7998 0 10.7002 -4.09961 11.7998 -9.69922c5 -25.7002 18.4004 -93.8008 19.0996 -99
c0.300781 -1.7002 0.400391 -3.10059 0.5 -4.2002c0.800781 7.5 0.400391 4.7002 24.8008 103.7c1.39941 5.2998 6.19922 9.09961 11.6992 9.09961h13.3008c5.59961 0 10.3994 -3.7998 11.6992 -9.2002c23.9004 -99.7002 22.8008 -94.3994 23.6006 -99.5
c0.299805 -1.7002 0.5 -3.09961 0.700195 -4.2998c0.599609 8.09961 0.399414 5.7998 21 103.5c1.09961 5.5 6 9.5 11.6992 9.5z" />
<glyph glyph-name="file-excel" unicode="&#xf1c3;" horiz-adv-x="384"
d="M369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM332.1 320l-76.0996 76.0996v-76.0996h76.0996zM48 -16h288v288
h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416zM260 224c9.2002 0 15 -10 10.2998 -18c-16 -27.5 -45.5996 -76.9004 -46.2998 -78l46.4004 -78c4.59961 -8 -1.10059 -18 -10.4004 -18h-28.7998c-4.40039 0 -8.5 2.40039 -10.6006 6.2998
c-22.6992 41.7998 -13.6992 27.5 -28.5996 57.7002c-5.59961 -12.7002 -6.90039 -17.7002 -28.5996 -57.7002c-2.10059 -3.89941 -6.10059 -6.2998 -10.5 -6.2998h-28.9004c-9.2998 0 -15.0996 10 -10.4004 18l46.3008 78l-46.3008 78c-4.59961 8 1.10059 18 10.4004 18
h28.9004c4.39941 0 8.5 -2.40039 10.5996 -6.2998c21.7002 -40.4004 14.7002 -28.6006 28.5996 -57.7002c6.40039 15.2998 10.6006 24.5996 28.6006 57.7002c2.09961 3.89941 6.09961 6.2998 10.5 6.2998h28.7998z" />
<glyph glyph-name="file-powerpoint" unicode="&#xf1c4;" horiz-adv-x="384"
d="M369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM332.1 320l-76.0996 76.0996v-76.0996h76.0996zM48 -16h288v288
h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416zM120 44v168c0 6.59961 5.40039 12 12 12h69.2002c36.7002 0 62.7998 -27 62.7998 -66.2998c0 -74.2998 -68.7002 -66.5 -95.5 -66.5v-47.2002c0 -6.59961 -5.40039 -12 -12 -12h-24.5c-6.59961 0 -12 5.40039 -12 12z
M168.5 131.4h23c7.90039 0 13.9004 2.39941 18.0996 7.19922c8.5 9.80078 8.40039 28.5 0.100586 37.8008c-4.10059 4.59961 -9.90039 7 -17.4004 7h-23.8994v-52h0.0996094z" />
<glyph glyph-name="file-image" unicode="&#xf1c5;" horiz-adv-x="384"
d="M369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM332.1 320l-76.0996 76.0996v-76.0996h76.0996zM48 -16h288v288
h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416zM80 32v64l39.5 39.5c4.7002 4.7002 12.2998 4.7002 17 0l39.5 -39.5l87.5 87.5c4.7002 4.7002 12.2998 4.7002 17 0l23.5 -23.5v-128h-224zM128 272c26.5 0 48 -21.5 48 -48s-21.5 -48 -48 -48s-48 21.5 -48 48
s21.5 48 48 48z" />
<glyph glyph-name="file-archive" unicode="&#xf1c6;" horiz-adv-x="384"
d="M128.3 288h32v-32h-32v32zM192.3 384v-32h-32v32h32zM128.3 352h32v-32h-32v32zM192.3 320v-32h-32v32h32zM369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1
c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM256 396.1v-76.0996h76.0996zM336 -16v288h-104c-13.2998 0 -24 10.7002 -24 24v104h-48.2998v-16h-32v16h-79.7002v-416h288zM194.2 182.3l17.2998 -87.7002c6.40039 -32.3994 -18.4004 -62.5996 -51.5 -62.5996
c-33.2002 0 -58 30.4004 -51.4004 62.9004l19.7002 97.0996v32h32v-32h22.1006c5.7998 0 10.6992 -4.09961 11.7998 -9.7002zM160.3 57.9004c17.9004 0 32.4004 12.0996 32.4004 27c0 14.8994 -14.5 27 -32.4004 27c-17.8994 0 -32.3994 -12.1006 -32.3994 -27
c0 -14.9004 14.5 -27 32.3994 -27zM192.3 256v-32h-32v32h32z" />
<glyph glyph-name="file-audio" unicode="&#xf1c7;" horiz-adv-x="384"
d="M369.941 350.059c7.75977 -7.75977 14.0586 -22.9658 14.0586 -33.9404v-332.118c0 -26.5098 -21.4902 -48 -48 -48h-288c-26.5098 0 -48 21.4902 -48 48v416c0 26.5098 21.4902 48 48 48h204.118c10.9746 0 26.1807 -6.29883 33.9404 -14.0586zM332.118 320
l-76.1182 76.1182v-76.1182h76.1182zM48 -16h288v288h-104c-13.2549 0 -24 10.7451 -24 24v104h-160v-416zM192 60.0244c0 -10.6914 -12.9258 -16.0459 -20.4854 -8.48535l-35.5146 35.9746h-28c-6.62695 0 -12 5.37305 -12 12v56c0 6.62695 5.37305 12 12 12h28
l35.5146 36.9473c7.56055 7.56055 20.4854 2.20605 20.4854 -8.48535v-135.951zM233.201 107.154c9.05078 9.29688 9.05957 24.1328 0.000976562 33.4385c-22.1494 22.752 12.2344 56.2461 34.3945 33.4814c27.1982 -27.9404 27.2119 -72.4443 0.000976562 -100.401
c-21.793 -22.3857 -56.9463 10.3154 -34.3965 33.4814z" />
<glyph glyph-name="file-video" unicode="&#xf1c8;" horiz-adv-x="384"
d="M369.941 350.059c7.75977 -7.75977 14.0586 -22.9658 14.0586 -33.9404v-332.118c0 -26.5098 -21.4902 -48 -48 -48h-288c-26.5098 0 -48 21.4902 -48 48v416c0 26.5098 21.4902 48 48 48h204.118c10.9746 0 26.1807 -6.29883 33.9404 -14.0586zM332.118 320
l-76.1182 76.1182v-76.1182h76.1182zM48 -16h288v288h-104c-13.2549 0 -24 10.7451 -24 24v104h-160v-416zM276.687 195.303c10.0049 10.0049 27.3135 2.99707 27.3135 -11.3135v-111.976c0 -14.2939 -17.2959 -21.332 -27.3135 -11.3135l-52.6865 52.6738v-37.374
c0 -11.0459 -8.9541 -20 -20 -20h-104c-11.0459 0 -20 8.9541 -20 20v104c0 11.0459 8.9541 20 20 20h104c11.0459 0 20 -8.9541 20 -20v-37.374z" />
<glyph glyph-name="file-code" unicode="&#xf1c9;" horiz-adv-x="384"
d="M149.9 98.9004c3.5 -3.30078 3.69922 -8.90039 0.399414 -12.4004l-17.3994 -18.5996c-1.60059 -1.80078 -4 -2.80078 -6.40039 -2.80078c-2.2002 0 -4.40039 0.900391 -6 2.40039l-57.7002 54.0996c-3.7002 3.40039 -3.7002 9.30078 0 12.8008l57.7002 54.0996
c3.40039 3.2998 9 3.2002 12.4004 -0.400391l17.3994 -18.5996l0.200195 -0.200195c3.2002 -3.59961 2.7998 -9.2002 -0.799805 -12.3994l-32.7998 -28.9004l32.7998 -28.9004zM369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288
c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM256 396.1v-76.0996h76.0996zM336 -16v288h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416h288zM209.6 234l24.4004 -7
c4.7002 -1.2998 7.40039 -6.2002 6 -10.9004l-54.7002 -188.199c-1.2998 -4.60059 -6.2002 -7.40039 -10.8994 -6l-24.4004 7.09961c-4.7002 1.2998 -7.40039 6.2002 -6 10.9004l54.7002 188.1c1.39941 4.7002 6.2002 7.40039 10.8994 6zM234.1 157.1
c-3.5 3.30078 -3.69922 8.90039 -0.399414 12.4004l17.3994 18.5996c3.30078 3.60059 8.90039 3.7002 12.4004 0.400391l57.7002 -54.0996c3.7002 -3.40039 3.7002 -9.30078 0 -12.8008l-57.7002 -54.0996c-3.5 -3.2998 -9.09961 -3.09961 -12.4004 0.400391
l-17.3994 18.5996l-0.200195 0.200195c-3.2002 3.59961 -2.7998 9.2002 0.799805 12.3994l32.7998 28.9004l-32.7998 28.9004z" />
<glyph glyph-name="life-ring" unicode="&#xf1cd;"
d="M256 -56c-136.967 0 -248 111.033 -248 248s111.033 248 248 248s248 -111.033 248 -248s-111.033 -248 -248 -248zM152.602 20.7197c63.2178 -38.3184 143.579 -38.3184 206.797 0l-53.4111 53.4111c-31.8467 -13.5215 -68.168 -13.5059 -99.9746 0zM336 192
c0 44.1123 -35.8877 80 -80 80s-80 -35.8877 -80 -80s35.8877 -80 80 -80s80 35.8877 80 80zM427.28 88.6016c38.3184 63.2178 38.3184 143.579 0 206.797l-53.4111 -53.4111c13.5215 -31.8467 13.5049 -68.168 0 -99.9746zM359.397 363.28
c-63.2168 38.3184 -143.578 38.3184 -206.796 0l53.4111 -53.4111c31.8457 13.5215 68.167 13.5049 99.9736 0zM84.7197 295.398c-38.3184 -63.2178 -38.3184 -143.579 0 -206.797l53.4111 53.4111c-13.5215 31.8467 -13.5059 68.168 0 99.9746z" />
<glyph glyph-name="paper-plane" unicode="&#xf1d8;"
d="M440 441.5c34.5996 19.9004 77.5996 -8.7998 71.5 -48.9004l-59.4004 -387.199c-2.2998 -14.5 -11.0996 -27.3008 -23.8994 -34.5c-7.2998 -4.10059 -15.4004 -6.2002 -23.6006 -6.2002c-6.19922 0 -12.3994 1.2002 -18.2998 3.59961l-111.899 46.2002l-43.8008 -59.0996
c-27.3994 -36.9004 -86.5996 -17.8008 -86.5996 28.5996v84.4004l-114.3 47.2998c-36.7998 15.0996 -40.1006 66 -5.7002 85.8994zM192 -16l36.5996 49.5l-36.5996 15.0996v-64.5996zM404.6 12.7002l59.4004 387.3l-416 -240l107.8 -44.5996l211.5 184.3
c14.2002 12.2998 34.4004 -5.7002 23.7002 -21.2002l-140.2 -202.3z" />
<glyph glyph-name="futbol" unicode="&#xf1e3;" horiz-adv-x="496"
d="M483.8 268.6c42.2998 -130.199 -29 -270.1 -159.2 -312.399c-25.5 -8.2998 -51.2998 -12.2002 -76.6992 -12.2002c-104.5 0 -201.7 66.5996 -235.7 171.4c-42.2998 130.199 29 270.1 159.2 312.399c25.5 8.2998 51.2998 12.2002 76.6992 12.2002
c104.5 0 201.7 -66.5996 235.7 -171.4zM409.3 74.9004c6.10059 8.39941 12.1006 16.8994 16.7998 26.1992c14.3008 28.1006 21.5 58.5 21.7002 89.2002l-38.8994 36.4004l-71.1006 -22.1006l-24.3994 -75.1992l43.6992 -60.9004zM409.3 310.3
c-24.5 33.4004 -58.7002 58.4004 -97.8994 71.4004l-47.4004 -26.2002v-73.7998l64.2002 -46.5l70.7002 22zM184.9 381.6c-39.9004 -13.2998 -73.5 -38.5 -97.8008 -71.8994l10.1006 -52.5l70.5996 -22l64.2002 46.5v73.7998zM139 68.5l43.5 61.7002l-24.2998 74.2998
l-71.1006 22.2002l-39 -36.4004c0.5 -55.7002 23.4004 -95.2002 37.8008 -115.3zM187.2 1.5c64.0996 -20.4004 115.5 -1.7998 121.7 0l22.3994 48.0996l-44.2998 61.7002h-78.5996l-43.6006 -61.7002z" />
<glyph glyph-name="newspaper" unicode="&#xf1ea;" horiz-adv-x="576"
d="M552 384c13.2549 0 24 -10.7451 24 -24v-336c0 -13.2549 -10.7451 -24 -24 -24h-496c-30.9277 0 -56 25.0723 -56 56v272c0 13.2549 10.7451 24 24 24h42.752c6.60547 18.623 24.3896 32 45.248 32h440zM48 56c0 -4.41113 3.58887 -8 8 -8s8 3.58887 8 8v248h-16v-248z
M528 48v288h-416v-280c0 -2.7168 -0.204102 -5.38574 -0.578125 -8h416.578zM172 168c-6.62695 0 -12 5.37305 -12 12v96c0 6.62695 5.37305 12 12 12h136c6.62695 0 12 -5.37305 12 -12v-96c0 -6.62695 -5.37305 -12 -12 -12h-136zM200 248v-40h80v40h-80zM160 108v24
c0 6.62695 5.37305 12 12 12h136c6.62695 0 12 -5.37305 12 -12v-24c0 -6.62695 -5.37305 -12 -12 -12h-136c-6.62695 0 -12 5.37305 -12 12zM352 108v24c0 6.62695 5.37305 12 12 12h104c6.62695 0 12 -5.37305 12 -12v-24c0 -6.62695 -5.37305 -12 -12 -12h-104
c-6.62695 0 -12 5.37305 -12 12zM352 252v24c0 6.62695 5.37305 12 12 12h104c6.62695 0 12 -5.37305 12 -12v-24c0 -6.62695 -5.37305 -12 -12 -12h-104c-6.62695 0 -12 5.37305 -12 12zM352 180v24c0 6.62695 5.37305 12 12 12h104c6.62695 0 12 -5.37305 12 -12v-24
c0 -6.62695 -5.37305 -12 -12 -12h-104c-6.62695 0 -12 5.37305 -12 12z" />
<glyph glyph-name="bell-slash" unicode="&#xf1f6;" horiz-adv-x="640"
d="M633.99 -23.0195c6.91016 -5.52051 8.01953 -15.5908 2.5 -22.4902l-10 -12.4902c-5.53027 -6.88965 -15.5898 -8.00977 -22.4902 -2.49023l-598 467.51c-6.90039 5.52051 -8.01953 15.5908 -2.49023 22.4902l10 12.4902
c5.52051 6.90039 15.5898 8.00977 22.4902 2.49023zM163.53 80h182.84l61.3994 -48h-279.659c-19.1201 0 -31.9902 15.5996 -32.1006 32c-0.0498047 7.5498 2.61035 15.2598 8.61035 21.71c18.3701 19.7402 51.5703 49.6904 54.8398 140.42l45.4697 -35.5498
c-6.91992 -54.7803 -24.6895 -88.5498 -41.3994 -110.58zM320 352c-23.3496 0 -45 -7.17969 -62.9404 -19.4004l-38.1699 29.8408c19.6807 15.7793 43.1104 27.3096 69.1299 32.7197v20.8398c0 17.6699 14.3203 32 31.9805 32s31.9805 -14.3301 31.9805 -32v-20.8398
c73.46 -15.2598 127.939 -77.46 127.939 -155.16c0 -41.3604 6.03027 -70.7197 14.3398 -92.8496l-59.5293 46.54c-1.63086 13.96 -2.77051 28.8896 -2.79004 45.7295c0 0.200195 0.0595703 0.379883 0.0595703 0.580078c0 61.8604 -50.1396 112 -112 112zM320 -64
c-35.3203 0 -63.9697 28.6504 -63.9697 64h127.939c0 -35.3496 -28.6494 -64 -63.9697 -64z" />
<glyph glyph-name="copyright" unicode="&#xf1f9;"
d="M256 440c136.967 0 248 -111.033 248 -248s-111.033 -248 -248 -248s-248 111.033 -248 248s111.033 248 248 248zM256 -8c110.549 0 200 89.4678 200 200c0 110.549 -89.4678 200 -200 200c-110.549 0 -200 -89.4688 -200 -200c0 -110.549 89.4678 -200 200 -200z
M363.351 93.0645c-9.61328 -9.71289 -45.5293 -41.3965 -104.064 -41.3965c-82.4297 0 -140.484 61.4248 -140.484 141.567c0 79.1514 60.2754 139.4 139.763 139.4c55.5303 0 88.7373 -26.6201 97.5928 -34.7783c2.13379 -1.96289 3.86523 -5.9082 3.86523 -8.80762
c0 -1.95508 -0.864258 -4.87402 -1.92969 -6.51465l-18.1543 -28.1133c-3.8418 -5.9502 -11.9668 -7.28223 -17.499 -2.9209c-8.5957 6.77637 -31.8145 22.5381 -61.708 22.5381c-48.3037 0 -77.916 -35.3301 -77.916 -80.082c0 -41.5889 26.8877 -83.6924 78.2764 -83.6924
c32.6572 0 56.8428 19.0391 65.7266 27.2256c5.26953 4.85645 13.5957 4.03906 17.8193 -1.73828l19.8652 -27.1699c1.28613 -1.74512 2.33008 -4.91992 2.33008 -7.08789c0 -2.72363 -1.56055 -6.5 -3.48242 -8.42969z" />
<glyph glyph-name="closed-captioning" unicode="&#xf20a;"
d="M464 384c26.5 0 48 -21.5 48 -48v-288c0 -26.5 -21.5 -48 -48 -48h-416c-26.5 0 -48 21.5 -48 48v288c0 26.5 21.5 48 48 48h416zM458 48c3.2998 0 6 2.7002 6 6v276c0 3.2998 -2.7002 6 -6 6h-404c-3.2998 0 -6 -2.7002 -6 -6v-276c0 -3.2998 2.7002 -6 6 -6h404z
M246.9 133.7c1.69922 -2.40039 1.5 -5.60059 -0.5 -7.7002c-53.6006 -56.7998 -172.801 -32.0996 -172.801 67.9004c0 97.2998 121.7 119.5 172.5 70.0996c2.10059 -2 2.5 -3.2002 1 -5.7002l-17.5 -30.5c-1.89941 -3.09961 -6.19922 -4 -9.09961 -1.7002
c-40.7998 32 -94.5996 14.9004 -94.5996 -31.1992c0 -48 51 -70.5 92.1992 -32.6006c2.80078 2.5 7.10059 2.10059 9.2002 -0.899414zM437.3 133.7c1.7002 -2.40039 1.5 -5.60059 -0.5 -7.7002c-53.5996 -56.9004 -172.8 -32.0996 -172.8 67.9004
c0 97.2998 121.7 119.5 172.5 70.0996c2.09961 -2 2.5 -3.2002 1 -5.7002l-17.5 -30.5c-1.90039 -3.09961 -6.2002 -4 -9.09961 -1.7002c-40.8008 32 -94.6006 14.9004 -94.6006 -31.1992c0 -48 51 -70.5 92.2002 -32.6006c2.7998 2.5 7.09961 2.10059 9.2002 -0.899414z
" />
<glyph glyph-name="object-group" unicode="&#xf247;"
d="M500 320h-12v-256h12c6.62695 0 12 -5.37305 12 -12v-72c0 -6.62695 -5.37305 -12 -12 -12h-72c-6.62695 0 -12 5.37305 -12 12v12h-320v-12c0 -6.62695 -5.37305 -12 -12 -12h-72c-6.62695 0 -12 5.37305 -12 12v72c0 6.62695 5.37305 12 12 12h12v256h-12
c-6.62695 0 -12 5.37305 -12 12v72c0 6.62695 5.37305 12 12 12h72c6.62695 0 12 -5.37305 12 -12v-12h320v12c0 6.62695 5.37305 12 12 12h72c6.62695 0 12 -5.37305 12 -12v-72c0 -6.62695 -5.37305 -12 -12 -12zM448 384v-32h32v32h-32zM32 384v-32h32v32h-32zM64 0v32
h-32v-32h32zM480 0v32h-32v-32h32zM440 64v256h-12c-6.62695 0 -12 5.37305 -12 12v12h-320v-12c0 -6.62695 -5.37305 -12 -12 -12h-12v-256h12c6.62695 0 12 -5.37305 12 -12v-12h320v12c0 6.62695 5.37305 12 12 12h12zM404 256c6.62695 0 12 -5.37207 12 -12v-168
c0 -6.62793 -5.37305 -12 -12 -12h-200c-6.62695 0 -12 5.37207 -12 12v52h-84c-6.62695 0 -12 5.37207 -12 12v168c0 6.62793 5.37305 12 12 12h200c6.62695 0 12 -5.37207 12 -12v-52h84zM136 280v-112h144v112h-144zM376 104v112h-56v-76
c0 -6.62793 -5.37305 -12 -12 -12h-76v-24h144z" />
<glyph glyph-name="object-ungroup" unicode="&#xf248;" horiz-adv-x="576"
d="M564 224h-12v-160h12c6.62695 0 12 -5.37305 12 -12v-72c0 -6.62695 -5.37305 -12 -12 -12h-72c-6.62695 0 -12 5.37305 -12 12v12h-224v-12c0 -6.62695 -5.37305 -12 -12 -12h-72c-6.62695 0 -12 5.37305 -12 12v72c0 6.62695 5.37305 12 12 12h12v24h-88v-12
c0 -6.62695 -5.37305 -12 -12 -12h-72c-6.62695 0 -12 5.37305 -12 12v72c0 6.62695 5.37305 12 12 12h12v160h-12c-6.62695 0 -12 5.37305 -12 12v72c0 6.62695 5.37305 12 12 12h72c6.62695 0 12 -5.37305 12 -12v-12h224v12c0 6.62695 5.37305 12 12 12h72
c6.62695 0 12 -5.37305 12 -12v-72c0 -6.62695 -5.37305 -12 -12 -12h-12v-24h88v12c0 6.62695 5.37305 12 12 12h72c6.62695 0 12 -5.37305 12 -12v-72c0 -6.62695 -5.37305 -12 -12 -12zM352 384v-32h32v32h-32zM352 128v-32h32v32h-32zM64 96v32h-32v-32h32zM64 352v32
h-32v-32h32zM96 136h224v12c0 6.62695 5.37305 12 12 12h12v160h-12c-6.62695 0 -12 5.37305 -12 12v12h-224v-12c0 -6.62695 -5.37305 -12 -12 -12h-12v-160h12c6.62695 0 12 -5.37305 12 -12v-12zM224 0v32h-32v-32h32zM504 64v160h-12c-6.62695 0 -12 5.37305 -12 12v12
h-88v-88h12c6.62695 0 12 -5.37305 12 -12v-72c0 -6.62695 -5.37305 -12 -12 -12h-72c-6.62695 0 -12 5.37305 -12 12v12h-88v-24h12c6.62695 0 12 -5.37305 12 -12v-12h224v12c0 6.62695 5.37305 12 12 12h12zM544 0v32h-32v-32h32zM544 256v32h-32v-32h32z" />
<glyph glyph-name="sticky-note" unicode="&#xf249;" horiz-adv-x="448"
d="M448 99.8936c0 -10.9746 -6.29883 -26.1797 -14.0586 -33.9404l-83.8828 -83.8818c-7.75977 -7.76074 -22.9658 -14.0596 -33.9404 -14.0596h-268.118c-26.5098 0 -48 21.4902 -48 48v351.988c0 26.5098 21.4902 48 48 48h352c26.5098 0 48 -21.4902 48 -48v-268.106z
M320 19.8936l76.1182 76.1182h-76.1182v-76.1182zM400 368h-352v-351.988h224v104c0 13.2549 10.7451 24 24 24h104v223.988z" />
<glyph glyph-name="clone" unicode="&#xf24d;"
d="M464 448c26.5098 0 48 -21.4902 48 -48v-320c0 -26.5098 -21.4902 -48 -48 -48h-48v-48c0 -26.5098 -21.4902 -48 -48 -48h-320c-26.5098 0 -48 21.4902 -48 48v320c0 26.5098 21.4902 48 48 48h48v48c0 26.5098 21.4902 48 48 48h320zM362 -16c3.31152 0 6 2.68848 6 6
v42h-224c-26.5098 0 -48 21.4902 -48 48v224h-42c-3.31152 0 -6 -2.68848 -6 -6v-308c0 -3.31152 2.68848 -6 6 -6h308zM458 80c3.31152 0 6 2.68848 6 6v308c0 3.31152 -2.68848 6 -6 6h-308c-3.31152 0 -6 -2.68848 -6 -6v-308c0 -3.31152 2.68848 -6 6 -6h308z" />
<glyph glyph-name="hourglass" unicode="&#xf254;" horiz-adv-x="384"
d="M368 400c0 -80.0996 -31.8984 -165.619 -97.1797 -208c64.9912 -42.1934 97.1797 -127.436 97.1797 -208h4c6.62695 0 12 -5.37305 12 -12v-24c0 -6.62695 -5.37305 -12 -12 -12h-360c-6.62695 0 -12 5.37305 -12 12v24c0 6.62695 5.37305 12 12 12h4
c0 80.0996 31.8994 165.619 97.1797 208c-64.9912 42.1934 -97.1797 127.436 -97.1797 208h-4c-6.62695 0 -12 5.37305 -12 12v24c0 6.62695 5.37305 12 12 12h360c6.62695 0 12 -5.37305 12 -12v-24c0 -6.62695 -5.37305 -12 -12 -12h-4zM64 400
c0 -101.621 57.3066 -184 128 -184s128 82.3799 128 184h-256zM320 -16c0 101.62 -57.3076 184 -128 184s-128 -82.3799 -128 -184h256z" />
<glyph glyph-name="hand-rock" unicode="&#xf255;"
d="M408.864 368.948c48.8213 20.751 103.136 -15.0723 103.136 -67.9111v-114.443c0 -15.3955 -3.08887 -30.3906 -9.18262 -44.5674l-42.835 -99.6562c-4.99707 -11.625 -3.98242 -18.8574 -3.98242 -42.3701c0 -17.6729 -14.3271 -32 -32 -32h-252
c-17.6729 0 -32 14.3271 -32 32c0 27.3301 1.1416 29.2012 -3.11035 32.9033l-97.71 85.0811c-24.8994 21.6797 -39.1797 52.8926 -39.1797 85.6338v56.9531c0 47.4277 44.8457 82.0215 91.0459 71.1807c1.96094 55.751 63.5107 87.8262 110.671 60.8057
c29.1895 31.0713 78.8604 31.4473 108.334 -0.0214844c32.7051 18.6846 76.4121 10.3096 98.8135 -23.5879zM464 186.594v114.445c0 34.29 -52 33.8232 -52 0.676758c0 -8.83594 -7.16309 -16 -16 -16h-7c-8.83691 0 -16 7.16406 -16 16v26.751
c0 34.457 -52 33.707 -52 0.676758v-27.4287c0 -8.83594 -7.16309 -16 -16 -16h-7c-8.83691 0 -16 7.16406 -16 16v40.4658c0 34.3525 -52 33.8115 -52 0.677734v-41.1436c0 -8.83594 -7.16406 -16 -16 -16h-7c-8.83594 0 -16 7.16406 -16 16v26.751
c0 34.4023 -52 33.7744 -52 0.676758v-116.571c0 -8.83203 -7.16797 -16 -16 -16c-3.30664 0 -8.01367 1.7627 -10.5068 3.93359l-7 6.09473c-3.03223 2.64062 -5.49316 8.04688 -5.49316 12.0674v0v41.2275c0 34.2148 -52 33.8857 -52 0.677734v-56.9531
c0 -18.8555 8.27441 -36.874 22.7002 -49.4365l97.71 -85.0801c12.4502 -10.8398 19.5898 -26.4463 19.5898 -42.8164v-10.2861h220v7.07617c0 13.21 2.65332 26.0791 7.88281 38.25l42.835 99.6553c2.91602 6.75391 5.28223 18.207 5.28223 25.5635v0.0488281z" />
<glyph glyph-name="hand-paper" unicode="&#xf256;" horiz-adv-x="448"
d="M372.57 335.359c39.9062 5.63281 75.4297 -25.7393 75.4297 -66.3594v-131.564c-0.00195312 -12.7666 -2.33008 -33.2246 -5.19531 -45.666l-30.1836 -130.958c-3.34668 -14.5234 -16.2783 -24.8125 -31.1816 -24.8125h-222.897
c-9.10352 0 -20.7793 6.01758 -26.0615 13.4316l-119.97 168.415c-21.2441 29.8203 -14.8047 71.3574 14.5498 93.1533c18.7754 13.9395 42.1309 16.2979 62.083 8.87109v126.13c0 44.0547 41.125 75.5439 82.4053 64.9834c23.8926 48.1963 92.3535 50.2471 117.982 0.74707
c42.5186 11.1445 83.0391 -21.9346 83.0391 -65.5469v-10.8242zM399.997 137.437l-0.00195312 131.563c0 24.9492 -36.5703 25.5508 -36.5703 -0.691406v-76.3086c0 -8.83691 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16309 -16 16v154.184
c0 25.501 -36.5703 26.3633 -36.5703 0.691406v-154.875c0 -8.83691 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16309 -16 16v188.309c0 25.501 -36.5703 26.3545 -36.5703 0.691406v-189c0 -8.83691 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16309 -16 16
v153.309c0 25.501 -36.5713 26.3359 -36.5713 0.691406v-206.494c0 -15.5703 -20.0352 -21.9092 -29.0303 -9.2832l-27.1279 38.0791c-14.3711 20.1709 -43.833 -2.33496 -29.3945 -22.6045l115.196 -161.697h201.92l27.3252 118.551
c2.63086 11.417 3.96484 23.1553 3.96484 34.8857z" />
<glyph glyph-name="hand-scissors" unicode="&#xf257;"
d="M256 -32c-44.9561 0 -77.3428 43.2627 -64.0244 85.8535c-21.6484 13.71 -34.0156 38.7617 -30.3408 65.0068h-87.6348c-40.8037 0 -74 32.8105 -74 73.1406c0 40.3291 33.1963 73.1396 74 73.1396l94 -9.14062l-78.8496 18.6787
c-38.3076 14.7422 -57.04 57.4707 -41.9424 95.1123c15.0303 37.4736 57.7549 55.7803 95.6416 41.2012l144.929 -55.7568c24.9551 30.5566 57.8086 43.9932 92.2178 24.7324l97.999 -54.8525c20.9746 -11.7393 34.0049 -33.8457 34.0049 -57.6904v-205.702
c0 -30.7422 -21.4404 -57.5576 -51.7979 -64.5537l-118.999 -27.4268c-4.97168 -1.14648 -10.0889 -1.72949 -15.2031 -1.72949zM256 16.0127l70 -0.000976562c1.23633 0 3.21777 0.225586 4.42285 0.501953l119.001 27.4277
c8.58203 1.97754 14.5762 9.29102 14.5762 17.7812v205.701c0 6.4873 -3.62109 12.542 -9.44922 15.8047l-98 54.8545c-8.13965 4.55566 -18.668 2.61914 -24.4873 -4.50781l-21.7646 -26.6475c-2.65039 -3.24512 -8.20215 -5.87891 -12.3926 -5.87891
c-1.64062 0 -4.21484 0.477539 -5.74609 1.06738l-166.549 64.0908c-32.6543 12.5664 -50.7744 -34.5771 -19.2227 -46.7168l155.357 -59.7852c5.66016 -2.17773 10.2539 -8.86816 10.2539 -14.9326v0v-11.6328c0 -8.83691 -7.16309 -16 -16 -16h-182
c-34.375 0 -34.4297 -50.2803 0 -50.2803h182c8.83691 0 16 -7.16309 16 -16v-6.85645c0 -8.83691 -7.16309 -16 -16 -16h-28c-25.1221 0 -25.1592 -36.5674 0 -36.5674h28c8.83691 0 16 -7.16211 16 -16v-6.85547c0 -8.83691 -7.16309 -16 -16 -16
c-25.1201 0 -25.1602 -36.5674 0 -36.5674z" />
<glyph glyph-name="hand-lizard" unicode="&#xf258;" horiz-adv-x="576"
d="M556.686 157.458c12.6357 -19.4863 19.3145 -42.0615 19.3145 -65.2871v-124.171h-224v71.582l-99.751 38.7871c-2.7832 1.08203 -5.70996 1.63086 -8.69727 1.63086h-131.552c-30.8789 0 -56 25.1211 -56 56c0 48.5234 39.4766 88 88 88h113.709l18.333 48h-196.042
c-44.1123 0 -80 35.8877 -80 80v8c0 30.8779 25.1211 56 56 56h293.917c24.5 0 47.084 -12.2725 60.4111 -32.8291zM528 16v76.1709v0.0478516c0 11.7461 -5.19141 29.2734 -11.5879 39.124l-146.358 225.715c-4.44336 6.85254 -11.9707 10.9424 -20.1367 10.9424h-293.917
c-4.41113 0 -8 -3.58887 -8 -8v-8c0 -17.6445 14.3555 -32 32 -32h213.471c25.2021 0 42.626 -25.293 33.6299 -48.8457l-24.5518 -64.2812c-7.05371 -18.4658 -25.0732 -30.873 -44.8398 -30.873h-113.709c-22.0557 0 -40 -17.9443 -40 -40c0 -4.41113 3.58887 -8 8 -8
h131.552h0.0517578c7.44141 0 19.1074 -2.19238 26.041 -4.89355l99.752 -38.7881c18.5898 -7.22852 30.6035 -24.7881 30.6035 -44.7363v-23.582h128z" />
<glyph glyph-name="hand-spock" unicode="&#xf259;"
d="M501.03 331.824c6.05762 -9.77832 10.9746 -27.0498 10.9746 -38.5518c0 -4.80664 -0.915039 -12.499 -2.04297 -17.1709l-57.623 -241.963c-12.748 -54.1729 -68.2627 -98.1387 -123.915 -98.1387h-0.345703h-107.455h-0.224609
c-33.8135 0 -81.2148 18.834 -105.807 42.041l-91.3652 85.9766c-12.8213 12.0469 -23.2266 36.1016 -23.2266 53.6943c0 16.1299 8.97266 38.7529 20.0273 50.499c5.31836 5.66406 29.875 29.3926 68.1152 21.8477l-24.3594 82.1973
c-1.68164 5.66406 -3.0459 15.0576 -3.0459 20.9668c0 37.5938 30.417 70.502 67.8955 73.4551c-0.204102 2.03125 -0.369141 5.33691 -0.369141 7.37891c0 31.627 24.8594 63.6895 55.4902 71.5684c43.248 10.9785 80.5645 -17.7012 89.6602 -53.0723l13.6836 -53.207
l4.64648 22.6602c6.76074 32.417 39.123 58.8115 72.2373 58.916c8.73438 0 56.625 -3.26953 70.7383 -54.0801c15.0664 0.710938 46.9199 -3.50977 66.3105 -35.0176zM463.271 287.219c7.86914 32.9844 -42.1211 45.2695 -50.0859 11.9219l-24.8008 -104.146
c-4.38867 -18.4141 -31.7783 -11.8926 -28.0557 6.2168l28.5479 139.166c7.39844 36.0703 -43.3076 45.0703 -50.1182 11.9629l-31.791 -154.971c-3.54883 -17.3086 -28.2832 -18.0469 -32.7109 -0.804688l-47.3262 184.035
c-8.43359 32.8105 -58.3691 20.2676 -49.8652 -12.8359l42.4414 -165.039c4.81641 -18.7207 -23.3711 -26.9121 -28.9648 -8.00781l-31.3438 105.779c-9.6875 32.6465 -59.1191 18.2578 -49.3867 -14.625l36.0137 -121.539
c5.61816 -18.9521 10.1777 -50.377 10.1777 -70.1436v-0.00878906c0 -6.54297 -8.05664 -10.9355 -13.4824 -5.82617l-51.123 48.1074c-24.7852 23.4082 -60.0527 -14.1875 -35.2793 -37.4902l91.3691 -85.9805c16.9629 -16.0068 49.6592 -28.998 72.9824 -28.998h0.154297
h107.455h0.216797c34.7402 0 69.3936 27.4443 77.3525 61.2598z" />
<glyph glyph-name="hand-pointer" unicode="&#xf25a;" horiz-adv-x="448"
d="M358.182 268.639c43.1934 16.6348 89.8184 -15.7949 89.8184 -62.6387v-84c-0.000976562 -4.25 -0.775391 -11.0615 -1.72754 -15.2041l-27.4297 -118.999c-6.98242 -30.2969 -33.7549 -51.7969 -64.5566 -51.7969h-178.286c-21.2588 0 -41.3682 10.4102 -53.791 27.8457
l-109.699 154.001c-21.2432 29.8193 -14.8047 71.3574 14.5498 93.1523c18.8115 13.9658 42.1748 16.2822 62.083 8.87207v161.129c0 36.9443 29.7363 67 66.2861 67s66.2861 -30.0557 66.2861 -67v-73.6338c20.4131 2.85742 41.4678 -3.94238 56.5947 -19.6289
c27.1934 12.8467 60.3799 5.66992 79.8721 -19.0986zM80.9854 168.303c-14.4004 20.2119 -43.8008 -2.38281 -29.3945 -22.6055l109.712 -154c3.43457 -4.81934 8.92871 -7.69727 14.6973 -7.69727h178.285c8.49219 0 15.8037 5.99414 17.7822 14.5762l27.4297 119.001
c0.333008 1.44629 0.501953 2.93457 0.501953 4.42285v84c0 25.1602 -36.5713 25.1211 -36.5713 0c0 -8.83594 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16406 -16 16v21c0 25.1602 -36.5713 25.1201 -36.5713 0v-21c0 -8.83594 -7.16309 -16 -16 -16h-6.85938
c-8.83691 0 -16 7.16406 -16 16v35c0 25.1602 -36.5703 25.1201 -36.5703 0v-35c0 -8.83594 -7.16309 -16 -16 -16h-6.85742c-8.83691 0 -16 7.16406 -16 16v175c0 25.1602 -36.5713 25.1201 -36.5713 0v-241.493c0 -15.5703 -20.0352 -21.9092 -29.0303 -9.2832z
M176.143 48v96c0 8.83691 6.26855 16 14 16h6c7.73242 0 14 -7.16309 14 -16v-96c0 -8.83691 -6.26758 -16 -14 -16h-6c-7.73242 0 -14 7.16309 -14 16zM251.571 48v96c0 8.83691 6.26758 16 14 16h6c7.73145 0 14 -7.16309 14 -16v-96c0 -8.83691 -6.26855 -16 -14 -16h-6
c-7.73242 0 -14 7.16309 -14 16zM327 48v96c0 8.83691 6.26758 16 14 16h6c7.73242 0 14 -7.16309 14 -16v-96c0 -8.83691 -6.26758 -16 -14 -16h-6c-7.73242 0 -14 7.16309 -14 16z" />
<glyph glyph-name="hand-peace" unicode="&#xf25b;" horiz-adv-x="448"
d="M362.146 256.024c42.5908 13.3184 85.8535 -19.0684 85.8535 -64.0244l-0.0117188 -70.001c-0.000976562 -4.25 -0.775391 -11.0615 -1.72949 -15.2031l-27.4268 -118.999c-6.99707 -30.3564 -33.8105 -51.7969 -64.5547 -51.7969h-205.702
c-23.8447 0 -45.9502 13.0303 -57.6904 34.0059l-54.8525 97.999c-19.2607 34.4092 -5.82422 67.2617 24.7324 92.2178l-55.7568 144.928c-14.5791 37.8867 3.72754 80.6113 41.2012 95.6416c37.6406 15.0977 80.3691 -3.63477 95.1123 -41.9424l18.6787 -78.8496
l-9.14062 94c0 40.8037 32.8096 74 73.1396 74s73.1406 -33.1963 73.1406 -74v-87.6348c26.2451 3.6748 51.2959 -8.69238 65.0068 -30.3408zM399.987 122l-0.000976562 70c0 25.1602 -36.5674 25.1201 -36.5674 0c0 -8.83691 -7.16309 -16 -16 -16h-6.85547
c-8.83789 0 -16 7.16309 -16 16v28c0 25.1592 -36.5674 25.1221 -36.5674 0v-28c0 -8.83691 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16309 -16 16v182c0 34.4297 -50.2803 34.375 -50.2803 0v-182c0 -8.83691 -7.16309 -16 -16 -16h-11.6328v0
c-6.06445 0 -12.7549 4.59375 -14.9326 10.2539l-59.7842 155.357c-12.1396 31.5518 -59.2842 13.4326 -46.7168 -19.2227l64.0898 -166.549c0.589844 -1.53125 1.06738 -4.10547 1.06738 -5.74609c0 -4.19043 -2.63379 -9.74219 -5.87891 -12.3926l-26.6475 -21.7646
c-7.12695 -5.81934 -9.06445 -16.3467 -4.50781 -24.4873l54.8535 -98c3.26367 -5.82812 9.31934 -9.44922 15.8057 -9.44922h205.701c8.49121 0 15.8037 5.99414 17.7812 14.5762l27.4277 119.001c0.333008 1.44629 0.501953 2.93457 0.501953 4.42285z" />
<glyph glyph-name="registered" unicode="&#xf25d;"
d="M256 440c136.967 0 248 -111.033 248 -248s-111.033 -248 -248 -248s-248 111.033 -248 248s111.033 248 248 248zM256 -8c110.549 0 200 89.4678 200 200c0 110.549 -89.4678 200 -200 200c-110.549 0 -200 -89.4688 -200 -200c0 -110.549 89.4678 -200 200 -200z
M366.442 73.791c4.40332 -7.99219 -1.37012 -17.791 -10.5107 -17.791h-42.8096h-0.0126953c-3.97559 0 -8.71582 2.84961 -10.5801 6.36035l-47.5156 89.3027h-31.958v-83.6631c0 -6.61719 -5.38281 -12 -12 -12h-38.5674c-6.61719 0 -12 5.38281 -12 12v248.304
c0 6.61719 5.38281 12 12 12h78.667c71.251 0 101.498 -32.749 101.498 -85.252c0 -31.6123 -15.2148 -59.2969 -39.4824 -73.1758c3.02148 -4.61719 0.225586 0.199219 53.2715 -96.085zM256.933 208.094c20.9131 0 32.4307 11.5186 32.4316 32.4316
c0 19.5752 -6.5127 31.709 -38.9297 31.709h-27.377v-64.1406h33.875z" />
<glyph glyph-name="calendar-plus" unicode="&#xf271;" horiz-adv-x="448"
d="M336 156v-24c0 -6.59961 -5.40039 -12 -12 -12h-76v-76c0 -6.59961 -5.40039 -12 -12 -12h-24c-6.59961 0 -12 5.40039 -12 12v76h-76c-6.59961 0 -12 5.40039 -12 12v24c0 6.59961 5.40039 12 12 12h76v76c0 6.59961 5.40039 12 12 12h24c6.59961 0 12 -5.40039 12 -12
v-76h76c6.59961 0 12 -5.40039 12 -12zM448 336v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h48v52c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-52h128v52c0 6.59961 5.40039 12 12 12h40
c6.59961 0 12 -5.40039 12 -12v-52h48c26.5 0 48 -21.5 48 -48zM400 -10v298h-352v-298c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="calendar-minus" unicode="&#xf272;" horiz-adv-x="448"
d="M124 120c-6.59961 0 -12 5.40039 -12 12v24c0 6.59961 5.40039 12 12 12h200c6.59961 0 12 -5.40039 12 -12v-24c0 -6.59961 -5.40039 -12 -12 -12h-200zM448 336v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h48v52
c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-52h128v52c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-52h48c26.5 0 48 -21.5 48 -48zM400 -10v298h-352v-298c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="calendar-times" unicode="&#xf273;" horiz-adv-x="448"
d="M311.7 73.2998l-17 -17c-4.7002 -4.7002 -12.2998 -4.7002 -17 0l-53.7002 53.7998l-53.7002 -53.6992c-4.7002 -4.7002 -12.2998 -4.7002 -17 0l-17 17c-4.7002 4.69922 -4.7002 12.2998 0 17l53.7002 53.6992l-53.7002 53.7002c-4.7002 4.7002 -4.7002 12.2998 0 17
l17 17c4.7002 4.7002 12.2998 4.7002 17 0l53.7002 -53.7002l53.7002 53.7002c4.7002 4.7002 12.2998 4.7002 17 0l17 -17c4.7002 -4.7002 4.7002 -12.2998 0 -17l-53.7998 -53.7998l53.6992 -53.7002c4.80078 -4.7002 4.80078 -12.2998 0.100586 -17zM448 336v-352
c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h48v52c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-52h128v52c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-52h48c26.5 0 48 -21.5 48 -48zM400 -10
v298h-352v-298c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="calendar-check" unicode="&#xf274;" horiz-adv-x="448"
d="M400 384c26.5098 0 48 -21.4902 48 -48v-352c0 -26.5098 -21.4902 -48 -48 -48h-352c-26.5098 0 -48 21.4902 -48 48v352c0 26.5098 21.4902 48 48 48h48v52c0 6.62695 5.37305 12 12 12h40c6.62695 0 12 -5.37305 12 -12v-52h128v52c0 6.62695 5.37305 12 12 12h40
c6.62695 0 12 -5.37305 12 -12v-52h48zM394 -16c3.31152 0 6 2.68848 6 6v298h-352v-298c0 -3.31152 2.68848 -6 6 -6h340zM341.151 184.65l-142.31 -141.169c-4.70508 -4.66699 -12.3027 -4.6377 -16.9707 0.0673828l-75.0908 75.6992
c-4.66699 4.70508 -4.6377 12.3027 0.0673828 16.9707l22.7197 22.5361c4.70508 4.66699 12.3027 4.63672 16.9697 -0.0693359l44.1035 -44.4609l111.072 110.182c4.70508 4.66699 12.3027 4.63672 16.9707 -0.0683594l22.5361 -22.7178
c4.66699 -4.70508 4.63672 -12.3027 -0.0683594 -16.9697z" />
<glyph glyph-name="map" unicode="&#xf279;" horiz-adv-x="576"
d="M560.02 416c8.4502 0 15.9805 -6.83008 15.9805 -16.0195v-346.32c0 -11.9609 -9.01367 -25.2705 -20.1201 -29.71l-151.83 -52.8105c-5.32617 -1.7334 -14.1953 -3.13965 -19.7969 -3.13965c-5.7373 0 -14.8105 1.47363 -20.2529 3.29004l-172 60.71l-170.05 -62.8398
c-1.99023 -0.790039 -4 -1.16016 -5.95996 -1.16016c-8.45996 0 -15.9902 6.83008 -15.9902 16.0195v346.32c0.00292969 11.959 9.0166 25.2686 20.1201 29.71l151.83 52.8105c6.43945 2.08984 13.1201 3.13965 19.8096 3.13965
c5.73242 -0.00195312 14.8008 -1.47168 20.2402 -3.28027l172 -60.7197h0.00976562l170.05 62.8398c1.98047 0.790039 4 1.16016 5.95996 1.16016zM224 357.58v-285.97l128 -45.1904v285.97zM48 29.9502l127.36 47.0801l0.639648 0.229492v286.2l-128 -44.5303v-288.979z
M528 65.0801v288.97l-127.36 -47.0693l-0.639648 -0.240234v-286.19z" />
<glyph glyph-name="comment-alt" unicode="&#xf27a;"
d="M448 448c35.2998 0 64 -28.7002 64 -64v-288c0 -35.2998 -28.7002 -64 -64 -64h-144l-124.9 -93.5996c-2.19922 -1.7002 -4.69922 -2.40039 -7.09961 -2.40039c-6.2002 0 -12 4.90039 -12 12v84h-96c-35.2998 0 -64 28.7002 -64 64v288c0 35.2998 28.7002 64 64 64h384z
M464 96v288c0 8.7998 -7.2002 16 -16 16h-384c-8.7998 0 -16 -7.2002 -16 -16v-288c0 -8.7998 7.2002 -16 16 -16h144v-60l67.2002 50.4004l12.7998 9.59961h160c8.7998 0 16 7.2002 16 16z" />
<glyph glyph-name="pause-circle" unicode="&#xf28b;"
d="M256 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM256 -8c110.5 0 200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200s89.5 -200 200 -200zM352 272v-160c0 -8.7998 -7.2002 -16 -16 -16h-48
c-8.7998 0 -16 7.2002 -16 16v160c0 8.7998 7.2002 16 16 16h48c8.7998 0 16 -7.2002 16 -16zM240 272v-160c0 -8.7998 -7.2002 -16 -16 -16h-48c-8.7998 0 -16 7.2002 -16 16v160c0 8.7998 7.2002 16 16 16h48c8.7998 0 16 -7.2002 16 -16z" />
<glyph glyph-name="stop-circle" unicode="&#xf28d;"
d="M504 192c0 -137 -111 -248 -248 -248s-248 111 -248 248s111 248 248 248s248 -111 248 -248zM56 192c0 -110.5 89.5 -200 200 -200s200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200zM352 272v-160c0 -8.7998 -7.2002 -16 -16 -16h-160
c-8.7998 0 -16 7.2002 -16 16v160c0 8.7998 7.2002 16 16 16h160c8.7998 0 16 -7.2002 16 -16z" />
<glyph glyph-name="handshake" unicode="&#xf2b5;" horiz-adv-x="640"
d="M519.2 320.1h120.8v-255.699h-64c-17.5 0 -31.7998 14.1992 -31.9004 31.6992h-57.8994c-1.7998 -8.19922 -5.2998 -16.0996 -10.9004 -23l-26.2002 -32.2998c-15.7998 -19.3994 -41.8994 -25.5 -64 -16.7998c-13.5 -16.5996 -30.5996 -24 -48.7998 -24
c-15.0996 0 -28.5996 5.09961 -41.0996 15.9004c-31.7998 -21.9004 -74.7002 -21.3008 -105.601 3.7998l-84.5996 76.3994h-9.09961c-0.100586 -17.5 -14.3008 -31.6992 -31.9004 -31.6992h-64v255.699h118l47.5996 47.6006c10.5 10.3994 24.8008 16.2998 39.6006 16.2998
h226.8v0c12.7812 0 30.5225 -7.30273 39.5996 -16.2998zM48 96.4004c8.7998 0 16 7.09961 16 16c0 8.7998 -7.2002 16 -16 16s-16 -7.2002 -16 -16c0 -8.80078 7.2002 -16 16 -16zM438 103.3c2.7002 3.40039 2.2002 8.5 -1.2002 11.2998l-108.2 87.8008l-8.19922 -7.5
c-40.3008 -36.8008 -86.7002 -11.8008 -101.5 4.39941c-26.7002 29 -25 74.4004 4.39941 101.3l38.7002 35.5h-56.7002c-2 -0.799805 -3.7002 -1.5 -5.7002 -2.2998l-61.6992 -61.5996h-41.9004v-128.101h27.7002l97.2998 -88
c16.0996 -13.0996 41.4004 -10.5 55.2998 6.60059l15.6006 19.2002l36.7998 -31.5c3 -2.40039 12 -4.90039 18 2.39941l30 36.5l23.8994 -19.3994c3.5 -2.80078 8.5 -2.2002 11.3008 1.19922zM544 144.1v128h-44.7002l-61.7002 61.6006
c-1.39941 1.5 -3.39941 2.2998 -5.5 2.2998l-83.6992 -0.200195c-10 0 -19.6006 -3.7002 -27 -10.5l-65.6006 -60.0996c-9.7002 -8.7998 -10.5 -24 -1.2002 -33.9004c8.90039 -9.39941 25.1006 -8.7002 34.6006 0l55.2002 50.6006c6.5 5.89941 16.5996 5.5 22.5996 -1
l10.9004 -11.7002c6 -6.5 5.5 -16.6006 -1 -22.6006l-12.5 -11.3994l102.699 -83.4004c2.80078 -2.2998 5.40039 -4.89941 7.7002 -7.7002h69.2002zM592 96.4004c8.7998 0 16 7.09961 16 16c0 8.7998 -7.2002 16 -16 16s-16 -7.2002 -16 -16c0 -8.80078 7.2002 -16 16 -16z
" />
<glyph glyph-name="envelope-open" unicode="&#xf2b6;"
d="M494.586 283.484c9.6123 -7.94824 17.4141 -24.5205 17.4141 -36.9932v-262.491c0 -26.5098 -21.4902 -48 -48 -48h-416c-26.5098 0 -48 21.4902 -48 48v262.515c0 12.5166 7.84668 29.1279 17.5146 37.0771c4.08008 3.35449 110.688 89.0996 135.15 108.549
c22.6992 18.1426 60.1299 55.8594 103.335 55.8594c43.4365 0 81.2314 -38.1914 103.335 -55.8594c23.5283 -18.707 130.554 -104.773 135.251 -108.656zM464 -10v253.632v0.00488281c0 1.5791 -0.996094 3.66602 -2.22363 4.6582
c-15.8633 12.8232 -108.793 87.5752 -132.366 106.316c-17.5527 14.0195 -49.7168 45.3887 -73.4102 45.3887c-23.6016 0 -55.2451 -30.8799 -73.4102 -45.3887c-23.5713 -18.7393 -116.494 -93.4795 -132.364 -106.293
c-1.40918 -1.13965 -2.22559 -2.85254 -2.22559 -4.66504v-253.653c0 -3.31152 2.68848 -6 6 -6h404c3.31152 0 6 2.68848 6 6zM432.009 177.704c4.24902 -5.15918 3.46484 -12.7949 -1.74512 -16.9814c-28.9746 -23.2822 -59.2734 -47.5967 -70.9287 -56.8623
c-22.6992 -18.1436 -60.1299 -55.8604 -103.335 -55.8604c-43.4521 0 -81.2871 38.2373 -103.335 55.8604c-11.2793 8.9668 -41.7441 33.4131 -70.9268 56.8643c-5.20996 4.1875 -5.99316 11.8223 -1.74512 16.9814l15.2578 18.5283
c4.17773 5.07227 11.6572 5.84277 16.7793 1.72559c28.6182 -23.001 58.5654 -47.0352 70.5596 -56.5713c17.5527 -14.0195 49.7168 -45.3887 73.4102 -45.3887c23.6016 0 55.2461 30.8799 73.4102 45.3887c11.9941 9.53516 41.9434 33.5703 70.5625 56.5684
c5.12207 4.11621 12.6016 3.3457 16.7783 -1.72656z" />
<glyph glyph-name="address-book" unicode="&#xf2b9;" horiz-adv-x="448"
d="M436 288h-20v-64h20c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12h-20v-64h20c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12h-20v-48c0 -26.5 -21.5 -48 -48 -48h-320c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48
h320c26.5 0 48 -21.5 48 -48v-48h20c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12zM368 -16v416h-320v-416h320zM208 192c-35.2998 0 -64 28.7002 -64 64s28.7002 64 64 64s64 -28.7002 64 -64s-28.7002 -64 -64 -64zM118.4 64
c-12.4004 0 -22.4004 8.59961 -22.4004 19.2002v19.2002c0 31.7998 30.0996 57.5996 67.2002 57.5996c11.3994 0 17.8994 -8 44.7998 -8c26.0996 0 34 8 44.7998 8c37.1006 0 67.2002 -25.7998 67.2002 -57.5996v-19.2002c0 -10.6006 -10 -19.2002 -22.4004 -19.2002
h-179.199z" />
<glyph glyph-name="address-card" unicode="&#xf2bb;" horiz-adv-x="576"
d="M528 416c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-480c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h480zM528 16v352h-480v-352h480zM208 192c-35.2998 0 -64 28.7002 -64 64s28.7002 64 64 64s64 -28.7002 64 -64s-28.7002 -64 -64 -64z
M118.4 64c-12.4004 0 -22.4004 8.59961 -22.4004 19.2002v19.2002c0 31.7998 30.0996 57.5996 67.2002 57.5996c11.3994 0 17.8994 -8 44.7998 -8c26.0996 0 34 8 44.7998 8c37.1006 0 67.2002 -25.7998 67.2002 -57.5996v-19.2002
c0 -10.6006 -10 -19.2002 -22.4004 -19.2002h-179.199zM360 128c-4.40039 0 -8 3.59961 -8 8v16c0 4.40039 3.59961 8 8 8h112c4.40039 0 8 -3.59961 8 -8v-16c0 -4.40039 -3.59961 -8 -8 -8h-112zM360 192c-4.40039 0 -8 3.59961 -8 8v16c0 4.40039 3.59961 8 8 8h112
c4.40039 0 8 -3.59961 8 -8v-16c0 -4.40039 -3.59961 -8 -8 -8h-112zM360 256c-4.40039 0 -8 3.59961 -8 8v16c0 4.40039 3.59961 8 8 8h112c4.40039 0 8 -3.59961 8 -8v-16c0 -4.40039 -3.59961 -8 -8 -8h-112z" />
<glyph glyph-name="user-circle" unicode="&#xf2bd;" horiz-adv-x="496"
d="M248 344c53 0 96 -43 96 -96s-43 -96 -96 -96s-96 43 -96 96s43 96 96 96zM248 200c26.5 0 48 21.5 48 48s-21.5 48 -48 48s-48 -21.5 -48 -48s21.5 -48 48 -48zM248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8
c49.7002 0 95.0996 18.2998 130.1 48.4004c-14.8994 23 -40.3994 38.5 -69.5996 39.5c-20.7998 -6.5 -40.5996 -9.60059 -60.5 -9.60059s-39.7002 3.2002 -60.5 9.60059c-29.2002 -0.900391 -54.7002 -16.5 -69.5996 -39.5c35 -30.1006 80.3994 -48.4004 130.1 -48.4004z
M410.7 76.0996c23.3994 32.7002 37.2998 72.7002 37.2998 115.9c0 110.3 -89.7002 200 -200 200s-200 -89.7002 -200 -200c0 -43.2002 13.9004 -83.2002 37.2998 -115.9c24.5 31.4004 62.2002 51.9004 105.101 51.9004c10.1992 0 26.0996 -9.59961 57.5996 -9.59961
c31.5996 0 47.4004 9.59961 57.5996 9.59961c43 0 80.7002 -20.5 105.101 -51.9004z" />
<glyph glyph-name="id-badge" unicode="&#xf2c1;" horiz-adv-x="384"
d="M336 448c26.5 0 48 -21.5 48 -48v-416c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48h288zM336 -16v416h-288v-416h288zM144 336c-8.7998 0 -16 7.2002 -16 16s7.2002 16 16 16h96c8.7998 0 16 -7.2002 16 -16s-7.2002 -16 -16 -16
h-96zM192 160c-35.2998 0 -64 28.7002 -64 64s28.7002 64 64 64s64 -28.7002 64 -64s-28.7002 -64 -64 -64zM102.4 32c-12.4004 0 -22.4004 8.59961 -22.4004 19.2002v19.2002c0 31.7998 30.0996 57.5996 67.2002 57.5996c11.3994 0 17.8994 -8 44.7998 -8
c26.0996 0 34 8 44.7998 8c37.1006 0 67.2002 -25.7998 67.2002 -57.5996v-19.2002c0 -10.6006 -10 -19.2002 -22.4004 -19.2002h-179.199z" />
<glyph glyph-name="id-card" unicode="&#xf2c2;" horiz-adv-x="576"
d="M528 416c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-480c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h480zM528 16v288h-480v-288h32.7998c-1 4.5 -0.799805 -3.59961 -0.799805 22.4004c0 31.7998 30.0996 57.5996 67.2002 57.5996
c11.3994 0 17.8994 -8 44.7998 -8c26.0996 0 34 8 44.7998 8c37.1006 0 67.2002 -25.7998 67.2002 -57.5996c0 -26 0.0996094 -17.9004 -0.799805 -22.4004h224.8zM360 96c-4.40039 0 -8 3.59961 -8 8v16c0 4.40039 3.59961 8 8 8h112c4.40039 0 8 -3.59961 8 -8v-16
c0 -4.40039 -3.59961 -8 -8 -8h-112zM360 160c-4.40039 0 -8 3.59961 -8 8v16c0 4.40039 3.59961 8 8 8h112c4.40039 0 8 -3.59961 8 -8v-16c0 -4.40039 -3.59961 -8 -8 -8h-112zM360 224c-4.40039 0 -8 3.59961 -8 8v16c0 4.40039 3.59961 8 8 8h112
c4.40039 0 8 -3.59961 8 -8v-16c0 -4.40039 -3.59961 -8 -8 -8h-112zM192 128c-35.2998 0 -64 28.7002 -64 64s28.7002 64 64 64s64 -28.7002 64 -64s-28.7002 -64 -64 -64z" />
<glyph glyph-name="window-maximize" unicode="&#xf2d0;"
d="M464 416c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-416c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h416zM464 22v234h-416v-234c0 -3.2998 2.7002 -6 6 -6h404c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="window-minimize" unicode="&#xf2d1;"
d="M480 -32h-448c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32h448c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32z" />
<glyph glyph-name="window-restore" unicode="&#xf2d2;"
d="M464 448c26.5 0 48 -21.5 48 -48v-320c0 -26.5 -21.5 -48 -48 -48h-48v-48c0 -26.5 -21.5 -48 -48 -48h-320c-26.5 0 -48 21.5 -48 48v320c0 26.5 21.5 48 48 48h48v48c0 26.5 21.5 48 48 48h320zM368 -16v208h-320v-208h320zM464 80v320h-320v-48h224
c26.5 0 48 -21.5 48 -48v-224h48z" />
<glyph glyph-name="snowflake" unicode="&#xf2dc;" horiz-adv-x="448"
d="M440.1 92.7998c7.60059 -4.39941 10.1006 -14.2002 5.5 -21.7002l-7.89941 -13.8994c-4.40039 -7.7002 -14 -10.2998 -21.5 -5.90039l-39.2002 23l9.09961 -34.7002c2.30078 -8.5 -2.69922 -17.2998 -11.0996 -19.5996l-15.2002 -4.09961
c-8.39941 -2.30078 -17.0996 2.7998 -19.2998 11.2998l-21.2998 81l-71.9004 42.2002v-84.5l58.2998 -59.3008c6.10059 -6.19922 6.10059 -16.3994 0 -22.5996l-11.0996 -11.2998c-6.09961 -6.2002 -16.0996 -6.2002 -22.2002 0l-24.8994 25.3994v-46.0996
c0 -8.7998 -7 -16 -15.7002 -16h-15.7002c-8.7002 0 -15.7002 7.2002 -15.7002 16v45.9004l-24.8994 -25.4004c-6.10059 -6.2002 -16.1006 -6.2002 -22.2002 0l-11.1006 11.2998c-6.09961 6.2002 -6.09961 16.4004 0 22.6006l58.3008 59.2998v84.5l-71.9004 -42.2002
l-21.2998 -81c-2.2998 -8.5 -10.9004 -13.5996 -19.2998 -11.2998l-15.2002 4.09961c-8.40039 2.2998 -13.2998 11.1006 -11.1006 19.6006l9.10059 34.6992l-39.2002 -23c-7.5 -4.39941 -17.2002 -1.7998 -21.5 5.90039l-7.90039 13.9004
c-4.2998 7.69922 -1.69922 17.5 5.80078 21.8994l39.1992 23l-34.0996 9.2998c-8.40039 2.30078 -13.2998 11.1006 -11.0996 19.6006l4.09961 15.5c2.2998 8.5 10.9004 13.5996 19.2998 11.2998l79.7002 -21.7002l71.9004 42.2002l-71.9004 42.2002l-79.7002 -21.7002
c-8.39941 -2.2998 -17.0996 2.7998 -19.2998 11.2998l-4.09961 15.5c-2.30078 8.5 2.69922 17.2998 11.0996 19.6006l34.0996 9.09961l-39.1992 23c-7.60059 4.5 -10.1006 14.2002 -5.80078 21.9004l7.90039 13.8994c4.40039 7.7002 14 10.2998 21.5 5.90039l39.2002 -23
l-9.10059 34.7002c-2.2998 8.5 2.7002 17.2998 11.1006 19.5996l15.2002 4.09961c8.39941 2.30078 17.0996 -2.7998 19.2998 -11.2998l21.2998 -81l71.9004 -42.2002v84.5l-58.3008 59.3008c-6.09961 6.19922 -6.09961 16.3994 0 22.5996l11.5 11.2998
c6.10059 6.2002 16.1006 6.2002 22.2002 0l24.9004 -25.3994v46.0996c0 8.7998 7 16 15.7002 16h15.6992c8.7002 0 15.7002 -7.2002 15.7002 -16v-45.9004l24.9004 25.4004c6.09961 6.2002 16.0996 6.2002 22.2002 0l11.0996 -11.2998
c6.09961 -6.2002 6.09961 -16.4004 0 -22.6006l-58.2998 -59.2998v-84.5l71.8994 42.2002l21.3008 81c2.2998 8.5 10.8994 13.5996 19.2998 11.2998l15.2002 -4.09961c8.39941 -2.2998 13.2998 -11.1006 11.0996 -19.6006l-9.09961 -34.6992l39.1992 23
c7.5 4.39941 17.2002 1.7998 21.5 -5.90039l7.90039 -13.9004c4.2998 -7.69922 1.7002 -17.5 -5.7998 -21.8994l-39.2002 -23l34.0996 -9.2998c8.40039 -2.30078 13.3008 -11.1006 11.1006 -19.6006l-4.10059 -15.5c-2.2998 -8.5 -10.8994 -13.5996 -19.2998 -11.2998
l-79.7002 21.7002l-71.8994 -42.2002l71.7998 -42.2002l79.7002 21.7002c8.39941 2.2998 17.0996 -2.7998 19.2998 -11.2998l4.09961 -15.5c2.30078 -8.5 -2.69922 -17.2998 -11.0996 -19.6006l-34.0996 -9.2998z" />
<glyph glyph-name="trash-alt" unicode="&#xf2ed;" horiz-adv-x="448"
d="M268 32c-6.62402 0 -12 5.37598 -12 12v216c0 6.62402 5.37598 12 12 12h24c6.62402 0 12 -5.37598 12 -12v-216c0 -6.62402 -5.37598 -12 -12 -12h-24zM432 368c8.83203 0 16 -7.16797 16 -16v-16c0 -8.83203 -7.16797 -16 -16 -16h-16v-336
c0 -26.4961 -21.5039 -48 -48 -48h-288c-26.4961 0 -48 21.5039 -48 48v336h-16c-8.83203 0 -16 7.16797 -16 16v16c0 8.83203 7.16797 16 16 16h82.4102l34.0195 56.7002c7.71875 12.8613 26.1572 23.2998 41.1572 23.2998h0.00292969h100.82h0.0224609
c15 0 33.4385 -10.4385 41.1572 -23.2998l34 -56.7002h82.4102zM171.84 397.09l-17.4502 -29.0898h139.221l-17.46 29.0898c-0.96582 1.60645 -3.26953 2.91016 -5.14355 2.91016h-0.00683594h-94h-0.0166016c-1.87402 0 -4.17871 -1.30371 -5.14355 -2.91016zM368 -16v336
h-288v-336h288zM156 32c-6.62402 0 -12 5.37598 -12 12v216c0 6.62402 5.37598 12 12 12h24c6.62402 0 12 -5.37598 12 -12v-216c0 -6.62402 -5.37598 -12 -12 -12h-24z" />
<glyph glyph-name="images" unicode="&#xf302;" horiz-adv-x="576"
d="M480 32v-16c0 -26.5098 -21.4902 -48 -48 -48h-384c-26.5098 0 -48 21.4902 -48 48v256c0 26.5098 21.4902 48 48 48h16v-48h-10c-3.31152 0 -6 -2.68848 -6 -6v-244c0 -3.31152 2.68848 -6 6 -6h372c3.31152 0 6 2.68848 6 6v10h48zM522 368h-372
c-3.31152 0 -6 -2.68848 -6 -6v-244c0 -3.31152 2.68848 -6 6 -6h372c3.31152 0 6 2.68848 6 6v244c0 3.31152 -2.68848 6 -6 6zM528 416c26.5098 0 48 -21.4902 48 -48v-256c0 -26.5098 -21.4902 -48 -48 -48h-384c-26.5098 0 -48 21.4902 -48 48v256
c0 26.5098 21.4902 48 48 48h384zM264 304c0 -22.0908 -17.9092 -40 -40 -40s-40 17.9092 -40 40s17.9092 40 40 40s40 -17.9092 40 -40zM192 208l39.5146 39.5146c4.68652 4.68652 12.2842 4.68652 16.9717 0l39.5137 -39.5146l103.515 103.515
c4.68652 4.68652 12.2842 4.68652 16.9717 0l71.5137 -71.5146v-80h-288v48z" />
<glyph glyph-name="clipboard" unicode="&#xf328;" horiz-adv-x="384"
d="M336 384c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h80c0 35.2998 28.7002 64 64 64s64 -28.7002 64 -64h80zM192 408c-13.2998 0 -24 -10.7002 -24 -24s10.7002 -24 24 -24s24 10.7002 24 24
s-10.7002 24 -24 24zM336 -10v340c0 3.2998 -2.7002 6 -6 6h-42v-36c0 -6.59961 -5.40039 -12 -12 -12h-168c-6.59961 0 -12 5.40039 -12 12v36h-42c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h276c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="arrow-alt-circle-down" unicode="&#xf358;"
d="M256 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM256 -8c110.5 0 200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200s89.5 -200 200 -200zM224 308c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-116
h67c10.7002 0 16.0996 -12.9004 8.5 -20.5l-99 -99c-4.7002 -4.7002 -12.2998 -4.7002 -17 0l-99 99c-7.5 7.59961 -2.2002 20.5 8.5 20.5h67v116z" />
<glyph glyph-name="arrow-alt-circle-left" unicode="&#xf359;"
d="M8 192c0 137 111 248 248 248s248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248zM456 192c0 110.5 -89.5 200 -200 200s-200 -89.5 -200 -200s89.5 -200 200 -200s200 89.5 200 200zM384 212v-40c0 -6.59961 -5.40039 -12 -12 -12h-116v-67
c0 -10.7002 -12.9004 -16 -20.5 -8.5l-99 99c-4.7002 4.7002 -4.7002 12.2998 0 17l99 99c7.59961 7.59961 20.5 2.2002 20.5 -8.5v-67h116c6.59961 0 12 -5.40039 12 -12z" />
<glyph glyph-name="arrow-alt-circle-right" unicode="&#xf35a;"
d="M504 192c0 -137 -111 -248 -248 -248s-248 111 -248 248s111 248 248 248s248 -111 248 -248zM56 192c0 -110.5 89.5 -200 200 -200s200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200zM128 172v40c0 6.59961 5.40039 12 12 12h116v67
c0 10.7002 12.9004 16 20.5 8.5l99 -99c4.7002 -4.7002 4.7002 -12.2998 0 -17l-99 -99c-7.59961 -7.59961 -20.5 -2.2002 -20.5 8.5v67h-116c-6.59961 0 -12 5.40039 -12 12z" />
<glyph glyph-name="arrow-alt-circle-up" unicode="&#xf35b;"
d="M256 -56c-137 0 -248 111 -248 248s111 248 248 248s248 -111 248 -248s-111 -248 -248 -248zM256 392c-110.5 0 -200 -89.5 -200 -200s89.5 -200 200 -200s200 89.5 200 200s-89.5 200 -200 200zM276 64h-40c-6.59961 0 -12 5.40039 -12 12v116h-67
c-10.7002 0 -16 12.9004 -8.5 20.5l99 99c4.7002 4.7002 12.2998 4.7002 17 0l99 -99c7.59961 -7.59961 2.2002 -20.5 -8.5 -20.5h-67v-116c0 -6.59961 -5.40039 -12 -12 -12z" />
<glyph glyph-name="gem" unicode="&#xf3a5;" horiz-adv-x="576"
d="M464 448c4.09961 0 7.7998 -2 10.0996 -5.40039l99.9004 -147.199c2.90039 -4.40039 2.59961 -10.1006 -0.700195 -14.2002l-276 -340.8c-4.7998 -5.90039 -13.7998 -5.90039 -18.5996 0l-276 340.8c-3.2998 4 -3.60059 9.7998 -0.700195 14.2002l100 147.199
c2.2002 3.40039 6 5.40039 10 5.40039h352zM444.7 400h-56.7998l51.6992 -96h68.4004zM242.6 400l-51.5996 -96h194l-51.7002 96h-90.7002zM131.3 400l-63.2998 -96h68.4004l51.6992 96h-56.7998zM88.2998 256l119.7 -160l-68.2998 160h-51.4004zM191.2 256l96.7998 -243.3
l96.7998 243.3h-193.6zM368 96l119.6 160h-51.3994z" />
<glyph glyph-name="money-bill-alt" unicode="&#xf3d1;" horiz-adv-x="640"
d="M320 304c53.0195 0 96 -50.1396 96 -112c0 -61.8701 -43 -112 -96 -112c-53.0195 0 -96 50.1504 -96 112c0 61.8604 42.9805 112 96 112zM360 136v16c0 4.41992 -3.58008 8 -8 8h-16v88c0 4.41992 -3.58008 8 -8 8h-13.5801h-0.000976562
c-4.01074 0 -9.97266 -1.80566 -13.3086 -4.03027l-15.3301 -10.2197c-1.96777 -1.30957 -3.56445 -4.29004 -3.56445 -6.65332c0 -1.33691 0.601562 -3.32422 1.34375 -4.43652l8.88086 -13.3105c1.30859 -1.9668 4.29004 -3.56445 6.65332 -3.56445
c1.33691 0 3.32422 0.602539 4.43652 1.34473l0.469727 0.310547v-55.4404h-16c-4.41992 0 -8 -3.58008 -8 -8v-16c0 -4.41992 3.58008 -8 8 -8h64c4.41992 0 8 3.58008 8 8zM608 384c17.6699 0 32 -14.3301 32 -32v-320c0 -17.6699 -14.3301 -32 -32 -32h-576
c-17.6699 0 -32 14.3301 -32 32v320c0 17.6699 14.3301 32 32 32h576zM592 112v160c-35.3496 0 -64 28.6504 -64 64h-416c0 -35.3496 -28.6504 -64 -64 -64v-160c35.3496 0 64 -28.6504 64 -64h416c0 35.3496 28.6504 64 64 64z" />
<glyph glyph-name="window-close" unicode="&#xf410;"
d="M464 416c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-416c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h416zM464 22v340c0 3.2998 -2.7002 6 -6 6h-404c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h404c3.2998 0 6 2.7002 6 6z
M356.5 253.4l-61.4004 -61.4004l61.4004 -61.4004c4.59961 -4.59961 4.59961 -12.0996 0 -16.7998l-22.2998 -22.2998c-4.60059 -4.59961 -12.1006 -4.59961 -16.7998 0l-61.4004 61.4004l-61.4004 -61.4004c-4.59961 -4.59961 -12.0996 -4.59961 -16.7998 0
l-22.2998 22.2998c-4.59961 4.60059 -4.59961 12.1006 0 16.7998l61.4004 61.4004l-61.4004 61.4004c-4.59961 4.59961 -4.59961 12.0996 0 16.7998l22.2998 22.2998c4.60059 4.59961 12.1006 4.59961 16.7998 0l61.4004 -61.4004l61.4004 61.4004
c4.59961 4.59961 12.0996 4.59961 16.7998 0l22.2998 -22.2998c4.7002 -4.60059 4.7002 -12.1006 0 -16.7998z" />
<glyph glyph-name="comment-dots" unicode="&#xf4ad;"
d="M144 240c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32zM256 240c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32zM368 240c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32
s-32 14.2998 -32 32s14.2998 32 32 32zM256 416c141.4 0 256 -93.0996 256 -208s-114.6 -208 -256 -208c-32.7998 0 -64 5.2002 -92.9004 14.2998c-29.0996 -20.5996 -77.5996 -46.2998 -139.1 -46.2998c-9.59961 0 -18.2998 5.7002 -22.0996 14.5
c-3.80078 8.7998 -2 19 4.59961 26c0.5 0.400391 31.5 33.7998 46.4004 73.2002c-33 35.0996 -52.9004 78.7002 -52.9004 126.3c0 114.9 114.6 208 256 208zM256 48c114.7 0 208 71.7998 208 160s-93.2998 160 -208 160s-208 -71.7998 -208 -160
c0 -42.2002 21.7002 -74.0996 39.7998 -93.4004l20.6006 -21.7998l-10.6006 -28.0996c-5.5 -14.5 -12.5996 -28.1006 -19.8994 -40.2002c23.5996 7.59961 43.1992 18.9004 57.5 29l19.5 13.7998l22.6992 -7.2002c25.3008 -8 51.7002 -12.0996 78.4004 -12.0996z" />
<glyph glyph-name="smile-wink" unicode="&#xf4da;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM365.8 138.4c10.2002 -8.5 11.6006 -23.6006 3.10059 -33.8008
c-30 -36 -74.1006 -56.5996 -120.9 -56.5996s-90.9004 20.5996 -120.9 56.5996c-8.39941 10.2002 -7.09961 25.3008 3.10059 33.8008c10.0996 8.39941 25.2998 7.09961 33.7998 -3.10059c20.7998 -25.0996 51.5 -39.3994 84 -39.3994s63.2002 14.3994 84 39.3994
c8.5 10.2002 23.5996 11.6006 33.7998 3.10059zM168 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM328 268c25.7002 0 55.9004 -16.9004 59.7002 -42.0996c1.7998 -11.1006 -11.2998 -18.2002 -19.7998 -10.8008l-9.5 8.5
c-14.8008 13.2002 -46.2002 13.2002 -61 0l-9.5 -8.5c-8.30078 -7.39941 -21.5 -0.399414 -19.8008 10.8008c4 25.1992 34.2002 42.0996 59.9004 42.0996z" />
<glyph glyph-name="angry" unicode="&#xf556;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM248 136c33.5996 0 65.2002 -14.7998 86.7998 -40.5996
c8.40039 -10.2002 7.10059 -25.3008 -3.09961 -33.8008c-10.6006 -8.89941 -25.7002 -6.69922 -33.7998 3c-24.8008 29.7002 -75 29.7002 -99.8008 0c-8.5 -10.1992 -23.5996 -11.5 -33.7998 -3s-11.5996 23.6006 -3.09961 33.8008
c21.5996 25.7998 53.2002 40.5996 86.7998 40.5996zM200 208c0 -17.7002 -14.2998 -32.0996 -32 -32.0996s-32 14.2998 -32 32c0 6.19922 2.2002 11.6992 5.2998 16.5996l-28.2002 8.5c-12.6992 3.7998 -19.8994 17.2002 -16.0996 29.9004
c3.7998 12.6992 17.0996 20 29.9004 16.0996l80 -24c12.6992 -3.7998 19.8994 -17.2002 16.0996 -29.9004c-3.09961 -10.3994 -12.7002 -17.0996 -23 -17.0996zM399 262.9c3.7998 -12.7002 -3.40039 -26.1006 -16.0996 -29.8008l-28.2002 -8.5
c3.09961 -4.89941 5.2998 -10.3994 5.2998 -16.5996c0 -17.7002 -14.2998 -32 -32 -32s-32 14.2998 -32 32c-10.2998 0 -19.9004 6.7002 -23 17.0996c-3.7998 12.7002 3.40039 26.1006 16.0996 29.9004l80 24c12.8008 3.7998 26.1006 -3.40039 29.9004 -16.0996z" />
<glyph glyph-name="dizzy" unicode="&#xf567;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM214.2 209.9
c-7.90039 -7.90039 -20.5 -7.90039 -28.4004 -0.200195l-17.7998 17.7998l-17.7998 -17.7998c-7.7998 -7.7998 -20.5 -7.7998 -28.2998 0c-7.80078 7.7998 -7.80078 20.5 0 28.2998l17.8994 17.9004l-17.8994 17.8994c-7.80078 7.7998 -7.80078 20.5 0 28.2998
c7.7998 7.80078 20.5 7.80078 28.2998 0l17.7998 -17.7998l17.9004 17.9004c7.7998 7.7998 20.5 7.7998 28.2998 0s7.7998 -20.5 0 -28.2998l-17.9004 -17.9004l17.9004 -17.7998c7.7998 -7.7998 7.7998 -20.5 0 -28.2998zM374.2 302.1
c7.7002 -7.7998 7.7002 -20.3994 0 -28.1992l-17.9004 -17.9004l17.7998 -18c7.80078 -7.7998 7.80078 -20.5 0 -28.2998c-7.7998 -7.7998 -20.5 -7.7998 -28.2998 0l-17.7998 17.7998l-17.7998 -17.7998c-7.7998 -7.7998 -20.5 -7.7998 -28.2998 0
c-7.80078 7.7998 -7.80078 20.5 0 28.2998l17.8994 17.9004l-17.8994 17.8994c-7.80078 7.7998 -7.80078 20.5 0 28.2998c7.7998 7.80078 20.5 7.80078 28.2998 0l17.7998 -17.7998l17.9004 17.7998c7.7998 7.80078 20.5 7.80078 28.2998 0zM248 176
c35.2998 0 64 -28.7002 64 -64s-28.7002 -64 -64 -64s-64 28.7002 -64 64s28.7002 64 64 64z" />
<glyph glyph-name="flushed" unicode="&#xf579;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM344 304c44.2002 0 80 -35.7998 80 -80s-35.7998 -80 -80 -80
s-80 35.7998 -80 80s35.7998 80 80 80zM344 176c26.5 0 48 21.5 48 48s-21.5 48 -48 48s-48 -21.5 -48 -48s21.5 -48 48 -48zM344 248c13.2998 0 24 -10.7002 24 -24s-10.7002 -24 -24 -24s-24 10.7002 -24 24s10.7002 24 24 24zM232 224c0 -44.2002 -35.7998 -80 -80 -80
s-80 35.7998 -80 80s35.7998 80 80 80s80 -35.7998 80 -80zM152 176c26.5 0 48 21.5 48 48s-21.5 48 -48 48s-48 -21.5 -48 -48s21.5 -48 48 -48zM152 248c13.2998 0 24 -10.7002 24 -24s-10.7002 -24 -24 -24s-24 10.7002 -24 24s10.7002 24 24 24zM312 104
c13.2002 0 24 -10.7998 24 -24s-10.7998 -24 -24 -24h-128c-13.2002 0 -24 10.7998 -24 24s10.7998 24 24 24h128z" />
<glyph glyph-name="frown-open" unicode="&#xf57a;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM200 240c0 -17.7002 -14.2998 -32 -32 -32s-32 14.2998 -32 32
s14.2998 32 32 32s32 -14.2998 32 -32zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32zM248 160c35.5996 0 88.7998 -21.2998 95.7998 -61.2002c2 -11.7998 -9.09961 -21.5996 -20.5 -18.0996
c-31.2002 9.59961 -59.3994 15.2998 -75.2998 15.2998s-44.0996 -5.7002 -75.2998 -15.2998c-11.5 -3.40039 -22.5 6.2998 -20.5 18.0996c7 39.9004 60.2002 61.2002 95.7998 61.2002z" />
<glyph glyph-name="grimace" unicode="&#xf57f;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM168 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32
s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM328 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM344 192c26.5 0 48 -21.5 48 -48v-32c0 -26.5 -21.5 -48 -48 -48h-192c-26.5 0 -48 21.5 -48 48v32c0 26.5 21.5 48 48 48
h192zM176 96v24h-40v-8c0 -8.7998 7.2002 -16 16 -16h24zM176 136v24h-24c-8.7998 0 -16 -7.2002 -16 -16v-8h40zM240 96v24h-48v-24h48zM240 136v24h-48v-24h48zM304 96v24h-48v-24h48zM304 136v24h-48v-24h48zM360 112v8h-40v-24h24c8.7998 0 16 7.2002 16 16zM360 136v8
c0 8.7998 -7.2002 16 -16 16h-24v-24h40z" />
<glyph glyph-name="grin" unicode="&#xf580;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM353.6 143.4c10 3.09961 19.3008 -5.5 17.7002 -15.3008
c-8 -47.0996 -71.2998 -80 -123.3 -80s-115.4 32.9004 -123.3 80c-1.7002 9.90039 7.7998 18.4004 17.7002 15.3008c26 -8.30078 64.3994 -13.1006 105.6 -13.1006s79.7002 4.7998 105.6 13.1006zM168 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32
s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM328 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32z" />
<glyph glyph-name="grin-alt" unicode="&#xf581;" horiz-adv-x="496"
d="M200.3 200c-7.5 -11.4004 -24.5996 -12 -32.7002 0c-12.3994 18.7002 -15.1992 37.2998 -15.6992 56c0.599609 18.7002 3.2998 37.2998 15.6992 56c7.60059 11.4004 24.7002 12 32.7002 0c12.4004 -18.7002 15.2002 -37.2998 15.7002 -56
c-0.599609 -18.7002 -3.2998 -37.2998 -15.7002 -56zM328.3 200c-7.5 -11.4004 -24.5996 -12 -32.7002 0c-12.3994 18.7002 -15.1992 37.2998 -15.6992 56c0.599609 18.7002 3.2998 37.2998 15.6992 56c7.60059 11.4004 24.7002 12 32.7002 0
c12.4004 -18.7002 15.2002 -37.2998 15.7002 -56c-0.599609 -18.7002 -3.2998 -37.2998 -15.7002 -56zM248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200
s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM353.6 143.4c10 3.09961 19.3008 -5.5 17.7002 -15.3008c-8 -47.0996 -71.2998 -80 -123.3 -80s-115.4 32.8008 -123.3 80c-1.7002 10 7.7998 18.4004 17.7002 15.3008c26 -8.30078 64.3994 -13.1006 105.6 -13.1006
s79.7002 4.7998 105.6 13.1006z" />
<glyph glyph-name="grin-beam" unicode="&#xf582;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM353.6 143.4c10 3.09961 19.3008 -5.5 17.7002 -15.3008
c-8 -47.0996 -71.2998 -80 -123.3 -80s-115.4 32.9004 -123.3 80c-1.7002 10 7.89941 18.4004 17.7002 15.3008c26 -8.30078 64.3994 -13.1006 105.6 -13.1006s79.7002 4.7998 105.6 13.1006zM117.7 216.3c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998
c3.2998 42.1006 32.2002 71.4004 56 71.4004s52.7002 -29.2998 56 -71.4004c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998c-3.09961 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996s-23.7998 -7.89941 -31.5 -21.5996
l-9.5 -17c-1.90039 -3.2002 -5.7998 -4.7998 -9.2998 -3.7002zM277.7 216.3c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998c3.2998 42.1006 32.2002 71.4004 56 71.4004s52.7002 -29.2998 56 -71.4004c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998
c-3.09961 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996s-23.7998 -7.89941 -31.5 -21.5996l-9.5 -17c-1.90039 -3.2002 -5.7998 -4.7998 -9.2998 -3.7002z" />
<glyph glyph-name="grin-beam-sweat" unicode="&#xf583;" horiz-adv-x="496"
d="M440 288c-29.5 0 -53.2998 26.2998 -53.2998 58.7002c0 25 31.7002 75.5 46.2002 97.2998c3.5 5.2998 10.5996 5.2998 14.1992 0c14.5 -21.7998 46.2002 -72.2998 46.2002 -97.2998c0 -32.4004 -23.7998 -58.7002 -53.2998 -58.7002zM248 48
c-51.9004 0 -115.3 32.9004 -123.3 80c-1.7002 10 7.89941 18.4004 17.7002 15.2998c26 -8.2998 64.3994 -13.0996 105.6 -13.0996s79.7002 4.7998 105.6 13.0996c10 3.2002 19.4004 -5.39941 17.7002 -15.2998c-8 -47.0996 -71.3994 -80 -123.3 -80zM378.3 216.3
c-3.09961 -0.899414 -7.2002 0.100586 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996s-23.7998 -7.89941 -31.5 -21.5996l-9.5 -17c-1.90039 -3.2002 -5.7998 -4.7998 -9.2998 -3.7002c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998
c3.2998 42.1006 32.2002 71.4004 56 71.4004s52.7002 -29.2998 56 -71.4004c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998zM483.6 269.2c8 -24.2998 12.4004 -50.2002 12.4004 -77.2002c0 -137 -111 -248 -248 -248s-248 111 -248 248s111 248 248 248
c45.7002 0 88.4004 -12.5996 125.2 -34.2002c-10.9004 -21.5996 -15.5 -36.2002 -17.2002 -45.7002c-31.2002 20.1006 -68.2002 31.9004 -108 31.9004c-110.3 0 -200 -89.7002 -200 -200s89.7002 -200 200 -200s200 89.7002 200 200
c0 22.5 -3.90039 44.0996 -10.7998 64.2998c0.399414 0 21.7998 -2.7998 46.3994 12.9004zM168 258.6c-12.2998 0 -23.7998 -7.7998 -31.5 -21.5996l-9.5 -17c-1.90039 -3.2002 -5.7998 -4.7998 -9.2998 -3.7002c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998
c3.2998 42.1006 32.2002 71.4004 56 71.4004s52.7002 -29.2998 56 -71.4004c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998c-3.09961 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996z" />
<glyph glyph-name="grin-hearts" unicode="&#xf584;" horiz-adv-x="496"
d="M353.6 143.4c10 3.09961 19.3008 -5.5 17.7002 -15.3008c-8 -47.0996 -71.2998 -80 -123.3 -80s-115.4 32.8008 -123.3 80c-1.7002 10 7.89941 18.4004 17.7002 15.3008c26 -8.30078 64.3994 -13.1006 105.6 -13.1006s79.7002 4.7998 105.6 13.1006zM200.8 192.3
l-70.2002 18.1006c-20.3994 5.2998 -31.8994 27 -24.1992 47.1992c6.69922 17.7002 26.6992 26.7002 44.8994 22l7.10059 -1.89941l2 7.09961c5.09961 18.1006 22.8994 30.9004 41.5 27.9004c21.3994 -3.40039 34.3994 -24.2002 28.7998 -44.5l-19.4004 -69.9004
c-1.2998 -4.5 -6 -7.2002 -10.5 -6zM389.6 257.6c7.7002 -20.1992 -3.7998 -41.7998 -24.1992 -47.0996l-70.2002 -18.2002c-4.60059 -1.2002 -9.2998 1.5 -10.5 6l-19.4004 69.9004c-5.59961 20.2998 7.40039 41.0996 28.7998 44.5c18.7002 3 36.5 -9.7998 41.5 -27.9004
l2 -7.09961l7.10059 1.89941c18.2002 4.7002 38.2002 -4.39941 44.8994 -22zM248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200
s89.7002 -200 200 -200z" />
<glyph glyph-name="grin-squint" unicode="&#xf585;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM353.6 143.4c10 3.09961 19.3008 -5.5 17.7002 -15.3008
c-8 -47.0996 -71.2998 -80 -123.3 -80s-115.4 32.9004 -123.3 80c-1.7002 9.90039 7.7998 18.4004 17.7002 15.3008c26 -8.30078 64.3994 -13.1006 105.6 -13.1006s79.7002 4.7998 105.6 13.1006zM118.9 184.2c-3.80078 4.39941 -3.90039 11 -0.100586 15.5l33.6006 40.2998
l-33.6006 40.2998c-3.7002 4.5 -3.7002 11 0.100586 15.5c3.89941 4.40039 10.1992 5.5 15.2998 2.5l80 -48c3.59961 -2.2002 5.7998 -6.09961 5.7998 -10.2998s-2.2002 -8.09961 -5.7998 -10.2998l-80 -48c-5.40039 -3.2002 -11.7002 -1.7002 -15.2998 2.5zM361.8 181.7
l-80 48c-3.59961 2.2002 -5.7998 6.09961 -5.7998 10.2998s2.2002 8.09961 5.7998 10.2998l80 48c5.10059 2.90039 11.5 1.90039 15.2998 -2.5c3.80078 -4.5 3.90039 -11 0.100586 -15.5l-33.6006 -40.2998l33.6006 -40.2998c3.7002 -4.5 3.7002 -11 -0.100586 -15.5
c-3.59961 -4.2002 -9.89941 -5.7002 -15.2998 -2.5z" />
<glyph glyph-name="grin-squint-tears" unicode="&#xf586;"
d="M117.1 63.9004c6.30078 0.899414 11.7002 -4.5 10.9004 -10.9004c-3.7002 -25.7998 -13.7002 -84 -30.5996 -100.9c-22 -21.8994 -57.9004 -21.5 -80.3008 0.900391c-22.3994 22.4004 -22.7998 58.4004 -0.899414 80.2998
c16.8994 16.9004 75.0996 26.9004 100.899 30.6006zM75.9004 105.6c-19.6006 -3.89941 -35.1006 -8.09961 -47.3008 -12.1992c-39.2998 90.5996 -22.0996 199.899 52 274c48.5 48.3994 111.9 72.5996 175.4 72.5996c38.9004 0 77.7998 -9.2002 113.2 -27.4004
c-4 -12.1992 -8.2002 -28 -12 -48.2998c-30.4004 17.9004 -65 27.7002 -101.2 27.7002c-53.4004 0 -103.6 -20.7998 -141.4 -58.5996c-61.5996 -61.5 -74.2998 -153.4 -38.6992 -227.801zM428.2 293.2c20.2998 3.89941 36.2002 8 48.5 12
c47.8994 -93.2002 32.8994 -210.5 -45.2002 -288.601c-48.5 -48.3994 -111.9 -72.5996 -175.4 -72.5996c-33.6992 0 -67.2998 7 -98.6992 20.5996c4.19922 12.2002 8.2998 27.7002 12.1992 47.2002c26.6006 -12.7998 55.9004 -19.7998 86.4004 -19.7998
c53.4004 0 103.6 20.7998 141.4 58.5996c65.6992 65.7002 75.7998 166 30.7998 242.601zM394.9 320.1c-6.30078 -0.899414 -11.7002 4.5 -10.9004 10.9004c3.7002 25.7998 13.7002 84 30.5996 100.9c22 21.8994 57.9004 21.5 80.3008 -0.900391
c22.3994 -22.4004 22.7998 -58.4004 0.899414 -80.2998c-16.8994 -16.9004 -75.0996 -26.9004 -100.899 -30.6006zM207.9 211.8c3 -3 4.19922 -7.2998 3.19922 -11.5l-22.5996 -90.5c-1.40039 -5.39941 -6.2002 -9.09961 -11.7002 -9.09961h-0.899414
c-5.80078 0.5 -10.5 5.09961 -11 10.8994l-4.80078 52.3008l-52.2998 4.7998c-5.7998 0.5 -10.3994 5.2002 -10.8994 11c-0.400391 5.89941 3.39941 11.2002 9.09961 12.5996l90.5 22.7002c4.2002 1 8.40039 -0.200195 11.4004 -3.2002zM247.6 236.9
c-0.0996094 0 -6.39941 -1.80078 -11.3994 3.19922c-3 3 -4.2002 7.30078 -3.2002 11.4004l22.5996 90.5c1.40039 5.7002 7 9.2002 12.6006 9.09961c5.7998 -0.5 10.5 -5.09961 11 -10.8994l4.7998 -52.2998l52.2998 -4.80078c5.7998 -0.5 10.4004 -5.19922 10.9004 -11
c0.399414 -5.89941 -3.40039 -11.1992 -9.10059 -12.5996zM299.6 148.4c29.1006 29.0996 53 59.5996 65.3008 83.7998c4.89941 9.2998 17.5996 9.89941 23.3994 1.7002c27.7002 -38.9004 6.10059 -106.9 -30.5996 -143.7s-104.8 -58.2998 -143.7 -30.6006
c-8.2998 5.90039 -7.5 18.6006 1.7002 23.4004c24.2002 12.5 54.7998 36.2998 83.8994 65.4004z" />
<glyph glyph-name="grin-stars" unicode="&#xf587;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM353.6 143.4c10 3.09961 19.3008 -5.5 17.7002 -15.3008
c-8 -47.0996 -71.2998 -80 -123.3 -80s-115.4 32.8008 -123.3 80c-1.7002 10 7.89941 18.4004 17.7002 15.3008c26 -8.30078 64.3994 -13.1006 105.6 -13.1006s79.7002 4.7998 105.6 13.1006zM125.7 200.9l6.09961 34.8994l-25.3994 24.6006
c-4.60059 4.59961 -1.90039 12.2998 4.2998 13.1992l34.8994 5l15.5 31.6006c2.90039 5.7998 11 5.7998 13.9004 0l15.5 -31.6006l34.9004 -5c6.19922 -1 8.7998 -8.69922 4.2998 -13.1992l-25.4004 -24.6006l6 -34.8994c1 -6.2002 -5.39941 -11 -11 -7.90039
l-31.2998 16.2998l-31.2998 -16.2998c-5.60059 -3.09961 -12 1.7002 -11 7.90039zM385.4 273.6c6.19922 -1 8.89941 -8.59961 4.39941 -13.1992l-25.3994 -24.6006l6 -34.8994c1 -6.2002 -5.40039 -11 -11 -7.90039l-31.3008 16.2998l-31.2998 -16.2998
c-5.59961 -3.09961 -12 1.7002 -11 7.90039l6 34.8994l-25.3994 24.6006c-4.60059 4.59961 -1.90039 12.2998 4.2998 13.1992l34.8994 5l15.5 31.6006c2.90039 5.7998 11 5.7998 13.9004 0l15.5 -31.6006z" />
<glyph glyph-name="grin-tears" unicode="&#xf588;" horiz-adv-x="640"
d="M117.1 191.9c6.30078 0.899414 11.7002 -4.5 10.9004 -10.9004c-3.7002 -25.7998 -13.7002 -84 -30.5996 -100.9c-22 -21.8994 -57.9004 -21.5 -80.3008 0.900391c-22.3994 22.4004 -22.7998 58.4004 -0.899414 80.2998c16.8994 16.9004 75.0996 26.9004 100.899 30.6006
zM623.8 161.3c21.9004 -21.8994 21.5 -57.8994 -0.799805 -80.2002c-22.4004 -22.3994 -58.4004 -22.7998 -80.2998 -0.899414c-16.9004 16.8994 -26.9004 75.0996 -30.6006 100.899c-0.899414 6.30078 4.5 11.7002 10.8008 10.8008
c25.7998 -3.7002 84 -13.7002 100.899 -30.6006zM497.2 99.5996c12.3994 -37.2998 25.0996 -43.7998 28.2998 -46.5c-44.5996 -65.7998 -120 -109.1 -205.5 -109.1s-160.9 43.2998 -205.5 109.1c3.09961 2.60059 15.7998 9.10059 28.2998 46.5
c33.4004 -63.8994 100.3 -107.6 177.2 -107.6s143.8 43.7002 177.2 107.6zM122.7 223.5c-2.40039 0.299805 -5 2.5 -49.5 -6.90039c12.3994 125.4 118.1 223.4 246.8 223.4s234.4 -98 246.8 -223.5c-44.2998 9.40039 -47.3994 7.2002 -49.5 7
c-15.2002 95.2998 -97.7998 168.5 -197.3 168.5s-182.1 -73.2002 -197.3 -168.5zM320 48c-51.9004 0 -115.3 32.9004 -123.3 80c-1.7002 10 7.89941 18.4004 17.7002 15.2998c26 -8.2998 64.3994 -13.0996 105.6 -13.0996s79.7002 4.7998 105.6 13.0996
c10 3.2002 19.4004 -5.39941 17.7002 -15.2998c-8 -47.0996 -71.3994 -80 -123.3 -80zM450.3 216.3c-3.09961 -0.899414 -7.2002 0.100586 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996s-23.7998 -7.89941 -31.5 -21.5996l-9.5 -17
c-1.90039 -3.2002 -5.7998 -4.7998 -9.2998 -3.7002c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998c3.2998 42.1006 32.2002 71.4004 56 71.4004s52.7002 -29.2998 56 -71.4004c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998zM240 258.6
c-12.2998 0 -23.7998 -7.7998 -31.5 -21.5996l-9.5 -17c-1.90039 -3.2002 -5.7998 -4.7998 -9.2998 -3.7002c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998c3.2998 42.1006 32.2002 71.4004 56 71.4004s52.7002 -29.2998 56 -71.4004
c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998c-3.09961 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996z" />
<glyph glyph-name="grin-tongue" unicode="&#xf589;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM312 40h0.0996094v43.7998l-17.6992 8.7998c-15.1006 7.60059 -31.5 -1.69922 -34.9004 -16.5l-2.7998 -12.0996c-2.10059 -9.2002 -15.2002 -9.2002 -17.2998 0
l-2.80078 12.0996c-3.39941 14.8008 -19.8994 24 -34.8994 16.5l-17.7002 -8.7998v-42.7998c0 -35.2002 28 -64.5 63.0996 -65c35.8008 -0.5 64.9004 28.4004 64.9004 64zM340.2 14.7002c64 33.3994 107.8 100.3 107.8 177.3c0 110.3 -89.7002 200 -200 200
s-200 -89.7002 -200 -200c0 -77 43.7998 -143.9 107.8 -177.3c-2.2002 8.09961 -3.7998 16.5 -3.7998 25.2998v43.5c-14.2002 12.4004 -24.4004 27.5 -27.2998 44.5c-1.7002 10 7.7998 18.4004 17.7002 15.2998c26 -8.2998 64.3994 -13.0996 105.6 -13.0996
s79.7002 4.7998 105.6 13.0996c10 3.2002 19.4004 -5.39941 17.7002 -15.2998c-2.89941 -17 -13.0996 -32.0996 -27.2998 -44.5v-43.5c0 -8.7998 -1.59961 -17.2002 -3.7998 -25.2998zM168 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32
s14.2998 32 32 32zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32z" />
<glyph glyph-name="grin-tongue-squint" unicode="&#xf58a;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM312 40h0.0996094v43.7998l-17.6992 8.7998c-15.1006 7.60059 -31.5 -1.69922 -34.9004 -16.5l-2.7998 -12.0996c-2.10059 -9.2002 -15.2002 -9.2002 -17.2998 0
l-2.80078 12.0996c-3.39941 14.8008 -19.8994 24 -34.8994 16.5l-17.7002 -8.7998v-42.7998c0 -35.2002 28 -64.5 63.0996 -65c35.8008 -0.5 64.9004 28.4004 64.9004 64zM340.2 14.7002c64 33.3994 107.8 100.3 107.8 177.3c0 110.3 -89.7002 200 -200 200
s-200 -89.7002 -200 -200c0 -77 43.7998 -143.9 107.8 -177.3c-2.2002 8.09961 -3.7998 16.5 -3.7998 25.2998v43.5c-14.2002 12.4004 -24.4004 27.5 -27.2998 44.5c-1.7002 10 7.7998 18.4004 17.7002 15.2998c26 -8.2998 64.3994 -13.0996 105.6 -13.0996
s79.7002 4.7998 105.6 13.0996c10 3.2002 19.4004 -5.39941 17.7002 -15.2998c-2.89941 -17 -13.0996 -32.0996 -27.2998 -44.5v-43.5c0 -8.7998 -1.59961 -17.2002 -3.7998 -25.2998zM377.1 295.8c3.80078 -4.39941 3.90039 -11 0.100586 -15.5l-33.6006 -40.2998
l33.6006 -40.2998c3.7002 -4.5 3.7002 -11 -0.100586 -15.5c-3.59961 -4.2002 -9.89941 -5.7002 -15.2998 -2.5l-80 48c-3.59961 2.2002 -5.7998 6.09961 -5.7998 10.2998s2.2002 8.09961 5.7998 10.2998l80 48c5 3 11.5 1.90039 15.2998 -2.5zM214.2 250.3
c3.59961 -2.2002 5.7998 -6.09961 5.7998 -10.2998s-2.2002 -8.09961 -5.7998 -10.2998l-80 -48c-5.40039 -3.2002 -11.7002 -1.7002 -15.2998 2.5c-3.80078 4.5 -3.90039 11 -0.100586 15.5l33.6006 40.2998l-33.6006 40.2998c-3.7002 4.5 -3.7002 11 0.100586 15.5
c3.89941 4.5 10.2998 5.5 15.2998 2.5z" />
<glyph glyph-name="grin-tongue-wink" unicode="&#xf58b;" horiz-adv-x="496"
d="M152 268c25.7002 0 55.9004 -16.9004 59.7998 -42.0996c0.799805 -5 -1.7002 -10 -6.09961 -12.4004c-5.7002 -3.09961 -11.2002 -0.599609 -13.7002 1.59961l-9.5 8.5c-14.7998 13.2002 -46.2002 13.2002 -61 0l-9.5 -8.5
c-3.7998 -3.39941 -9.2998 -4 -13.7002 -1.59961c-4.39941 2.40039 -6.89941 7.40039 -6.09961 12.4004c3.89941 25.1992 34.0996 42.0996 59.7998 42.0996zM328 320c44.2002 0 80 -35.7998 80 -80s-35.7998 -80 -80 -80s-80 35.7998 -80 80s35.7998 80 80 80zM328 192
c26.5 0 48 21.5 48 48s-21.5 48 -48 48s-48 -21.5 -48 -48s21.5 -48 48 -48zM328 264c13.2998 0 24 -10.7002 24 -24s-10.7002 -24 -24 -24s-24 10.7002 -24 24s10.7002 24 24 24zM248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248z
M312 40h0.0996094v43.7998l-17.6992 8.7998c-15.1006 7.60059 -31.5 -1.69922 -34.9004 -16.5l-2.7998 -12.0996c-2.10059 -9.2002 -15.2002 -9.2002 -17.2998 0l-2.80078 12.0996c-3.39941 14.8008 -19.8994 24 -34.8994 16.5l-17.7002 -8.7998v-42.7998
c0 -35.2002 28 -64.5 63.0996 -65c35.8008 -0.5 64.9004 28.4004 64.9004 64zM340.2 14.7002c64 33.3994 107.8 100.3 107.8 177.3c0 110.3 -89.7002 200 -200 200s-200 -89.7002 -200 -200c0 -77 43.7998 -143.9 107.8 -177.3
c-2.2002 8.09961 -3.7998 16.5 -3.7998 25.2998v43.5c-14.2002 12.4004 -24.4004 27.5 -27.2998 44.5c-1.7002 10 7.7998 18.4004 17.7002 15.2998c26 -8.2998 64.3994 -13.0996 105.6 -13.0996s79.7002 4.7998 105.6 13.0996c10 3.2002 19.4004 -5.39941 17.7002 -15.2998
c-2.89941 -17 -13.0996 -32.0996 -27.2998 -44.5v-43.5c0 -8.7998 -1.59961 -17.2002 -3.7998 -25.2998z" />
<glyph glyph-name="grin-wink" unicode="&#xf58c;" horiz-adv-x="496"
d="M328 268c25.6904 0 55.8799 -16.9199 59.8701 -42.1201c1.72949 -11.0898 -11.3506 -18.2695 -19.8301 -10.8398l-9.5498 8.47949c-14.8105 13.1904 -46.1602 13.1904 -60.9707 0l-9.5498 -8.47949c-8.33008 -7.40039 -21.5801 -0.379883 -19.8301 10.8398
c3.98047 25.2002 34.1699 42.1201 59.8604 42.1201zM168 208c-17.6699 0 -32 14.3301 -32 32s14.3301 32 32 32s32 -14.3301 32 -32s-14.3301 -32 -32 -32zM353.55 143.36c10.04 3.13965 19.3906 -5.4502 17.71 -15.3408
c-7.92969 -47.1494 -71.3193 -80.0195 -123.26 -80.0195s-115.33 32.8701 -123.26 80.0195c-1.69043 9.9707 7.76953 18.4707 17.71 15.3408c25.9297 -8.31055 64.3994 -13.0605 105.55 -13.0605s79.6201 4.75977 105.55 13.0605zM248 440c136.97 0 248 -111.03 248 -248
s-111.03 -248 -248 -248s-248 111.03 -248 248s111.03 248 248 248zM248 -8c110.28 0 200 89.7197 200 200s-89.7197 200 -200 200s-200 -89.7197 -200 -200s89.7197 -200 200 -200z" />
<glyph glyph-name="kiss" unicode="&#xf596;" horiz-adv-x="496"
d="M168 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32zM304 140c0 -13 -13.4004 -27.2998 -35.0996 -36.4004c21.7998 -8.69922 35.1992 -23 35.1992 -36c0 -19.1992 -28.6992 -41.5 -71.5 -44h-0.5
c-3.69922 0 -7 2.60059 -7.7998 6.2002c-0.899414 3.7998 1.10059 7.7002 4.7002 9.2002l17 7.2002c12.9004 5.5 20.7002 13.5 20.7002 21.5s-7.7998 16 -20.7998 21.5l-16.9004 7.2002c-6 2.59961 -5.7002 12.3994 0 14.7998l17 7.2002
c12.9004 5.5 20.7002 13.5 20.7002 21.5s-7.7998 16 -20.7998 21.5l-16.9004 7.19922c-3.59961 1.5 -5.59961 5.40039 -4.7002 9.2002c0.799805 3.7998 4.40039 6.60059 8.2002 6.2002c42.7002 -2.5 71.5 -24.7998 71.5 -44zM248 440c137 0 248 -111 248 -248
s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32z
" />
<glyph glyph-name="kiss-beam" unicode="&#xf597;" horiz-adv-x="496"
d="M168 296c23.7998 0 52.7002 -29.2998 55.7998 -71.4004c0.299805 -3.7998 -2 -7.19922 -5.59961 -8.2998c-3.10059 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996c-12.3008 0 -23.8008 -7.89941 -31.5 -21.5996l-9.5 -17
c-1.80078 -3.2002 -5.80078 -4.7002 -9.30078 -3.7002c-3.59961 1.10059 -5.89941 4.60059 -5.59961 8.2998c3.2998 42.1006 32.2002 71.4004 56 71.4004zM248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8
c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM304 140c0 -13 -13.4004 -27.2998 -35.0996 -36.4004c21.7998 -8.69922 35.1992 -23 35.1992 -36c0 -19.1992 -28.6992 -41.5 -71.5 -44h-0.5
c-3.69922 0 -7 2.60059 -7.7998 6.2002c-0.899414 3.7998 1.10059 7.7002 4.7002 9.2002l17 7.2002c12.9004 5.5 20.7002 13.5 20.7002 21.5s-7.7998 16 -20.7998 21.5l-16.9004 7.2002c-6 2.59961 -5.7002 12.3994 0 14.7998l17 7.2002
c12.9004 5.5 20.7002 13.5 20.7002 21.5s-7.7998 16 -20.7998 21.5l-16.9004 7.19922c-3.59961 1.5 -5.59961 5.40039 -4.7002 9.2002c0.799805 3.7998 4.40039 6.60059 8.2002 6.2002c42.7002 -2.5 71.5 -24.7998 71.5 -44zM328 296
c23.7998 0 52.7002 -29.2998 55.7998 -71.4004c0.299805 -3.7998 -2 -7.19922 -5.59961 -8.2998c-3.10059 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996c-12.3008 0 -23.8008 -7.89941 -31.5 -21.5996l-9.5 -17
c-1.80078 -3.2002 -5.80078 -4.7002 -9.30078 -3.7002c-3.59961 1.10059 -5.89941 4.60059 -5.59961 8.2998c3.2998 42.1006 32.2002 71.4004 56 71.4004z" />
<glyph glyph-name="kiss-wink-heart" unicode="&#xf598;" horiz-adv-x="504"
d="M304 139.5c0 -13 -13.4004 -27.2998 -35.0996 -36.4004c21.7998 -8.69922 35.1992 -23 35.1992 -36c0 -19.1992 -28.6992 -41.5 -71.5 -44h-0.5c-3.69922 0 -7 2.60059 -7.7998 6.2002c-0.899414 3.7998 1.10059 7.7002 4.7002 9.2002l17 7.2002
c12.9004 5.5 20.7002 13.5 20.7002 21.5s-7.7998 16 -20.7998 21.5l-16.9004 7.2002c-6 2.59961 -5.7002 12.3994 0 14.7998l17 7.2002c12.9004 5.5 20.7002 13.5 20.7002 21.5s-7.7998 16 -20.7998 21.5l-16.9004 7.19922c-3.59961 1.5 -5.59961 5.40039 -4.7002 9.2002
c0.799805 3.7998 4.40039 6.60059 8.2002 6.2002c42.7002 -2.5 71.5 -24.7998 71.5 -44zM374.5 223c-14.7998 13.2002 -46.2002 13.2002 -61 0l-9.5 -8.5c-2.5 -2.2998 -7.90039 -4.7002 -13.7002 -1.59961c-4.39941 2.39941 -6.89941 7.39941 -6.09961 12.3994
c3.89941 25.2002 34.2002 42.1006 59.7998 42.1006s55.7998 -16.9004 59.7998 -42.1006c0.799805 -5 -1.7002 -10 -6.09961 -12.3994c-4.40039 -2.40039 -9.90039 -1.7002 -13.7002 1.59961zM136 239.5c0 17.7002 14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32
s-32 14.2998 -32 32zM501.1 45.5c9.2002 -23.9004 -4.39941 -49.4004 -28.5 -55.7002l-83 -21.5c-5.39941 -1.39941 -10.8994 1.7998 -12.3994 7.10059l-22.9004 82.5996c-6.59961 24 8.7998 48.5996 34 52.5996c22 3.5 43.1006 -11.5996 49 -33l2.2998 -8.39941
l8.40039 2.2002c21.5996 5.59961 45.0996 -5.10059 53.0996 -25.9004zM334 11.7002c17.7002 -64 10.9004 -39.5 13.4004 -46.7998c-30.5 -13.4004 -64 -20.9004 -99.4004 -20.9004c-137 0 -248 111 -248 248s111 248 248 248s248 -111 247.9 -248
c0 -31.7998 -6.2002 -62.0996 -17.1006 -90c-6 1.5 -12.2002 2.7998 -18.5996 2.90039c-5.60059 9.69922 -13.6006 17.5 -22.6006 23.8994c6.7002 19.9004 10.4004 41.1006 10.4004 63.2002c0 110.3 -89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200
c30.7998 0 59.9004 7.2002 86 19.7002z" />
<glyph glyph-name="laugh" unicode="&#xf599;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM389.4 50.5996c37.7998 37.8008 58.5996 88 58.5996 141.4s-20.7998 103.6 -58.5996 141.4c-37.8008 37.7998 -88 58.5996 -141.4 58.5996s-103.6 -20.7998 -141.4 -58.5996
c-37.7998 -37.8008 -58.5996 -88 -58.5996 -141.4s20.7998 -103.6 58.5996 -141.4c37.8008 -37.7998 88 -58.5996 141.4 -58.5996s103.6 20.7998 141.4 58.5996zM328 224c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM168 224
c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM362.4 160c8.19922 0 14.5 -7 13.5 -15c-7.5 -59.2002 -58.9004 -105 -121.101 -105h-13.5996c-62.2002 0 -113.601 45.7998 -121.101 105c-1 8 5.30078 15 13.5 15h228.801z" />
<glyph glyph-name="laugh-beam" unicode="&#xf59a;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM389.4 50.5996c37.7998 37.8008 58.5996 88 58.5996 141.4s-20.7998 103.6 -58.5996 141.4c-37.8008 37.7998 -88 58.5996 -141.4 58.5996s-103.6 -20.7998 -141.4 -58.5996
c-37.7998 -37.8008 -58.5996 -88 -58.5996 -141.4s20.7998 -103.6 58.5996 -141.4c37.8008 -37.7998 88 -58.5996 141.4 -58.5996s103.6 20.7998 141.4 58.5996zM328 296c23.7998 0 52.7002 -29.2998 55.7998 -71.4004c0.700195 -8.5 -10.7998 -11.8994 -14.8994 -4.5
l-9.5 17c-7.7002 13.7002 -19.2002 21.6006 -31.5 21.6006c-12.3008 0 -23.8008 -7.90039 -31.5 -21.6006l-9.5 -17c-4.10059 -7.39941 -15.6006 -4.09961 -14.9004 4.5c3.2998 42.1006 32.2002 71.4004 56 71.4004zM127 220.1c-4.2002 -7.39941 -15.7002 -4 -15.0996 4.5
c3.2998 42.1006 32.1992 71.4004 56 71.4004c23.7998 0 52.6992 -29.2998 56 -71.4004c0.699219 -8.5 -10.8008 -11.8994 -14.9004 -4.5l-9.5 17c-7.7002 13.7002 -19.2002 21.6006 -31.5 21.6006s-23.7998 -7.90039 -31.5 -21.6006zM362.4 160c8.19922 0 14.5 -7 13.5 -15
c-7.5 -59.2002 -58.9004 -105 -121.101 -105h-13.5996c-62.2002 0 -113.601 45.7998 -121.101 105c-1 8 5.30078 15 13.5 15h228.801z" />
<glyph glyph-name="laugh-squint" unicode="&#xf59b;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM389.4 50.5996c37.7998 37.8008 58.5996 88 58.5996 141.4s-20.7998 103.6 -58.5996 141.4c-37.8008 37.7998 -88 58.5996 -141.4 58.5996s-103.6 -20.7998 -141.4 -58.5996
c-37.7998 -37.8008 -58.5996 -88 -58.5996 -141.4s20.7998 -103.6 58.5996 -141.4c37.8008 -37.7998 88 -58.5996 141.4 -58.5996s103.6 20.7998 141.4 58.5996zM343.6 252l33.6006 -40.2998c8.59961 -10.4004 -3.90039 -24.7998 -15.4004 -18l-80 48
c-7.7998 4.7002 -7.7998 15.8994 0 20.5996l80 48c11.6006 6.7998 24 -7.7002 15.4004 -18zM134.2 193.7c-11.6006 -6.7998 -24.1006 7.59961 -15.4004 18l33.6006 40.2998l-33.6006 40.2998c-8.59961 10.2998 3.7998 24.9004 15.4004 18l80 -48
c7.7998 -4.7002 7.7998 -15.8994 0 -20.5996zM362.4 160c8.19922 0 14.5 -7 13.5 -15c-7.5 -59.2002 -58.9004 -105 -121.101 -105h-13.5996c-62.2002 0 -113.601 45.7998 -121.101 105c-1 8 5.30078 15 13.5 15h228.801z" />
<glyph glyph-name="laugh-wink" unicode="&#xf59c;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM389.4 50.5996c37.7998 37.8008 58.5996 88 58.5996 141.4s-20.7998 103.6 -58.5996 141.4c-37.8008 37.7998 -88 58.5996 -141.4 58.5996s-103.6 -20.7998 -141.4 -58.5996
c-37.7998 -37.8008 -58.5996 -88 -58.5996 -141.4s20.7998 -103.6 58.5996 -141.4c37.8008 -37.7998 88 -58.5996 141.4 -58.5996s103.6 20.7998 141.4 58.5996zM328 284c25.7002 0 55.9004 -16.9004 59.7002 -42.0996c1.7998 -11.1006 -11.2998 -18.2002 -19.7998 -10.8008
l-9.5 8.5c-14.8008 13.2002 -46.2002 13.2002 -61 0l-9.5 -8.5c-8.30078 -7.39941 -21.5 -0.399414 -19.8008 10.8008c4 25.1992 34.2002 42.0996 59.9004 42.0996zM168 224c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32z
M362.4 160c8.19922 0 14.5 -7 13.5 -15c-7.5 -59.2002 -58.9004 -105 -121.101 -105h-13.5996c-62.2002 0 -113.601 45.7998 -121.101 105c-1 8 5.30078 15 13.5 15h228.801z" />
<glyph glyph-name="meh-blank" unicode="&#xf5a4;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM168 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32
s-32 14.2998 -32 32s14.2998 32 32 32zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32z" />
<glyph glyph-name="meh-rolling-eyes" unicode="&#xf5a5;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM336 296c39.7998 0 72 -32.2002 72 -72s-32.2002 -72 -72 -72
s-72 32.2002 -72 72s32.2002 72 72 72zM336 184c22.0996 0 40 17.9004 40 40c0 13.5996 -7.2998 25.0996 -17.7002 32.2998c1 -2.59961 1.7002 -5.39941 1.7002 -8.2998c0 -13.2998 -10.7002 -24 -24 -24s-24 10.7002 -24 24c0 3 0.700195 5.7002 1.7002 8.2998
c-10.4004 -7.2002 -17.7002 -18.7002 -17.7002 -32.2998c0 -22.0996 17.9004 -40 40 -40zM232 224c0 -39.7998 -32.2002 -72 -72 -72s-72 32.2002 -72 72s32.2002 72 72 72s72 -32.2002 72 -72zM120 224c0 -22.0996 17.9004 -40 40 -40s40 17.9004 40 40
c0 13.5996 -7.2998 25.0996 -17.7002 32.2998c1 -2.59961 1.7002 -5.39941 1.7002 -8.2998c0 -13.2998 -10.7002 -24 -24 -24s-24 10.7002 -24 24c0 3 0.700195 5.7002 1.7002 8.2998c-10.4004 -7.2002 -17.7002 -18.7002 -17.7002 -32.2998zM312 96
c13.2002 0 24 -10.7998 24 -24s-10.7998 -24 -24 -24h-128c-13.2002 0 -24 10.7998 -24 24s10.7998 24 24 24h128z" />
<glyph glyph-name="sad-cry" unicode="&#xf5b3;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM392 53.5996c34.5996 35.9004 56 84.7002 56 138.4c0 110.3 -89.7002 200 -200 200s-200 -89.7002 -200 -200c0 -53.7002 21.4004 -102.4 56 -138.4v114.4
c0 13.2002 10.7998 24 24 24s24 -10.7998 24 -24v-151.4c28.5 -15.5996 61.2002 -24.5996 96 -24.5996s67.5 9 96 24.5996v151.4c0 13.2002 10.7998 24 24 24s24 -10.7998 24 -24v-114.4zM205.8 213.5c-5.7998 -3.2002 -11.2002 -0.700195 -13.7002 1.59961l-9.5 8.5
c-14.7998 13.2002 -46.1992 13.2002 -61 0l-9.5 -8.5c-3.7998 -3.39941 -9.2998 -4 -13.6992 -1.59961c-4.40039 2.40039 -6.90039 7.40039 -6.10059 12.4004c3.90039 25.1992 34.2002 42.0996 59.7998 42.0996c25.6006 0 55.8008 -16.9004 59.8008 -42.0996
c0.799805 -5 -1.7002 -10 -6.10059 -12.4004zM344 268c25.7002 0 55.9004 -16.9004 59.7998 -42.0996c0.799805 -5 -1.7002 -10 -6.09961 -12.4004c-5.7002 -3.09961 -11.2002 -0.599609 -13.7002 1.59961l-9.5 8.5c-14.7998 13.2002 -46.2002 13.2002 -61 0l-9.5 -8.5
c-3.7998 -3.39941 -9.2002 -4 -13.7002 -1.59961c-4.39941 2.40039 -6.89941 7.40039 -6.09961 12.4004c3.89941 25.1992 34.0996 42.0996 59.7998 42.0996zM248 176c30.9004 0 56 -28.7002 56 -64s-25.0996 -64 -56 -64s-56 28.7002 -56 64s25.0996 64 56 64z" />
<glyph glyph-name="sad-tear" unicode="&#xf5b4;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM256 144c38.0996 0 74 -16.7998 98.5 -46.0996
c8.5 -10.2002 7.09961 -25.3008 -3.09961 -33.8008c-10.6006 -8.7998 -25.7002 -6.69922 -33.8008 3.10059c-15.2998 18.2998 -37.7998 28.7998 -61.5996 28.7998c-13.2002 0 -24 10.7998 -24 24s10.7998 24 24 24zM168 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32
s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32zM162.4 173.2c2.7998 3.7002 8.39941 3.7002 11.1992 0c11.4004 -15.2998 36.4004 -50.6006 36.4004 -68.1006
c0 -22.6992 -18.7998 -41.0996 -42 -41.0996s-42 18.4004 -42 41.0996c0 17.5 25 52.8008 36.4004 68.1006z" />
<glyph glyph-name="smile-beam" unicode="&#xf5b8;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM332 135.4c8.5 10.1992 23.5996 11.5 33.7998 3.09961
c10.2002 -8.5 11.6006 -23.5996 3.10059 -33.7998c-30 -36 -74.1006 -56.6006 -120.9 -56.6006s-90.9004 20.6006 -120.9 56.6006c-8.39941 10.2002 -7.09961 25.2998 3.10059 33.7998c10.2002 8.40039 25.2998 7.09961 33.7998 -3.09961
c20.7998 -25.1006 51.5 -39.4004 84 -39.4004s63.2002 14.4004 84 39.4004zM136.5 237l-9.5 -17c-1.90039 -3.2002 -5.90039 -4.7998 -9.2998 -3.7002c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998c3.2998 42.1006 32.2002 71.4004 56 71.4004s52.7002 -29.2998 56 -71.4004
c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998c-3.09961 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996s-23.7998 -7.89941 -31.5 -21.5996zM328 296c23.7998 0 52.7002 -29.2998 56 -71.4004
c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998c-3.09961 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996s-23.7998 -7.89941 -31.5 -21.5996l-9.5 -17c-1.90039 -3.2002 -5.7998 -4.7998 -9.2998 -3.7002
c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998c3.2998 42.1006 32.2002 71.4004 56 71.4004z" />
<glyph glyph-name="surprise" unicode="&#xf5c2;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM248 168c35.2998 0 64 -28.7002 64 -64s-28.7002 -64 -64 -64
s-64 28.7002 -64 64s28.7002 64 64 64zM200 240c0 -17.7002 -14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32z" />
<glyph glyph-name="tired" unicode="&#xf5c8;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM377.1 295.8c3.80078 -4.39941 3.90039 -11 0.100586 -15.5
l-33.6006 -40.2998l33.6006 -40.2998c3.7998 -4.5 3.7002 -11 -0.100586 -15.5c-3.5 -4.10059 -9.89941 -5.7002 -15.2998 -2.5l-80 48c-3.59961 2.2002 -5.7998 6.09961 -5.7998 10.2998s2.2002 8.09961 5.7998 10.2998l80 48c5 2.90039 11.5 1.90039 15.2998 -2.5z
M220 240c0 -4.2002 -2.2002 -8.09961 -5.7998 -10.2998l-80 -48c-5.40039 -3.2002 -11.7998 -1.60059 -15.2998 2.5c-3.80078 4.5 -3.90039 11 -0.100586 15.5l33.6006 40.2998l-33.6006 40.2998c-3.7998 4.5 -3.7002 11 0.100586 15.5
c3.7998 4.40039 10.2998 5.5 15.2998 2.5l80 -48c3.59961 -2.2002 5.7998 -6.09961 5.7998 -10.2998zM248 176c45.4004 0 100.9 -38.2998 107.8 -93.2998c1.5 -11.9004 -7 -21.6006 -15.5 -17.9004c-22.7002 9.7002 -56.2998 15.2002 -92.2998 15.2002
s-69.5996 -5.5 -92.2998 -15.2002c-8.60059 -3.7002 -17 6.10059 -15.5 17.9004c6.89941 55 62.3994 93.2998 107.8 93.2998z" />
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 141 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 876 KiB

@ -0,0 +1,25 @@
<!-- these macros are generated by "yarn build:production". do not edit by hand. -->
{% macro head_pre_icons() %}
<link rel="stylesheet"
href="{{ pathto('_static/vendor/fontawesome/5.13.0/css/all.min.css', 1) }}">
<link rel="preload" as="font" type="font/woff2" crossorigin
href="{{ pathto('_static/vendor/fontawesome/5.13.0/webfonts/fa-solid-900.woff2', 1) }}">
<link rel="preload" as="font" type="font/woff2" crossorigin
href="{{ pathto('_static/vendor/fontawesome/5.13.0/webfonts/fa-brands-400.woff2', 1) }}">
{% endmacro %}
{% macro head_pre_fonts() %}
{% endmacro %}
{% macro head_pre_bootstrap() %}
<link href="{{ pathto('_static/css/theme.css', 1) }}" rel="stylesheet" />
<link href="{{ pathto('_static/css/index.c5995385ac14fb8791e8eb36b4908be2.css', 1) }}" rel="stylesheet" />
{% endmacro %}
{% macro head_js_preload() %}
<link rel="preload" as="script" href="{{ pathto('_static/js/index.1c5a1a01449ed65a7b51.js', 1) }}">
{% endmacro %}
{% macro body_post() %}
<script src="{{ pathto('_static/js/index.1c5a1a01449ed65a7b51.js', 1) }}"></script>
{% endmacro %}

@ -0,0 +1,771 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<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" />
<title>11. Collectors &amp; Extractors &#8212; MIPLearn 0.4</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" type="text/css" href="../../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<link rel="index" title="Index" href="../../genindex/" />
<link rel="search" title="Search" href="../../search/" />
<link rel="next" title="12. Components" href="../components/" />
<link rel="prev" title="10. Benchmark Problems" href="../problems/" />
<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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../guide/problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="current nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1 current active">
<a class="current reference internal" href="#">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../components/">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../helpers/">
14. 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/api/collectors.rst.txt"><button type="button"
class="btn btn-secondary topbarbtn" title="Download source file" data-toggle="tooltip"
data-placement="left">.rst</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="#module-miplearn.classifiers.minprob">
11.1. miplearn.classifiers.minprob
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.classifiers.minprob.MinProbabilityClassifier">
<code class="docutils literal notranslate">
<span class="pre">
MinProbabilityClassifier
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.classifiers.minprob.MinProbabilityClassifier.fit">
<code class="docutils literal notranslate">
<span class="pre">
MinProbabilityClassifier.fit()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.classifiers.minprob.MinProbabilityClassifier.predict">
<code class="docutils literal notranslate">
<span class="pre">
MinProbabilityClassifier.predict()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.classifiers.minprob.MinProbabilityClassifier.set_fit_request">
<code class="docutils literal notranslate">
<span class="pre">
MinProbabilityClassifier.set_fit_request()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.classifiers.minprob.MinProbabilityClassifier.set_predict_request">
<code class="docutils literal notranslate">
<span class="pre">
MinProbabilityClassifier.set_predict_request()
</span>
</code>
</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.classifiers.singleclass">
11.2. miplearn.classifiers.singleclass
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.classifiers.singleclass.SingleClassFix">
<code class="docutils literal notranslate">
<span class="pre">
SingleClassFix
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.classifiers.singleclass.SingleClassFix.fit">
<code class="docutils literal notranslate">
<span class="pre">
SingleClassFix.fit()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.classifiers.singleclass.SingleClassFix.predict">
<code class="docutils literal notranslate">
<span class="pre">
SingleClassFix.predict()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.classifiers.singleclass.SingleClassFix.set_fit_request">
<code class="docutils literal notranslate">
<span class="pre">
SingleClassFix.set_fit_request()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.classifiers.singleclass.SingleClassFix.set_predict_request">
<code class="docutils literal notranslate">
<span class="pre">
SingleClassFix.set_predict_request()
</span>
</code>
</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.collectors.basic">
11.3. miplearn.collectors.basic
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.collectors.basic.BasicCollector">
<code class="docutils literal notranslate">
<span class="pre">
BasicCollector
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.collectors.basic.BasicCollector.collect">
<code class="docutils literal notranslate">
<span class="pre">
BasicCollector.collect()
</span>
</code>
</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.extractors.fields">
11.4. miplearn.extractors.fields
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.extractors.fields.H5FieldsExtractor">
<code class="docutils literal notranslate">
<span class="pre">
H5FieldsExtractor
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.extractors.fields.H5FieldsExtractor.get_constr_features">
<code class="docutils literal notranslate">
<span class="pre">
H5FieldsExtractor.get_constr_features()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.extractors.fields.H5FieldsExtractor.get_instance_features">
<code class="docutils literal notranslate">
<span class="pre">
H5FieldsExtractor.get_instance_features()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.extractors.fields.H5FieldsExtractor.get_var_features">
<code class="docutils literal notranslate">
<span class="pre">
H5FieldsExtractor.get_var_features()
</span>
</code>
</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.extractors.AlvLouWeh2017">
11.5. miplearn.extractors.AlvLouWeh2017
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor">
<code class="docutils literal notranslate">
<span class="pre">
AlvLouWeh2017Extractor
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor.get_constr_features">
<code class="docutils literal notranslate">
<span class="pre">
AlvLouWeh2017Extractor.get_constr_features()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor.get_instance_features">
<code class="docutils literal notranslate">
<span class="pre">
AlvLouWeh2017Extractor.get_instance_features()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor.get_var_features">
<code class="docutils literal notranslate">
<span class="pre">
AlvLouWeh2017Extractor.get_var_features()
</span>
</code>
</a>
</li>
</ul>
</li>
</ul>
</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>
<section id="collectors-extractors">
<h1><span class="section-number">11. </span>Collectors &amp; Extractors<a class="headerlink" href="#collectors-extractors" title="Link to this heading"></a></h1>
<section id="module-miplearn.classifiers.minprob">
<span id="miplearn-classifiers-minprob"></span><h2><span class="section-number">11.1. </span>miplearn.classifiers.minprob<a class="headerlink" href="#module-miplearn.classifiers.minprob" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.classifiers.minprob.MinProbabilityClassifier">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.classifiers.minprob.</span></span><span class="sig-name descname"><span class="pre">MinProbabilityClassifier</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="pre">base_clf:</span> <span class="pre">~typing.Any,</span> <span class="pre">thresholds:</span> <span class="pre">~typing.List[float],</span> <span class="pre">clone_fn:</span> <span class="pre">~typing.Callable[[~typing.Any],</span> <span class="pre">~typing.Any]</span> <span class="pre">=</span> <span class="pre">&lt;function</span> <span class="pre">clone&gt;</span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.classifiers.minprob.MinProbabilityClassifier" title="Link to this definition"></a></dt>
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">BaseEstimator</span></code></p>
<p>Meta-classifier that returns NaN for predictions made by a base classifier that
have probability below a given threshold. More specifically, this meta-classifier
calls base_clf.predict_proba and compares the result against the provided
thresholds. If the probability for one of the classes is above its threshold,
the meta-classifier returns that prediction. Otherwise, it returns NaN.</p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.classifiers.minprob.MinProbabilityClassifier.fit">
<span class="sig-name descname"><span class="pre">fit</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">x</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">y</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.classifiers.minprob.MinProbabilityClassifier.fit" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.classifiers.minprob.MinProbabilityClassifier.predict">
<span class="sig-name descname"><span class="pre">predict</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">x</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ndarray</span></span></span><a class="headerlink" href="#miplearn.classifiers.minprob.MinProbabilityClassifier.predict" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.classifiers.minprob.MinProbabilityClassifier.set_fit_request">
<span class="sig-name descname"><span class="pre">set_fit_request</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="o"><span class="pre">*</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">x</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">str</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'$UNCHANGED$'</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference internal" href="#miplearn.classifiers.minprob.MinProbabilityClassifier" title="miplearn.classifiers.minprob.MinProbabilityClassifier"><span class="pre">MinProbabilityClassifier</span></a></span></span><a class="headerlink" href="#miplearn.classifiers.minprob.MinProbabilityClassifier.set_fit_request" title="Link to this definition"></a></dt>
<dd><p>Request metadata passed to the <code class="docutils literal notranslate"><span class="pre">fit</span></code> method.</p>
<p>Note that this method is only relevant if
<code class="docutils literal notranslate"><span class="pre">enable_metadata_routing=True</span></code> (see <code class="xref py py-func docutils literal notranslate"><span class="pre">sklearn.set_config()</span></code>).
Please see <span class="xref std std-ref">User Guide</span> on how the routing
mechanism works.</p>
<p>The options for each parameter are:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">True</span></code>: metadata is requested, and passed to <code class="docutils literal notranslate"><span class="pre">fit</span></code> if provided. The request is ignored if metadata is not provided.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">False</span></code>: metadata is not requested and the meta-estimator will not pass it to <code class="docutils literal notranslate"><span class="pre">fit</span></code>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">None</span></code>: metadata is not requested, and the meta-estimator will raise an error if the user provides it.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">str</span></code>: metadata should be passed to the meta-estimator with this given alias instead of the original name.</p></li>
</ul>
<p>The default (<code class="docutils literal notranslate"><span class="pre">sklearn.utils.metadata_routing.UNCHANGED</span></code>) retains the
existing request. This allows you to change the request for some
parameters and not others.</p>
<div class="versionadded">
<p><span class="versionmodified added">New in version 1.3.</span></p>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>This method is only relevant if this estimator is used as a
sub-estimator of a meta-estimator, e.g. used inside a
<code class="xref py py-class docutils literal notranslate"><span class="pre">Pipeline</span></code>. Otherwise it has no effect.</p>
</div>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><p><strong>x</strong> (<em>str</em><em>, </em><em>True</em><em>, </em><em>False</em><em>, or </em><em>None</em><em>, </em><em>default=sklearn.utils.metadata_routing.UNCHANGED</em>) Metadata routing for <code class="docutils literal notranslate"><span class="pre">x</span></code> parameter in <code class="docutils literal notranslate"><span class="pre">fit</span></code>.</p>
</dd>
<dt class="field-even">Returns<span class="colon">:</span></dt>
<dd class="field-even"><p><strong>self</strong> The updated object.</p>
</dd>
<dt class="field-odd">Return type<span class="colon">:</span></dt>
<dd class="field-odd"><p>object</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.classifiers.minprob.MinProbabilityClassifier.set_predict_request">
<span class="sig-name descname"><span class="pre">set_predict_request</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="o"><span class="pre">*</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">x</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">str</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'$UNCHANGED$'</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference internal" href="#miplearn.classifiers.minprob.MinProbabilityClassifier" title="miplearn.classifiers.minprob.MinProbabilityClassifier"><span class="pre">MinProbabilityClassifier</span></a></span></span><a class="headerlink" href="#miplearn.classifiers.minprob.MinProbabilityClassifier.set_predict_request" title="Link to this definition"></a></dt>
<dd><p>Request metadata passed to the <code class="docutils literal notranslate"><span class="pre">predict</span></code> method.</p>
<p>Note that this method is only relevant if
<code class="docutils literal notranslate"><span class="pre">enable_metadata_routing=True</span></code> (see <code class="xref py py-func docutils literal notranslate"><span class="pre">sklearn.set_config()</span></code>).
Please see <span class="xref std std-ref">User Guide</span> on how the routing
mechanism works.</p>
<p>The options for each parameter are:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">True</span></code>: metadata is requested, and passed to <code class="docutils literal notranslate"><span class="pre">predict</span></code> if provided. The request is ignored if metadata is not provided.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">False</span></code>: metadata is not requested and the meta-estimator will not pass it to <code class="docutils literal notranslate"><span class="pre">predict</span></code>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">None</span></code>: metadata is not requested, and the meta-estimator will raise an error if the user provides it.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">str</span></code>: metadata should be passed to the meta-estimator with this given alias instead of the original name.</p></li>
</ul>
<p>The default (<code class="docutils literal notranslate"><span class="pre">sklearn.utils.metadata_routing.UNCHANGED</span></code>) retains the
existing request. This allows you to change the request for some
parameters and not others.</p>
<div class="versionadded">
<p><span class="versionmodified added">New in version 1.3.</span></p>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>This method is only relevant if this estimator is used as a
sub-estimator of a meta-estimator, e.g. used inside a
<code class="xref py py-class docutils literal notranslate"><span class="pre">Pipeline</span></code>. Otherwise it has no effect.</p>
</div>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><p><strong>x</strong> (<em>str</em><em>, </em><em>True</em><em>, </em><em>False</em><em>, or </em><em>None</em><em>, </em><em>default=sklearn.utils.metadata_routing.UNCHANGED</em>) Metadata routing for <code class="docutils literal notranslate"><span class="pre">x</span></code> parameter in <code class="docutils literal notranslate"><span class="pre">predict</span></code>.</p>
</dd>
<dt class="field-even">Returns<span class="colon">:</span></dt>
<dd class="field-even"><p><strong>self</strong> The updated object.</p>
</dd>
<dt class="field-odd">Return type<span class="colon">:</span></dt>
<dd class="field-odd"><p>object</p>
</dd>
</dl>
</dd></dl>
</dd></dl>
</section>
<section id="module-miplearn.classifiers.singleclass">
<span id="miplearn-classifiers-singleclass"></span><h2><span class="section-number">11.2. </span>miplearn.classifiers.singleclass<a class="headerlink" href="#module-miplearn.classifiers.singleclass" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.classifiers.singleclass.SingleClassFix">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.classifiers.singleclass.</span></span><span class="sig-name descname"><span class="pre">SingleClassFix</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">base_clf:</span> <span class="pre">~sklearn.base.BaseEstimator</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">clone_fn:</span> <span class="pre">~typing.Callable</span> <span class="pre">=</span> <span class="pre">&lt;function</span> <span class="pre">clone&gt;</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.classifiers.singleclass.SingleClassFix" title="Link to this definition"></a></dt>
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">BaseEstimator</span></code></p>
<p>Some sklearn classifiers, such as logistic regression, have issues with datasets
that contain a single class. This meta-classifier fixes the issue. If the
training data contains a single class, this meta-classifier always returns that
class as a prediction. Otherwise, it fits the provided base classifier,
and returns its predictions instead.</p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.classifiers.singleclass.SingleClassFix.fit">
<span class="sig-name descname"><span class="pre">fit</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">x</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">y</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.classifiers.singleclass.SingleClassFix.fit" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.classifiers.singleclass.SingleClassFix.predict">
<span class="sig-name descname"><span class="pre">predict</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">x</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ndarray</span></span></span><a class="headerlink" href="#miplearn.classifiers.singleclass.SingleClassFix.predict" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.classifiers.singleclass.SingleClassFix.set_fit_request">
<span class="sig-name descname"><span class="pre">set_fit_request</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="o"><span class="pre">*</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">x</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">str</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'$UNCHANGED$'</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference internal" href="#miplearn.classifiers.singleclass.SingleClassFix" title="miplearn.classifiers.singleclass.SingleClassFix"><span class="pre">SingleClassFix</span></a></span></span><a class="headerlink" href="#miplearn.classifiers.singleclass.SingleClassFix.set_fit_request" title="Link to this definition"></a></dt>
<dd><p>Request metadata passed to the <code class="docutils literal notranslate"><span class="pre">fit</span></code> method.</p>
<p>Note that this method is only relevant if
<code class="docutils literal notranslate"><span class="pre">enable_metadata_routing=True</span></code> (see <code class="xref py py-func docutils literal notranslate"><span class="pre">sklearn.set_config()</span></code>).
Please see <span class="xref std std-ref">User Guide</span> on how the routing
mechanism works.</p>
<p>The options for each parameter are:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">True</span></code>: metadata is requested, and passed to <code class="docutils literal notranslate"><span class="pre">fit</span></code> if provided. The request is ignored if metadata is not provided.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">False</span></code>: metadata is not requested and the meta-estimator will not pass it to <code class="docutils literal notranslate"><span class="pre">fit</span></code>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">None</span></code>: metadata is not requested, and the meta-estimator will raise an error if the user provides it.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">str</span></code>: metadata should be passed to the meta-estimator with this given alias instead of the original name.</p></li>
</ul>
<p>The default (<code class="docutils literal notranslate"><span class="pre">sklearn.utils.metadata_routing.UNCHANGED</span></code>) retains the
existing request. This allows you to change the request for some
parameters and not others.</p>
<div class="versionadded">
<p><span class="versionmodified added">New in version 1.3.</span></p>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>This method is only relevant if this estimator is used as a
sub-estimator of a meta-estimator, e.g. used inside a
<code class="xref py py-class docutils literal notranslate"><span class="pre">Pipeline</span></code>. Otherwise it has no effect.</p>
</div>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><p><strong>x</strong> (<em>str</em><em>, </em><em>True</em><em>, </em><em>False</em><em>, or </em><em>None</em><em>, </em><em>default=sklearn.utils.metadata_routing.UNCHANGED</em>) Metadata routing for <code class="docutils literal notranslate"><span class="pre">x</span></code> parameter in <code class="docutils literal notranslate"><span class="pre">fit</span></code>.</p>
</dd>
<dt class="field-even">Returns<span class="colon">:</span></dt>
<dd class="field-even"><p><strong>self</strong> The updated object.</p>
</dd>
<dt class="field-odd">Return type<span class="colon">:</span></dt>
<dd class="field-odd"><p>object</p>
</dd>
</dl>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.classifiers.singleclass.SingleClassFix.set_predict_request">
<span class="sig-name descname"><span class="pre">set_predict_request</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="o"><span class="pre">*</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">x</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">str</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'$UNCHANGED$'</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference internal" href="#miplearn.classifiers.singleclass.SingleClassFix" title="miplearn.classifiers.singleclass.SingleClassFix"><span class="pre">SingleClassFix</span></a></span></span><a class="headerlink" href="#miplearn.classifiers.singleclass.SingleClassFix.set_predict_request" title="Link to this definition"></a></dt>
<dd><p>Request metadata passed to the <code class="docutils literal notranslate"><span class="pre">predict</span></code> method.</p>
<p>Note that this method is only relevant if
<code class="docutils literal notranslate"><span class="pre">enable_metadata_routing=True</span></code> (see <code class="xref py py-func docutils literal notranslate"><span class="pre">sklearn.set_config()</span></code>).
Please see <span class="xref std std-ref">User Guide</span> on how the routing
mechanism works.</p>
<p>The options for each parameter are:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">True</span></code>: metadata is requested, and passed to <code class="docutils literal notranslate"><span class="pre">predict</span></code> if provided. The request is ignored if metadata is not provided.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">False</span></code>: metadata is not requested and the meta-estimator will not pass it to <code class="docutils literal notranslate"><span class="pre">predict</span></code>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">None</span></code>: metadata is not requested, and the meta-estimator will raise an error if the user provides it.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">str</span></code>: metadata should be passed to the meta-estimator with this given alias instead of the original name.</p></li>
</ul>
<p>The default (<code class="docutils literal notranslate"><span class="pre">sklearn.utils.metadata_routing.UNCHANGED</span></code>) retains the
existing request. This allows you to change the request for some
parameters and not others.</p>
<div class="versionadded">
<p><span class="versionmodified added">New in version 1.3.</span></p>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>This method is only relevant if this estimator is used as a
sub-estimator of a meta-estimator, e.g. used inside a
<code class="xref py py-class docutils literal notranslate"><span class="pre">Pipeline</span></code>. Otherwise it has no effect.</p>
</div>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><p><strong>x</strong> (<em>str</em><em>, </em><em>True</em><em>, </em><em>False</em><em>, or </em><em>None</em><em>, </em><em>default=sklearn.utils.metadata_routing.UNCHANGED</em>) Metadata routing for <code class="docutils literal notranslate"><span class="pre">x</span></code> parameter in <code class="docutils literal notranslate"><span class="pre">predict</span></code>.</p>
</dd>
<dt class="field-even">Returns<span class="colon">:</span></dt>
<dd class="field-even"><p><strong>self</strong> The updated object.</p>
</dd>
<dt class="field-odd">Return type<span class="colon">:</span></dt>
<dd class="field-odd"><p>object</p>
</dd>
</dl>
</dd></dl>
</dd></dl>
</section>
<section id="module-miplearn.collectors.basic">
<span id="miplearn-collectors-basic"></span><h2><span class="section-number">11.3. </span>miplearn.collectors.basic<a class="headerlink" href="#module-miplearn.collectors.basic" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.collectors.basic.BasicCollector">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.collectors.basic.</span></span><span class="sig-name descname"><span class="pre">BasicCollector</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">skip_lp</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">False</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">write_mps</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">True</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.collectors.basic.BasicCollector" title="Link to this definition"></a></dt>
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.collectors.basic.BasicCollector.collect">
<span class="sig-name descname"><span class="pre">collect</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filenames</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></em>, <em class="sig-param"><span class="n"><span class="pre">build_model</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Callable</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">n_jobs</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">1</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">progress</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">False</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">verbose</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">False</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.collectors.basic.BasicCollector.collect" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
</section>
<section id="module-miplearn.extractors.fields">
<span id="miplearn-extractors-fields"></span><h2><span class="section-number">11.4. </span>miplearn.extractors.fields<a class="headerlink" href="#module-miplearn.extractors.fields" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.extractors.fields.H5FieldsExtractor">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.extractors.fields.</span></span><span class="sig-name descname"><span class="pre">H5FieldsExtractor</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">instance_fields</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">var_fields</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">constr_fields</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.extractors.fields.H5FieldsExtractor" title="Link to this definition"></a></dt>
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">FeaturesExtractor</span></code></p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.extractors.fields.H5FieldsExtractor.get_constr_features">
<span class="sig-name descname"><span class="pre">get_constr_features</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../helpers/#miplearn.h5.H5File" title="miplearn.h5.H5File"><span class="pre">H5File</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ndarray</span></span></span><a class="headerlink" href="#miplearn.extractors.fields.H5FieldsExtractor.get_constr_features" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.extractors.fields.H5FieldsExtractor.get_instance_features">
<span class="sig-name descname"><span class="pre">get_instance_features</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../helpers/#miplearn.h5.H5File" title="miplearn.h5.H5File"><span class="pre">H5File</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ndarray</span></span></span><a class="headerlink" href="#miplearn.extractors.fields.H5FieldsExtractor.get_instance_features" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.extractors.fields.H5FieldsExtractor.get_var_features">
<span class="sig-name descname"><span class="pre">get_var_features</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../helpers/#miplearn.h5.H5File" title="miplearn.h5.H5File"><span class="pre">H5File</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ndarray</span></span></span><a class="headerlink" href="#miplearn.extractors.fields.H5FieldsExtractor.get_var_features" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
</section>
<section id="module-miplearn.extractors.AlvLouWeh2017">
<span id="miplearn-extractors-alvlouweh2017"></span><h2><span class="section-number">11.5. </span>miplearn.extractors.AlvLouWeh2017<a class="headerlink" href="#module-miplearn.extractors.AlvLouWeh2017" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.extractors.AlvLouWeh2017.</span></span><span class="sig-name descname"><span class="pre">AlvLouWeh2017Extractor</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">with_m1</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">True</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">with_m2</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">True</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">with_m3</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">True</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor" title="Link to this definition"></a></dt>
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">FeaturesExtractor</span></code></p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor.get_constr_features">
<span class="sig-name descname"><span class="pre">get_constr_features</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../helpers/#miplearn.h5.H5File" title="miplearn.h5.H5File"><span class="pre">H5File</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ndarray</span></span></span><a class="headerlink" href="#miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor.get_constr_features" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor.get_instance_features">
<span class="sig-name descname"><span class="pre">get_instance_features</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../helpers/#miplearn.h5.H5File" title="miplearn.h5.H5File"><span class="pre">H5File</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ndarray</span></span></span><a class="headerlink" href="#miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor.get_instance_features" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor.get_var_features">
<span class="sig-name descname"><span class="pre">get_var_features</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../helpers/#miplearn.h5.H5File" title="miplearn.h5.H5File"><span class="pre">H5File</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ndarray</span></span></span><a class="headerlink" href="#miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor.get_var_features" title="Link to this definition"></a></dt>
<dd><dl class="simple">
<dt>Computes static variable features described in:</dt><dd><p>Alvarez, A. M., Louveaux, Q., &amp; Wehenkel, L. (2017). A machine learning-based
approximation of strong branching. INFORMS Journal on Computing, 29(1),
185-195.</p>
</dd>
</dl>
</dd></dl>
</dd></dl>
</section>
</section>
</div>
<div class='prev-next-bottom'>
<a class='left-prev' id="prev-link" href="../problems/" title="previous page"><span class="section-number">10. </span>Benchmark Problems</a>
<a class='right-next' id="next-link" href="../components/" title="next page"><span class="section-number">12. </span>Components</a>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

@ -0,0 +1,728 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<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" />
<title>12. Components &#8212; MIPLearn 0.4</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" type="text/css" href="../../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<link rel="index" title="Index" href="../../genindex/" />
<link rel="search" title="Search" href="../../search/" />
<link rel="next" title="13. Solvers" href="../solvers/" />
<link rel="prev" title="11. Collectors &amp; Extractors" href="../collectors/" />
<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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../guide/problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="current nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1 current active">
<a class="current reference internal" href="#">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../helpers/">
14. 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/api/components.rst.txt"><button type="button"
class="btn btn-secondary topbarbtn" title="Download source file" data-toggle="tooltip"
data-placement="left">.rst</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="#module-miplearn.components.primal.actions">
12.1. miplearn.components.primal.actions
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.actions.EnforceProximity">
<code class="docutils literal notranslate">
<span class="pre">
EnforceProximity
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.actions.EnforceProximity.perform">
<code class="docutils literal notranslate">
<span class="pre">
EnforceProximity.perform()
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.actions.FixVariables">
<code class="docutils literal notranslate">
<span class="pre">
FixVariables
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.actions.FixVariables.perform">
<code class="docutils literal notranslate">
<span class="pre">
FixVariables.perform()
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.actions.PrimalComponentAction">
<code class="docutils literal notranslate">
<span class="pre">
PrimalComponentAction
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.actions.PrimalComponentAction.perform">
<code class="docutils literal notranslate">
<span class="pre">
PrimalComponentAction.perform()
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.actions.SetWarmStart">
<code class="docutils literal notranslate">
<span class="pre">
SetWarmStart
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.actions.SetWarmStart.perform">
<code class="docutils literal notranslate">
<span class="pre">
SetWarmStart.perform()
</span>
</code>
</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.components.primal.expert">
12.2. miplearn.components.primal.expert
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.expert.ExpertPrimalComponent">
<code class="docutils literal notranslate">
<span class="pre">
ExpertPrimalComponent
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.expert.ExpertPrimalComponent.before_mip">
<code class="docutils literal notranslate">
<span class="pre">
ExpertPrimalComponent.before_mip()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.expert.ExpertPrimalComponent.fit">
<code class="docutils literal notranslate">
<span class="pre">
ExpertPrimalComponent.fit()
</span>
</code>
</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.components.primal.indep">
12.3. miplearn.components.primal.indep
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.indep.IndependentVarsPrimalComponent">
<code class="docutils literal notranslate">
<span class="pre">
IndependentVarsPrimalComponent
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.indep.IndependentVarsPrimalComponent.before_mip">
<code class="docutils literal notranslate">
<span class="pre">
IndependentVarsPrimalComponent.before_mip()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.indep.IndependentVarsPrimalComponent.fit">
<code class="docutils literal notranslate">
<span class="pre">
IndependentVarsPrimalComponent.fit()
</span>
</code>
</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.components.primal.joint">
12.4. miplearn.components.primal.joint
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.joint.JointVarsPrimalComponent">
<code class="docutils literal notranslate">
<span class="pre">
JointVarsPrimalComponent
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.joint.JointVarsPrimalComponent.before_mip">
<code class="docutils literal notranslate">
<span class="pre">
JointVarsPrimalComponent.before_mip()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.joint.JointVarsPrimalComponent.fit">
<code class="docutils literal notranslate">
<span class="pre">
JointVarsPrimalComponent.fit()
</span>
</code>
</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.components.primal.mem">
12.5. miplearn.components.primal.mem
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.mem.MemorizingPrimalComponent">
<code class="docutils literal notranslate">
<span class="pre">
MemorizingPrimalComponent
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.mem.MemorizingPrimalComponent.before_mip">
<code class="docutils literal notranslate">
<span class="pre">
MemorizingPrimalComponent.before_mip()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.mem.MemorizingPrimalComponent.fit">
<code class="docutils literal notranslate">
<span class="pre">
MemorizingPrimalComponent.fit()
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.mem.MergeTopSolutions">
<code class="docutils literal notranslate">
<span class="pre">
MergeTopSolutions
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.mem.MergeTopSolutions.construct">
<code class="docutils literal notranslate">
<span class="pre">
MergeTopSolutions.construct()
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.mem.SelectTopSolutions">
<code class="docutils literal notranslate">
<span class="pre">
SelectTopSolutions
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.mem.SelectTopSolutions.construct">
<code class="docutils literal notranslate">
<span class="pre">
SelectTopSolutions.construct()
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.mem.SolutionConstructor">
<code class="docutils literal notranslate">
<span class="pre">
SolutionConstructor
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.components.primal.mem.SolutionConstructor.construct">
<code class="docutils literal notranslate">
<span class="pre">
SolutionConstructor.construct()
</span>
</code>
</a>
</li>
</ul>
</li>
</ul>
</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>
<section id="components">
<h1><span class="section-number">12. </span>Components<a class="headerlink" href="#components" title="Link to this heading"></a></h1>
<section id="module-miplearn.components.primal.actions">
<span id="miplearn-components-primal-actions"></span><h2><span class="section-number">12.1. </span>miplearn.components.primal.actions<a class="headerlink" href="#module-miplearn.components.primal.actions" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.components.primal.actions.EnforceProximity">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.components.primal.actions.</span></span><span class="sig-name descname"><span class="pre">EnforceProximity</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">tol</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">float</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.components.primal.actions.EnforceProximity" title="Link to this definition"></a></dt>
<dd><p>Bases: <a class="reference internal" href="#miplearn.components.primal.actions.PrimalComponentAction" title="miplearn.components.primal.actions.PrimalComponentAction"><code class="xref py py-class docutils literal notranslate"><span class="pre">PrimalComponentAction</span></code></a></p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.components.primal.actions.EnforceProximity.perform">
<span class="sig-name descname"><span class="pre">perform</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">model</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../solvers/#miplearn.solvers.abstract.AbstractModel" title="miplearn.solvers.abstract.AbstractModel"><span class="pre">AbstractModel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">var_names</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">var_values</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">stats</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Dict</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.components.primal.actions.EnforceProximity.perform" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.components.primal.actions.FixVariables">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.components.primal.actions.</span></span><span class="sig-name descname"><span class="pre">FixVariables</span></span><a class="headerlink" href="#miplearn.components.primal.actions.FixVariables" title="Link to this definition"></a></dt>
<dd><p>Bases: <a class="reference internal" href="#miplearn.components.primal.actions.PrimalComponentAction" title="miplearn.components.primal.actions.PrimalComponentAction"><code class="xref py py-class docutils literal notranslate"><span class="pre">PrimalComponentAction</span></code></a></p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.components.primal.actions.FixVariables.perform">
<span class="sig-name descname"><span class="pre">perform</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">model</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../solvers/#miplearn.solvers.abstract.AbstractModel" title="miplearn.solvers.abstract.AbstractModel"><span class="pre">AbstractModel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">var_names</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">var_values</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">stats</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Dict</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.components.primal.actions.FixVariables.perform" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.components.primal.actions.PrimalComponentAction">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.components.primal.actions.</span></span><span class="sig-name descname"><span class="pre">PrimalComponentAction</span></span><a class="headerlink" href="#miplearn.components.primal.actions.PrimalComponentAction" title="Link to this definition"></a></dt>
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">ABC</span></code></p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.components.primal.actions.PrimalComponentAction.perform">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">perform</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">model</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../solvers/#miplearn.solvers.abstract.AbstractModel" title="miplearn.solvers.abstract.AbstractModel"><span class="pre">AbstractModel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">var_names</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">var_values</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">stats</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Dict</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.components.primal.actions.PrimalComponentAction.perform" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.components.primal.actions.SetWarmStart">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.components.primal.actions.</span></span><span class="sig-name descname"><span class="pre">SetWarmStart</span></span><a class="headerlink" href="#miplearn.components.primal.actions.SetWarmStart" title="Link to this definition"></a></dt>
<dd><p>Bases: <a class="reference internal" href="#miplearn.components.primal.actions.PrimalComponentAction" title="miplearn.components.primal.actions.PrimalComponentAction"><code class="xref py py-class docutils literal notranslate"><span class="pre">PrimalComponentAction</span></code></a></p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.components.primal.actions.SetWarmStart.perform">
<span class="sig-name descname"><span class="pre">perform</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">model</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../solvers/#miplearn.solvers.abstract.AbstractModel" title="miplearn.solvers.abstract.AbstractModel"><span class="pre">AbstractModel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">var_names</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">var_values</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">stats</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Dict</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.components.primal.actions.SetWarmStart.perform" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
</section>
<section id="module-miplearn.components.primal.expert">
<span id="miplearn-components-primal-expert"></span><h2><span class="section-number">12.2. </span>miplearn.components.primal.expert<a class="headerlink" href="#module-miplearn.components.primal.expert" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.components.primal.expert.ExpertPrimalComponent">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.components.primal.expert.</span></span><span class="sig-name descname"><span class="pre">ExpertPrimalComponent</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">action</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#miplearn.components.primal.actions.PrimalComponentAction" title="miplearn.components.primal.actions.PrimalComponentAction"><span class="pre">PrimalComponentAction</span></a></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.components.primal.expert.ExpertPrimalComponent" title="Link to this definition"></a></dt>
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.components.primal.expert.ExpertPrimalComponent.before_mip">
<span class="sig-name descname"><span class="pre">before_mip</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">test_h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">model</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../solvers/#miplearn.solvers.abstract.AbstractModel" title="miplearn.solvers.abstract.AbstractModel"><span class="pre">AbstractModel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">stats</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Dict</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">Any</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.components.primal.expert.ExpertPrimalComponent.before_mip" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.components.primal.expert.ExpertPrimalComponent.fit">
<span class="sig-name descname"><span class="pre">fit</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">train_h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.components.primal.expert.ExpertPrimalComponent.fit" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
</section>
<section id="module-miplearn.components.primal.indep">
<span id="miplearn-components-primal-indep"></span><h2><span class="section-number">12.3. </span>miplearn.components.primal.indep<a class="headerlink" href="#module-miplearn.components.primal.indep" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.components.primal.indep.IndependentVarsPrimalComponent">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.components.primal.indep.</span></span><span class="sig-name descname"><span class="pre">IndependentVarsPrimalComponent</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="pre">base_clf:</span> <span class="pre">~typing.Any,</span> <span class="pre">extractor:</span> <span class="pre">~miplearn.extractors.abstract.FeaturesExtractor,</span> <span class="pre">action:</span> <span class="pre">~miplearn.components.primal.actions.PrimalComponentAction,</span> <span class="pre">clone_fn:</span> <span class="pre">~typing.Callable[[~typing.Any],</span> <span class="pre">~typing.Any]</span> <span class="pre">=</span> <span class="pre">&lt;function</span> <span class="pre">clone&gt;</span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.components.primal.indep.IndependentVarsPrimalComponent" title="Link to this definition"></a></dt>
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.components.primal.indep.IndependentVarsPrimalComponent.before_mip">
<span class="sig-name descname"><span class="pre">before_mip</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">test_h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">model</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../solvers/#miplearn.solvers.abstract.AbstractModel" title="miplearn.solvers.abstract.AbstractModel"><span class="pre">AbstractModel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">stats</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Dict</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">Any</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.components.primal.indep.IndependentVarsPrimalComponent.before_mip" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.components.primal.indep.IndependentVarsPrimalComponent.fit">
<span class="sig-name descname"><span class="pre">fit</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">train_h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.components.primal.indep.IndependentVarsPrimalComponent.fit" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
</section>
<section id="module-miplearn.components.primal.joint">
<span id="miplearn-components-primal-joint"></span><h2><span class="section-number">12.4. </span>miplearn.components.primal.joint<a class="headerlink" href="#module-miplearn.components.primal.joint" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.components.primal.joint.JointVarsPrimalComponent">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.components.primal.joint.</span></span><span class="sig-name descname"><span class="pre">JointVarsPrimalComponent</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">clf</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Any</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">extractor</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">FeaturesExtractor</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">action</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#miplearn.components.primal.actions.PrimalComponentAction" title="miplearn.components.primal.actions.PrimalComponentAction"><span class="pre">PrimalComponentAction</span></a></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.components.primal.joint.JointVarsPrimalComponent" title="Link to this definition"></a></dt>
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.components.primal.joint.JointVarsPrimalComponent.before_mip">
<span class="sig-name descname"><span class="pre">before_mip</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">test_h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">model</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../solvers/#miplearn.solvers.abstract.AbstractModel" title="miplearn.solvers.abstract.AbstractModel"><span class="pre">AbstractModel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">stats</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Dict</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">Any</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.components.primal.joint.JointVarsPrimalComponent.before_mip" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.components.primal.joint.JointVarsPrimalComponent.fit">
<span class="sig-name descname"><span class="pre">fit</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">train_h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.components.primal.joint.JointVarsPrimalComponent.fit" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
</section>
<section id="module-miplearn.components.primal.mem">
<span id="miplearn-components-primal-mem"></span><h2><span class="section-number">12.5. </span>miplearn.components.primal.mem<a class="headerlink" href="#module-miplearn.components.primal.mem" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.components.primal.mem.MemorizingPrimalComponent">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.components.primal.mem.</span></span><span class="sig-name descname"><span class="pre">MemorizingPrimalComponent</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">clf</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Any</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">extractor</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">FeaturesExtractor</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">constructor</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#miplearn.components.primal.mem.SolutionConstructor" title="miplearn.components.primal.mem.SolutionConstructor"><span class="pre">SolutionConstructor</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">action</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="#miplearn.components.primal.actions.PrimalComponentAction" title="miplearn.components.primal.actions.PrimalComponentAction"><span class="pre">PrimalComponentAction</span></a></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.components.primal.mem.MemorizingPrimalComponent" title="Link to this definition"></a></dt>
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></p>
<p>Component that memorizes all solutions seen during training, then fits a
single classifier to predict which of the memorized solutions should be
provided to the solver. Optionally combines multiple memorized solutions
into a single, partial one.</p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.components.primal.mem.MemorizingPrimalComponent.before_mip">
<span class="sig-name descname"><span class="pre">before_mip</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">test_h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">model</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../solvers/#miplearn.solvers.abstract.AbstractModel" title="miplearn.solvers.abstract.AbstractModel"><span class="pre">AbstractModel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">stats</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Dict</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">Any</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.components.primal.mem.MemorizingPrimalComponent.before_mip" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.components.primal.mem.MemorizingPrimalComponent.fit">
<span class="sig-name descname"><span class="pre">fit</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">train_h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.components.primal.mem.MemorizingPrimalComponent.fit" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.components.primal.mem.MergeTopSolutions">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.components.primal.mem.</span></span><span class="sig-name descname"><span class="pre">MergeTopSolutions</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">k</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">thresholds</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">float</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.components.primal.mem.MergeTopSolutions" title="Link to this definition"></a></dt>
<dd><p>Bases: <a class="reference internal" href="#miplearn.components.primal.mem.SolutionConstructor" title="miplearn.components.primal.mem.SolutionConstructor"><code class="xref py py-class docutils literal notranslate"><span class="pre">SolutionConstructor</span></code></a></p>
<p>Warm start construction strategy that first selects the top k solutions,
then merges them into a single solution.</p>
<p>To merge the solutions, the strategy first computes the mean optimal value of each
decision variable, then: (i) sets the variable to zero if the mean is below
thresholds[0]; (ii) sets the variable to one if the mean is above thresholds[1];
(iii) leaves the variable free otherwise.</p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.components.primal.mem.MergeTopSolutions.construct">
<span class="sig-name descname"><span class="pre">construct</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">y_proba</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">solutions</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ndarray</span></span></span><a class="headerlink" href="#miplearn.components.primal.mem.MergeTopSolutions.construct" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.components.primal.mem.SelectTopSolutions">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.components.primal.mem.</span></span><span class="sig-name descname"><span class="pre">SelectTopSolutions</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">k</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.components.primal.mem.SelectTopSolutions" title="Link to this definition"></a></dt>
<dd><p>Bases: <a class="reference internal" href="#miplearn.components.primal.mem.SolutionConstructor" title="miplearn.components.primal.mem.SolutionConstructor"><code class="xref py py-class docutils literal notranslate"><span class="pre">SolutionConstructor</span></code></a></p>
<p>Warm start construction strategy that selects and returns the top k solutions.</p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.components.primal.mem.SelectTopSolutions.construct">
<span class="sig-name descname"><span class="pre">construct</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">y_proba</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">solutions</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ndarray</span></span></span><a class="headerlink" href="#miplearn.components.primal.mem.SelectTopSolutions.construct" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.components.primal.mem.SolutionConstructor">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.components.primal.mem.</span></span><span class="sig-name descname"><span class="pre">SolutionConstructor</span></span><a class="headerlink" href="#miplearn.components.primal.mem.SolutionConstructor" title="Link to this definition"></a></dt>
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">ABC</span></code></p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.components.primal.mem.SolutionConstructor.construct">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">construct</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">y_proba</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">solutions</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ndarray</span></span></span><a class="headerlink" href="#miplearn.components.primal.mem.SolutionConstructor.construct" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
</section>
</section>
</div>
<div class='prev-next-bottom'>
<a class='left-prev' id="prev-link" href="../collectors/" title="previous page"><span class="section-number">11. </span>Collectors &amp; Extractors</a>
<a class='right-next' id="next-link" href="../solvers/" title="next page"><span class="section-number">13. </span>Solvers</a>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

@ -0,0 +1,473 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<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" />
<title>14. Helpers &#8212; MIPLearn 0.4</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" type="text/css" href="../../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<link rel="index" title="Index" href="../../genindex/" />
<link rel="search" title="Search" href="../../search/" />
<link rel="prev" title="13. Solvers" href="../solvers/" />
<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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../guide/problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="current nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../components/">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1 current active">
<a class="current reference internal" href="#">
14. 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/api/helpers.rst.txt"><button type="button"
class="btn btn-secondary topbarbtn" title="Download source file" data-toggle="tooltip"
data-placement="left">.rst</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="#module-miplearn.io">
14.1. miplearn.io
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.io.gzip">
<code class="docutils literal notranslate">
<span class="pre">
gzip()
</span>
</code>
</a>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.io.read_pkl_gz">
<code class="docutils literal notranslate">
<span class="pre">
read_pkl_gz()
</span>
</code>
</a>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.io.write_pkl_gz">
<code class="docutils literal notranslate">
<span class="pre">
write_pkl_gz()
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.h5">
14.2. miplearn.h5
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.h5.H5File">
<code class="docutils literal notranslate">
<span class="pre">
H5File
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.h5.H5File.close">
<code class="docutils literal notranslate">
<span class="pre">
H5File.close()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.h5.H5File.get_array">
<code class="docutils literal notranslate">
<span class="pre">
H5File.get_array()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.h5.H5File.get_bytes">
<code class="docutils literal notranslate">
<span class="pre">
H5File.get_bytes()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.h5.H5File.get_scalar">
<code class="docutils literal notranslate">
<span class="pre">
H5File.get_scalar()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.h5.H5File.get_sparse">
<code class="docutils literal notranslate">
<span class="pre">
H5File.get_sparse()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.h5.H5File.put_array">
<code class="docutils literal notranslate">
<span class="pre">
H5File.put_array()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.h5.H5File.put_bytes">
<code class="docutils literal notranslate">
<span class="pre">
H5File.put_bytes()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.h5.H5File.put_scalar">
<code class="docutils literal notranslate">
<span class="pre">
H5File.put_scalar()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.h5.H5File.put_sparse">
<code class="docutils literal notranslate">
<span class="pre">
H5File.put_sparse()
</span>
</code>
</a>
</li>
</ul>
</li>
</ul>
</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>
<section id="helpers">
<h1><span class="section-number">14. </span>Helpers<a class="headerlink" href="#helpers" title="Link to this heading"></a></h1>
<section id="module-miplearn.io">
<span id="miplearn-io"></span><h2><span class="section-number">14.1. </span>miplearn.io<a class="headerlink" href="#module-miplearn.io" title="Link to this heading"></a></h2>
<dl class="py function">
<dt class="sig sig-object py" id="miplearn.io.gzip">
<span class="sig-prename descclassname"><span class="pre">miplearn.io.</span></span><span class="sig-name descname"><span class="pre">gzip</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.io.gzip" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py function">
<dt class="sig sig-object py" id="miplearn.io.read_pkl_gz">
<span class="sig-prename descclassname"><span class="pre">miplearn.io.</span></span><span class="sig-name descname"><span class="pre">read_pkl_gz</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">Any</span></span></span><a class="headerlink" href="#miplearn.io.read_pkl_gz" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py function">
<dt class="sig sig-object py" id="miplearn.io.write_pkl_gz">
<span class="sig-prename descclassname"><span class="pre">miplearn.io.</span></span><span class="sig-name descname"><span class="pre">write_pkl_gz</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">objs</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">Any</span><span class="p"><span class="pre">]</span></span></span></em>, <em class="sig-param"><span class="n"><span class="pre">dirname</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">prefix</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">''</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">n_jobs</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">1</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">progress</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">False</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#miplearn.io.write_pkl_gz" title="Link to this definition"></a></dt>
<dd></dd></dl>
</section>
<section id="module-miplearn.h5">
<span id="miplearn-h5"></span><h2><span class="section-number">14.2. </span>miplearn.h5<a class="headerlink" href="#module-miplearn.h5" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.h5.H5File">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.h5.</span></span><span class="sig-name descname"><span class="pre">H5File</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">mode</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">'r+'</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.h5.H5File" title="Link to this definition"></a></dt>
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.h5.H5File.close">
<span class="sig-name descname"><span class="pre">close</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.h5.H5File.close" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.h5.H5File.get_array">
<span class="sig-name descname"><span class="pre">get_array</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">key</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">ndarray</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.h5.H5File.get_array" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.h5.H5File.get_bytes">
<span class="sig-name descname"><span class="pre">get_bytes</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">key</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">bytes</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">bytearray</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.h5.H5File.get_bytes" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.h5.H5File.get_scalar">
<span class="sig-name descname"><span class="pre">get_scalar</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">key</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">Any</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.h5.H5File.get_scalar" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.h5.H5File.get_sparse">
<span class="sig-name descname"><span class="pre">get_sparse</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">key</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">coo_matrix</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.h5.H5File.get_sparse" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.h5.H5File.put_array">
<span class="sig-name descname"><span class="pre">put_array</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">key</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">value</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.h5.H5File.put_array" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.h5.H5File.put_bytes">
<span class="sig-name descname"><span class="pre">put_bytes</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">key</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">value</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bytes</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">bytearray</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.h5.H5File.put_bytes" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.h5.H5File.put_scalar">
<span class="sig-name descname"><span class="pre">put_scalar</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">key</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">value</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Any</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.h5.H5File.put_scalar" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.h5.H5File.put_sparse">
<span class="sig-name descname"><span class="pre">put_sparse</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">key</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">value</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">coo_matrix</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.h5.H5File.put_sparse" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
</section>
</section>
</div>
<div class='prev-next-bottom'>
<a class='left-prev' id="prev-link" href="../solvers/" title="previous page"><span class="section-number">13. </span>Solvers</a>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

@ -0,0 +1,783 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<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" />
<title>10. Benchmark Problems &#8212; MIPLearn 0.4</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" type="text/css" href="../../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<link rel="index" title="Index" href="../../genindex/" />
<link rel="search" title="Search" href="../../search/" />
<link rel="next" title="11. Collectors &amp; Extractors" href="../collectors/" />
<link rel="prev" title="9. Learning Solver" href="../../guide/solvers/" />
<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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../guide/problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="current nav bd-sidenav">
<li class="toctree-l1 current active">
<a class="current reference internal" href="#">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../components/">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../helpers/">
14. 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/api/problems.rst.txt"><button type="button"
class="btn btn-secondary topbarbtn" title="Download source file" data-toggle="tooltip"
data-placement="left">.rst</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="#module-miplearn.problems.binpack">
10.1. miplearn.problems.binpack
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.binpack.BinPackData">
<code class="docutils literal notranslate">
<span class="pre">
BinPackData
</span>
</code>
</a>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.binpack.BinPackGenerator">
<code class="docutils literal notranslate">
<span class="pre">
BinPackGenerator
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.binpack.BinPackGenerator.generate">
<code class="docutils literal notranslate">
<span class="pre">
BinPackGenerator.generate()
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.binpack.build_binpack_model_gurobipy">
<code class="docutils literal notranslate">
<span class="pre">
build_binpack_model_gurobipy()
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.problems.multiknapsack">
10.2. miplearn.problems.multiknapsack
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.multiknapsack.MultiKnapsackData">
<code class="docutils literal notranslate">
<span class="pre">
MultiKnapsackData
</span>
</code>
</a>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.multiknapsack.MultiKnapsackGenerator">
<code class="docutils literal notranslate">
<span class="pre">
MultiKnapsackGenerator
</span>
</code>
</a>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.multiknapsack.build_multiknapsack_model_gurobipy">
<code class="docutils literal notranslate">
<span class="pre">
build_multiknapsack_model_gurobipy()
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.problems.pmedian">
10.3. miplearn.problems.pmedian
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.pmedian.PMedianData">
<code class="docutils literal notranslate">
<span class="pre">
PMedianData
</span>
</code>
</a>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.pmedian.PMedianGenerator">
<code class="docutils literal notranslate">
<span class="pre">
PMedianGenerator
</span>
</code>
</a>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.pmedian.build_pmedian_model_gurobipy">
<code class="docutils literal notranslate">
<span class="pre">
build_pmedian_model_gurobipy()
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.problems.setcover">
10.4. miplearn.problems.setcover
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.setcover.SetCoverData">
<code class="docutils literal notranslate">
<span class="pre">
SetCoverData
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.problems.setpack">
10.5. miplearn.problems.setpack
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.setpack.SetPackData">
<code class="docutils literal notranslate">
<span class="pre">
SetPackData
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.problems.stab">
10.6. miplearn.problems.stab
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.stab.MaxWeightStableSetData">
<code class="docutils literal notranslate">
<span class="pre">
MaxWeightStableSetData
</span>
</code>
</a>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.stab.MaxWeightStableSetGenerator">
<code class="docutils literal notranslate">
<span class="pre">
MaxWeightStableSetGenerator
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.problems.tsp">
10.7. miplearn.problems.tsp
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.tsp.TravelingSalesmanData">
<code class="docutils literal notranslate">
<span class="pre">
TravelingSalesmanData
</span>
</code>
</a>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.tsp.TravelingSalesmanGenerator">
<code class="docutils literal notranslate">
<span class="pre">
TravelingSalesmanGenerator
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.problems.uc">
10.8. miplearn.problems.uc
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.uc.UnitCommitmentData">
<code class="docutils literal notranslate">
<span class="pre">
UnitCommitmentData
</span>
</code>
</a>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.uc.build_uc_model_gurobipy">
<code class="docutils literal notranslate">
<span class="pre">
build_uc_model_gurobipy()
</span>
</code>
</a>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.problems.vertexcover">
10.9. miplearn.problems.vertexcover
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.problems.vertexcover.MinWeightVertexCoverData">
<code class="docutils literal notranslate">
<span class="pre">
MinWeightVertexCoverData
</span>
</code>
</a>
</li>
</ul>
</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>
<section id="benchmark-problems">
<h1><span class="section-number">10. </span>Benchmark Problems<a class="headerlink" href="#benchmark-problems" title="Link to this heading"></a></h1>
<section id="module-miplearn.problems.binpack">
<span id="miplearn-problems-binpack"></span><h2><span class="section-number">10.1. </span>miplearn.problems.binpack<a class="headerlink" href="#module-miplearn.problems.binpack" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.problems.binpack.BinPackData">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.problems.binpack.</span></span><span class="sig-name descname"><span class="pre">BinPackData</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">sizes</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">capacity</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.problems.binpack.BinPackData" title="Link to this definition"></a></dt>
<dd><p>Data for the bin packing problem.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>sizes</strong> (<em>numpy.ndarray</em>) Sizes of the items</p></li>
<li><p><strong>capacity</strong> (<em>int</em>) Capacity of the bin</p></li>
</ul>
</dd>
</dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.problems.binpack.BinPackGenerator">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.problems.binpack.</span></span><span class="sig-name descname"><span class="pre">BinPackGenerator</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">n</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">rv_frozen</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">sizes</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">rv_frozen</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">capacity</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">rv_frozen</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">sizes_jitter</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">rv_frozen</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">capacity_jitter</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">rv_frozen</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">fix_items</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.problems.binpack.BinPackGenerator" title="Link to this definition"></a></dt>
<dd><p>Random instance generator for the bin packing problem.</p>
<p>If <cite>fix_items=False</cite>, the class samples the user-provided probability distributions
n, sizes and capacity to decide, respectively, the number of items, the sizes of
the items and capacity of the bin. All values are sampled independently.</p>
<p>If <cite>fix_items=True</cite>, the class creates a reference instance, using the method
previously described, then generates additional instances by perturbing its item
sizes and bin capacity. More specifically, the sizes of the items are set to <cite>s_i
* gamma_i</cite> where <cite>s_i</cite> is the size of the i-th item in the reference instance and
<cite>gamma_i</cite> is sampled from <cite>sizes_jitter</cite>. Similarly, the bin capacity is set to <cite>B *
beta</cite>, where <cite>B</cite> is the reference bin capacity and <cite>beta</cite> is sampled from
<cite>capacity_jitter</cite>. The number of items remains the same across all generated
instances.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>n</strong> Probability distribution for the number of items.</p></li>
<li><p><strong>sizes</strong> Probability distribution for the item sizes.</p></li>
<li><p><strong>capacity</strong> Probability distribution for the bin capacity.</p></li>
<li><p><strong>sizes_jitter</strong> Probability distribution for the item size randomization.</p></li>
<li><p><strong>capacity_jitter</strong> Probability distribution for the bin capacity.</p></li>
<li><p><strong>fix_items</strong> If <cite>True</cite>, generates a reference instance, then applies some perturbation to it.
If <cite>False</cite>, generates completely different instances.</p></li>
</ul>
</dd>
</dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.problems.binpack.BinPackGenerator.generate">
<span class="sig-name descname"><span class="pre">generate</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">n_samples</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><a class="reference internal" href="#miplearn.problems.binpack.BinPackData" title="miplearn.problems.binpack.BinPackData"><span class="pre">BinPackData</span></a><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#miplearn.problems.binpack.BinPackGenerator.generate" title="Link to this definition"></a></dt>
<dd><p>Generates random instances.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><p><strong>n_samples</strong> Number of samples to generate.</p>
</dd>
</dl>
</dd></dl>
</dd></dl>
<dl class="py function">
<dt class="sig sig-object py" id="miplearn.problems.binpack.build_binpack_model_gurobipy">
<span class="sig-prename descclassname"><span class="pre">miplearn.problems.binpack.</span></span><span class="sig-name descname"><span class="pre">build_binpack_model_gurobipy</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">data</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><a class="reference internal" href="#miplearn.problems.binpack.BinPackData" title="miplearn.problems.binpack.BinPackData"><span class="pre">BinPackData</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference internal" href="../solvers/#miplearn.solvers.gurobi.GurobiModel" title="miplearn.solvers.gurobi.GurobiModel"><span class="pre">GurobiModel</span></a></span></span><a class="headerlink" href="#miplearn.problems.binpack.build_binpack_model_gurobipy" title="Link to this definition"></a></dt>
<dd><p>Converts bin packing problem data into a concrete Gurobipy model.</p>
</dd></dl>
</section>
<section id="module-miplearn.problems.multiknapsack">
<span id="miplearn-problems-multiknapsack"></span><h2><span class="section-number">10.2. </span>miplearn.problems.multiknapsack<a class="headerlink" href="#module-miplearn.problems.multiknapsack" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.problems.multiknapsack.MultiKnapsackData">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.problems.multiknapsack.</span></span><span class="sig-name descname"><span class="pre">MultiKnapsackData</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">prices</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">capacities</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">weights</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.problems.multiknapsack.MultiKnapsackData" title="Link to this definition"></a></dt>
<dd><p>Data for the multi-dimensional knapsack problem</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>prices</strong> (<em>numpy.ndarray</em>) Item prices.</p></li>
<li><p><strong>capacities</strong> (<em>numpy.ndarray</em>) Knapsack capacities.</p></li>
<li><p><strong>weights</strong> (<em>numpy.ndarray</em>) Matrix of item weights.</p></li>
</ul>
</dd>
</dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.problems.multiknapsack.MultiKnapsackGenerator">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.problems.multiknapsack.</span></span><span class="sig-name descname"><span class="pre">MultiKnapsackGenerator</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">n:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_discrete_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">m:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_discrete_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">w:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_discrete_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">K:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_discrete_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">u:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">alpha:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">fix_w:</span> <span class="pre">bool</span> <span class="pre">=</span> <span class="pre">False</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">w_jitter:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">p_jitter:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">round:</span> <span class="pre">bool</span> <span class="pre">=</span> <span class="pre">True</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.problems.multiknapsack.MultiKnapsackGenerator" title="Link to this definition"></a></dt>
<dd><p>Random instance generator for the multi-dimensional knapsack problem.</p>
<p>Instances have a random number of items (or variables) and a random number of
knapsacks (or constraints), as specified by the provided probability
distributions <cite>n</cite> and <cite>m</cite>, respectively. The weight of each item <cite>i</cite> on knapsack
<cite>j</cite> is sampled independently from the provided distribution <cite>w</cite>. The capacity of
knapsack <cite>j</cite> is set to <code class="docutils literal notranslate"><span class="pre">alpha_j</span> <span class="pre">*</span> <span class="pre">sum(w[i,j]</span> <span class="pre">for</span> <span class="pre">i</span> <span class="pre">in</span> <span class="pre">range(n))</span></code>,
where <cite>alpha_j</cite>, the tightness ratio, is sampled from the provided probability
distribution <cite>alpha</cite>.</p>
<p>To make the instances more challenging, the costs of the items are linearly
correlated to their average weights. More specifically, the weight of each item
<cite>i</cite> is set to <code class="docutils literal notranslate"><span class="pre">sum(w[i,j]/m</span> <span class="pre">for</span> <span class="pre">j</span> <span class="pre">in</span> <span class="pre">range(m))</span> <span class="pre">+</span> <span class="pre">K</span> <span class="pre">*</span> <span class="pre">u_i</span></code>, where <cite>K</cite>,
the correlation coefficient, and <cite>u_i</cite>, the correlation multiplier, are sampled
from the provided probability distributions. Note that <cite>K</cite> is only sample once
for the entire instance.</p>
<p>If <cite>fix_w=True</cite>, then <cite>weights[i,j]</cite> are kept the same in all generated
instances. This also implies that n and m are kept fixed. Although the prices and
capacities are derived from <cite>weights[i,j]</cite>, as long as <cite>u</cite> and <cite>K</cite> are not
constants, the generated instances will still not be completely identical.</p>
<p>If a probability distribution <cite>w_jitter</cite> is provided, then item weights will be
set to <code class="docutils literal notranslate"><span class="pre">w[i,j]</span> <span class="pre">*</span> <span class="pre">gamma[i,j]</span></code> where <cite>gamma[i,j]</cite> is sampled from <cite>w_jitter</cite>.
When combined with <cite>fix_w=True</cite>, this argument may be used to generate instances
where the weight of each item is roughly the same, but not exactly identical,
across all instances. The prices of the items and the capacities of the knapsacks
will be calculated as above, but using these perturbed weights instead.</p>
<p>By default, all generated prices, weights and capacities are rounded to the
nearest integer number. If <cite>round=False</cite> is provided, this rounding will be
disabled.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>n</strong> (<em>rv_discrete</em>) Probability distribution for the number of items (or variables).</p></li>
<li><p><strong>m</strong> (<em>rv_discrete</em>) Probability distribution for the number of knapsacks (or constraints).</p></li>
<li><p><strong>w</strong> (<em>rv_continuous</em>) Probability distribution for the item weights.</p></li>
<li><p><strong>K</strong> (<em>rv_continuous</em>) Probability distribution for the profit correlation coefficient.</p></li>
<li><p><strong>u</strong> (<em>rv_continuous</em>) Probability distribution for the profit multiplier.</p></li>
<li><p><strong>alpha</strong> (<em>rv_continuous</em>) Probability distribution for the tightness ratio.</p></li>
<li><p><strong>fix_w</strong> (<em>boolean</em>) If true, weights are kept the same (minus the noise from w_jitter) in all
instances.</p></li>
<li><p><strong>w_jitter</strong> (<em>rv_continuous</em>) Probability distribution for random noise added to the weights.</p></li>
<li><p><strong>round</strong> (<em>boolean</em>) If true, all prices, weights and capacities are rounded to the nearest
integer.</p></li>
</ul>
</dd>
</dl>
</dd></dl>
<dl class="py function">
<dt class="sig sig-object py" id="miplearn.problems.multiknapsack.build_multiknapsack_model_gurobipy">
<span class="sig-prename descclassname"><span class="pre">miplearn.problems.multiknapsack.</span></span><span class="sig-name descname"><span class="pre">build_multiknapsack_model_gurobipy</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">data</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><a class="reference internal" href="#miplearn.problems.multiknapsack.MultiKnapsackData" title="miplearn.problems.multiknapsack.MultiKnapsackData"><span class="pre">MultiKnapsackData</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference internal" href="../solvers/#miplearn.solvers.gurobi.GurobiModel" title="miplearn.solvers.gurobi.GurobiModel"><span class="pre">GurobiModel</span></a></span></span><a class="headerlink" href="#miplearn.problems.multiknapsack.build_multiknapsack_model_gurobipy" title="Link to this definition"></a></dt>
<dd><p>Converts multi-knapsack problem data into a concrete Gurobipy model.</p>
</dd></dl>
</section>
<section id="module-miplearn.problems.pmedian">
<span id="miplearn-problems-pmedian"></span><h2><span class="section-number">10.3. </span>miplearn.problems.pmedian<a class="headerlink" href="#module-miplearn.problems.pmedian" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.problems.pmedian.PMedianData">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.problems.pmedian.</span></span><span class="sig-name descname"><span class="pre">PMedianData</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">distances</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">demands</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">p</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">capacities</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.problems.pmedian.PMedianData" title="Link to this definition"></a></dt>
<dd><p>Data for the capacitated p-median problem</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>distances</strong> (<em>numpy.ndarray</em>) Matrix of distances between customer i and facility j.</p></li>
<li><p><strong>demands</strong> (<em>numpy.ndarray</em>) Customer demands.</p></li>
<li><p><strong>p</strong> (<em>int</em>) Number of medians that need to be chosen.</p></li>
<li><p><strong>capacities</strong> (<em>numpy.ndarray</em>) Facility capacities.</p></li>
</ul>
</dd>
</dl>
</dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.problems.pmedian.PMedianGenerator">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.problems.pmedian.</span></span><span class="sig-name descname"><span class="pre">PMedianGenerator</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">x:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">y:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">n:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_discrete_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">p:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_discrete_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">demands:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">capacities:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">distances_jitter:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">demands_jitter:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">capacities_jitter:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">fixed:</span> <span class="pre">bool</span> <span class="pre">=</span> <span class="pre">True</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.problems.pmedian.PMedianGenerator" title="Link to this definition"></a></dt>
<dd><p>Random generator for the capacitated p-median problem.</p>
<p>This class first decides the number of customers and the parameter <cite>p</cite> by
sampling the provided <cite>n</cite> and <cite>p</cite> distributions, respectively. Then, for each
customer <cite>i</cite>, the class builds its geographical location <cite>(xi, yi)</cite> by sampling
the provided <cite>x</cite> and <cite>y</cite> distributions. For each <cite>i</cite>, the demand for customer <cite>i</cite>
and the capacity of facility <cite>i</cite> are decided by sampling the distributions
<cite>demands</cite> and <cite>capacities</cite>, respectively. Finally, the costs <cite>w[i,j]</cite> are set to
the Euclidean distance between the locations of customers <cite>i</cite> and <cite>j</cite>.</p>
<p>If <cite>fixed=True</cite>, then the number of customers, their locations, the parameter
<cite>p</cite>, the demands and the capacities are only sampled from their respective
distributions exactly once, to build a reference instance which is then
perturbed. Specifically, for each perturbation, the distances, demands and
capacities are multiplied by factors sampled from the distributions
<cite>distances_jitter</cite>, <cite>demands_jitter</cite> and <cite>capacities_jitter</cite>, respectively. The
result is a list of instances that have the same set of customers, but slightly
different demands, capacities and distances.</p>
<dl class="field-list simple">
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
<dd class="field-odd"><ul class="simple">
<li><p><strong>x</strong> Probability distribution for the x-coordinate of the points.</p></li>
<li><p><strong>y</strong> Probability distribution for the y-coordinate of the points.</p></li>
<li><p><strong>n</strong> Probability distribution for the number of customer.</p></li>
<li><p><strong>p</strong> Probability distribution for the number of medians.</p></li>
<li><p><strong>demands</strong> Probability distribution for the customer demands.</p></li>
<li><p><strong>capacities</strong> Probability distribution for the facility capacities.</p></li>
<li><p><strong>distances_jitter</strong> Probability distribution for the random scaling factor applied to distances.</p></li>
<li><p><strong>demands_jitter</strong> Probability distribution for the random scaling factor applied to demands.</p></li>
<li><p><strong>capacities_jitter</strong> Probability distribution for the random scaling factor applied to capacities.</p></li>
<li><p><strong>fixed</strong> If <cite>True</cite>, then customer are kept the same across instances.</p></li>
</ul>
</dd>
</dl>
</dd></dl>
<dl class="py function">
<dt class="sig sig-object py" id="miplearn.problems.pmedian.build_pmedian_model_gurobipy">
<span class="sig-prename descclassname"><span class="pre">miplearn.problems.pmedian.</span></span><span class="sig-name descname"><span class="pre">build_pmedian_model_gurobipy</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">data</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><a class="reference internal" href="#miplearn.problems.pmedian.PMedianData" title="miplearn.problems.pmedian.PMedianData"><span class="pre">PMedianData</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference internal" href="../solvers/#miplearn.solvers.gurobi.GurobiModel" title="miplearn.solvers.gurobi.GurobiModel"><span class="pre">GurobiModel</span></a></span></span><a class="headerlink" href="#miplearn.problems.pmedian.build_pmedian_model_gurobipy" title="Link to this definition"></a></dt>
<dd><p>Converts capacitated p-median data into a concrete Gurobipy model.</p>
</dd></dl>
</section>
<section id="module-miplearn.problems.setcover">
<span id="miplearn-problems-setcover"></span><h2><span class="section-number">10.4. </span>miplearn.problems.setcover<a class="headerlink" href="#module-miplearn.problems.setcover" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.problems.setcover.SetCoverData">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.problems.setcover.</span></span><span class="sig-name descname"><span class="pre">SetCoverData</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">costs</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">numpy.ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">incidence_matrix</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">numpy.ndarray</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.problems.setcover.SetCoverData" title="Link to this definition"></a></dt>
<dd></dd></dl>
</section>
<section id="module-miplearn.problems.setpack">
<span id="miplearn-problems-setpack"></span><h2><span class="section-number">10.5. </span>miplearn.problems.setpack<a class="headerlink" href="#module-miplearn.problems.setpack" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.problems.setpack.SetPackData">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.problems.setpack.</span></span><span class="sig-name descname"><span class="pre">SetPackData</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">costs</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">numpy.ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">incidence_matrix</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">numpy.ndarray</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.problems.setpack.SetPackData" title="Link to this definition"></a></dt>
<dd></dd></dl>
</section>
<section id="module-miplearn.problems.stab">
<span id="miplearn-problems-stab"></span><h2><span class="section-number">10.6. </span>miplearn.problems.stab<a class="headerlink" href="#module-miplearn.problems.stab" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.problems.stab.MaxWeightStableSetData">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.problems.stab.</span></span><span class="sig-name descname"><span class="pre">MaxWeightStableSetData</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">graph</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">networkx.classes.graph.Graph</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">weights</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">numpy.ndarray</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.problems.stab.MaxWeightStableSetData" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.problems.stab.MaxWeightStableSetGenerator">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.problems.stab.</span></span><span class="sig-name descname"><span class="pre">MaxWeightStableSetGenerator</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">w:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">n:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_discrete_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">p:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">fix_graph:</span> <span class="pre">bool</span> <span class="pre">=</span> <span class="pre">True</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.problems.stab.MaxWeightStableSetGenerator" title="Link to this definition"></a></dt>
<dd><p>Random instance generator for the Maximum-Weight Stable Set Problem.</p>
<p>The generator has two modes of operation. When <cite>fix_graph=True</cite> is provided,
one random Erdős-Rényi graph $G_{n,p}$ is generated in the constructor, where $n$
and $p$ are sampled from user-provided probability distributions <cite>n</cite> and <cite>p</cite>. To
generate each instance, the generator independently samples each $w_v$ from the
user-provided probability distribution <cite>w</cite>.</p>
<p>When <cite>fix_graph=False</cite>, a new random graph is generated for each instance; the
remaining parameters are sampled in the same way.</p>
</dd></dl>
</section>
<section id="module-miplearn.problems.tsp">
<span id="miplearn-problems-tsp"></span><h2><span class="section-number">10.7. </span>miplearn.problems.tsp<a class="headerlink" href="#module-miplearn.problems.tsp" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.problems.tsp.TravelingSalesmanData">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.problems.tsp.</span></span><span class="sig-name descname"><span class="pre">TravelingSalesmanData</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">n_cities</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">int</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">distances</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">numpy.ndarray</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.problems.tsp.TravelingSalesmanData" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.problems.tsp.TravelingSalesmanGenerator">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.problems.tsp.</span></span><span class="sig-name descname"><span class="pre">TravelingSalesmanGenerator</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">x:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">y:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">n:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_discrete_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">gamma:</span> <span class="pre">~scipy.stats._distn_infrastructure.rv_frozen</span> <span class="pre">=</span> <span class="pre">&lt;scipy.stats._distn_infrastructure.rv_continuous_frozen</span> <span class="pre">object&gt;</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">fix_cities:</span> <span class="pre">bool</span> <span class="pre">=</span> <span class="pre">True</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">round:</span> <span class="pre">bool</span> <span class="pre">=</span> <span class="pre">True</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.problems.tsp.TravelingSalesmanGenerator" title="Link to this definition"></a></dt>
<dd><p>Random generator for the Traveling Salesman Problem.</p>
</dd></dl>
</section>
<section id="module-miplearn.problems.uc">
<span id="miplearn-problems-uc"></span><h2><span class="section-number">10.8. </span>miplearn.problems.uc<a class="headerlink" href="#module-miplearn.problems.uc" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.problems.uc.UnitCommitmentData">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.problems.uc.</span></span><span class="sig-name descname"><span class="pre">UnitCommitmentData</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">demand</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">numpy.ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">min_power</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">numpy.ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">max_power</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">numpy.ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">min_uptime</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">numpy.ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">min_downtime</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">numpy.ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">cost_startup</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">numpy.ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">cost_prod</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">numpy.ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">cost_fixed</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">numpy.ndarray</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.problems.uc.UnitCommitmentData" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py function">
<dt class="sig sig-object py" id="miplearn.problems.uc.build_uc_model_gurobipy">
<span class="sig-prename descclassname"><span class="pre">miplearn.problems.uc.</span></span><span class="sig-name descname"><span class="pre">build_uc_model_gurobipy</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">data</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><a class="reference internal" href="#miplearn.problems.uc.UnitCommitmentData" title="miplearn.problems.uc.UnitCommitmentData"><span class="pre">UnitCommitmentData</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference internal" href="../solvers/#miplearn.solvers.gurobi.GurobiModel" title="miplearn.solvers.gurobi.GurobiModel"><span class="pre">GurobiModel</span></a></span></span><a class="headerlink" href="#miplearn.problems.uc.build_uc_model_gurobipy" title="Link to this definition"></a></dt>
<dd><p>Models the unit commitment problem according to equations (1)-(5) of:</p>
<blockquote>
<div><p>Bendotti, P., Fouilhoux, P. &amp; Rottner, C. The min-up/min-down unit
commitment polytope. J Comb Optim 36, 1024-1058 (2018).
<a class="reference external" href="https://doi.org/10.1007/s10878-018-0273-y">https://doi.org/10.1007/s10878-018-0273-y</a></p>
</div></blockquote>
</dd></dl>
</section>
<section id="module-miplearn.problems.vertexcover">
<span id="miplearn-problems-vertexcover"></span><h2><span class="section-number">10.9. </span>miplearn.problems.vertexcover<a class="headerlink" href="#module-miplearn.problems.vertexcover" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.problems.vertexcover.MinWeightVertexCoverData">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.problems.vertexcover.</span></span><span class="sig-name descname"><span class="pre">MinWeightVertexCoverData</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">graph</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">networkx.classes.graph.Graph</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">weights</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">numpy.ndarray</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.problems.vertexcover.MinWeightVertexCoverData" title="Link to this definition"></a></dt>
<dd></dd></dl>
</section>
</section>
</div>
<div class='prev-next-bottom'>
<a class='left-prev' id="prev-link" href="../../guide/solvers/" title="previous page"><span class="section-number">9. </span>Learning Solver</a>
<a class='right-next' id="next-link" href="../collectors/" title="next page"><span class="section-number">11. </span>Collectors &amp; Extractors</a>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

@ -0,0 +1,736 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<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" />
<title>13. Solvers &#8212; MIPLearn 0.4</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" type="text/css" href="../../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<link rel="index" title="Index" href="../../genindex/" />
<link rel="search" title="Search" href="../../search/" />
<link rel="next" title="14. Helpers" href="../helpers/" />
<link rel="prev" title="12. Components" href="../components/" />
<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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../guide/problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="current nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../components/">
12. Components
</a>
</li>
<li class="toctree-l1 current active">
<a class="current reference internal" href="#">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../helpers/">
14. 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/api/solvers.rst.txt"><button type="button"
class="btn btn-secondary topbarbtn" title="Download source file" data-toggle="tooltip"
data-placement="left">.rst</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="#module-miplearn.solvers.abstract">
13.1. miplearn.solvers.abstract
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.abstract.AbstractModel">
<code class="docutils literal notranslate">
<span class="pre">
AbstractModel
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.abstract.AbstractModel.WHERE_CUTS">
<code class="docutils literal notranslate">
<span class="pre">
AbstractModel.WHERE_CUTS
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.abstract.AbstractModel.WHERE_DEFAULT">
<code class="docutils literal notranslate">
<span class="pre">
AbstractModel.WHERE_DEFAULT
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.abstract.AbstractModel.WHERE_LAZY">
<code class="docutils literal notranslate">
<span class="pre">
AbstractModel.WHERE_LAZY
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.abstract.AbstractModel.add_constrs">
<code class="docutils literal notranslate">
<span class="pre">
AbstractModel.add_constrs()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.abstract.AbstractModel.extract_after_load">
<code class="docutils literal notranslate">
<span class="pre">
AbstractModel.extract_after_load()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.abstract.AbstractModel.extract_after_lp">
<code class="docutils literal notranslate">
<span class="pre">
AbstractModel.extract_after_lp()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.abstract.AbstractModel.extract_after_mip">
<code class="docutils literal notranslate">
<span class="pre">
AbstractModel.extract_after_mip()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.abstract.AbstractModel.fix_variables">
<code class="docutils literal notranslate">
<span class="pre">
AbstractModel.fix_variables()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.abstract.AbstractModel.lazy_enforce">
<code class="docutils literal notranslate">
<span class="pre">
AbstractModel.lazy_enforce()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.abstract.AbstractModel.optimize">
<code class="docutils literal notranslate">
<span class="pre">
AbstractModel.optimize()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.abstract.AbstractModel.relax">
<code class="docutils literal notranslate">
<span class="pre">
AbstractModel.relax()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.abstract.AbstractModel.set_cuts">
<code class="docutils literal notranslate">
<span class="pre">
AbstractModel.set_cuts()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.abstract.AbstractModel.set_warm_starts">
<code class="docutils literal notranslate">
<span class="pre">
AbstractModel.set_warm_starts()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.abstract.AbstractModel.write">
<code class="docutils literal notranslate">
<span class="pre">
AbstractModel.write()
</span>
</code>
</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.solvers.gurobi">
13.2. miplearn.solvers.gurobi
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.gurobi.GurobiModel">
<code class="docutils literal notranslate">
<span class="pre">
GurobiModel
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.gurobi.GurobiModel.add_constr">
<code class="docutils literal notranslate">
<span class="pre">
GurobiModel.add_constr()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.gurobi.GurobiModel.add_constrs">
<code class="docutils literal notranslate">
<span class="pre">
GurobiModel.add_constrs()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.gurobi.GurobiModel.extract_after_load">
<code class="docutils literal notranslate">
<span class="pre">
GurobiModel.extract_after_load()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.gurobi.GurobiModel.extract_after_lp">
<code class="docutils literal notranslate">
<span class="pre">
GurobiModel.extract_after_lp()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.gurobi.GurobiModel.extract_after_mip">
<code class="docutils literal notranslate">
<span class="pre">
GurobiModel.extract_after_mip()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.gurobi.GurobiModel.fix_variables">
<code class="docutils literal notranslate">
<span class="pre">
GurobiModel.fix_variables()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.gurobi.GurobiModel.optimize">
<code class="docutils literal notranslate">
<span class="pre">
GurobiModel.optimize()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.gurobi.GurobiModel.relax">
<code class="docutils literal notranslate">
<span class="pre">
GurobiModel.relax()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.gurobi.GurobiModel.set_time_limit">
<code class="docutils literal notranslate">
<span class="pre">
GurobiModel.set_time_limit()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.gurobi.GurobiModel.set_warm_starts">
<code class="docutils literal notranslate">
<span class="pre">
GurobiModel.set_warm_starts()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.gurobi.GurobiModel.write">
<code class="docutils literal notranslate">
<span class="pre">
GurobiModel.write()
</span>
</code>
</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#module-miplearn.solvers.learning">
13.3. miplearn.solvers.learning
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.learning.LearningSolver">
<code class="docutils literal notranslate">
<span class="pre">
LearningSolver
</span>
</code>
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.learning.LearningSolver.fit">
<code class="docutils literal notranslate">
<span class="pre">
LearningSolver.fit()
</span>
</code>
</a>
</li>
<li class="toc-h4 nav-item toc-entry">
<a class="reference internal nav-link" href="#miplearn.solvers.learning.LearningSolver.optimize">
<code class="docutils literal notranslate">
<span class="pre">
LearningSolver.optimize()
</span>
</code>
</a>
</li>
</ul>
</li>
</ul>
</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>
<section id="solvers">
<h1><span class="section-number">13. </span>Solvers<a class="headerlink" href="#solvers" title="Link to this heading"></a></h1>
<section id="module-miplearn.solvers.abstract">
<span id="miplearn-solvers-abstract"></span><h2><span class="section-number">13.1. </span>miplearn.solvers.abstract<a class="headerlink" href="#module-miplearn.solvers.abstract" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.solvers.abstract.AbstractModel">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.solvers.abstract.</span></span><span class="sig-name descname"><span class="pre">AbstractModel</span></span><a class="headerlink" href="#miplearn.solvers.abstract.AbstractModel" title="Link to this definition"></a></dt>
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">ABC</span></code></p>
<dl class="py attribute">
<dt class="sig sig-object py" id="miplearn.solvers.abstract.AbstractModel.WHERE_CUTS">
<span class="sig-name descname"><span class="pre">WHERE_CUTS</span></span><em class="property"><span class="w"> </span><span class="p"><span class="pre">=</span></span><span class="w"> </span><span class="pre">'cuts'</span></em><a class="headerlink" href="#miplearn.solvers.abstract.AbstractModel.WHERE_CUTS" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py" id="miplearn.solvers.abstract.AbstractModel.WHERE_DEFAULT">
<span class="sig-name descname"><span class="pre">WHERE_DEFAULT</span></span><em class="property"><span class="w"> </span><span class="p"><span class="pre">=</span></span><span class="w"> </span><span class="pre">'default'</span></em><a class="headerlink" href="#miplearn.solvers.abstract.AbstractModel.WHERE_DEFAULT" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py attribute">
<dt class="sig sig-object py" id="miplearn.solvers.abstract.AbstractModel.WHERE_LAZY">
<span class="sig-name descname"><span class="pre">WHERE_LAZY</span></span><em class="property"><span class="w"> </span><span class="p"><span class="pre">=</span></span><span class="w"> </span><span class="pre">'lazy'</span></em><a class="headerlink" href="#miplearn.solvers.abstract.AbstractModel.WHERE_LAZY" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.abstract.AbstractModel.add_constrs">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">add_constrs</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">var_names</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">constrs_lhs</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">constrs_sense</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">constrs_rhs</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">stats</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Dict</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.abstract.AbstractModel.add_constrs" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.abstract.AbstractModel.extract_after_load">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">extract_after_load</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../helpers/#miplearn.h5.H5File" title="miplearn.h5.H5File"><span class="pre">H5File</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.abstract.AbstractModel.extract_after_load" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.abstract.AbstractModel.extract_after_lp">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">extract_after_lp</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../helpers/#miplearn.h5.H5File" title="miplearn.h5.H5File"><span class="pre">H5File</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.abstract.AbstractModel.extract_after_lp" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.abstract.AbstractModel.extract_after_mip">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">extract_after_mip</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../helpers/#miplearn.h5.H5File" title="miplearn.h5.H5File"><span class="pre">H5File</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.abstract.AbstractModel.extract_after_mip" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.abstract.AbstractModel.fix_variables">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">fix_variables</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">var_names</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">var_values</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">stats</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Dict</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.abstract.AbstractModel.fix_variables" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.abstract.AbstractModel.lazy_enforce">
<span class="sig-name descname"><span class="pre">lazy_enforce</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">violations</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">Any</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.abstract.AbstractModel.lazy_enforce" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.abstract.AbstractModel.optimize">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">optimize</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.abstract.AbstractModel.optimize" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.abstract.AbstractModel.relax">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">relax</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference internal" href="#miplearn.solvers.abstract.AbstractModel" title="miplearn.solvers.abstract.AbstractModel"><span class="pre">AbstractModel</span></a></span></span><a class="headerlink" href="#miplearn.solvers.abstract.AbstractModel.relax" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.abstract.AbstractModel.set_cuts">
<span class="sig-name descname"><span class="pre">set_cuts</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">cuts</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.abstract.AbstractModel.set_cuts" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.abstract.AbstractModel.set_warm_starts">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">set_warm_starts</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">var_names</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">var_values</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">stats</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Dict</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.abstract.AbstractModel.set_warm_starts" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.abstract.AbstractModel.write">
<em class="property"><span class="pre">abstract</span><span class="w"> </span></em><span class="sig-name descname"><span class="pre">write</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.abstract.AbstractModel.write" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
</section>
<section id="module-miplearn.solvers.gurobi">
<span id="miplearn-solvers-gurobi"></span><h2><span class="section-number">13.2. </span>miplearn.solvers.gurobi<a class="headerlink" href="#module-miplearn.solvers.gurobi" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.solvers.gurobi.GurobiModel">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.solvers.gurobi.</span></span><span class="sig-name descname"><span class="pre">GurobiModel</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">inner</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Model</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">lazy_separate</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Callable</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">lazy_enforce</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Callable</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">cuts_separate</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Callable</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">cuts_enforce</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Callable</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.solvers.gurobi.GurobiModel" title="Link to this definition"></a></dt>
<dd><p>Bases: <a class="reference internal" href="#miplearn.solvers.abstract.AbstractModel" title="miplearn.solvers.abstract.AbstractModel"><code class="xref py py-class docutils literal notranslate"><span class="pre">AbstractModel</span></code></a></p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.gurobi.GurobiModel.add_constr">
<span class="sig-name descname"><span class="pre">add_constr</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">constr</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Any</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.gurobi.GurobiModel.add_constr" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.gurobi.GurobiModel.add_constrs">
<span class="sig-name descname"><span class="pre">add_constrs</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">var_names</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">constrs_lhs</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">constrs_sense</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">constrs_rhs</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">stats</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Dict</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.gurobi.GurobiModel.add_constrs" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.gurobi.GurobiModel.extract_after_load">
<span class="sig-name descname"><span class="pre">extract_after_load</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../helpers/#miplearn.h5.H5File" title="miplearn.h5.H5File"><span class="pre">H5File</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.gurobi.GurobiModel.extract_after_load" title="Link to this definition"></a></dt>
<dd><p>Given a model that has just been loaded, extracts static problem
features, such as variable names and types, objective coefficients, etc.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.gurobi.GurobiModel.extract_after_lp">
<span class="sig-name descname"><span class="pre">extract_after_lp</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../helpers/#miplearn.h5.H5File" title="miplearn.h5.H5File"><span class="pre">H5File</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.gurobi.GurobiModel.extract_after_lp" title="Link to this definition"></a></dt>
<dd><p>Given a linear programming model that has just been solved, extracts
dynamic problem features, such as optimal LP solution, basis status,
etc.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.gurobi.GurobiModel.extract_after_mip">
<span class="sig-name descname"><span class="pre">extract_after_mip</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">h5</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><a class="reference internal" href="../helpers/#miplearn.h5.H5File" title="miplearn.h5.H5File"><span class="pre">H5File</span></a></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.gurobi.GurobiModel.extract_after_mip" title="Link to this definition"></a></dt>
<dd><p>Given a mixed-integer linear programming model that has just been
solved, extracts dynamic problem features, such as optimal MIP solution.</p>
</dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.gurobi.GurobiModel.fix_variables">
<span class="sig-name descname"><span class="pre">fix_variables</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">var_names</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">var_values</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">stats</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Dict</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.gurobi.GurobiModel.fix_variables" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.gurobi.GurobiModel.optimize">
<span class="sig-name descname"><span class="pre">optimize</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.gurobi.GurobiModel.optimize" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.gurobi.GurobiModel.relax">
<span class="sig-name descname"><span class="pre">relax</span></span><span class="sig-paren">(</span><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><a class="reference internal" href="#miplearn.solvers.gurobi.GurobiModel" title="miplearn.solvers.gurobi.GurobiModel"><span class="pre">GurobiModel</span></a></span></span><a class="headerlink" href="#miplearn.solvers.gurobi.GurobiModel.relax" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.gurobi.GurobiModel.set_time_limit">
<span class="sig-name descname"><span class="pre">set_time_limit</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">time_limit_sec</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">float</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.gurobi.GurobiModel.set_time_limit" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.gurobi.GurobiModel.set_warm_starts">
<span class="sig-name descname"><span class="pre">set_warm_starts</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">var_names</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">var_values</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">ndarray</span></span></em>, <em class="sig-param"><span class="n"><span class="pre">stats</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Dict</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.gurobi.GurobiModel.set_warm_starts" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.gurobi.GurobiModel.write">
<span class="sig-name descname"><span class="pre">write</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">filename</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.gurobi.GurobiModel.write" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
</section>
<section id="module-miplearn.solvers.learning">
<span id="miplearn-solvers-learning"></span><h2><span class="section-number">13.3. </span>miplearn.solvers.learning<a class="headerlink" href="#module-miplearn.solvers.learning" title="Link to this heading"></a></h2>
<dl class="py class">
<dt class="sig sig-object py" id="miplearn.solvers.learning.LearningSolver">
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">miplearn.solvers.learning.</span></span><span class="sig-name descname"><span class="pre">LearningSolver</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">components</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">Any</span><span class="p"><span class="pre">]</span></span></span></em>, <em class="sig-param"><span class="n"><span class="pre">skip_lp</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">bool</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">False</span></span></em><span class="sig-paren">)</span><a class="headerlink" href="#miplearn.solvers.learning.LearningSolver" title="Link to this definition"></a></dt>
<dd><p>Bases: <code class="xref py py-class docutils literal notranslate"><span class="pre">object</span></code></p>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.learning.LearningSolver.fit">
<span class="sig-name descname"><span class="pre">fit</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">data_filenames</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">List</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">]</span></span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">None</span></span></span><a class="headerlink" href="#miplearn.solvers.learning.LearningSolver.fit" title="Link to this definition"></a></dt>
<dd></dd></dl>
<dl class="py method">
<dt class="sig sig-object py" id="miplearn.solvers.learning.LearningSolver.optimize">
<span class="sig-name descname"><span class="pre">optimize</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="n"><span class="pre">model</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">str</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><a class="reference internal" href="#miplearn.solvers.abstract.AbstractModel" title="miplearn.solvers.abstract.AbstractModel"><span class="pre">AbstractModel</span></a></span></em>, <em class="sig-param"><span class="n"><span class="pre">build_model</span></span><span class="p"><span class="pre">:</span></span><span class="w"> </span><span class="n"><span class="pre">Callable</span><span class="w"> </span><span class="p"><span class="pre">|</span></span><span class="w"> </span><span class="pre">None</span></span><span class="w"> </span><span class="o"><span class="pre">=</span></span><span class="w"> </span><span class="default_value"><span class="pre">None</span></span></em><span class="sig-paren">)</span> <span class="sig-return"><span class="sig-return-icon">&#x2192;</span> <span class="sig-return-typehint"><span class="pre">Dict</span><span class="p"><span class="pre">[</span></span><span class="pre">str</span><span class="p"><span class="pre">,</span></span><span class="w"> </span><span class="pre">Any</span><span class="p"><span class="pre">]</span></span></span></span><a class="headerlink" href="#miplearn.solvers.learning.LearningSolver.optimize" title="Link to this definition"></a></dt>
<dd></dd></dl>
</dd></dl>
</section>
</section>
</div>
<div class='prev-next-bottom'>
<a class='left-prev' id="prev-link" href="../components/" title="previous page"><span class="section-number">12. </span>Components</a>
<a class='right-next' id="next-link" href="../helpers/" title="next page"><span class="section-number">14. </span>Helpers</a>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

@ -0,0 +1,872 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Index &#8212; MIPLearn 0.4</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" type="text/css" href="../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../_static/doctools.js?v=888ff710"></script>
<script src="../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<link rel="index" title="Index" href="#" />
<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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../tutorials/getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../tutorials/getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../tutorials/getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../tutorials/cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../guide/problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../guide/collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../guide/features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../guide/primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../guide/solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../api/problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../api/collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../api/components/">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../api/solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../api/helpers/">
14. 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>
<!-- 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>
</div>
</div>
<div id="main-content" class="row">
<div class="col-12 col-md-9 pl-md-3 pr-md-0">
<div>
<h1 id="index">Index</h1>
<div class="genindex-jumpbox">
<a href="#A"><strong>A</strong></a>
| <a href="#B"><strong>B</strong></a>
| <a href="#C"><strong>C</strong></a>
| <a href="#E"><strong>E</strong></a>
| <a href="#F"><strong>F</strong></a>
| <a href="#G"><strong>G</strong></a>
| <a href="#H"><strong>H</strong></a>
| <a href="#I"><strong>I</strong></a>
| <a href="#J"><strong>J</strong></a>
| <a href="#L"><strong>L</strong></a>
| <a href="#M"><strong>M</strong></a>
| <a href="#O"><strong>O</strong></a>
| <a href="#P"><strong>P</strong></a>
| <a href="#R"><strong>R</strong></a>
| <a href="#S"><strong>S</strong></a>
| <a href="#T"><strong>T</strong></a>
| <a href="#U"><strong>U</strong></a>
| <a href="#W"><strong>W</strong></a>
</div>
<h2 id="A">A</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/solvers/#miplearn.solvers.abstract.AbstractModel">AbstractModel (class in miplearn.solvers.abstract)</a>
</li>
<li><a href="../api/solvers/#miplearn.solvers.gurobi.GurobiModel.add_constr">add_constr() (miplearn.solvers.gurobi.GurobiModel method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/solvers/#miplearn.solvers.abstract.AbstractModel.add_constrs">add_constrs() (miplearn.solvers.abstract.AbstractModel method)</a>
<ul>
<li><a href="../api/solvers/#miplearn.solvers.gurobi.GurobiModel.add_constrs">(miplearn.solvers.gurobi.GurobiModel method)</a>
</li>
</ul></li>
<li><a href="../api/collectors/#miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor">AlvLouWeh2017Extractor (class in miplearn.extractors.AlvLouWeh2017)</a>
</li>
</ul></td>
</tr></table>
<h2 id="B">B</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/collectors/#miplearn.collectors.basic.BasicCollector">BasicCollector (class in miplearn.collectors.basic)</a>
</li>
<li><a href="../api/components/#miplearn.components.primal.expert.ExpertPrimalComponent.before_mip">before_mip() (miplearn.components.primal.expert.ExpertPrimalComponent method)</a>
<ul>
<li><a href="../api/components/#miplearn.components.primal.indep.IndependentVarsPrimalComponent.before_mip">(miplearn.components.primal.indep.IndependentVarsPrimalComponent method)</a>
</li>
<li><a href="../api/components/#miplearn.components.primal.joint.JointVarsPrimalComponent.before_mip">(miplearn.components.primal.joint.JointVarsPrimalComponent method)</a>
</li>
<li><a href="../api/components/#miplearn.components.primal.mem.MemorizingPrimalComponent.before_mip">(miplearn.components.primal.mem.MemorizingPrimalComponent method)</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/problems/#miplearn.problems.binpack.BinPackData">BinPackData (class in miplearn.problems.binpack)</a>
</li>
<li><a href="../api/problems/#miplearn.problems.binpack.BinPackGenerator">BinPackGenerator (class in miplearn.problems.binpack)</a>
</li>
<li><a href="../api/problems/#miplearn.problems.binpack.build_binpack_model_gurobipy">build_binpack_model_gurobipy() (in module miplearn.problems.binpack)</a>
</li>
<li><a href="../api/problems/#miplearn.problems.multiknapsack.build_multiknapsack_model_gurobipy">build_multiknapsack_model_gurobipy() (in module miplearn.problems.multiknapsack)</a>
</li>
<li><a href="../api/problems/#miplearn.problems.pmedian.build_pmedian_model_gurobipy">build_pmedian_model_gurobipy() (in module miplearn.problems.pmedian)</a>
</li>
<li><a href="../api/problems/#miplearn.problems.uc.build_uc_model_gurobipy">build_uc_model_gurobipy() (in module miplearn.problems.uc)</a>
</li>
</ul></td>
</tr></table>
<h2 id="C">C</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/helpers/#miplearn.h5.H5File.close">close() (miplearn.h5.H5File method)</a>
</li>
<li><a href="../api/collectors/#miplearn.collectors.basic.BasicCollector.collect">collect() (miplearn.collectors.basic.BasicCollector method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/components/#miplearn.components.primal.mem.MergeTopSolutions.construct">construct() (miplearn.components.primal.mem.MergeTopSolutions method)</a>
<ul>
<li><a href="../api/components/#miplearn.components.primal.mem.SelectTopSolutions.construct">(miplearn.components.primal.mem.SelectTopSolutions method)</a>
</li>
<li><a href="../api/components/#miplearn.components.primal.mem.SolutionConstructor.construct">(miplearn.components.primal.mem.SolutionConstructor method)</a>
</li>
</ul></li>
</ul></td>
</tr></table>
<h2 id="E">E</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/components/#miplearn.components.primal.actions.EnforceProximity">EnforceProximity (class in miplearn.components.primal.actions)</a>
</li>
<li><a href="../api/components/#miplearn.components.primal.expert.ExpertPrimalComponent">ExpertPrimalComponent (class in miplearn.components.primal.expert)</a>
</li>
<li><a href="../api/solvers/#miplearn.solvers.abstract.AbstractModel.extract_after_load">extract_after_load() (miplearn.solvers.abstract.AbstractModel method)</a>
<ul>
<li><a href="../api/solvers/#miplearn.solvers.gurobi.GurobiModel.extract_after_load">(miplearn.solvers.gurobi.GurobiModel method)</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/solvers/#miplearn.solvers.abstract.AbstractModel.extract_after_lp">extract_after_lp() (miplearn.solvers.abstract.AbstractModel method)</a>
<ul>
<li><a href="../api/solvers/#miplearn.solvers.gurobi.GurobiModel.extract_after_lp">(miplearn.solvers.gurobi.GurobiModel method)</a>
</li>
</ul></li>
<li><a href="../api/solvers/#miplearn.solvers.abstract.AbstractModel.extract_after_mip">extract_after_mip() (miplearn.solvers.abstract.AbstractModel method)</a>
<ul>
<li><a href="../api/solvers/#miplearn.solvers.gurobi.GurobiModel.extract_after_mip">(miplearn.solvers.gurobi.GurobiModel method)</a>
</li>
</ul></li>
</ul></td>
</tr></table>
<h2 id="F">F</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/collectors/#miplearn.classifiers.minprob.MinProbabilityClassifier.fit">fit() (miplearn.classifiers.minprob.MinProbabilityClassifier method)</a>
<ul>
<li><a href="../api/collectors/#miplearn.classifiers.singleclass.SingleClassFix.fit">(miplearn.classifiers.singleclass.SingleClassFix method)</a>
</li>
<li><a href="../api/components/#miplearn.components.primal.expert.ExpertPrimalComponent.fit">(miplearn.components.primal.expert.ExpertPrimalComponent method)</a>
</li>
<li><a href="../api/components/#miplearn.components.primal.indep.IndependentVarsPrimalComponent.fit">(miplearn.components.primal.indep.IndependentVarsPrimalComponent method)</a>
</li>
<li><a href="../api/components/#miplearn.components.primal.joint.JointVarsPrimalComponent.fit">(miplearn.components.primal.joint.JointVarsPrimalComponent method)</a>
</li>
<li><a href="../api/components/#miplearn.components.primal.mem.MemorizingPrimalComponent.fit">(miplearn.components.primal.mem.MemorizingPrimalComponent method)</a>
</li>
<li><a href="../api/solvers/#miplearn.solvers.learning.LearningSolver.fit">(miplearn.solvers.learning.LearningSolver method)</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/solvers/#miplearn.solvers.abstract.AbstractModel.fix_variables">fix_variables() (miplearn.solvers.abstract.AbstractModel method)</a>
<ul>
<li><a href="../api/solvers/#miplearn.solvers.gurobi.GurobiModel.fix_variables">(miplearn.solvers.gurobi.GurobiModel method)</a>
</li>
</ul></li>
<li><a href="../api/components/#miplearn.components.primal.actions.FixVariables">FixVariables (class in miplearn.components.primal.actions)</a>
</li>
</ul></td>
</tr></table>
<h2 id="G">G</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/problems/#miplearn.problems.binpack.BinPackGenerator.generate">generate() (miplearn.problems.binpack.BinPackGenerator method)</a>
</li>
<li><a href="../api/helpers/#miplearn.h5.H5File.get_array">get_array() (miplearn.h5.H5File method)</a>
</li>
<li><a href="../api/helpers/#miplearn.h5.H5File.get_bytes">get_bytes() (miplearn.h5.H5File method)</a>
</li>
<li><a href="../api/collectors/#miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor.get_constr_features">get_constr_features() (miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor method)</a>
<ul>
<li><a href="../api/collectors/#miplearn.extractors.fields.H5FieldsExtractor.get_constr_features">(miplearn.extractors.fields.H5FieldsExtractor method)</a>
</li>
</ul></li>
<li><a href="../api/collectors/#miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor.get_instance_features">get_instance_features() (miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor method)</a>
<ul>
<li><a href="../api/collectors/#miplearn.extractors.fields.H5FieldsExtractor.get_instance_features">(miplearn.extractors.fields.H5FieldsExtractor method)</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/helpers/#miplearn.h5.H5File.get_scalar">get_scalar() (miplearn.h5.H5File method)</a>
</li>
<li><a href="../api/helpers/#miplearn.h5.H5File.get_sparse">get_sparse() (miplearn.h5.H5File method)</a>
</li>
<li><a href="../api/collectors/#miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor.get_var_features">get_var_features() (miplearn.extractors.AlvLouWeh2017.AlvLouWeh2017Extractor method)</a>
<ul>
<li><a href="../api/collectors/#miplearn.extractors.fields.H5FieldsExtractor.get_var_features">(miplearn.extractors.fields.H5FieldsExtractor method)</a>
</li>
</ul></li>
<li><a href="../api/solvers/#miplearn.solvers.gurobi.GurobiModel">GurobiModel (class in miplearn.solvers.gurobi)</a>
</li>
<li><a href="../api/helpers/#miplearn.io.gzip">gzip() (in module miplearn.io)</a>
</li>
</ul></td>
</tr></table>
<h2 id="H">H</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/collectors/#miplearn.extractors.fields.H5FieldsExtractor">H5FieldsExtractor (class in miplearn.extractors.fields)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/helpers/#miplearn.h5.H5File">H5File (class in miplearn.h5)</a>
</li>
</ul></td>
</tr></table>
<h2 id="I">I</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/components/#miplearn.components.primal.indep.IndependentVarsPrimalComponent">IndependentVarsPrimalComponent (class in miplearn.components.primal.indep)</a>
</li>
</ul></td>
</tr></table>
<h2 id="J">J</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/components/#miplearn.components.primal.joint.JointVarsPrimalComponent">JointVarsPrimalComponent (class in miplearn.components.primal.joint)</a>
</li>
</ul></td>
</tr></table>
<h2 id="L">L</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/solvers/#miplearn.solvers.abstract.AbstractModel.lazy_enforce">lazy_enforce() (miplearn.solvers.abstract.AbstractModel method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/solvers/#miplearn.solvers.learning.LearningSolver">LearningSolver (class in miplearn.solvers.learning)</a>
</li>
</ul></td>
</tr></table>
<h2 id="M">M</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/problems/#miplearn.problems.stab.MaxWeightStableSetData">MaxWeightStableSetData (class in miplearn.problems.stab)</a>
</li>
<li><a href="../api/problems/#miplearn.problems.stab.MaxWeightStableSetGenerator">MaxWeightStableSetGenerator (class in miplearn.problems.stab)</a>
</li>
<li><a href="../api/components/#miplearn.components.primal.mem.MemorizingPrimalComponent">MemorizingPrimalComponent (class in miplearn.components.primal.mem)</a>
</li>
<li><a href="../api/components/#miplearn.components.primal.mem.MergeTopSolutions">MergeTopSolutions (class in miplearn.components.primal.mem)</a>
</li>
<li><a href="../api/collectors/#miplearn.classifiers.minprob.MinProbabilityClassifier">MinProbabilityClassifier (class in miplearn.classifiers.minprob)</a>
</li>
<li><a href="../api/problems/#miplearn.problems.vertexcover.MinWeightVertexCoverData">MinWeightVertexCoverData (class in miplearn.problems.vertexcover)</a>
</li>
<li>
miplearn.classifiers.minprob
<ul>
<li><a href="../api/collectors/#module-miplearn.classifiers.minprob">module</a>
</li>
</ul></li>
<li>
miplearn.classifiers.singleclass
<ul>
<li><a href="../api/collectors/#module-miplearn.classifiers.singleclass">module</a>
</li>
</ul></li>
<li>
miplearn.collectors.basic
<ul>
<li><a href="../api/collectors/#module-miplearn.collectors.basic">module</a>
</li>
</ul></li>
<li>
miplearn.components.primal.actions
<ul>
<li><a href="../api/components/#module-miplearn.components.primal.actions">module</a>
</li>
</ul></li>
<li>
miplearn.components.primal.expert
<ul>
<li><a href="../api/components/#module-miplearn.components.primal.expert">module</a>
</li>
</ul></li>
<li>
miplearn.components.primal.indep
<ul>
<li><a href="../api/components/#module-miplearn.components.primal.indep">module</a>
</li>
</ul></li>
<li>
miplearn.components.primal.joint
<ul>
<li><a href="../api/components/#module-miplearn.components.primal.joint">module</a>
</li>
</ul></li>
<li>
miplearn.components.primal.mem
<ul>
<li><a href="../api/components/#module-miplearn.components.primal.mem">module</a>
</li>
</ul></li>
<li>
miplearn.extractors.AlvLouWeh2017
<ul>
<li><a href="../api/collectors/#module-miplearn.extractors.AlvLouWeh2017">module</a>
</li>
</ul></li>
<li>
miplearn.extractors.fields
<ul>
<li><a href="../api/collectors/#module-miplearn.extractors.fields">module</a>
</li>
</ul></li>
<li>
miplearn.h5
<ul>
<li><a href="../api/helpers/#module-miplearn.h5">module</a>
</li>
</ul></li>
<li>
miplearn.io
<ul>
<li><a href="../api/helpers/#module-miplearn.io">module</a>
</li>
</ul></li>
<li>
miplearn.problems.binpack
<ul>
<li><a href="../api/problems/#module-miplearn.problems.binpack">module</a>
</li>
</ul></li>
<li>
miplearn.problems.multiknapsack
<ul>
<li><a href="../api/problems/#module-miplearn.problems.multiknapsack">module</a>
</li>
</ul></li>
<li>
miplearn.problems.pmedian
<ul>
<li><a href="../api/problems/#module-miplearn.problems.pmedian">module</a>
</li>
</ul></li>
<li>
miplearn.problems.setcover
<ul>
<li><a href="../api/problems/#module-miplearn.problems.setcover">module</a>
</li>
</ul></li>
<li>
miplearn.problems.setpack
<ul>
<li><a href="../api/problems/#module-miplearn.problems.setpack">module</a>
</li>
</ul></li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li>
miplearn.problems.stab
<ul>
<li><a href="../api/problems/#module-miplearn.problems.stab">module</a>
</li>
</ul></li>
<li>
miplearn.problems.tsp
<ul>
<li><a href="../api/problems/#module-miplearn.problems.tsp">module</a>
</li>
</ul></li>
<li>
miplearn.problems.uc
<ul>
<li><a href="../api/problems/#module-miplearn.problems.uc">module</a>
</li>
</ul></li>
<li>
miplearn.problems.vertexcover
<ul>
<li><a href="../api/problems/#module-miplearn.problems.vertexcover">module</a>
</li>
</ul></li>
<li>
miplearn.solvers.abstract
<ul>
<li><a href="../api/solvers/#module-miplearn.solvers.abstract">module</a>
</li>
</ul></li>
<li>
miplearn.solvers.gurobi
<ul>
<li><a href="../api/solvers/#module-miplearn.solvers.gurobi">module</a>
</li>
</ul></li>
<li>
miplearn.solvers.learning
<ul>
<li><a href="../api/solvers/#module-miplearn.solvers.learning">module</a>
</li>
</ul></li>
<li>
module
<ul>
<li><a href="../api/collectors/#module-miplearn.classifiers.minprob">miplearn.classifiers.minprob</a>
</li>
<li><a href="../api/collectors/#module-miplearn.classifiers.singleclass">miplearn.classifiers.singleclass</a>
</li>
<li><a href="../api/collectors/#module-miplearn.collectors.basic">miplearn.collectors.basic</a>
</li>
<li><a href="../api/components/#module-miplearn.components.primal.actions">miplearn.components.primal.actions</a>
</li>
<li><a href="../api/components/#module-miplearn.components.primal.expert">miplearn.components.primal.expert</a>
</li>
<li><a href="../api/components/#module-miplearn.components.primal.indep">miplearn.components.primal.indep</a>
</li>
<li><a href="../api/components/#module-miplearn.components.primal.joint">miplearn.components.primal.joint</a>
</li>
<li><a href="../api/components/#module-miplearn.components.primal.mem">miplearn.components.primal.mem</a>
</li>
<li><a href="../api/collectors/#module-miplearn.extractors.AlvLouWeh2017">miplearn.extractors.AlvLouWeh2017</a>
</li>
<li><a href="../api/collectors/#module-miplearn.extractors.fields">miplearn.extractors.fields</a>
</li>
<li><a href="../api/helpers/#module-miplearn.h5">miplearn.h5</a>
</li>
<li><a href="../api/helpers/#module-miplearn.io">miplearn.io</a>
</li>
<li><a href="../api/problems/#module-miplearn.problems.binpack">miplearn.problems.binpack</a>
</li>
<li><a href="../api/problems/#module-miplearn.problems.multiknapsack">miplearn.problems.multiknapsack</a>
</li>
<li><a href="../api/problems/#module-miplearn.problems.pmedian">miplearn.problems.pmedian</a>
</li>
<li><a href="../api/problems/#module-miplearn.problems.setcover">miplearn.problems.setcover</a>
</li>
<li><a href="../api/problems/#module-miplearn.problems.setpack">miplearn.problems.setpack</a>
</li>
<li><a href="../api/problems/#module-miplearn.problems.stab">miplearn.problems.stab</a>
</li>
<li><a href="../api/problems/#module-miplearn.problems.tsp">miplearn.problems.tsp</a>
</li>
<li><a href="../api/problems/#module-miplearn.problems.uc">miplearn.problems.uc</a>
</li>
<li><a href="../api/problems/#module-miplearn.problems.vertexcover">miplearn.problems.vertexcover</a>
</li>
<li><a href="../api/solvers/#module-miplearn.solvers.abstract">miplearn.solvers.abstract</a>
</li>
<li><a href="../api/solvers/#module-miplearn.solvers.gurobi">miplearn.solvers.gurobi</a>
</li>
<li><a href="../api/solvers/#module-miplearn.solvers.learning">miplearn.solvers.learning</a>
</li>
</ul></li>
<li><a href="../api/problems/#miplearn.problems.multiknapsack.MultiKnapsackData">MultiKnapsackData (class in miplearn.problems.multiknapsack)</a>
</li>
<li><a href="../api/problems/#miplearn.problems.multiknapsack.MultiKnapsackGenerator">MultiKnapsackGenerator (class in miplearn.problems.multiknapsack)</a>
</li>
</ul></td>
</tr></table>
<h2 id="O">O</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/solvers/#miplearn.solvers.abstract.AbstractModel.optimize">optimize() (miplearn.solvers.abstract.AbstractModel method)</a>
<ul>
<li><a href="../api/solvers/#miplearn.solvers.gurobi.GurobiModel.optimize">(miplearn.solvers.gurobi.GurobiModel method)</a>
</li>
<li><a href="../api/solvers/#miplearn.solvers.learning.LearningSolver.optimize">(miplearn.solvers.learning.LearningSolver method)</a>
</li>
</ul></li>
</ul></td>
</tr></table>
<h2 id="P">P</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/components/#miplearn.components.primal.actions.EnforceProximity.perform">perform() (miplearn.components.primal.actions.EnforceProximity method)</a>
<ul>
<li><a href="../api/components/#miplearn.components.primal.actions.FixVariables.perform">(miplearn.components.primal.actions.FixVariables method)</a>
</li>
<li><a href="../api/components/#miplearn.components.primal.actions.PrimalComponentAction.perform">(miplearn.components.primal.actions.PrimalComponentAction method)</a>
</li>
<li><a href="../api/components/#miplearn.components.primal.actions.SetWarmStart.perform">(miplearn.components.primal.actions.SetWarmStart method)</a>
</li>
</ul></li>
<li><a href="../api/problems/#miplearn.problems.pmedian.PMedianData">PMedianData (class in miplearn.problems.pmedian)</a>
</li>
<li><a href="../api/problems/#miplearn.problems.pmedian.PMedianGenerator">PMedianGenerator (class in miplearn.problems.pmedian)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/collectors/#miplearn.classifiers.minprob.MinProbabilityClassifier.predict">predict() (miplearn.classifiers.minprob.MinProbabilityClassifier method)</a>
<ul>
<li><a href="../api/collectors/#miplearn.classifiers.singleclass.SingleClassFix.predict">(miplearn.classifiers.singleclass.SingleClassFix method)</a>
</li>
</ul></li>
<li><a href="../api/components/#miplearn.components.primal.actions.PrimalComponentAction">PrimalComponentAction (class in miplearn.components.primal.actions)</a>
</li>
<li><a href="../api/helpers/#miplearn.h5.H5File.put_array">put_array() (miplearn.h5.H5File method)</a>
</li>
<li><a href="../api/helpers/#miplearn.h5.H5File.put_bytes">put_bytes() (miplearn.h5.H5File method)</a>
</li>
<li><a href="../api/helpers/#miplearn.h5.H5File.put_scalar">put_scalar() (miplearn.h5.H5File method)</a>
</li>
<li><a href="../api/helpers/#miplearn.h5.H5File.put_sparse">put_sparse() (miplearn.h5.H5File method)</a>
</li>
</ul></td>
</tr></table>
<h2 id="R">R</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/helpers/#miplearn.io.read_pkl_gz">read_pkl_gz() (in module miplearn.io)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/solvers/#miplearn.solvers.abstract.AbstractModel.relax">relax() (miplearn.solvers.abstract.AbstractModel method)</a>
<ul>
<li><a href="../api/solvers/#miplearn.solvers.gurobi.GurobiModel.relax">(miplearn.solvers.gurobi.GurobiModel method)</a>
</li>
</ul></li>
</ul></td>
</tr></table>
<h2 id="S">S</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/components/#miplearn.components.primal.mem.SelectTopSolutions">SelectTopSolutions (class in miplearn.components.primal.mem)</a>
</li>
<li><a href="../api/solvers/#miplearn.solvers.abstract.AbstractModel.set_cuts">set_cuts() (miplearn.solvers.abstract.AbstractModel method)</a>
</li>
<li><a href="../api/collectors/#miplearn.classifiers.minprob.MinProbabilityClassifier.set_fit_request">set_fit_request() (miplearn.classifiers.minprob.MinProbabilityClassifier method)</a>
<ul>
<li><a href="../api/collectors/#miplearn.classifiers.singleclass.SingleClassFix.set_fit_request">(miplearn.classifiers.singleclass.SingleClassFix method)</a>
</li>
</ul></li>
<li><a href="../api/collectors/#miplearn.classifiers.minprob.MinProbabilityClassifier.set_predict_request">set_predict_request() (miplearn.classifiers.minprob.MinProbabilityClassifier method)</a>
<ul>
<li><a href="../api/collectors/#miplearn.classifiers.singleclass.SingleClassFix.set_predict_request">(miplearn.classifiers.singleclass.SingleClassFix method)</a>
</li>
</ul></li>
<li><a href="../api/solvers/#miplearn.solvers.gurobi.GurobiModel.set_time_limit">set_time_limit() (miplearn.solvers.gurobi.GurobiModel method)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/solvers/#miplearn.solvers.abstract.AbstractModel.set_warm_starts">set_warm_starts() (miplearn.solvers.abstract.AbstractModel method)</a>
<ul>
<li><a href="../api/solvers/#miplearn.solvers.gurobi.GurobiModel.set_warm_starts">(miplearn.solvers.gurobi.GurobiModel method)</a>
</li>
</ul></li>
<li><a href="../api/problems/#miplearn.problems.setcover.SetCoverData">SetCoverData (class in miplearn.problems.setcover)</a>
</li>
<li><a href="../api/problems/#miplearn.problems.setpack.SetPackData">SetPackData (class in miplearn.problems.setpack)</a>
</li>
<li><a href="../api/components/#miplearn.components.primal.actions.SetWarmStart">SetWarmStart (class in miplearn.components.primal.actions)</a>
</li>
<li><a href="../api/collectors/#miplearn.classifiers.singleclass.SingleClassFix">SingleClassFix (class in miplearn.classifiers.singleclass)</a>
</li>
<li><a href="../api/components/#miplearn.components.primal.mem.SolutionConstructor">SolutionConstructor (class in miplearn.components.primal.mem)</a>
</li>
</ul></td>
</tr></table>
<h2 id="T">T</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/problems/#miplearn.problems.tsp.TravelingSalesmanData">TravelingSalesmanData (class in miplearn.problems.tsp)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/problems/#miplearn.problems.tsp.TravelingSalesmanGenerator">TravelingSalesmanGenerator (class in miplearn.problems.tsp)</a>
</li>
</ul></td>
</tr></table>
<h2 id="U">U</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/problems/#miplearn.problems.uc.UnitCommitmentData">UnitCommitmentData (class in miplearn.problems.uc)</a>
</li>
</ul></td>
</tr></table>
<h2 id="W">W</h2>
<table style="width: 100%" class="indextable genindextable"><tr>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/solvers/#miplearn.solvers.abstract.AbstractModel.WHERE_CUTS">WHERE_CUTS (miplearn.solvers.abstract.AbstractModel attribute)</a>
</li>
<li><a href="../api/solvers/#miplearn.solvers.abstract.AbstractModel.WHERE_DEFAULT">WHERE_DEFAULT (miplearn.solvers.abstract.AbstractModel attribute)</a>
</li>
<li><a href="../api/solvers/#miplearn.solvers.abstract.AbstractModel.WHERE_LAZY">WHERE_LAZY (miplearn.solvers.abstract.AbstractModel attribute)</a>
</li>
</ul></td>
<td style="width: 33%; vertical-align: top;"><ul>
<li><a href="../api/solvers/#miplearn.solvers.abstract.AbstractModel.write">write() (miplearn.solvers.abstract.AbstractModel method)</a>
<ul>
<li><a href="../api/solvers/#miplearn.solvers.gurobi.GurobiModel.write">(miplearn.solvers.gurobi.GurobiModel method)</a>
</li>
</ul></li>
<li><a href="../api/helpers/#miplearn.io.write_pkl_gz">write_pkl_gz() (in module miplearn.io)</a>
</li>
</ul></td>
</tr></table>
</div>
<div class='prev-next-bottom'>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

@ -0,0 +1,288 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "505cea0b-5f5d-478a-9107-42bb5515937d",
"metadata": {},
"source": [
"# Training Data Collectors\n",
"The first step in solving mixed-integer optimization problems with the assistance of supervised machine learning methods is solving a large set of training instances and collecting the raw training data. In this section, we describe the various training data collectors included in MIPLearn. Additionally, the framework follows the convention of storing all training data in files with a specific data format (namely, HDF5). In this section, we briefly describe this format and the rationale for choosing it.\n",
"\n",
"## Overview\n",
"\n",
"In MIPLearn, a **collector** is a class that solves or analyzes the problem and collects raw data which may be later useful for machine learning methods. Collectors, by convention, take as input: (i) a list of problem data filenames, in gzipped pickle format, ending with `.pkl.gz`; (ii) a function that builds the optimization model, such as `build_tsp_model`. After processing is done, collectors store the training data in a HDF5 file located alongside with the problem data. For example, if the problem data is stored in file `problem.pkl.gz`, then the collector writes to `problem.h5`. Collectors are, in general, very time consuming, as they may need to solve the problem to optimality, potentially multiple times.\n",
"\n",
"## HDF5 Format\n",
"\n",
"MIPLearn stores all training data in [HDF5](HDF5) (Hierarchical Data Format, Version 5) files. The HDF format was originally developed by the [National Center for Supercomputing Applications][NCSA] (NCSA) for storing and organizing large amounts of data, and supports a variety of data types, including integers, floating-point numbers, strings, and arrays. Compared to other formats, such as CSV, JSON or SQLite, the HDF5 format provides several advantages for MIPLearn, including:\n",
"\n",
"- *Storage of multiple scalars, vectors and matrices in a single file* --- This allows MIPLearn to store all training data related to a given problem instance in a single file, which makes training data easier to store, organize and transfer.\n",
"- *High-performance partial I/O* --- Partial I/O allows MIPLearn to read a single element from the training data (e.g. value of the optimal solution) without loading the entire file to memory or reading it from beginning to end, which dramatically improves performance and reduces memory requirements. This is especially important when processing a large number of training data files.\n",
"- *On-the-fly compression* --- HDF5 files can be transparently compressed, using the gzip method, which reduces storage requirements and accelerates network transfers.\n",
"- *Stable, portable and well-supported data format* --- Training data files are typically expensive to generate. Having a stable and well supported data format ensures that these files remain usable in the future, potentially even by other non-Python MIP/ML frameworks.\n",
"\n",
"MIPLearn currently uses HDF5 as simple key-value storage for numerical data; more advanced features of the format, such as metadata, are not currently used. Although files generated by MIPLearn can be read with any HDF5 library, such as [h5py][h5py], some convenience functions are provided to make the access more simple and less error-prone. Specifically, the class [H5File][H5File], which is built on top of h5py, provides the methods [put_scalar][put_scalar], [put_array][put_array], [put_sparse][put_sparse], [put_bytes][put_bytes] to store, respectively, scalar values, dense multi-dimensional arrays, sparse multi-dimensional arrays and arbitrary binary data. The corresponding *get* methods are also provided. Compared to pure h5py methods, these methods automatically perform type-checking and gzip compression. The example below shows their usage.\n",
"\n",
"[HDF5]: https://en.wikipedia.org/wiki/Hierarchical_Data_Format\n",
"[NCSA]: https://en.wikipedia.org/wiki/National_Center_for_Supercomputing_Applications\n",
"[h5py]: https://www.h5py.org/\n",
"[H5File]: ../../api/helpers/#miplearn.h5.H5File\n",
"[put_scalar]: ../../api/helpers/#miplearn.h5.H5File.put_scalar\n",
"[put_array]: ../../api/helpers/#miplearn.h5.H5File.put_scalar\n",
"[put_sparse]: ../../api/helpers/#miplearn.h5.H5File.put_scalar\n",
"[put_bytes]: ../../api/helpers/#miplearn.h5.H5File.put_scalar\n",
"\n",
"\n",
"### Example"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "f906fe9c",
"metadata": {
"ExecuteTime": {
"end_time": "2024-01-30T22:19:30.826123021Z",
"start_time": "2024-01-30T22:19:30.766066926Z"
},
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"x1 = 1\n",
"x2 = hello world\n",
"x3 = [1 2 3]\n",
"x4 = [[0.37454012 0.9507143 0.7319939 ]\n",
" [0.5986585 0.15601864 0.15599452]\n",
" [0.05808361 0.8661761 0.601115 ]]\n",
"x5 = (3, 2)\t0.6803075671195984\n",
" (2, 3)\t0.4504992663860321\n",
" (0, 4)\t0.013264961540699005\n",
" (2, 0)\t0.9422017335891724\n",
" (2, 4)\t0.5632882118225098\n",
" (1, 2)\t0.38541650772094727\n",
" (1, 1)\t0.015966251492500305\n",
" (0, 3)\t0.2308938205242157\n",
" (4, 4)\t0.24102546274662018\n",
" (3, 1)\t0.6832635402679443\n",
" (1, 3)\t0.6099966764450073\n",
" (3, 0)\t0.83319491147995\n"
]
}
],
"source": [
"import numpy as np\n",
"import scipy.sparse\n",
"\n",
"from miplearn.h5 import H5File\n",
"\n",
"# Set random seed to make example reproducible\n",
"np.random.seed(42)\n",
"\n",
"# Create a new empty HDF5 file\n",
"with H5File(\"test.h5\", \"w\") as h5:\n",
" # Store a scalar\n",
" h5.put_scalar(\"x1\", 1)\n",
" h5.put_scalar(\"x2\", \"hello world\")\n",
"\n",
" # Store a dense array and a dense matrix\n",
" h5.put_array(\"x3\", np.array([1, 2, 3]))\n",
" h5.put_array(\"x4\", np.random.rand(3, 3))\n",
"\n",
" # Store a sparse matrix\n",
" h5.put_sparse(\"x5\", scipy.sparse.random(5, 5, 0.5))\n",
"\n",
"# Re-open the file we just created and print\n",
"# previously-stored data\n",
"with H5File(\"test.h5\", \"r\") as h5:\n",
" print(\"x1 =\", h5.get_scalar(\"x1\"))\n",
" print(\"x2 =\", h5.get_scalar(\"x2\"))\n",
" print(\"x3 =\", h5.get_array(\"x3\"))\n",
" print(\"x4 =\", h5.get_array(\"x4\"))\n",
" print(\"x5 =\", h5.get_sparse(\"x5\"))"
]
},
{
"cell_type": "markdown",
"id": "50441907",
"metadata": {},
"source": []
},
{
"cell_type": "markdown",
"id": "d0000c8d",
"metadata": {},
"source": [
"## Basic collector\n",
"\n",
"[BasicCollector][BasicCollector] is the most fundamental collector, and performs the following steps:\n",
"\n",
"1. Extracts all model data, such as objective function and constraint right-hand sides into numpy arrays, which can later be easily and efficiently accessed without rebuilding the model or invoking the solver;\n",
"2. Solves the linear relaxation of the problem and stores its optimal solution, basis status and sensitivity information, among other information;\n",
"3. Solves the original mixed-integer optimization problem to optimality and stores its optimal solution, along with solve statistics, such as number of explored nodes and wallclock time.\n",
"\n",
"Data extracted in Phases 1, 2 and 3 above are prefixed, respectively as `static_`, `lp_` and `mip_`. The entire set of fields is shown in the table below.\n",
"\n",
"[BasicCollector]: ../../api/collectors/#miplearn.collectors.basic.BasicCollector\n"
]
},
{
"cell_type": "markdown",
"id": "6529f667",
"metadata": {},
"source": [
"### Data fields\n",
"\n",
"| Field | Type | Description |\n",
"|-----------------------------------|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------|\n",
"| `static_constr_lhs` | `(nconstrs, nvars)` | Constraint left-hand sides, in sparse matrix format |\n",
"| `static_constr_names` | `(nconstrs,)` | Constraint names |\n",
"| `static_constr_rhs` | `(nconstrs,)` | Constraint right-hand sides |\n",
"| `static_constr_sense` | `(nconstrs,)` | Constraint senses (`\"<\"`, `\">\"` or `\"=\"`) |\n",
"| `static_obj_offset` | `float` | Constant value added to the objective function |\n",
"| `static_sense` | `str` | `\"min\"` if minimization problem or `\"max\"` otherwise |\n",
"| `static_var_lower_bounds` | `(nvars,)` | Variable lower bounds |\n",
"| `static_var_names` | `(nvars,)` | Variable names |\n",
"| `static_var_obj_coeffs` | `(nvars,)` | Objective coefficients |\n",
"| `static_var_types` | `(nvars,)` | Types of the decision variables (`\"C\"`, `\"B\"` and `\"I\"` for continuous, binary and integer, respectively) |\n",
"| `static_var_upper_bounds` | `(nvars,)` | Variable upper bounds |\n",
"| `lp_constr_basis_status` | `(nconstr,)` | Constraint basis status (`0` for basic, `-1` for non-basic) |\n",
"| `lp_constr_dual_values` | `(nconstr,)` | Constraint dual value (or shadow price) |\n",
"| `lp_constr_sa_rhs_{up,down}` | `(nconstr,)` | Sensitivity information for the constraint RHS |\n",
"| `lp_constr_slacks` | `(nconstr,)` | Constraint slack in the solution to the LP relaxation |\n",
"| `lp_obj_value` | `float` | Optimal value of the LP relaxation |\n",
"| `lp_var_basis_status` | `(nvars,)` | Variable basis status (`0`, `-1`, `-2` or `-3` for basic, non-basic at lower bound, non-basic at upper bound, and superbasic, respectively) |\n",
"| `lp_var_reduced_costs` | `(nvars,)` | Variable reduced costs |\n",
"| `lp_var_sa_{obj,ub,lb}_{up,down}` | `(nvars,)` | Sensitivity information for the variable objective coefficient, lower and upper bound. |\n",
"| `lp_var_values` | `(nvars,)` | Optimal solution to the LP relaxation |\n",
"| `lp_wallclock_time` | `float` | Time taken to solve the LP relaxation (in seconds) |\n",
"| `mip_constr_slacks` | `(nconstrs,)` | Constraint slacks in the best MIP solution |\n",
"| `mip_gap` | `float` | Relative MIP optimality gap |\n",
"| `mip_node_count` | `float` | Number of explored branch-and-bound nodes |\n",
"| `mip_obj_bound` | `float` | Dual bound |\n",
"| `mip_obj_value` | `float` | Value of the best MIP solution |\n",
"| `mip_var_values` | `(nvars,)` | Best MIP solution |\n",
"| `mip_wallclock_time` | `float` | Time taken to solve the MIP (in seconds) |"
]
},
{
"cell_type": "markdown",
"id": "f2894594",
"metadata": {},
"source": [
"### Example\n",
"\n",
"The example below shows how to generate a few random instances of the traveling salesman problem, store its problem data, run the collector and print some of the training data to screen."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "ac6f8c6f",
"metadata": {
"ExecuteTime": {
"end_time": "2024-01-30T22:19:30.826707866Z",
"start_time": "2024-01-30T22:19:30.825940503Z"
},
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"lp_obj_value = 2909.0\n",
"mip_obj_value = 2921.0\n"
]
}
],
"source": [
"import random\n",
"import numpy as np\n",
"from scipy.stats import uniform, randint\n",
"from glob import glob\n",
"\n",
"from miplearn.problems.tsp import (\n",
" TravelingSalesmanGenerator,\n",
" build_tsp_model_gurobipy,\n",
")\n",
"from miplearn.io import write_pkl_gz\n",
"from miplearn.h5 import H5File\n",
"from miplearn.collectors.basic import BasicCollector\n",
"\n",
"# Set random seed to make example reproducible.\n",
"random.seed(42)\n",
"np.random.seed(42)\n",
"\n",
"# Generate a few instances of the traveling salesman problem.\n",
"data = TravelingSalesmanGenerator(\n",
" n=randint(low=10, high=11),\n",
" x=uniform(loc=0.0, scale=1000.0),\n",
" y=uniform(loc=0.0, scale=1000.0),\n",
" gamma=uniform(loc=0.90, scale=0.20),\n",
" fix_cities=True,\n",
" round=True,\n",
").generate(10)\n",
"\n",
"# Save instance data to data/tsp/00000.pkl.gz, data/tsp/00001.pkl.gz, ...\n",
"write_pkl_gz(data, \"data/tsp\")\n",
"\n",
"# Solve all instances and collect basic solution information.\n",
"# Process at most four instances in parallel.\n",
"bc = BasicCollector()\n",
"bc.collect(glob(\"data/tsp/*.pkl.gz\"), build_tsp_model_gurobipy, n_jobs=4)\n",
"\n",
"# Read and print some training data for the first instance.\n",
"with H5File(\"data/tsp/00000.h5\", \"r\") as h5:\n",
" print(\"lp_obj_value = \", h5.get_scalar(\"lp_obj_value\"))\n",
" print(\"mip_obj_value = \", h5.get_scalar(\"mip_obj_value\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "78f0b07a",
"metadata": {
"ExecuteTime": {
"start_time": "2024-01-30T22:19:30.826179789Z"
},
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,595 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<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" />
<title>6. Training Data Collectors &#8212; MIPLearn 0.4</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" type="text/css" href="../../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css?v=2aa19091" />
<link rel="stylesheet" type="text/css" href="../../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script>
<script>window.MathJax = {"tex": {"inlineMath": [["$", "$"], ["\\(", "\\)"]], "processEscapes": true}, "options": {"ignoreHtmlClass": "tex2jax_ignore|mathjax_ignore|document", "processHtmlClass": "tex2jax_process|mathjax_process|math|output_area"}}</script>
<script defer="defer" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<link rel="index" title="Index" href="../../genindex/" />
<link rel="search" title="Search" href="../../search/" />
<link rel="next" title="7. Feature Extractors" href="../features/" />
<link rel="prev" title="5. Benchmark Problems" href="../problems/" />
<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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="current nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1 current active">
<a class="current reference internal" href="#">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../api/problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/components/">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/helpers/">
14. 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/guide/collectors.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="#Overview">
6.1. Overview
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#HDF5-Format">
6.2. HDF5 Format
</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 class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Basic-collector">
6.3. Basic collector
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#Data-fields">
Data fields
</a>
</li>
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#id1">
Example
</a>
</li>
</ul>
</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>
<section id="Training-Data-Collectors">
<h1><span class="section-number">6. </span>Training Data Collectors<a class="headerlink" href="#Training-Data-Collectors" title="Link to this heading"></a></h1>
<p>The first step in solving mixed-integer optimization problems with the assistance of supervised machine learning methods is solving a large set of training instances and collecting the raw training data. In this section, we describe the various training data collectors included in MIPLearn. Additionally, the framework follows the convention of storing all training data in files with a specific data format (namely, HDF5). In this section, we briefly describe this format and the rationale for
choosing it.</p>
<section id="Overview">
<h2><span class="section-number">6.1. </span>Overview<a class="headerlink" href="#Overview" title="Link to this heading"></a></h2>
<p>In MIPLearn, a <strong>collector</strong> is a class that solves or analyzes the problem and collects raw data which may be later useful for machine learning methods. Collectors, by convention, take as input: (i) a list of problem data filenames, in gzipped pickle format, ending with <code class="docutils literal notranslate"><span class="pre">.pkl.gz</span></code>; (ii) a function that builds the optimization model, such as <code class="docutils literal notranslate"><span class="pre">build_tsp_model</span></code>. After processing is done, collectors store the training data in a HDF5 file located alongside with the problem data. For example, if
the problem data is stored in file <code class="docutils literal notranslate"><span class="pre">problem.pkl.gz</span></code>, then the collector writes to <code class="docutils literal notranslate"><span class="pre">problem.h5</span></code>. Collectors are, in general, very time consuming, as they may need to solve the problem to optimality, potentially multiple times.</p>
</section>
<section id="HDF5-Format">
<h2><span class="section-number">6.2. </span>HDF5 Format<a class="headerlink" href="#HDF5-Format" title="Link to this heading"></a></h2>
<p>MIPLearn stores all training data in <a class="reference external" href="HDF5">HDF5</a> (Hierarchical Data Format, Version 5) files. The HDF format was originally developed by the <a class="reference external" href="https://en.wikipedia.org/wiki/National_Center_for_Supercomputing_Applications">National Center for Supercomputing Applications</a> (NCSA) for storing and organizing large amounts of data, and supports a variety of data types, including integers, floating-point numbers, strings, and arrays. Compared to other formats, such as CSV, JSON or SQLite, the
HDF5 format provides several advantages for MIPLearn, including:</p>
<ul class="simple">
<li><p><em>Storage of multiple scalars, vectors and matrices in a single file</em> — This allows MIPLearn to store all training data related to a given problem instance in a single file, which makes training data easier to store, organize and transfer.</p></li>
<li><p><em>High-performance partial I/O</em> — Partial I/O allows MIPLearn to read a single element from the training data (e.g. value of the optimal solution) without loading the entire file to memory or reading it from beginning to end, which dramatically improves performance and reduces memory requirements. This is especially important when processing a large number of training data files.</p></li>
<li><p><em>On-the-fly compression</em> — HDF5 files can be transparently compressed, using the gzip method, which reduces storage requirements and accelerates network transfers.</p></li>
<li><p><em>Stable, portable and well-supported data format</em> — Training data files are typically expensive to generate. Having a stable and well supported data format ensures that these files remain usable in the future, potentially even by other non-Python MIP/ML frameworks.</p></li>
</ul>
<p>MIPLearn currently uses HDF5 as simple key-value storage for numerical data; more advanced features of the format, such as metadata, are not currently used. Although files generated by MIPLearn can be read with any HDF5 library, such as <a class="reference external" href="https://www.h5py.org/">h5py</a>, some convenience functions are provided to make the access more simple and less error-prone. Specifically, the class <a class="reference external" href="../../api/helpers/#miplearn.h5.H5File">H5File</a>, which is built on top of h5py, provides the methods
<a class="reference external" href="../../api/helpers/#miplearn.h5.H5File.put_scalar">put_scalar</a>, <a class="reference external" href="../../api/helpers/#miplearn.h5.H5File.put_scalar">put_array</a>, <a class="reference external" href="../../api/helpers/#miplearn.h5.H5File.put_scalar">put_sparse</a>, <a class="reference external" href="../../api/helpers/#miplearn.h5.H5File.put_scalar">put_bytes</a> to store, respectively, scalar values, dense multi-dimensional arrays, sparse multi-dimensional arrays and arbitrary binary data. The corresponding <em>get</em> methods are also provided. Compared to pure h5py methods, these methods
automatically perform type-checking and gzip compression. The example below shows their usage.</p>
<section id="Example">
<h3>Example<a class="headerlink" href="#Example" title="Link to this heading"></a></h3>
<div class="nbinput 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="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
<span class="kn">import</span> <span class="nn">scipy.sparse</span>
<span class="kn">from</span> <span class="nn">miplearn.h5</span> <span class="kn">import</span> <span class="n">H5File</span>
<span class="c1"># Set random seed to make example reproducible</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="mi">42</span><span class="p">)</span>
<span class="c1"># Create a new empty HDF5 file</span>
<span class="k">with</span> <span class="n">H5File</span><span class="p">(</span><span class="s2">&quot;test.h5&quot;</span><span class="p">,</span> <span class="s2">&quot;w&quot;</span><span class="p">)</span> <span class="k">as</span> <span class="n">h5</span><span class="p">:</span>
<span class="c1"># Store a scalar</span>
<span class="n">h5</span><span class="o">.</span><span class="n">put_scalar</span><span class="p">(</span><span class="s2">&quot;x1&quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">h5</span><span class="o">.</span><span class="n">put_scalar</span><span class="p">(</span><span class="s2">&quot;x2&quot;</span><span class="p">,</span> <span class="s2">&quot;hello world&quot;</span><span class="p">)</span>
<span class="c1"># Store a dense array and a dense matrix</span>
<span class="n">h5</span><span class="o">.</span><span class="n">put_array</span><span class="p">(</span><span class="s2">&quot;x3&quot;</span><span class="p">,</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]))</span>
<span class="n">h5</span><span class="o">.</span><span class="n">put_array</span><span class="p">(</span><span class="s2">&quot;x4&quot;</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">rand</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">3</span><span class="p">))</span>
<span class="c1"># Store a sparse matrix</span>
<span class="n">h5</span><span class="o">.</span><span class="n">put_sparse</span><span class="p">(</span><span class="s2">&quot;x5&quot;</span><span class="p">,</span> <span class="n">scipy</span><span class="o">.</span><span class="n">sparse</span><span class="o">.</span><span class="n">random</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">))</span>
<span class="c1"># Re-open the file we just created and print</span>
<span class="c1"># previously-stored data</span>
<span class="k">with</span> <span class="n">H5File</span><span class="p">(</span><span class="s2">&quot;test.h5&quot;</span><span class="p">,</span> <span class="s2">&quot;r&quot;</span><span class="p">)</span> <span class="k">as</span> <span class="n">h5</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;x1 =&quot;</span><span class="p">,</span> <span class="n">h5</span><span class="o">.</span><span class="n">get_scalar</span><span class="p">(</span><span class="s2">&quot;x1&quot;</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;x2 =&quot;</span><span class="p">,</span> <span class="n">h5</span><span class="o">.</span><span class="n">get_scalar</span><span class="p">(</span><span class="s2">&quot;x2&quot;</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;x3 =&quot;</span><span class="p">,</span> <span class="n">h5</span><span class="o">.</span><span class="n">get_array</span><span class="p">(</span><span class="s2">&quot;x3&quot;</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;x4 =&quot;</span><span class="p">,</span> <span class="n">h5</span><span class="o">.</span><span class="n">get_array</span><span class="p">(</span><span class="s2">&quot;x4&quot;</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;x5 =&quot;</span><span class="p">,</span> <span class="n">h5</span><span class="o">.</span><span class="n">get_sparse</span><span class="p">(</span><span class="s2">&quot;x5&quot;</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>
x1 = 1
x2 = hello world
x3 = [1 2 3]
x4 = [[0.37454012 0.9507143 0.7319939 ]
[0.5986585 0.15601864 0.15599452]
[0.05808361 0.8661761 0.601115 ]]
x5 = (3, 2) 0.6803075671195984
(2, 3) 0.4504992663860321
(0, 4) 0.013264961540699005
(2, 0) 0.9422017335891724
(2, 4) 0.5632882118225098
(1, 2) 0.38541650772094727
(1, 1) 0.015966251492500305
(0, 3) 0.2308938205242157
(4, 4) 0.24102546274662018
(3, 1) 0.6832635402679443
(1, 3) 0.6099966764450073
(3, 0) 0.83319491147995
</pre></div></div>
</div>
</section>
</section>
<section id="Basic-collector">
<h2><span class="section-number">6.3. </span>Basic collector<a class="headerlink" href="#Basic-collector" title="Link to this heading"></a></h2>
<p><a class="reference external" href="../../api/collectors/#miplearn.collectors.basic.BasicCollector">BasicCollector</a> is the most fundamental collector, and performs the following steps:</p>
<ol class="arabic simple">
<li><p>Extracts all model data, such as objective function and constraint right-hand sides into numpy arrays, which can later be easily and efficiently accessed without rebuilding the model or invoking the solver;</p></li>
<li><p>Solves the linear relaxation of the problem and stores its optimal solution, basis status and sensitivity information, among other information;</p></li>
<li><p>Solves the original mixed-integer optimization problem to optimality and stores its optimal solution, along with solve statistics, such as number of explored nodes and wallclock time.</p></li>
</ol>
<p>Data extracted in Phases 1, 2 and 3 above are prefixed, respectively as <code class="docutils literal notranslate"><span class="pre">static_</span></code>, <code class="docutils literal notranslate"><span class="pre">lp_</span></code> and <code class="docutils literal notranslate"><span class="pre">mip_</span></code>. The entire set of fields is shown in the table below.</p>
<section id="Data-fields">
<h3>Data fields<a class="headerlink" href="#Data-fields" title="Link to this heading"></a></h3>
<table class="docutils align-default">
<thead>
<tr class="row-odd"><th class="head"><p>Field</p></th>
<th class="head"><p>Type</p></th>
<th class="head"><p>Description</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">static_constr_lhs</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nconstrs,</span> <span class="pre">nvars)</span></code></p></td>
<td><p>Constraint left-hand sides, in sparse matrix format</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">static_constr_names</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nconstrs,)</span></code></p></td>
<td><p>Constraint names</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">static_constr_rhs</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nconstrs,)</span></code></p></td>
<td><p>Constraint right-hand sides</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">static_constr_sense</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nconstrs,)</span></code></p></td>
<td><p>Constraint senses (<code class="docutils literal notranslate"><span class="pre">&quot;&lt;&quot;</span></code>, <code class="docutils literal notranslate"><span class="pre">&quot;&gt;&quot;</span></code> or <code class="docutils literal notranslate"><span class="pre">&quot;=&quot;</span></code>)</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">static_obj_offset</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">float</span></code></p></td>
<td><p>Constant value added to the objective function</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">static_sense</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">str</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">&quot;min&quot;</span></code> if minimization problem or <code class="docutils literal notranslate"><span class="pre">&quot;max&quot;</span></code> otherwise</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">static_var_lower_bounds</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nvars,)</span></code></p></td>
<td><p>Variable lower bounds</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">static_var_names</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nvars,)</span></code></p></td>
<td><p>Variable names</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">static_var_obj_coeffs</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nvars,)</span></code></p></td>
<td><p>Objective coefficients</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">static_var_types</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nvars,)</span></code></p></td>
<td><p>Types of the decision variables (<code class="docutils literal notranslate"><span class="pre">&quot;C&quot;</span></code>, <code class="docutils literal notranslate"><span class="pre">&quot;B&quot;</span></code> and <code class="docutils literal notranslate"><span class="pre">&quot;I&quot;</span></code> for continuous, binary and integer, respectively)</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">static_var_upper_bounds</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nvars,)</span></code></p></td>
<td><p>Variable upper bounds</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">lp_constr_basis_status</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nconstr,)</span></code></p></td>
<td><p>Constraint basis status (<code class="docutils literal notranslate"><span class="pre">0</span></code> for basic, <code class="docutils literal notranslate"><span class="pre">-1</span></code> for non-basic)</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">lp_constr_dual_values</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nconstr,)</span></code></p></td>
<td><p>Constraint dual value (or shadow price)</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">lp_constr_sa_rhs_{up,down}</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nconstr,)</span></code></p></td>
<td><p>Sensitivity information for the constraint RHS</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">lp_constr_slacks</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nconstr,)</span></code></p></td>
<td><p>Constraint slack in the solution to the LP relaxation</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">lp_obj_value</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">float</span></code></p></td>
<td><p>Optimal value of the LP relaxation</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">lp_var_basis_status</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nvars,)</span></code></p></td>
<td><p>Variable basis status (<code class="docutils literal notranslate"><span class="pre">0</span></code>, <code class="docutils literal notranslate"><span class="pre">-1</span></code>, <code class="docutils literal notranslate"><span class="pre">-2</span></code> or <code class="docutils literal notranslate"><span class="pre">-3</span></code> for basic, non-basic at lower bound, non-basic at upper bound, and superbasic, respectively)</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">lp_var_reduced_costs</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nvars,)</span></code></p></td>
<td><p>Variable reduced costs</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">lp_var_sa_{obj,ub,lb}_{up,down}</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nvars,)</span></code></p></td>
<td><p>Sensitivity information for the variable objective coefficient, lower and upper bound.</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">lp_var_values</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nvars,)</span></code></p></td>
<td><p>Optimal solution to the LP relaxation</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">lp_wallclock_time</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">float</span></code></p></td>
<td><p>Time taken to solve the LP relaxation (in seconds)</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">mip_constr_slacks</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nconstrs,)</span></code></p></td>
<td><p>Constraint slacks in the best MIP solution</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">mip_gap</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">float</span></code></p></td>
<td><p>Relative MIP optimality gap</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">mip_node_count</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">float</span></code></p></td>
<td><p>Number of explored branch-and-bound nodes</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">mip_obj_bound</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">float</span></code></p></td>
<td><p>Dual bound</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">mip_obj_value</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">float</span></code></p></td>
<td><p>Value of the best MIP solution</p></td>
</tr>
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">mip_var_values</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">(nvars,)</span></code></p></td>
<td><p>Best MIP solution</p></td>
</tr>
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">mip_wallclock_time</span></code></p></td>
<td><p><code class="docutils literal notranslate"><span class="pre">float</span></code></p></td>
<td><p>Time taken to solve the MIP (in seconds)</p></td>
</tr>
</tbody>
</table>
</section>
<section id="id1">
<h3>Example<a class="headerlink" href="#id1" title="Link to this heading"></a></h3>
<p>The example below shows how to generate a few random instances of the traveling salesman problem, store its problem data, run the collector and print some of the training data to screen.</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="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
<span class="kn">from</span> <span class="nn">scipy.stats</span> <span class="kn">import</span> <span class="n">uniform</span><span class="p">,</span> <span class="n">randint</span>
<span class="kn">from</span> <span class="nn">glob</span> <span class="kn">import</span> <span class="n">glob</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">build_tsp_model_gurobipy</span><span class="p">,</span>
<span class="p">)</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.h5</span> <span class="kn">import</span> <span class="n">H5File</span>
<span class="kn">from</span> <span class="nn">miplearn.collectors.basic</span> <span class="kn">import</span> <span class="n">BasicCollector</span>
<span class="c1"># Set random seed to make example reproducible.</span>
<span class="n">random</span><span class="o">.</span><span class="n">seed</span><span class="p">(</span><span class="mi">42</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="mi">42</span><span class="p">)</span>
<span class="c1"># Generate a few instances of the traveling salesman problem.</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">TravelingSalesmanGenerator</span><span class="p">(</span>
<span class="n">n</span><span class="o">=</span><span class="n">randint</span><span class="p">(</span><span class="n">low</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">high</span><span class="o">=</span><span class="mi">11</span><span class="p">),</span>
<span class="n">x</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.0</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mf">1000.0</span><span class="p">),</span>
<span class="n">y</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.0</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mf">1000.0</span><span class="p">),</span>
<span class="n">gamma</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.90</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mf">0.20</span><span class="p">),</span>
<span class="n">fix_cities</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="nb">round</span><span class="o">=</span><span class="kc">True</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">10</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="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"># Solve all instances and collect basic solution information.</span>
<span class="c1"># Process at most four instances in parallel.</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">glob</span><span class="p">(</span><span class="s2">&quot;data/tsp/*.pkl.gz&quot;</span><span class="p">),</span> <span class="n">build_tsp_model_gurobipy</span><span class="p">,</span> <span class="n">n_jobs</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
<span class="c1"># Read and print some training data for the first instance.</span>
<span class="k">with</span> <span class="n">H5File</span><span class="p">(</span><span class="s2">&quot;data/tsp/00000.h5&quot;</span><span class="p">,</span> <span class="s2">&quot;r&quot;</span><span class="p">)</span> <span class="k">as</span> <span class="n">h5</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;lp_obj_value = &quot;</span><span class="p">,</span> <span class="n">h5</span><span class="o">.</span><span class="n">get_scalar</span><span class="p">(</span><span class="s2">&quot;lp_obj_value&quot;</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;mip_obj_value = &quot;</span><span class="p">,</span> <span class="n">h5</span><span class="o">.</span><span class="n">get_scalar</span><span class="p">(</span><span class="s2">&quot;mip_obj_value&quot;</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>
lp_obj_value = 2909.0
mip_obj_value = 2921.0
</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>
</section>
</section>
</section>
</div>
<div class='prev-next-bottom'>
<a class='left-prev' id="prev-link" href="../problems/" title="previous page"><span class="section-number">5. </span>Benchmark Problems</a>
<a class='right-next' id="next-link" href="../features/" title="next page"><span class="section-number">7. </span>Feature Extractors</a>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

@ -0,0 +1,334 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "cdc6ebe9-d1d4-4de1-9b5a-4fc8ef57b11b",
"metadata": {},
"source": [
"# Feature Extractors\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."
]
},
{
"cell_type": "markdown",
"id": "b4026de5",
"metadata": {},
"source": [
"\n",
"## Overview\n",
"\n",
"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.\n",
"\n",
"In the framework, we treat data collection and feature extraction as two separate steps to accelerate the model development cycle. Specifically, collectors are typically time-consuming, as they often need to solve the problem to optimality, and therefore focus on collecting and storing all data that may or may not be relevant, in its raw format. Feature extractors, on the other hand, focus entirely on filtering the data and improving its representation, and are therefore much faster to run. Experimenting with new data representations, therefore, can be done without resolving the instances.\n",
"\n",
"In MIPLearn, extractors implement the abstract class [FeatureExtractor][FeatureExtractor], which has methods that take as input an [H5File][H5File] and produce either: (i) instance features, which describe the entire instances; (ii) variable features, which describe a particular decision variables; or (iii) constraint features, which describe a particular constraint. The extractor is free to implement only a subset of these methods, if it is known that it will not be used with a machine learning component that requires the other types of features.\n",
"\n",
"[FeatureExtractor]: ../../api/collectors/#miplearn.features.fields.FeaturesExtractor\n",
"[H5File]: ../../api/helpers/#miplearn.h5.H5File"
]
},
{
"cell_type": "markdown",
"id": "b2d9736c",
"metadata": {},
"source": [
"\n",
"## H5FieldsExtractor\n",
"\n",
"[H5FieldsExtractor][H5FieldsExtractor], the most simple extractor in MIPLearn, simple extracts data that is already available in the HDF5 file, assembles it into a matrix and returns it as-is. The fields used to build instance, variable and constraint features are user-specified. The class also performs checks to ensure that the shapes of the returned matrices make sense."
]
},
{
"cell_type": "markdown",
"id": "e8184dff",
"metadata": {},
"source": [
"### Example\n",
"\n",
"The example below demonstrates the usage of H5FieldsExtractor in a randomly generated instance of the multi-dimensional knapsack problem."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "ed9a18c8",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"instance features (11,) \n",
" [-1531.24308771 -350. -692. -454.\n",
" -709. -605. -543. -321.\n",
" -674. -571. -341. ]\n",
"variable features (10, 4) \n",
" [[-1.53124309e+03 -3.50000000e+02 0.00000000e+00 9.43468018e+01]\n",
" [-1.53124309e+03 -6.92000000e+02 2.51703322e-01 0.00000000e+00]\n",
" [-1.53124309e+03 -4.54000000e+02 0.00000000e+00 8.25504150e+01]\n",
" [-1.53124309e+03 -7.09000000e+02 1.11373022e-01 0.00000000e+00]\n",
" [-1.53124309e+03 -6.05000000e+02 1.00000000e+00 -1.26055283e+02]\n",
" [-1.53124309e+03 -5.43000000e+02 0.00000000e+00 1.68693771e+02]\n",
" [-1.53124309e+03 -3.21000000e+02 1.07488781e-01 0.00000000e+00]\n",
" [-1.53124309e+03 -6.74000000e+02 8.82293701e-01 0.00000000e+00]\n",
" [-1.53124309e+03 -5.71000000e+02 0.00000000e+00 1.41129074e+02]\n",
" [-1.53124309e+03 -3.41000000e+02 1.28830120e-01 0.00000000e+00]]\n",
"constraint features (5, 3) \n",
" [[ 1.3100000e+03 -1.5978307e-01 0.0000000e+00]\n",
" [ 9.8800000e+02 -3.2881632e-01 0.0000000e+00]\n",
" [ 1.0040000e+03 -4.0601316e-01 0.0000000e+00]\n",
" [ 1.2690000e+03 -1.3659772e-01 0.0000000e+00]\n",
" [ 1.0070000e+03 -2.8800571e-01 0.0000000e+00]]\n"
]
}
],
"source": [
"from glob import glob\n",
"from shutil import rmtree\n",
"\n",
"import numpy as np\n",
"from scipy.stats import uniform, randint\n",
"\n",
"from miplearn.collectors.basic import BasicCollector\n",
"from miplearn.extractors.fields import H5FieldsExtractor\n",
"from miplearn.h5 import H5File\n",
"from miplearn.io import write_pkl_gz\n",
"from miplearn.problems.multiknapsack import (\n",
" MultiKnapsackGenerator,\n",
" build_multiknapsack_model_gurobipy,\n",
")\n",
"\n",
"# Set random seed to make example reproducible\n",
"np.random.seed(42)\n",
"\n",
"# Generate some random multiknapsack instances\n",
"rmtree(\"data/multiknapsack/\", ignore_errors=True)\n",
"write_pkl_gz(\n",
" MultiKnapsackGenerator(\n",
" n=randint(low=10, high=11),\n",
" m=randint(low=5, high=6),\n",
" w=uniform(loc=0, scale=1000),\n",
" K=uniform(loc=100, scale=0),\n",
" u=uniform(loc=1, scale=0),\n",
" alpha=uniform(loc=0.25, scale=0),\n",
" w_jitter=uniform(loc=0.95, scale=0.1),\n",
" p_jitter=uniform(loc=0.75, scale=0.5),\n",
" fix_w=True,\n",
" ).generate(10),\n",
" \"data/multiknapsack\",\n",
")\n",
"\n",
"# Run the basic collector\n",
"BasicCollector().collect(\n",
" glob(\"data/multiknapsack/*\"),\n",
" build_multiknapsack_model_gurobipy,\n",
" n_jobs=4,\n",
")\n",
"\n",
"ext = H5FieldsExtractor(\n",
" # Use as instance features the value of the LP relaxation and the\n",
" # vector of objective coefficients.\n",
" instance_fields=[\n",
" \"lp_obj_value\",\n",
" \"static_var_obj_coeffs\",\n",
" ],\n",
" # For each variable, use as features the optimal value of the LP\n",
" # relaxation, the variable objective coefficient, the variable's\n",
" # value its reduced cost.\n",
" var_fields=[\n",
" \"lp_obj_value\",\n",
" \"static_var_obj_coeffs\",\n",
" \"lp_var_values\",\n",
" \"lp_var_reduced_costs\",\n",
" ],\n",
" # For each constraint, use as features the RHS, dual value and slack.\n",
" constr_fields=[\n",
" \"static_constr_rhs\",\n",
" \"lp_constr_dual_values\",\n",
" \"lp_constr_slacks\",\n",
" ],\n",
")\n",
"\n",
"with H5File(\"data/multiknapsack/00000.h5\") as h5:\n",
" # Extract and print instance features\n",
" x1 = ext.get_instance_features(h5)\n",
" print(\"instance features\", x1.shape, \"\\n\", x1)\n",
"\n",
" # Extract and print variable features\n",
" x2 = ext.get_var_features(h5)\n",
" print(\"variable features\", x2.shape, \"\\n\", x2)\n",
"\n",
" # Extract and print constraint features\n",
" x3 = ext.get_constr_features(h5)\n",
" print(\"constraint features\", x3.shape, \"\\n\", x3)"
]
},
{
"cell_type": "markdown",
"id": "2da2e74e",
"metadata": {},
"source": [
"\n",
"[H5FieldsExtractor]: ../../api/collectors/#miplearn.features.fields.H5FieldsExtractor"
]
},
{
"cell_type": "markdown",
"id": "d879c0d3",
"metadata": {},
"source": [
"<div class=\"alert alert-warning\">\n",
"Warning\n",
"\n",
"You should ensure that the number of features remains the same for all relevant HDF5 files. In the previous example, to illustrate this issue, we used variable objective coefficients as instance features. While this is allowed, note that this requires all problem instances to have the same number of variables; otherwise the number of features would vary from instance to instance and MIPLearn would be unable to concatenate the matrices.\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "cd0ba071",
"metadata": {},
"source": [
"## AlvLouWeh2017Extractor\n",
"\n",
"Alvarez, Louveaux and Wehenkel (2017) proposed a set features to describe a particular decision variable in a given node of the branch-and-bound tree, and applied it to the problem of mimicking strong branching decisions. The class [AlvLouWeh2017Extractor][] implements a subset of these features (40 out of 64), which are available outside of the branch-and-bound tree. Some features are derived from the static defintion of the problem (i.e. from objective function and constraint data), while some features are derived from the solution to the LP relaxation. The features have been designed to be: (i) independent of the size of the problem; (ii) invariant with respect to irrelevant problem transformations, such as row and column permutation; and (iii) independent of the scale of the problem. We refer to the paper for a more complete description.\n",
"\n",
"### Example"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "a1bc38fe",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"x1 (10, 40) \n",
" [[-1.00e+00 1.00e+20 1.00e-01 1.00e+00 0.00e+00 1.00e+00 6.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 6.00e-01 1.00e+00 1.75e+01 1.00e+00 2.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 -1.00e+00 0.00e+00 1.00e+20]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 1.00e-01 1.00e+00 1.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 7.00e-01 1.00e+00 5.10e+00 1.00e+00 2.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 3.00e-01 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 0.00e+00 1.00e+00 9.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 5.00e-01 1.00e+00 1.30e+01 1.00e+00 2.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 -1.00e+00 0.00e+00 1.00e+20]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 2.00e-01 1.00e+00 9.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 8.00e-01 1.00e+00 3.40e+00 1.00e+00 2.00e-01\n",
" 1.00e+00 1.00e-01 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 1.00e-01 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 1.00e-01 1.00e+00 7.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 6.00e-01 1.00e+00 3.80e+00 1.00e+00 2.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 1.00e-01 1.00e+00 8.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 7.00e-01 1.00e+00 3.30e+00 1.00e+00 2.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 -1.00e+00 0.00e+00 1.00e+20]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 0.00e+00 1.00e+00 3.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 1.00e+00 1.00e+00 5.70e+00 1.00e+00 1.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 1.00e-01 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 1.00e-01 1.00e+00 6.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 8.00e-01 1.00e+00 6.80e+00 1.00e+00 2.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 1.00e-01 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 4.00e-01 1.00e+00 6.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 8.00e-01 1.00e+00 1.40e+00 1.00e+00 1.00e-01\n",
" 1.00e+00 1.00e-01 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 -1.00e+00 0.00e+00 1.00e+20]\n",
" [-1.00e+00 1.00e+20 1.00e-01 1.00e+00 0.00e+00 1.00e+00 5.00e-01\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 1.00e+00 5.00e-01 1.00e+00 7.60e+00 1.00e+00 1.00e-01\n",
" 1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00\n",
" 1.00e-01 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]]\n"
]
}
],
"source": [
"from miplearn.extractors.AlvLouWeh2017 import AlvLouWeh2017Extractor\n",
"from miplearn.h5 import H5File\n",
"\n",
"# Build the extractor\n",
"ext = AlvLouWeh2017Extractor()\n",
"\n",
"# Open previously-created multiknapsack training data\n",
"with H5File(\"data/multiknapsack/00000.h5\") as h5:\n",
" # Extract and print variable features\n",
" x1 = ext.get_var_features(h5)\n",
" print(\"x1\", x1.shape, \"\\n\", x1.round(1))"
]
},
{
"cell_type": "markdown",
"id": "286c9927",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
"References\n",
"\n",
"* **Alvarez, Alejandro Marcos.** *Computational and theoretical synergies between linear optimization and supervised machine learning.* (2016). University of Liège.\n",
"* **Alvarez, Alejandro Marcos, Quentin Louveaux, and Louis Wehenkel.** *A machine learning-based approximation of strong branching.* INFORMS Journal on Computing 29.1 (2017): 185-195.\n",
"\n",
"</div>"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,538 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<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" />
<title>7. Feature Extractors &#8212; MIPLearn 0.4</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" type="text/css" href="../../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css?v=2aa19091" />
<link rel="stylesheet" type="text/css" href="../../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script>
<script>window.MathJax = {"tex": {"inlineMath": [["$", "$"], ["\\(", "\\)"]], "processEscapes": true}, "options": {"ignoreHtmlClass": "tex2jax_ignore|mathjax_ignore|document", "processHtmlClass": "tex2jax_process|mathjax_process|math|output_area"}}</script>
<script defer="defer" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<link rel="index" title="Index" href="../../genindex/" />
<link rel="search" title="Search" href="../../search/" />
<link rel="next" title="8. Primal Components" href="../primal/" />
<link rel="prev" title="6. Training Data Collectors" href="../collectors/" />
<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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="current nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1 current active">
<a class="current reference internal" href="#">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../api/problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/components/">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/helpers/">
14. 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/guide/features.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="#Overview">
7.1. Overview
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#H5FieldsExtractor">
7.2. H5FieldsExtractor
</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 class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#AlvLouWeh2017Extractor">
7.3. AlvLouWeh2017Extractor
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#id1">
Example
</a>
</li>
</ul>
</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>
<section id="Feature-Extractors">
<h1><span class="section-number">7. </span>Feature Extractors<a class="headerlink" href="#Feature-Extractors" title="Link to this heading"></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.</p>
<section id="Overview">
<h2><span class="section-number">7.1. </span>Overview<a class="headerlink" href="#Overview" title="Link to this heading"></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>In the framework, we treat data collection and feature extraction as two separate steps to accelerate the model development cycle. Specifically, collectors are typically time-consuming, as they often need to solve the problem to optimality, and therefore focus on collecting and storing all data that may or may not be relevant, in its raw format. Feature extractors, on the other hand, focus entirely on filtering the data and improving its representation, and are therefore much faster to run.
Experimenting with new data representations, therefore, can be done without resolving the instances.</p>
<p>In MIPLearn, extractors implement the abstract class <a class="reference external" href="../../api/collectors/#miplearn.features.fields.FeaturesExtractor">FeatureExtractor</a>, which has methods that take as input an <a class="reference external" href="../../api/helpers/#miplearn.h5.H5File">H5File</a> and produce either: (i) instance features, which describe the entire instances; (ii) variable features, which describe a particular decision variables; or (iii) constraint features, which describe a particular constraint. The extractor is free to implement only a
subset of these methods, if it is known that it will not be used with a machine learning component that requires the other types of features.</p>
</section>
<section id="H5FieldsExtractor">
<h2><span class="section-number">7.2. </span>H5FieldsExtractor<a class="headerlink" href="#H5FieldsExtractor" title="Link to this heading"></a></h2>
<p><a class="reference internal" href="#H5FieldsExtractor"><span class="std std-ref">H5FieldsExtractor</span></a>, the most simple extractor in MIPLearn, simple extracts data that is already available in the HDF5 file, assembles it into a matrix and returns it as-is. The fields used to build instance, variable and constraint features are user-specified. The class also performs checks to ensure that the shapes of the returned matrices make sense.</p>
<section id="Example">
<h3>Example<a class="headerlink" href="#Example" title="Link to this heading"></a></h3>
<p>The example below demonstrates the usage of H5FieldsExtractor in a randomly generated instance of the multi-dimensional knapsack problem.</p>
<div class="nbinput 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="kn">from</span> <span class="nn">glob</span> <span class="kn">import</span> <span class="n">glob</span>
<span class="kn">from</span> <span class="nn">shutil</span> <span class="kn">import</span> <span class="n">rmtree</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
<span class="kn">from</span> <span class="nn">scipy.stats</span> <span class="kn">import</span> <span class="n">uniform</span><span class="p">,</span> <span class="n">randint</span>
<span class="kn">from</span> <span class="nn">miplearn.collectors.basic</span> <span class="kn">import</span> <span class="n">BasicCollector</span>
<span class="kn">from</span> <span class="nn">miplearn.extractors.fields</span> <span class="kn">import</span> <span class="n">H5FieldsExtractor</span>
<span class="kn">from</span> <span class="nn">miplearn.h5</span> <span class="kn">import</span> <span class="n">H5File</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.multiknapsack</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">MultiKnapsackGenerator</span><span class="p">,</span>
<span class="n">build_multiknapsack_model_gurobipy</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># Set random seed to make example reproducible</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="mi">42</span><span class="p">)</span>
<span class="c1"># Generate some random multiknapsack instances</span>
<span class="n">rmtree</span><span class="p">(</span><span class="s2">&quot;data/multiknapsack/&quot;</span><span class="p">,</span> <span class="n">ignore_errors</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">write_pkl_gz</span><span class="p">(</span>
<span class="n">MultiKnapsackGenerator</span><span class="p">(</span>
<span class="n">n</span><span class="o">=</span><span class="n">randint</span><span class="p">(</span><span class="n">low</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">high</span><span class="o">=</span><span class="mi">11</span><span class="p">),</span>
<span class="n">m</span><span class="o">=</span><span class="n">randint</span><span class="p">(</span><span class="n">low</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span> <span class="n">high</span><span class="o">=</span><span class="mi">6</span><span class="p">),</span>
<span class="n">w</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="mi">0</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mi">1000</span><span class="p">),</span>
<span class="n">K</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="mi">100</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mi">0</span><span class="p">),</span>
<span class="n">u</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="mi">1</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mi">0</span><span class="p">),</span>
<span class="n">alpha</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.25</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mi">0</span><span class="p">),</span>
<span class="n">w_jitter</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.95</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mf">0.1</span><span class="p">),</span>
<span class="n">p_jitter</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.75</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mf">0.5</span><span class="p">),</span>
<span class="n">fix_w</span><span class="o">=</span><span class="kc">True</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">10</span><span class="p">),</span>
<span class="s2">&quot;data/multiknapsack&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># Run the basic collector</span>
<span class="n">BasicCollector</span><span class="p">()</span><span class="o">.</span><span class="n">collect</span><span class="p">(</span>
<span class="n">glob</span><span class="p">(</span><span class="s2">&quot;data/multiknapsack/*&quot;</span><span class="p">),</span>
<span class="n">build_multiknapsack_model_gurobipy</span><span class="p">,</span>
<span class="n">n_jobs</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">ext</span> <span class="o">=</span> <span class="n">H5FieldsExtractor</span><span class="p">(</span>
<span class="c1"># Use as instance features the value of the LP relaxation and the</span>
<span class="c1"># vector of objective coefficients.</span>
<span class="n">instance_fields</span><span class="o">=</span><span class="p">[</span>
<span class="s2">&quot;lp_obj_value&quot;</span><span class="p">,</span>
<span class="s2">&quot;static_var_obj_coeffs&quot;</span><span class="p">,</span>
<span class="p">],</span>
<span class="c1"># For each variable, use as features the optimal value of the LP</span>
<span class="c1"># relaxation, the variable objective coefficient, the variable&#39;s</span>
<span class="c1"># value its reduced cost.</span>
<span class="n">var_fields</span><span class="o">=</span><span class="p">[</span>
<span class="s2">&quot;lp_obj_value&quot;</span><span class="p">,</span>
<span class="s2">&quot;static_var_obj_coeffs&quot;</span><span class="p">,</span>
<span class="s2">&quot;lp_var_values&quot;</span><span class="p">,</span>
<span class="s2">&quot;lp_var_reduced_costs&quot;</span><span class="p">,</span>
<span class="p">],</span>
<span class="c1"># For each constraint, use as features the RHS, dual value and slack.</span>
<span class="n">constr_fields</span><span class="o">=</span><span class="p">[</span>
<span class="s2">&quot;static_constr_rhs&quot;</span><span class="p">,</span>
<span class="s2">&quot;lp_constr_dual_values&quot;</span><span class="p">,</span>
<span class="s2">&quot;lp_constr_slacks&quot;</span><span class="p">,</span>
<span class="p">],</span>
<span class="p">)</span>
<span class="k">with</span> <span class="n">H5File</span><span class="p">(</span><span class="s2">&quot;data/multiknapsack/00000.h5&quot;</span><span class="p">)</span> <span class="k">as</span> <span class="n">h5</span><span class="p">:</span>
<span class="c1"># Extract and print instance features</span>
<span class="n">x1</span> <span class="o">=</span> <span class="n">ext</span><span class="o">.</span><span class="n">get_instance_features</span><span class="p">(</span><span class="n">h5</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;instance features&quot;</span><span class="p">,</span> <span class="n">x1</span><span class="o">.</span><span class="n">shape</span><span class="p">,</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">x1</span><span class="p">)</span>
<span class="c1"># Extract and print variable features</span>
<span class="n">x2</span> <span class="o">=</span> <span class="n">ext</span><span class="o">.</span><span class="n">get_var_features</span><span class="p">(</span><span class="n">h5</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;variable features&quot;</span><span class="p">,</span> <span class="n">x2</span><span class="o">.</span><span class="n">shape</span><span class="p">,</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">x2</span><span class="p">)</span>
<span class="c1"># Extract and print constraint features</span>
<span class="n">x3</span> <span class="o">=</span> <span class="n">ext</span><span class="o">.</span><span class="n">get_constr_features</span><span class="p">(</span><span class="n">h5</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;constraint features&quot;</span><span class="p">,</span> <span class="n">x3</span><span class="o">.</span><span class="n">shape</span><span class="p">,</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">x3</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>
instance features (11,)
[-1531.24308771 -350. -692. -454.
-709. -605. -543. -321.
-674. -571. -341. ]
variable features (10, 4)
[[-1.53124309e+03 -3.50000000e+02 0.00000000e+00 9.43468018e+01]
[-1.53124309e+03 -6.92000000e+02 2.51703322e-01 0.00000000e+00]
[-1.53124309e+03 -4.54000000e+02 0.00000000e+00 8.25504150e+01]
[-1.53124309e+03 -7.09000000e+02 1.11373022e-01 0.00000000e+00]
[-1.53124309e+03 -6.05000000e+02 1.00000000e+00 -1.26055283e+02]
[-1.53124309e+03 -5.43000000e+02 0.00000000e+00 1.68693771e+02]
[-1.53124309e+03 -3.21000000e+02 1.07488781e-01 0.00000000e+00]
[-1.53124309e+03 -6.74000000e+02 8.82293701e-01 0.00000000e+00]
[-1.53124309e+03 -5.71000000e+02 0.00000000e+00 1.41129074e+02]
[-1.53124309e+03 -3.41000000e+02 1.28830120e-01 0.00000000e+00]]
constraint features (5, 3)
[[ 1.3100000e+03 -1.5978307e-01 0.0000000e+00]
[ 9.8800000e+02 -3.2881632e-01 0.0000000e+00]
[ 1.0040000e+03 -4.0601316e-01 0.0000000e+00]
[ 1.2690000e+03 -1.3659772e-01 0.0000000e+00]
[ 1.0070000e+03 -2.8800571e-01 0.0000000e+00]]
</pre></div></div>
</div>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p>You should ensure that the number of features remains the same for all relevant HDF5 files. In the previous example, to illustrate this issue, we used variable objective coefficients as instance features. While this is allowed, note that this requires all problem instances to have the same number of variables; otherwise the number of features would vary from instance to instance and MIPLearn would be unable to concatenate the matrices.</p>
</div>
</section>
</section>
<section id="AlvLouWeh2017Extractor">
<h2><span class="section-number">7.3. </span>AlvLouWeh2017Extractor<a class="headerlink" href="#AlvLouWeh2017Extractor" title="Link to this heading"></a></h2>
<p>Alvarez, Louveaux and Wehenkel (2017) proposed a set features to describe a particular decision variable in a given node of the branch-and-bound tree, and applied it to the problem of mimicking strong branching decisions. The class <a class="reference internal" href="#AlvLouWeh2017Extractor"><span class="std std-ref">AlvLouWeh2017Extractor</span></a> implements a subset of these features (40 out of 64), which are available outside of the branch-and-bound tree. Some features are derived from the static defintion of the problem (i.e. from objective function and
constraint data), while some features are derived from the solution to the LP relaxation. The features have been designed to be: (i) independent of the size of the problem; (ii) invariant with respect to irrelevant problem transformations, such as row and column permutation; and (iii) independent of the scale of the problem. We refer to the paper for a more complete description.</p>
<section id="id1">
<h3>Example<a class="headerlink" href="#id1" title="Link to this heading"></a></h3>
<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="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.h5</span> <span class="kn">import</span> <span class="n">H5File</span>
<span class="c1"># Build the extractor</span>
<span class="n">ext</span> <span class="o">=</span> <span class="n">AlvLouWeh2017Extractor</span><span class="p">()</span>
<span class="c1"># Open previously-created multiknapsack training data</span>
<span class="k">with</span> <span class="n">H5File</span><span class="p">(</span><span class="s2">&quot;data/multiknapsack/00000.h5&quot;</span><span class="p">)</span> <span class="k">as</span> <span class="n">h5</span><span class="p">:</span>
<span class="c1"># Extract and print variable features</span>
<span class="n">x1</span> <span class="o">=</span> <span class="n">ext</span><span class="o">.</span><span class="n">get_var_features</span><span class="p">(</span><span class="n">h5</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;x1&quot;</span><span class="p">,</span> <span class="n">x1</span><span class="o">.</span><span class="n">shape</span><span class="p">,</span> <span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span> <span class="n">x1</span><span class="o">.</span><span class="n">round</span><span class="p">(</span><span class="mi">1</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>
x1 (10, 40)
[[-1.00e+00 1.00e+20 1.00e-01 1.00e+00 0.00e+00 1.00e+00 6.00e-01
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 1.00e+00 6.00e-01 1.00e+00 1.75e+01 1.00e+00 2.00e-01
1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 1.00e+00 -1.00e+00 0.00e+00 1.00e+20]
[-1.00e+00 1.00e+20 1.00e-01 1.00e+00 1.00e-01 1.00e+00 1.00e+00
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 1.00e+00 7.00e-01 1.00e+00 5.10e+00 1.00e+00 2.00e-01
1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
3.00e-01 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]
[-1.00e+00 1.00e+20 1.00e-01 1.00e+00 0.00e+00 1.00e+00 9.00e-01
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 1.00e+00 5.00e-01 1.00e+00 1.30e+01 1.00e+00 2.00e-01
1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 1.00e+00 -1.00e+00 0.00e+00 1.00e+20]
[-1.00e+00 1.00e+20 1.00e-01 1.00e+00 2.00e-01 1.00e+00 9.00e-01
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 1.00e+00 8.00e-01 1.00e+00 3.40e+00 1.00e+00 2.00e-01
1.00e+00 1.00e-01 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
1.00e-01 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]
[-1.00e+00 1.00e+20 1.00e-01 1.00e+00 1.00e-01 1.00e+00 7.00e-01
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 1.00e+00 6.00e-01 1.00e+00 3.80e+00 1.00e+00 2.00e-01
1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]
[-1.00e+00 1.00e+20 1.00e-01 1.00e+00 1.00e-01 1.00e+00 8.00e-01
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 1.00e+00 7.00e-01 1.00e+00 3.30e+00 1.00e+00 2.00e-01
1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 1.00e+00 -1.00e+00 0.00e+00 1.00e+20]
[-1.00e+00 1.00e+20 1.00e-01 1.00e+00 0.00e+00 1.00e+00 3.00e-01
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 1.00e+00 1.00e+00 1.00e+00 5.70e+00 1.00e+00 1.00e-01
1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
1.00e-01 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]
[-1.00e+00 1.00e+20 1.00e-01 1.00e+00 1.00e-01 1.00e+00 6.00e-01
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 1.00e+00 8.00e-01 1.00e+00 6.80e+00 1.00e+00 2.00e-01
1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
1.00e-01 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]
[-1.00e+00 1.00e+20 1.00e-01 1.00e+00 4.00e-01 1.00e+00 6.00e-01
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 1.00e+00 8.00e-01 1.00e+00 1.40e+00 1.00e+00 1.00e-01
1.00e+00 1.00e-01 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 1.00e+00 -1.00e+00 0.00e+00 1.00e+20]
[-1.00e+00 1.00e+20 1.00e-01 1.00e+00 0.00e+00 1.00e+00 5.00e-01
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 1.00e+00 5.00e-01 1.00e+00 7.60e+00 1.00e+00 1.00e-01
1.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.00e+00
1.00e-01 -1.00e+00 -1.00e+00 0.00e+00 0.00e+00]]
</pre></div></div>
</div>
<div class="admonition note">
<p class="admonition-title">References</p>
<ul class="simple">
<li><p><strong>Alvarez, Alejandro Marcos.</strong> <em>Computational and theoretical synergies between linear optimization and supervised machine learning.</em> (2016). University of Liège.</p></li>
<li><p><strong>Alvarez, Alejandro Marcos, Quentin Louveaux, and Louis Wehenkel.</strong> <em>A machine learning-based approximation of strong branching.</em> INFORMS Journal on Computing 29.1 (2017): 185-195.</p></li>
</ul>
</div>
</section>
</section>
</section>
</div>
<div class='prev-next-bottom'>
<a class='left-prev' id="prev-link" href="../collectors/" title="previous page"><span class="section-number">6. </span>Training Data Collectors</a>
<a class='right-next' id="next-link" href="../primal/" title="next page"><span class="section-number">8. </span>Primal Components</a>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

@ -0,0 +1,291 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "880cf4c7-d3c4-4b92-85c7-04a32264cdae",
"metadata": {},
"source": [
"# Primal Components\n",
"\n",
"In MIPLearn, a **primal component** is class that uses machine learning to predict a (potentially partial) assignment of values to the decision variables of the problem. Predicting high-quality primal solutions may be beneficial, as they allow the MIP solver to prune potentially large portions of the search space. Alternatively, if proof of optimality is not required, the MIP solver can be used to complete the partial solution generated by the machine learning model and and double-check its feasibility. MIPLearn allows both of these usage patterns.\n",
"\n",
"In this page, we describe the four primal components currently included in MIPLearn, which employ machine learning in different ways. Each component is highly configurable, and accepts an user-provided machine learning model, which it uses for all predictions. Each component can also be configured to provide the solution to the solver in multiple ways, depending on whether proof of optimality is required.\n",
"\n",
"## Primal component actions\n",
"\n",
"Before presenting the primal components themselves, we briefly discuss the three ways a solution may be provided to the solver. Each approach has benefits and limitations, which we also discuss in this section. All primal components can be configured to use any of the following approaches.\n",
"\n",
"The first approach is to provide the solution to the solver as a **warm start**. This is implemented by the class [SetWarmStart](SetWarmStart). The main advantage is that this method maintains all optimality and feasibility guarantees of the MIP solver, while still providing significant performance benefits for various classes of problems. If the machine learning model is able to predict multiple solutions, it is also possible to set multiple warm starts. In this case, the solver evaluates each warm start, discards the infeasible ones, then proceeds with the one that has the best objective value. The main disadvantage of this approach, compared to the next two, is that it provides relatively modest speedups for most problem classes, and no speedup at all for many others, even when the machine learning predictions are 100% accurate.\n",
"\n",
"[SetWarmStart]: ../../api/components/#miplearn.components.primal.actions.SetWarmStart\n",
"\n",
"The second approach is to **fix the decision variables** to their predicted values, then solve a restricted optimization problem on the remaining variables. This approach is implemented by the class `FixVariables`. The main advantage is its potential speedup: if machine learning can accurately predict values for a significant portion of the decision variables, then the MIP solver can typically complete the solution in a small fraction of the time it would take to find the same solution from scratch. The main disadvantage of this approach is that it loses optimality guarantees; that is, the complete solution found by the MIP solver may no longer be globally optimal. Also, if the machine learning predictions are not sufficiently accurate, there might not even be a feasible assignment for the variables that were left free.\n",
"\n",
"Finally, the third approach, which tries to strike a balance between the two previous ones, is to **enforce proximity** to a given solution. This strategy is implemented by the class `EnforceProximity`. More precisely, given values $\\bar{x}_1,\\ldots,\\bar{x}_n$ for a subset of binary decision variables $x_1,\\ldots,x_n$, this approach adds the constraint\n",
"\n",
"$$\n",
"\\sum_{i : \\bar{x}_i=0} x_i + \\sum_{i : \\bar{x}_i=1} \\left(1 - x_i\\right) \\leq k,\n",
"$$\n",
"to the problem, where $k$ is a user-defined parameter, which indicates how many of the predicted variables are allowed to deviate from the machine learning suggestion. The main advantage of this approach, compared to fixing variables, is its tolerance to lower-quality machine learning predictions. Its main disadvantage is that it typically leads to smaller speedups, especially for larger values of $k$. This approach also loses optimality guarantees.\n",
"\n",
"## Memorizing primal component\n",
"\n",
"A simple machine learning strategy for the prediction of primal solutions is to memorize all distinct solutions seen during training, then try to predict, during inference time, which of those memorized solutions are most likely to be feasible and to provide a good objective value for the current instance. The most promising solutions may alternatively be combined into a single partial solution, which is then provided to the MIP solver. Both variations of this strategy are implemented by the `MemorizingPrimalComponent` class. Note that it is only applicable if the problem size, and in fact if the meaning of the decision variables, remains the same across problem instances.\n",
"\n",
"More precisely, let $I_1,\\ldots,I_n$ be the training instances, and let $\\bar{x}^1,\\ldots,\\bar{x}^n$ be their respective optimal solutions. Given a new instance $I_{n+1}$, `MemorizingPrimalComponent` expects a user-provided binary classifier that assigns (through the `predict_proba` method, following scikit-learn's conventions) a score $\\delta_i$ to each solution $\\bar{x}^i$, such that solutions with higher score are more likely to be good solutions for $I_{n+1}$. The features provided to the classifier are the instance features computed by an user-provided extractor. Given these scores, the component then performs one of the following to actions, as decided by the user:\n",
"\n",
"1. Selects the top $k$ solutions with the highest scores and provides them to the solver; this is implemented by `SelectTopSolutions`, and it is typically used with the `SetWarmStart` action.\n",
"\n",
"2. Merges the top $k$ solutions into a single partial solution, then provides it to the solver. This is implemented by `MergeTopSolutions`. More precisely, suppose that the machine learning regressor ordered the solutions in the sequence $\\bar{x}^{i_1},\\ldots,\\bar{x}^{i_n}$, with the most promising solutions appearing first, and with ties being broken arbitrarily. The component starts by keeping only the $k$ most promising solutions $\\bar{x}^{i_1},\\ldots,\\bar{x}^{i_k}$. Then it computes, for each binary decision variable $x_l$, its average assigned value $\\tilde{x}_l$:\n",
"$$\n",
" \\tilde{x}_l = \\frac{1}{k} \\sum_{j=1}^k \\bar{x}^{i_j}_l.\n",
"$$\n",
" Finally, the component constructs a merged solution $y$, defined as:\n",
"$$\n",
" y_j = \\begin{cases}\n",
" 0 & \\text{ if } \\tilde{x}_l \\le \\theta_0 \\\\\n",
" 1 & \\text{ if } \\tilde{x}_l \\ge \\theta_1 \\\\\n",
" \\square & \\text{otherwise,}\n",
" \\end{cases}\n",
"$$\n",
" where $\\theta_0$ and $\\theta_1$ are user-specified parameters, and where $\\square$ indicates that the variable is left undefined. The solution $y$ is then provided by the solver using any of the three approaches defined in the previous section.\n",
"\n",
"The above specification of `MemorizingPrimalComponent` is meant to be as general as possible. Simpler strategies can be implemented by configuring this component in specific ways. For example, a simpler approach employed in the literature is to collect all optimal solutions, then provide the entire list of solutions to the solver as warm starts, without any filtering or post-processing. This strategy can be implemented with `MemorizingPrimalComponent` by using a model that returns a constant value for all solutions (e.g. [scikit-learn's DummyClassifier][DummyClassifier]), then selecting the top $n$ (instead of $k$) solutions. See example below. Another simple approach is taking the solution to the most similar instance, and using it, by itself, as a warm start. This can be implemented by using a model that computes distances between the current instance and the training ones (e.g. [scikit-learn's KNeighborsClassifier][KNeighborsClassifier]), then select the solution to the nearest one. See also example below. More complex strategies, of course, can also be configured.\n",
"\n",
"[DummyClassifier]: https://scikit-learn.org/stable/modules/generated/sklearn.dummy.DummyClassifier.html\n",
"[KNeighborsClassifier]: https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html\n",
"\n",
"### Examples"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "253adbf4",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"from sklearn.dummy import DummyClassifier\n",
"from sklearn.neighbors import KNeighborsClassifier\n",
"\n",
"from miplearn.components.primal.actions import (\n",
" SetWarmStart,\n",
" FixVariables,\n",
" EnforceProximity,\n",
")\n",
"from miplearn.components.primal.mem import (\n",
" MemorizingPrimalComponent,\n",
" SelectTopSolutions,\n",
" MergeTopSolutions,\n",
")\n",
"from miplearn.extractors.dummy import DummyExtractor\n",
"from miplearn.extractors.fields import H5FieldsExtractor\n",
"\n",
"# Configures a memorizing primal component that collects\n",
"# all distinct solutions seen during training and provides\n",
"# them to the solver without any filtering or post-processing.\n",
"comp1 = MemorizingPrimalComponent(\n",
" clf=DummyClassifier(),\n",
" extractor=DummyExtractor(),\n",
" constructor=SelectTopSolutions(1_000_000),\n",
" action=SetWarmStart(),\n",
")\n",
"\n",
"# Configures a memorizing primal component that finds the\n",
"# training instance with the closest objective function, then\n",
"# fixes the decision variables to the values they assumed\n",
"# at the optimal solution for that instance.\n",
"comp2 = MemorizingPrimalComponent(\n",
" clf=KNeighborsClassifier(n_neighbors=1),\n",
" extractor=H5FieldsExtractor(\n",
" instance_fields=[\"static_var_obj_coeffs\"],\n",
" ),\n",
" constructor=SelectTopSolutions(1),\n",
" action=FixVariables(),\n",
")\n",
"\n",
"# Configures a memorizing primal component that finds the distinct\n",
"# solutions to the 10 most similar training problem instances,\n",
"# selects the 3 solutions that were most often optimal to these\n",
"# training instances, combines them into a single partial solution,\n",
"# then enforces proximity, allowing at most 3 variables to deviate\n",
"# from the machine learning suggestion.\n",
"comp3 = MemorizingPrimalComponent(\n",
" clf=KNeighborsClassifier(n_neighbors=10),\n",
" extractor=H5FieldsExtractor(instance_fields=[\"static_var_obj_coeffs\"]),\n",
" constructor=MergeTopSolutions(k=3, thresholds=[0.25, 0.75]),\n",
" action=EnforceProximity(3),\n",
")"
]
},
{
"cell_type": "markdown",
"id": "f194a793",
"metadata": {},
"source": [
"## Independent vars primal component\n",
"\n",
"Instead of memorizing previously-seen primal solutions, it is also natural to use machine learning models to directly predict the values of the decision variables, constructing a solution from scratch. This approach has the benefit of potentially constructing novel high-quality solutions, never observed in the training data. Two variations of this strategy are supported by MIPLearn: (i) predicting the values of the decision variables independently, using multiple ML models; or (ii) predicting the values jointly, with a single model. We describe the first variation in this section, and the second variation in the next section.\n",
"\n",
"Let $I_1,\\ldots,I_n$ be the training instances, and let $\\bar{x}^1,\\ldots,\\bar{x}^n$ be their respective optimal solutions. For each binary decision variable $x_j$, the component `IndependentVarsPrimalComponent` creates a copy of a user-provided binary classifier and trains it to predict the optimal value of $x_j$, given $\\bar{x}^1_j,\\ldots,\\bar{x}^n_j$ as training labels. The features provided to the model are the variable features computed by an user-provided extractor. During inference time, the component uses these $n$ binary classifiers to construct a solution and provides it to the solver using one of the available actions.\n",
"\n",
"Three issues often arise in practice when using this approach:\n",
"\n",
" 1. For certain binary variables $x_j$, it is frequently the case that its optimal value is either always zero or always one in the training dataset, which poses problems to some standard scikit-learn classifiers, since they do not expect a single class. The wrapper `SingleClassFix` can be used to fix this issue (see example below).\n",
"2. It is also frequently the case that machine learning classifier can only reliably predict the values of some variables with high accuracy, not all of them. In this situation, instead of computing a complete primal solution, it may be more beneficial to construct a partial solution containing values only for the variables for which the ML made a high-confidence prediction. The meta-classifier `MinProbabilityClassifier` can be used for this purpose. It asks the base classifier for the probability of the value being zero or one (using the `predict_proba` method) and erases from the primal solution all values whose probabilities are below a given threshold.\n",
"3. To make multiple copies of the provided ML classifier, MIPLearn uses the standard `sklearn.base.clone` method, which may not be suitable for classifiers from other frameworks. To handle this, it is possible to override the clone function using the `clone_fn` constructor argument.\n",
"\n",
"### Examples"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "3fc0b5d1",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"from sklearn.linear_model import LogisticRegression\n",
"from miplearn.classifiers.minprob import MinProbabilityClassifier\n",
"from miplearn.classifiers.singleclass import SingleClassFix\n",
"from miplearn.components.primal.indep import IndependentVarsPrimalComponent\n",
"from miplearn.extractors.AlvLouWeh2017 import AlvLouWeh2017Extractor\n",
"from miplearn.components.primal.actions import SetWarmStart\n",
"\n",
"# Configures a primal component that independently predicts the value of each\n",
"# binary variable using logistic regression and provides it to the solver as\n",
"# warm start. Erases predictions with probability less than 99%; applies\n",
"# single-class fix; and uses AlvLouWeh2017 features.\n",
"comp = IndependentVarsPrimalComponent(\n",
" base_clf=SingleClassFix(\n",
" MinProbabilityClassifier(\n",
" base_clf=LogisticRegression(),\n",
" thresholds=[0.99, 0.99],\n",
" ),\n",
" ),\n",
" extractor=AlvLouWeh2017Extractor(),\n",
" action=SetWarmStart(),\n",
")"
]
},
{
"cell_type": "markdown",
"id": "45107a0c",
"metadata": {},
"source": [
"## Joint vars primal component\n",
"In the previous subsection, we used multiple machine learning models to independently predict the values of the binary decision variables. When these values are correlated, an alternative approach is to jointly predict the values of all binary variables using a single machine learning model. This strategy is implemented by `JointVarsPrimalComponent`. Compared to the previous ones, this component is much more straightforwad. It simply extracts instance features, using the user-provided feature extractor, then directly trains the user-provided binary classifier (using the `fit` method), without making any copies. The trained classifier is then used to predict entire solutions (using the `predict` method), which are given to the solver using one of the previously discussed methods. In the example below, we illustrate the usage of this component with a simple feed-forward neural network.\n",
"\n",
"`JointVarsPrimalComponent` can also be used to implement strategies that use multiple machine learning models, but not indepedently. For example, a common strategy in multioutput prediction is building a *classifier chain*. In this approach, the first decision variable is predicted using the instance features alone; but the $n$-th decision variable is predicted using the instance features plus the predicted values of the $n-1$ previous variables. This can be easily implemented using scikit-learn's `ClassifierChain` estimator, as shown in the example below.\n",
"\n",
"### Examples"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "cf9b52dd",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"from sklearn.multioutput import ClassifierChain\n",
"from sklearn.neural_network import MLPClassifier\n",
"from miplearn.components.primal.joint import JointVarsPrimalComponent\n",
"from miplearn.extractors.fields import H5FieldsExtractor\n",
"from miplearn.components.primal.actions import SetWarmStart\n",
"\n",
"# Configures a primal component that uses a feedforward neural network\n",
"# to jointly predict the values of the binary variables, based on the\n",
"# objective cost function, and provides the solution to the solver as\n",
"# a warm start.\n",
"comp = JointVarsPrimalComponent(\n",
" clf=MLPClassifier(),\n",
" extractor=H5FieldsExtractor(\n",
" instance_fields=[\"static_var_obj_coeffs\"],\n",
" ),\n",
" action=SetWarmStart(),\n",
")\n",
"\n",
"# Configures a primal component that uses a chain of logistic regression\n",
"# models to jointly predict the values of the binary variables, based on\n",
"# the objective function.\n",
"comp = JointVarsPrimalComponent(\n",
" clf=ClassifierChain(SingleClassFix(LogisticRegression())),\n",
" extractor=H5FieldsExtractor(\n",
" instance_fields=[\"static_var_obj_coeffs\"],\n",
" ),\n",
" action=SetWarmStart(),\n",
")"
]
},
{
"cell_type": "markdown",
"id": "dddf7be4",
"metadata": {},
"source": [
"## Expert primal component\n",
"\n",
"Before spending time and effort choosing a machine learning strategy and tweaking its parameters, it is usually a good idea to evaluate what would be the performance impact of the model if its predictions were 100% accurate. This is especially important for the prediction of warm starts, since they are not always very beneficial. To simplify this task, MIPLearn provides `ExpertPrimalComponent`, a component which simply loads the optimal solution from the HDF5 file, assuming that it has already been computed, then directly provides it to the solver using one of the available methods. This component is useful in benchmarks, to evaluate how close to the best theoretical performance the machine learning components are.\n",
"\n",
"### Example"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "9e2e81b9",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"from miplearn.components.primal.expert import ExpertPrimalComponent\n",
"from miplearn.components.primal.actions import SetWarmStart\n",
"\n",
"# Configures an expert primal component, which reads a pre-computed\n",
"# optimal solution from the HDF5 file and provides it to the solver\n",
"# as warm start.\n",
"comp = ExpertPrimalComponent(action=SetWarmStart())"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,541 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<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" />
<title>8. Primal Components &#8212; MIPLearn 0.4</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" type="text/css" href="../../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css?v=2aa19091" />
<link rel="stylesheet" type="text/css" href="../../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script>
<script>window.MathJax = {"tex": {"inlineMath": [["$", "$"], ["\\(", "\\)"]], "processEscapes": true}, "options": {"ignoreHtmlClass": "tex2jax_ignore|mathjax_ignore|document", "processHtmlClass": "tex2jax_process|mathjax_process|math|output_area"}}</script>
<script defer="defer" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<link rel="index" title="Index" href="../../genindex/" />
<link rel="search" title="Search" href="../../search/" />
<link rel="next" title="9. Learning Solver" href="../solvers/" />
<link rel="prev" title="7. Feature Extractors" href="../features/" />
<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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="current nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1 current active">
<a class="current reference internal" href="#">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../api/problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/components/">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/helpers/">
14. 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/guide/primal.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="#Primal-component-actions">
8.1. Primal component actions
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Memorizing-primal-component">
8.2. Memorizing primal component
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#Examples">
Examples
</a>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Independent-vars-primal-component">
8.3. Independent vars primal component
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#id1">
Examples
</a>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Joint-vars-primal-component">
8.4. Joint vars primal component
</a>
<ul class="nav section-nav flex-column">
<li class="toc-h3 nav-item toc-entry">
<a class="reference internal nav-link" href="#id2">
Examples
</a>
</li>
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Expert-primal-component">
8.5. Expert primal component
</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>
</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>
<section id="Primal-Components">
<h1><span class="section-number">8. </span>Primal Components<a class="headerlink" href="#Primal-Components" title="Link to this heading"></a></h1>
<p>In MIPLearn, a <strong>primal component</strong> is class that uses machine learning to predict a (potentially partial) assignment of values to the decision variables of the problem. Predicting high-quality primal solutions may be beneficial, as they allow the MIP solver to prune potentially large portions of the search space. Alternatively, if proof of optimality is not required, the MIP solver can be used to complete the partial solution generated by the machine learning model and and double-check its
feasibility. MIPLearn allows both of these usage patterns.</p>
<p>In this page, we describe the four primal components currently included in MIPLearn, which employ machine learning in different ways. Each component is highly configurable, and accepts an user-provided machine learning model, which it uses for all predictions. Each component can also be configured to provide the solution to the solver in multiple ways, depending on whether proof of optimality is required.</p>
<section id="Primal-component-actions">
<h2><span class="section-number">8.1. </span>Primal component actions<a class="headerlink" href="#Primal-component-actions" title="Link to this heading"></a></h2>
<p>Before presenting the primal components themselves, we briefly discuss the three ways a solution may be provided to the solver. Each approach has benefits and limitations, which we also discuss in this section. All primal components can be configured to use any of the following approaches.</p>
<p>The first approach is to provide the solution to the solver as a <strong>warm start</strong>. This is implemented by the class <a class="reference external" href="SetWarmStart">SetWarmStart</a>. The main advantage is that this method maintains all optimality and feasibility guarantees of the MIP solver, while still providing significant performance benefits for various classes of problems. If the machine learning model is able to predict multiple solutions, it is also possible to set multiple warm starts. In this case, the solver evaluates
each warm start, discards the infeasible ones, then proceeds with the one that has the best objective value. The main disadvantage of this approach, compared to the next two, is that it provides relatively modest speedups for most problem classes, and no speedup at all for many others, even when the machine learning predictions are 100% accurate.</p>
<p>The second approach is to <strong>fix the decision variables</strong> to their predicted values, then solve a restricted optimization problem on the remaining variables. This approach is implemented by the class <code class="docutils literal notranslate"><span class="pre">FixVariables</span></code>. The main advantage is its potential speedup: if machine learning can accurately predict values for a significant portion of the decision variables, then the MIP solver can typically complete the solution in a small fraction of the time it would take to find the same solution from
scratch. The main disadvantage of this approach is that it loses optimality guarantees; that is, the complete solution found by the MIP solver may no longer be globally optimal. Also, if the machine learning predictions are not sufficiently accurate, there might not even be a feasible assignment for the variables that were left free.</p>
<p>Finally, the third approach, which tries to strike a balance between the two previous ones, is to <strong>enforce proximity</strong> to a given solution. This strategy is implemented by the class <code class="docutils literal notranslate"><span class="pre">EnforceProximity</span></code>. More precisely, given values <span class="math notranslate nohighlight">\(\bar{x}_1,\ldots,\bar{x}_n\)</span> for a subset of binary decision variables <span class="math notranslate nohighlight">\(x_1,\ldots,x_n\)</span>, this approach adds the constraint</p>
<div class="math notranslate nohighlight">
\[\sum_{i : \bar{x}_i=0} x_i + \sum_{i : \bar{x}_i=1} \left(1 - x_i\right) \leq k,\]</div>
<p>to the problem, where <span class="math notranslate nohighlight">\(k\)</span> is a user-defined parameter, which indicates how many of the predicted variables are allowed to deviate from the machine learning suggestion. The main advantage of this approach, compared to fixing variables, is its tolerance to lower-quality machine learning predictions. Its main disadvantage is that it typically leads to smaller speedups, especially for larger values of <span class="math notranslate nohighlight">\(k\)</span>. This approach also loses optimality guarantees.</p>
</section>
<section id="Memorizing-primal-component">
<h2><span class="section-number">8.2. </span>Memorizing primal component<a class="headerlink" href="#Memorizing-primal-component" title="Link to this heading"></a></h2>
<p>A simple machine learning strategy for the prediction of primal solutions is to memorize all distinct solutions seen during training, then try to predict, during inference time, which of those memorized solutions are most likely to be feasible and to provide a good objective value for the current instance. The most promising solutions may alternatively be combined into a single partial solution, which is then provided to the MIP solver. Both variations of this strategy are implemented by the
<code class="docutils literal notranslate"><span class="pre">MemorizingPrimalComponent</span></code> class. Note that it is only applicable if the problem size, and in fact if the meaning of the decision variables, remains the same across problem instances.</p>
<p>More precisely, let <span class="math notranslate nohighlight">\(I_1,\ldots,I_n\)</span> be the training instances, and let <span class="math notranslate nohighlight">\(\bar{x}^1,\ldots,\bar{x}^n\)</span> be their respective optimal solutions. Given a new instance <span class="math notranslate nohighlight">\(I_{n+1}\)</span>, <code class="docutils literal notranslate"><span class="pre">MemorizingPrimalComponent</span></code> expects a user-provided binary classifier that assigns (through the <code class="docutils literal notranslate"><span class="pre">predict_proba</span></code> method, following scikit-learns conventions) a score <span class="math notranslate nohighlight">\(\delta_i\)</span> to each solution <span class="math notranslate nohighlight">\(\bar{x}^i\)</span>, such that solutions with higher score are more likely to be good solutions for
<span class="math notranslate nohighlight">\(I_{n+1}\)</span>. The features provided to the classifier are the instance features computed by an user-provided extractor. Given these scores, the component then performs one of the following to actions, as decided by the user:</p>
<ol class="arabic">
<li><p>Selects the top <span class="math notranslate nohighlight">\(k\)</span> solutions with the highest scores and provides them to the solver; this is implemented by <code class="docutils literal notranslate"><span class="pre">SelectTopSolutions</span></code>, and it is typically used with the <code class="docutils literal notranslate"><span class="pre">SetWarmStart</span></code> action.</p></li>
<li><p>Merges the top <span class="math notranslate nohighlight">\(k\)</span> solutions into a single partial solution, then provides it to the solver. This is implemented by <code class="docutils literal notranslate"><span class="pre">MergeTopSolutions</span></code>. More precisely, suppose that the machine learning regressor ordered the solutions in the sequence <span class="math notranslate nohighlight">\(\bar{x}^{i_1},\ldots,\bar{x}^{i_n}\)</span>, with the most promising solutions appearing first, and with ties being broken arbitrarily. The component starts by keeping only the <span class="math notranslate nohighlight">\(k\)</span> most promising solutions <span class="math notranslate nohighlight">\(\bar{x}^{i_1},\ldots,\bar{x}^{i_k}\)</span>.
Then it computes, for each binary decision variable <span class="math notranslate nohighlight">\(x_l\)</span>, its average assigned value <span class="math notranslate nohighlight">\(\tilde{x}_l\)</span>:</p>
<div class="math notranslate nohighlight">
\[\tilde{x}_l = \frac{1}{k} \sum_{j=1}^k \bar{x}^{i_j}_l.\]</div>
<p>Finally, the component constructs a merged solution <span class="math notranslate nohighlight">\(y\)</span>, defined as:</p>
<div class="math notranslate nohighlight">
\[\begin{split}y_j = \begin{cases}
0 &amp; \text{ if } \tilde{x}_l \le \theta_0 \\
1 &amp; \text{ if } \tilde{x}_l \ge \theta_1 \\
\square &amp; \text{otherwise,}
\end{cases}\end{split}\]</div>
<p>where <span class="math notranslate nohighlight">\(\theta_0\)</span> and <span class="math notranslate nohighlight">\(\theta_1\)</span> are user-specified parameters, and where <span class="math notranslate nohighlight">\(\square\)</span> indicates that the variable is left undefined. The solution <span class="math notranslate nohighlight">\(y\)</span> is then provided by the solver using any of the three approaches defined in the previous section.</p>
</li>
</ol>
<p>The above specification of <code class="docutils literal notranslate"><span class="pre">MemorizingPrimalComponent</span></code> is meant to be as general as possible. Simpler strategies can be implemented by configuring this component in specific ways. For example, a simpler approach employed in the literature is to collect all optimal solutions, then provide the entire list of solutions to the solver as warm starts, without any filtering or post-processing. This strategy can be implemented with <code class="docutils literal notranslate"><span class="pre">MemorizingPrimalComponent</span></code> by using a model that returns a constant
value for all solutions (e.g. <a class="reference external" href="https://scikit-learn.org/stable/modules/generated/sklearn.dummy.DummyClassifier.html">scikit-learns DummyClassifier</a>), then selecting the top <span class="math notranslate nohighlight">\(n\)</span> (instead of <span class="math notranslate nohighlight">\(k\)</span>) solutions. See example below. Another simple approach is taking the solution to the most similar instance, and using it, by itself, as a warm start. This can be implemented by using a model that computes distances between the current instance and the training ones (e.g. <a class="reference external" href="https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html">scikit-learns
KNeighborsClassifier</a>), then select the solution to the nearest one. See also example below. More complex strategies, of course, can also be configured.</p>
<section id="Examples">
<h3>Examples<a class="headerlink" href="#Examples" title="Link to this heading"></a></h3>
<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="kn">from</span> <span class="nn">sklearn.dummy</span> <span class="kn">import</span> <span class="n">DummyClassifier</span>
<span class="kn">from</span> <span class="nn">sklearn.neighbors</span> <span class="kn">import</span> <span class="n">KNeighborsClassifier</span>
<span class="kn">from</span> <span class="nn">miplearn.components.primal.actions</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">SetWarmStart</span><span class="p">,</span>
<span class="n">FixVariables</span><span class="p">,</span>
<span class="n">EnforceProximity</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">miplearn.components.primal.mem</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">MemorizingPrimalComponent</span><span class="p">,</span>
<span class="n">SelectTopSolutions</span><span class="p">,</span>
<span class="n">MergeTopSolutions</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">miplearn.extractors.dummy</span> <span class="kn">import</span> <span class="n">DummyExtractor</span>
<span class="kn">from</span> <span class="nn">miplearn.extractors.fields</span> <span class="kn">import</span> <span class="n">H5FieldsExtractor</span>
<span class="c1"># Configures a memorizing primal component that collects</span>
<span class="c1"># all distinct solutions seen during training and provides</span>
<span class="c1"># them to the solver without any filtering or post-processing.</span>
<span class="n">comp1</span> <span class="o">=</span> <span class="n">MemorizingPrimalComponent</span><span class="p">(</span>
<span class="n">clf</span><span class="o">=</span><span class="n">DummyClassifier</span><span class="p">(),</span>
<span class="n">extractor</span><span class="o">=</span><span class="n">DummyExtractor</span><span class="p">(),</span>
<span class="n">constructor</span><span class="o">=</span><span class="n">SelectTopSolutions</span><span class="p">(</span><span class="mi">1_000_000</span><span class="p">),</span>
<span class="n">action</span><span class="o">=</span><span class="n">SetWarmStart</span><span class="p">(),</span>
<span class="p">)</span>
<span class="c1"># Configures a memorizing primal component that finds the</span>
<span class="c1"># training instance with the closest objective function, then</span>
<span class="c1"># fixes the decision variables to the values they assumed</span>
<span class="c1"># at the optimal solution for that instance.</span>
<span class="n">comp2</span> <span class="o">=</span> <span class="n">MemorizingPrimalComponent</span><span class="p">(</span>
<span class="n">clf</span><span class="o">=</span><span class="n">KNeighborsClassifier</span><span class="p">(</span><span class="n">n_neighbors</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span>
<span class="n">extractor</span><span class="o">=</span><span class="n">H5FieldsExtractor</span><span class="p">(</span>
<span class="n">instance_fields</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;static_var_obj_coeffs&quot;</span><span class="p">],</span>
<span class="p">),</span>
<span class="n">constructor</span><span class="o">=</span><span class="n">SelectTopSolutions</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span>
<span class="n">action</span><span class="o">=</span><span class="n">FixVariables</span><span class="p">(),</span>
<span class="p">)</span>
<span class="c1"># Configures a memorizing primal component that finds the distinct</span>
<span class="c1"># solutions to the 10 most similar training problem instances,</span>
<span class="c1"># selects the 3 solutions that were most often optimal to these</span>
<span class="c1"># training instances, combines them into a single partial solution,</span>
<span class="c1"># then enforces proximity, allowing at most 3 variables to deviate</span>
<span class="c1"># from the machine learning suggestion.</span>
<span class="n">comp3</span> <span class="o">=</span> <span class="n">MemorizingPrimalComponent</span><span class="p">(</span>
<span class="n">clf</span><span class="o">=</span><span class="n">KNeighborsClassifier</span><span class="p">(</span><span class="n">n_neighbors</span><span class="o">=</span><span class="mi">10</span><span class="p">),</span>
<span class="n">extractor</span><span class="o">=</span><span class="n">H5FieldsExtractor</span><span class="p">(</span><span class="n">instance_fields</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;static_var_obj_coeffs&quot;</span><span class="p">]),</span>
<span class="n">constructor</span><span class="o">=</span><span class="n">MergeTopSolutions</span><span class="p">(</span><span class="n">k</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">thresholds</span><span class="o">=</span><span class="p">[</span><span class="mf">0.25</span><span class="p">,</span> <span class="mf">0.75</span><span class="p">]),</span>
<span class="n">action</span><span class="o">=</span><span class="n">EnforceProximity</span><span class="p">(</span><span class="mi">3</span><span class="p">),</span>
<span class="p">)</span>
</pre></div>
</div>
</div>
</section>
</section>
<section id="Independent-vars-primal-component">
<h2><span class="section-number">8.3. </span>Independent vars primal component<a class="headerlink" href="#Independent-vars-primal-component" title="Link to this heading"></a></h2>
<p>Instead of memorizing previously-seen primal solutions, it is also natural to use machine learning models to directly predict the values of the decision variables, constructing a solution from scratch. This approach has the benefit of potentially constructing novel high-quality solutions, never observed in the training data. Two variations of this strategy are supported by MIPLearn: (i) predicting the values of the decision variables independently, using multiple ML models; or (ii) predicting
the values jointly, with a single model. We describe the first variation in this section, and the second variation in the next section.</p>
<p>Let <span class="math notranslate nohighlight">\(I_1,\ldots,I_n\)</span> be the training instances, and let <span class="math notranslate nohighlight">\(\bar{x}^1,\ldots,\bar{x}^n\)</span> be their respective optimal solutions. For each binary decision variable <span class="math notranslate nohighlight">\(x_j\)</span>, the component <code class="docutils literal notranslate"><span class="pre">IndependentVarsPrimalComponent</span></code> creates a copy of a user-provided binary classifier and trains it to predict the optimal value of <span class="math notranslate nohighlight">\(x_j\)</span>, given <span class="math notranslate nohighlight">\(\bar{x}^1_j,\ldots,\bar{x}^n_j\)</span> as training labels. The features provided to the model are the variable features computed by an user-provided
extractor. During inference time, the component uses these <span class="math notranslate nohighlight">\(n\)</span> binary classifiers to construct a solution and provides it to the solver using one of the available actions.</p>
<p>Three issues often arise in practice when using this approach:</p>
<ol class="arabic simple">
<li><p>For certain binary variables <span class="math notranslate nohighlight">\(x_j\)</span>, it is frequently the case that its optimal value is either always zero or always one in the training dataset, which poses problems to some standard scikit-learn classifiers, since they do not expect a single class. The wrapper <code class="docutils literal notranslate"><span class="pre">SingleClassFix</span></code> can be used to fix this issue (see example below).</p></li>
<li><p>It is also frequently the case that machine learning classifier can only reliably predict the values of some variables with high accuracy, not all of them. In this situation, instead of computing a complete primal solution, it may be more beneficial to construct a partial solution containing values only for the variables for which the ML made a high-confidence prediction. The meta-classifier <code class="docutils literal notranslate"><span class="pre">MinProbabilityClassifier</span></code> can be used for this purpose. It asks the base classifier for the
probability of the value being zero or one (using the <code class="docutils literal notranslate"><span class="pre">predict_proba</span></code> method) and erases from the primal solution all values whose probabilities are below a given threshold.</p></li>
<li><p>To make multiple copies of the provided ML classifier, MIPLearn uses the standard <code class="docutils literal notranslate"><span class="pre">sklearn.base.clone</span></code> method, which may not be suitable for classifiers from other frameworks. To handle this, it is possible to override the clone function using the <code class="docutils literal notranslate"><span class="pre">clone_fn</span></code> constructor argument.</p></li>
</ol>
<section id="id1">
<h3>Examples<a class="headerlink" href="#id1" title="Link to this heading"></a></h3>
<div class="nbinput nblast 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="kn">from</span> <span class="nn">sklearn.linear_model</span> <span class="kn">import</span> <span class="n">LogisticRegression</span>
<span class="kn">from</span> <span class="nn">miplearn.classifiers.minprob</span> <span class="kn">import</span> <span class="n">MinProbabilityClassifier</span>
<span class="kn">from</span> <span class="nn">miplearn.classifiers.singleclass</span> <span class="kn">import</span> <span class="n">SingleClassFix</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.components.primal.actions</span> <span class="kn">import</span> <span class="n">SetWarmStart</span>
<span class="c1"># Configures a primal component that independently predicts the value of each</span>
<span class="c1"># binary variable using logistic regression and provides it to the solver as</span>
<span class="c1"># warm start. Erases predictions with probability less than 99%; applies</span>
<span class="c1"># single-class fix; and uses AlvLouWeh2017 features.</span>
<span class="n">comp</span> <span class="o">=</span> <span class="n">IndependentVarsPrimalComponent</span><span class="p">(</span>
<span class="n">base_clf</span><span class="o">=</span><span class="n">SingleClassFix</span><span class="p">(</span>
<span class="n">MinProbabilityClassifier</span><span class="p">(</span>
<span class="n">base_clf</span><span class="o">=</span><span class="n">LogisticRegression</span><span class="p">(),</span>
<span class="n">thresholds</span><span class="o">=</span><span class="p">[</span><span class="mf">0.99</span><span class="p">,</span> <span class="mf">0.99</span><span class="p">],</span>
<span class="p">),</span>
<span class="p">),</span>
<span class="n">extractor</span><span class="o">=</span><span class="n">AlvLouWeh2017Extractor</span><span class="p">(),</span>
<span class="n">action</span><span class="o">=</span><span class="n">SetWarmStart</span><span class="p">(),</span>
<span class="p">)</span>
</pre></div>
</div>
</div>
</section>
</section>
<section id="Joint-vars-primal-component">
<h2><span class="section-number">8.4. </span>Joint vars primal component<a class="headerlink" href="#Joint-vars-primal-component" title="Link to this heading"></a></h2>
<p>In the previous subsection, we used multiple machine learning models to independently predict the values of the binary decision variables. When these values are correlated, an alternative approach is to jointly predict the values of all binary variables using a single machine learning model. This strategy is implemented by <code class="docutils literal notranslate"><span class="pre">JointVarsPrimalComponent</span></code>. Compared to the previous ones, this component is much more straightforwad. It simply extracts instance features, using the user-provided feature
extractor, then directly trains the user-provided binary classifier (using the <code class="docutils literal notranslate"><span class="pre">fit</span></code> method), without making any copies. The trained classifier is then used to predict entire solutions (using the <code class="docutils literal notranslate"><span class="pre">predict</span></code> method), which are given to the solver using one of the previously discussed methods. In the example below, we illustrate the usage of this component with a simple feed-forward neural network.</p>
<p><code class="docutils literal notranslate"><span class="pre">JointVarsPrimalComponent</span></code> can also be used to implement strategies that use multiple machine learning models, but not indepedently. For example, a common strategy in multioutput prediction is building a <em>classifier chain</em>. In this approach, the first decision variable is predicted using the instance features alone; but the <span class="math notranslate nohighlight">\(n\)</span>-th decision variable is predicted using the instance features plus the predicted values of the <span class="math notranslate nohighlight">\(n-1\)</span> previous variables. This can be easily implemented
using scikit-learns <code class="docutils literal notranslate"><span class="pre">ClassifierChain</span></code> estimator, as shown in the example below.</p>
<section id="id2">
<h3>Examples<a class="headerlink" href="#id2" title="Link to this heading"></a></h3>
<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">sklearn.multioutput</span> <span class="kn">import</span> <span class="n">ClassifierChain</span>
<span class="kn">from</span> <span class="nn">sklearn.neural_network</span> <span class="kn">import</span> <span class="n">MLPClassifier</span>
<span class="kn">from</span> <span class="nn">miplearn.components.primal.joint</span> <span class="kn">import</span> <span class="n">JointVarsPrimalComponent</span>
<span class="kn">from</span> <span class="nn">miplearn.extractors.fields</span> <span class="kn">import</span> <span class="n">H5FieldsExtractor</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="c1"># Configures a primal component that uses a feedforward neural network</span>
<span class="c1"># to jointly predict the values of the binary variables, based on the</span>
<span class="c1"># objective cost function, and provides the solution to the solver as</span>
<span class="c1"># a warm start.</span>
<span class="n">comp</span> <span class="o">=</span> <span class="n">JointVarsPrimalComponent</span><span class="p">(</span>
<span class="n">clf</span><span class="o">=</span><span class="n">MLPClassifier</span><span class="p">(),</span>
<span class="n">extractor</span><span class="o">=</span><span class="n">H5FieldsExtractor</span><span class="p">(</span>
<span class="n">instance_fields</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;static_var_obj_coeffs&quot;</span><span class="p">],</span>
<span class="p">),</span>
<span class="n">action</span><span class="o">=</span><span class="n">SetWarmStart</span><span class="p">(),</span>
<span class="p">)</span>
<span class="c1"># Configures a primal component that uses a chain of logistic regression</span>
<span class="c1"># models to jointly predict the values of the binary variables, based on</span>
<span class="c1"># the objective function.</span>
<span class="n">comp</span> <span class="o">=</span> <span class="n">JointVarsPrimalComponent</span><span class="p">(</span>
<span class="n">clf</span><span class="o">=</span><span class="n">ClassifierChain</span><span class="p">(</span><span class="n">SingleClassFix</span><span class="p">(</span><span class="n">LogisticRegression</span><span class="p">())),</span>
<span class="n">extractor</span><span class="o">=</span><span class="n">H5FieldsExtractor</span><span class="p">(</span>
<span class="n">instance_fields</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;static_var_obj_coeffs&quot;</span><span class="p">],</span>
<span class="p">),</span>
<span class="n">action</span><span class="o">=</span><span class="n">SetWarmStart</span><span class="p">(),</span>
<span class="p">)</span>
</pre></div>
</div>
</div>
</section>
</section>
<section id="Expert-primal-component">
<h2><span class="section-number">8.5. </span>Expert primal component<a class="headerlink" href="#Expert-primal-component" title="Link to this heading"></a></h2>
<p>Before spending time and effort choosing a machine learning strategy and tweaking its parameters, it is usually a good idea to evaluate what would be the performance impact of the model if its predictions were 100% accurate. This is especially important for the prediction of warm starts, since they are not always very beneficial. To simplify this task, MIPLearn provides <code class="docutils literal notranslate"><span class="pre">ExpertPrimalComponent</span></code>, a component which simply loads the optimal solution from the HDF5 file, assuming that it has already
been computed, then directly provides it to the solver using one of the available methods. This component is useful in benchmarks, to evaluate how close to the best theoretical performance the machine learning components are.</p>
<section id="Example">
<h3>Example<a class="headerlink" href="#Example" title="Link to this heading"></a></h3>
<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">from</span> <span class="nn">miplearn.components.primal.expert</span> <span class="kn">import</span> <span class="n">ExpertPrimalComponent</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="c1"># Configures an expert primal component, which reads a pre-computed</span>
<span class="c1"># optimal solution from the HDF5 file and provides it to the solver</span>
<span class="c1"># as warm start.</span>
<span class="n">comp</span> <span class="o">=</span> <span class="n">ExpertPrimalComponent</span><span class="p">(</span><span class="n">action</span><span class="o">=</span><span class="n">SetWarmStart</span><span class="p">())</span>
</pre></div>
</div>
</div>
</section>
</section>
</section>
</div>
<div class='prev-next-bottom'>
<a class='left-prev' id="prev-link" href="../features/" title="previous page"><span class="section-number">7. </span>Feature Extractors</a>
<a class='right-next' id="next-link" href="../solvers/" title="next page"><span class="section-number">9. </span>Learning Solver</a>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,251 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"id": "9ec1907b-db93-4840-9439-c9005902b968",
"metadata": {},
"source": [
"# Learning Solver\n",
"\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",
"### 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",
"execution_count": 1,
"id": "92b09b98",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Restricted license - for non-production use only - expires 2024-10-28\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 10 rows, 45 columns and 90 nonzeros\n",
"Model fingerprint: 0x6ddcd141\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",
"Presolve time: 0.00s\n",
"Presolved: 10 rows, 45 columns, 90 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\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",
"\n",
"Solved in 15 iterations and 0.00 seconds (0.00 work units)\n",
"Optimal objective 2.761000000e+03\n",
"\n",
"User-callback calls 56, time in user-callback 0.00 sec\n",
"Set parameter PreCrush to value 1\n",
"Set parameter LazyConstraints to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"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",
"\n",
"User MIP start produced solution with objective 2796 (0.00s)\n",
"Loaded user MIP start with objective 2796\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 10 rows, 45 columns, 90 nonzeros\n",
"Variable types: 0 continuous, 45 integer (45 binary)\n",
"\n",
"Root relaxation: objective 2.761000e+03, 14 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 2761.00000 0 - 2796.00000 2761.00000 1.25% - 0s\n",
" 0 0 cutoff 0 2796.00000 2796.00000 0.00% - 0s\n",
"\n",
"Cutting planes:\n",
" Lazy constraints: 3\n",
"\n",
"Explored 1 nodes (16 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 20 (of 20 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 110, time in user-callback 0.00 sec\n"
]
},
{
"data": {
"text/plain": [
"{'WS: Count': 1, 'WS: Number of variables set': 41.0}"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import random\n",
"\n",
"import numpy as np\n",
"from scipy.stats import uniform, randint\n",
"from sklearn.linear_model import LogisticRegression\n",
"\n",
"from miplearn.classifiers.minprob import MinProbabilityClassifier\n",
"from miplearn.classifiers.singleclass import SingleClassFix\n",
"from miplearn.collectors.basic import BasicCollector\n",
"from miplearn.components.primal.actions import SetWarmStart\n",
"from miplearn.components.primal.indep import IndependentVarsPrimalComponent\n",
"from miplearn.extractors.AlvLouWeh2017 import AlvLouWeh2017Extractor\n",
"from miplearn.io import write_pkl_gz\n",
"from miplearn.problems.tsp import (\n",
" TravelingSalesmanGenerator,\n",
" build_tsp_model_gurobipy,\n",
")\n",
"from miplearn.solvers.learning import LearningSolver\n",
"\n",
"# Set random seed to make example reproducible.\n",
"random.seed(42)\n",
"np.random.seed(42)\n",
"\n",
"# Generate a few instances of the traveling salesman problem.\n",
"data = TravelingSalesmanGenerator(\n",
" n=randint(low=10, high=11),\n",
" x=uniform(loc=0.0, scale=1000.0),\n",
" y=uniform(loc=0.0, scale=1000.0),\n",
" gamma=uniform(loc=0.90, scale=0.20),\n",
" fix_cities=True,\n",
" round=True,\n",
").generate(50)\n",
"\n",
"# Save instance data to data/tsp/00000.pkl.gz, data/tsp/00001.pkl.gz, ...\n",
"all_data = write_pkl_gz(data, \"data/tsp\")\n",
"\n",
"# Split train/test data\n",
"train_data = all_data[:40]\n",
"test_data = all_data[40:]\n",
"\n",
"# Collect training data\n",
"bc = BasicCollector()\n",
"bc.collect(train_data, build_tsp_model_gurobipy, n_jobs=4)\n",
"\n",
"# Build learning solver\n",
"solver = LearningSolver(\n",
" components=[\n",
" IndependentVarsPrimalComponent(\n",
" base_clf=SingleClassFix(\n",
" MinProbabilityClassifier(\n",
" base_clf=LogisticRegression(),\n",
" thresholds=[0.95, 0.95],\n",
" ),\n",
" ),\n",
" extractor=AlvLouWeh2017Extractor(),\n",
" action=SetWarmStart(),\n",
" )\n",
" ]\n",
")\n",
"\n",
"# Train ML models\n",
"solver.fit(train_data)\n",
"\n",
"# Solve a test instance\n",
"solver.optimize(test_data[0], build_tsp_model_gurobipy)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e27d2cbd-5341-461d-bbc1-8131aee8d949",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,494 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<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" />
<title>9. Learning Solver &#8212; MIPLearn 0.4</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" type="text/css" href="../../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css?v=2aa19091" />
<link rel="stylesheet" type="text/css" href="../../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script>
<script>window.MathJax = {"tex": {"inlineMath": [["$", "$"], ["\\(", "\\)"]], "processEscapes": true}, "options": {"ignoreHtmlClass": "tex2jax_ignore|mathjax_ignore|document", "processHtmlClass": "tex2jax_process|mathjax_process|math|output_area"}}</script>
<script defer="defer" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<link rel="index" title="Index" href="../../genindex/" />
<link rel="search" title="Search" href="../../search/" />
<link rel="next" title="10. Benchmark Problems" href="../../api/problems/" />
<link rel="prev" title="8. Primal Components" href="../primal/" />
<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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../tutorials/cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="current nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1 current active">
<a class="current reference internal" href="#">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../api/problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/components/">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/helpers/">
14. 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/guide/solvers.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="#Configuring-the-solver">
9.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">
9.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">
9.3. Complete example
</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>
<section id="Learning-Solver">
<h1><span class="section-number">9. </span>Learning Solver<a class="headerlink" href="#Learning-Solver" title="Link to this heading"></a></h1>
<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
describe each of these steps, then conclude with a complete runnable example.</p>
<section id="Configuring-the-solver">
<h2><span class="section-number">9.1. </span>Configuring the solver<a class="headerlink" href="#Configuring-the-solver" title="Link to this heading"></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>
</section>
<section id="Training-and-solving-new-instances">
<h2><span class="section-number">9.2. </span>Training and solving new instances<a class="headerlink" href="#Training-and-solving-new-instances" title="Link to this heading"></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>
</section>
<section id="Complete-example">
<h2><span class="section-number">9.3. </span>Complete example<a class="headerlink" href="#Complete-example" title="Link to this heading"></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="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="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
<span class="kn">from</span> <span class="nn">scipy.stats</span> <span class="kn">import</span> <span class="n">uniform</span><span class="p">,</span> <span class="n">randint</span>
<span class="kn">from</span> <span class="nn">sklearn.linear_model</span> <span class="kn">import</span> <span class="n">LogisticRegression</span>
<span class="kn">from</span> <span class="nn">miplearn.classifiers.minprob</span> <span class="kn">import</span> <span class="n">MinProbabilityClassifier</span>
<span class="kn">from</span> <span class="nn">miplearn.classifiers.singleclass</span> <span class="kn">import</span> <span class="n">SingleClassFix</span>
<span class="kn">from</span> <span class="nn">miplearn.collectors.basic</span> <span class="kn">import</span> <span class="n">BasicCollector</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.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">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="n">TravelingSalesmanGenerator</span><span class="p">,</span>
<span class="n">build_tsp_model_gurobipy</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">miplearn.solvers.learning</span> <span class="kn">import</span> <span class="n">LearningSolver</span>
<span class="c1"># Set random seed to make example reproducible.</span>
<span class="n">random</span><span class="o">.</span><span class="n">seed</span><span class="p">(</span><span class="mi">42</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="mi">42</span><span class="p">)</span>
<span class="c1"># Generate a few instances of the traveling salesman problem.</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">TravelingSalesmanGenerator</span><span class="p">(</span>
<span class="n">n</span><span class="o">=</span><span class="n">randint</span><span class="p">(</span><span class="n">low</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">high</span><span class="o">=</span><span class="mi">11</span><span class="p">),</span>
<span class="n">x</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.0</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mf">1000.0</span><span class="p">),</span>
<span class="n">y</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.0</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mf">1000.0</span><span class="p">),</span>
<span class="n">gamma</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.90</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mf">0.20</span><span class="p">),</span>
<span class="n">fix_cities</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="nb">round</span><span class="o">=</span><span class="kc">True</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="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="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">test_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="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_tsp_model_gurobipy</span><span class="p">,</span> <span class="n">n_jobs</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
<span class="c1"># Build learning solver</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">IndependentVarsPrimalComponent</span><span class="p">(</span>
<span class="n">base_clf</span><span class="o">=</span><span class="n">SingleClassFix</span><span class="p">(</span>
<span class="n">MinProbabilityClassifier</span><span class="p">(</span>
<span class="n">base_clf</span><span class="o">=</span><span class="n">LogisticRegression</span><span class="p">(),</span>
<span class="n">thresholds</span><span class="o">=</span><span class="p">[</span><span class="mf">0.95</span><span class="p">,</span> <span class="mf">0.95</span><span class="p">],</span>
<span class="p">),</span>
<span class="p">),</span>
<span class="n">extractor</span><span class="o">=</span><span class="n">AlvLouWeh2017Extractor</span><span class="p">(),</span>
<span class="n">action</span><span class="o">=</span><span class="n">SetWarmStart</span><span class="p">(),</span>
<span class="p">)</span>
<span class="p">]</span>
<span class="p">)</span>
<span class="c1"># Train ML models</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="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_gurobipy</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="nboutput docutils container">
<div class="prompt empty docutils container">
</div>
<div class="output_area docutils container">
<div class="highlight"><pre>
Restricted license - for non-production use only - expires 2024-10-28
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 10 rows, 45 columns and 90 nonzeros
Model fingerprint: 0x6ddcd141
Coefficient statistics:
Matrix range [1e+00, 1e+00]
Objective range [4e+01, 1e+03]
Bounds range [1e+00, 1e+00]
RHS range [2e+00, 2e+00]
Presolve time: 0.00s
Presolved: 10 rows, 45 columns, 90 nonzeros
Iteration Objective Primal Inf. Dual Inf. Time
0 6.3600000e+02 1.700000e+01 0.000000e+00 0s
15 2.7610000e+03 0.000000e+00 0.000000e+00 0s
Solved in 15 iterations and 0.00 seconds (0.00 work units)
Optimal objective 2.761000000e+03
User-callback calls 56, time in user-callback 0.00 sec
Set parameter PreCrush to value 1
Set parameter LazyConstraints to value 1
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 10 rows, 45 columns and 90 nonzeros
Model fingerprint: 0x74ca3d0a
Variable types: 0 continuous, 45 integer (45 binary)
Coefficient statistics:
Matrix range [1e+00, 1e+00]
Objective range [4e+01, 1e+03]
Bounds range [1e+00, 1e+00]
RHS range [2e+00, 2e+00]
User MIP start produced solution with objective 2796 (0.00s)
Loaded user MIP start with objective 2796
Presolve time: 0.00s
Presolved: 10 rows, 45 columns, 90 nonzeros
Variable types: 0 continuous, 45 integer (45 binary)
Root relaxation: objective 2.761000e+03, 14 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 2761.00000 0 - 2796.00000 2761.00000 1.25% - 0s
0 0 cutoff 0 2796.00000 2796.00000 0.00% - 0s
Cutting planes:
Lazy constraints: 3
Explored 1 nodes (16 simplex iterations) in 0.01 seconds (0.00 work units)
Thread count was 20 (of 20 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 110, time in user-callback 0.00 sec
</pre></div></div>
</div>
<div class="nboutput nblast docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[1]:
</pre></div>
</div>
<div class="output_area docutils container">
<div class="highlight"><pre>
{&#39;WS: Count&#39;: 1, &#39;WS: Number of variables set&#39;: 41.0}
</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>
</section>
</section>
</div>
<div class='prev-next-bottom'>
<a class='left-prev' id="prev-link" href="../primal/" title="previous page"><span class="section-number">8. </span>Primal Components</a>
<a class='right-next' id="next-link" href="../../api/problems/" title="next page"><span class="section-number">10. </span>Benchmark Problems</a>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

@ -0,0 +1,386 @@
<!DOCTYPE html>
<html lang="en" data-content_root="./">
<head>
<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" />
<title>MIPLearn &#8212; MIPLearn 0.4</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" type="text/css" href="_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="_static/documentation_options.js?v=751a5dd3"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<link rel="index" title="Index" href="genindex/" />
<link rel="search" title="Search" href="search/" />
<link rel="next" title="1. Getting started (Pyomo)" href="tutorials/getting-started-pyomo/" />
<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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="tutorials/getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="tutorials/getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="tutorials/getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="tutorials/cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="guide/problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="guide/collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="guide/features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="guide/primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="guide/solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="api/problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="api/collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="api/components/">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="api/solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="api/helpers/">
14. 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/index.rst.txt"><button type="button"
class="btn btn-secondary topbarbtn" title="Download source file" data-toggle="tooltip"
data-placement="left">.rst</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="#contents">
Contents
</a>
<ul class="nav section-nav flex-column">
</ul>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#authors">
Authors
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#acknowledgments">
Acknowledgments
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#citing-miplearn">
Citing MIPLearn
</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>
<section id="miplearn">
<h1>MIPLearn<a class="headerlink" href="#miplearn" title="Link to this heading"></a></h1>
<p><strong>MIPLearn</strong> is an extensible framework for solving discrete optimization problems using a combination of Mixed-Integer Linear Programming (MIP) and Machine Learning (ML). MIPLearn uses ML methods to automatically identify patterns in previously solved instances of the problem, then uses these patterns to accelerate the performance of conventional state-of-the-art MIP solvers such as CPLEX, Gurobi or XPRESS.</p>
<p>Unlike pure ML methods, MIPLearn is not only able to find high-quality solutions to discrete optimization problems, but it can also prove the optimality and feasibility of these solutions. Unlike conventional MIP solvers, MIPLearn can take full advantage of very specific observations that happen to be true in a particular family of instances (such as the observation that a particular constraint is typically redundant, or that a particular variable typically assumes a certain value). For certain classes of problems, this approach may provide significant performance benefits.</p>
<section id="contents">
<h2>Contents<a class="headerlink" href="#contents" title="Link to this heading"></a></h2>
<div class="toctree-wrapper compound">
<p class="caption" role="heading"><span class="caption-text">Tutorials</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="tutorials/getting-started-pyomo/">1. Getting started (Pyomo)</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorials/getting-started-gurobipy/">2. Getting started (Gurobipy)</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorials/getting-started-jump/">3. Getting started (JuMP)</a></li>
<li class="toctree-l1"><a class="reference internal" href="tutorials/cuts-gurobipy/">4. User cuts and lazy constraints</a></li>
</ul>
</div>
<div class="toctree-wrapper compound">
<p class="caption" role="heading"><span class="caption-text">User Guide</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="guide/problems/">5. Benchmark Problems</a><ul>
<li class="toctree-l2"><a class="reference internal" href="guide/problems/#Overview">5.1. Overview</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/problems/#Bin-Packing">5.2. Bin Packing</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/problems/#Multi-Dimensional-Knapsack">5.3. Multi-Dimensional Knapsack</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/problems/#Capacitated-P-Median">5.4. Capacitated P-Median</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/problems/#Set-cover">5.5. Set cover</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/problems/#Set-Packing">5.6. Set Packing</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/problems/#Stable-Set">5.7. Stable Set</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/problems/#Traveling-Salesman">5.8. Traveling Salesman</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/problems/#Unit-Commitment">5.9. Unit Commitment</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/problems/#Vertex-Cover">5.10. Vertex Cover</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="guide/collectors/">6. Training Data Collectors</a><ul>
<li class="toctree-l2"><a class="reference internal" href="guide/collectors/#Overview">6.1. Overview</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/collectors/#HDF5-Format">6.2. HDF5 Format</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/collectors/#Basic-collector">6.3. Basic collector</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="guide/features/">7. Feature Extractors</a><ul>
<li class="toctree-l2"><a class="reference internal" href="guide/features/#Overview">7.1. Overview</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/features/#H5FieldsExtractor">7.2. H5FieldsExtractor</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/features/#AlvLouWeh2017Extractor">7.3. AlvLouWeh2017Extractor</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="guide/primal/">8. Primal Components</a><ul>
<li class="toctree-l2"><a class="reference internal" href="guide/primal/#Primal-component-actions">8.1. Primal component actions</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/primal/#Memorizing-primal-component">8.2. Memorizing primal component</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/primal/#Independent-vars-primal-component">8.3. Independent vars primal component</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/primal/#Joint-vars-primal-component">8.4. Joint vars primal component</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/primal/#Expert-primal-component">8.5. Expert primal component</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="guide/solvers/">9. Learning Solver</a><ul>
<li class="toctree-l2"><a class="reference internal" href="guide/solvers/#Configuring-the-solver">9.1. Configuring the solver</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/solvers/#Training-and-solving-new-instances">9.2. Training and solving new instances</a></li>
<li class="toctree-l2"><a class="reference internal" href="guide/solvers/#Complete-example">9.3. Complete example</a></li>
</ul>
</li>
</ul>
</div>
<div class="toctree-wrapper compound">
<p class="caption" role="heading"><span class="caption-text">Python API Reference</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="api/problems/">10. Benchmark Problems</a></li>
<li class="toctree-l1"><a class="reference internal" href="api/collectors/">11. Collectors &amp; Extractors</a></li>
<li class="toctree-l1"><a class="reference internal" href="api/components/">12. Components</a></li>
<li class="toctree-l1"><a class="reference internal" href="api/solvers/">13. Solvers</a></li>
<li class="toctree-l1"><a class="reference internal" href="api/helpers/">14. Helpers</a></li>
</ul>
</div>
</section>
<section id="authors">
<h2>Authors<a class="headerlink" href="#authors" title="Link to this heading"></a></h2>
<ul class="simple">
<li><p><strong>Alinson S. Xavier</strong> (Argonne National Laboratory)</p></li>
<li><p><strong>Feng Qiu</strong> (Argonne National Laboratory)</p></li>
<li><p><strong>Xiaoyi Gu</strong> (Georgia Institute of Technology)</p></li>
<li><p><strong>Berkay Becu</strong> (Georgia Institute of Technology)</p></li>
<li><p><strong>Santanu S. Dey</strong> (Georgia Institute of Technology)</p></li>
</ul>
</section>
<section id="acknowledgments">
<h2>Acknowledgments<a class="headerlink" href="#acknowledgments" title="Link to this heading"></a></h2>
<ul class="simple">
<li><p>Based upon work supported by <strong>Laboratory Directed Research and Development</strong> (LDRD) funding from Argonne National Laboratory, provided by the Director, Office of Science, of the U.S. Department of Energy.</p></li>
<li><p>Based upon work supported by the <strong>U.S. Department of Energy Advanced Grid Modeling Program</strong>.</p></li>
</ul>
</section>
<section id="citing-miplearn">
<h2>Citing MIPLearn<a class="headerlink" href="#citing-miplearn" title="Link to this heading"></a></h2>
<p>If you use MIPLearn in your research (either the solver or the included problem generators), we kindly request that you cite the package as follows:</p>
<ul class="simple">
<li><p><strong>Alinson S. Xavier, Feng Qiu, Xiaoyi Gu, Berkay Becu, Santanu S. Dey.</strong> <em>MIPLearn: An Extensible Framework for Learning-Enhanced Optimization (Version 0.3)</em>. Zenodo (2023). DOI: <a class="reference external" href="https://doi.org/10.5281/zenodo.4287567">https://doi.org/10.5281/zenodo.4287567</a></p></li>
</ul>
<p>If you use MIPLearn in the field of power systems optimization, we kindly request that you cite the reference below, in which the main techniques implemented in MIPLearn were first developed:</p>
<ul class="simple">
<li><p><strong>Alinson S. Xavier, Feng Qiu, Shabbir Ahmed.</strong> <em>Learning to Solve Large-Scale Unit Commitment Problems.</em> INFORMS Journal on Computing (2020). DOI: <a class="reference external" href="https://doi.org/10.1287/ijoc.2020.0976">https://doi.org/10.1287/ijoc.2020.0976</a></p></li>
</ul>
</section>
</section>
</div>
<div class='prev-next-bottom'>
<a class='right-next' id="next-link" href="tutorials/getting-started-pyomo/" title="next page"><span class="section-number">1. </span>Getting started (Pyomo)</a>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

Binary file not shown.

@ -0,0 +1,378 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Python Module Index &#8212; MIPLearn 0.4</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" type="text/css" href="../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../_static/doctools.js?v=888ff710"></script>
<script src="../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../tutorials/getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../tutorials/getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../tutorials/getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../tutorials/cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../guide/problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../guide/collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../guide/features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../guide/primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../guide/solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../api/problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../api/collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../api/components/">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../api/solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../api/helpers/">
14. 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>
<!-- 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>
</div>
</div>
<div id="main-content" class="row">
<div class="col-12 col-md-9 pl-md-3 pr-md-0">
<div>
<h1>Python Module Index</h1>
<div class="modindex-jumpbox">
<a href="#cap-m"><strong>m</strong></a>
</div>
<table class="indextable modindextable">
<tr class="pcap"><td></td><td>&#160;</td><td></td></tr>
<tr class="cap" id="cap-m"><td></td><td>
<strong>m</strong></td><td></td></tr>
<tr>
<td><img src="../_static/minus.png" class="toggler"
id="toggle-1" style="display: none" alt="-" /></td>
<td>
<code class="xref">miplearn</code></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/collectors/#module-miplearn.classifiers.minprob"><code class="xref">miplearn.classifiers.minprob</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/collectors/#module-miplearn.classifiers.singleclass"><code class="xref">miplearn.classifiers.singleclass</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/collectors/#module-miplearn.collectors.basic"><code class="xref">miplearn.collectors.basic</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/components/#module-miplearn.components.primal.actions"><code class="xref">miplearn.components.primal.actions</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/components/#module-miplearn.components.primal.expert"><code class="xref">miplearn.components.primal.expert</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/components/#module-miplearn.components.primal.indep"><code class="xref">miplearn.components.primal.indep</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/components/#module-miplearn.components.primal.joint"><code class="xref">miplearn.components.primal.joint</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/components/#module-miplearn.components.primal.mem"><code class="xref">miplearn.components.primal.mem</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/collectors/#module-miplearn.extractors.AlvLouWeh2017"><code class="xref">miplearn.extractors.AlvLouWeh2017</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/collectors/#module-miplearn.extractors.fields"><code class="xref">miplearn.extractors.fields</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/helpers/#module-miplearn.h5"><code class="xref">miplearn.h5</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/helpers/#module-miplearn.io"><code class="xref">miplearn.io</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/problems/#module-miplearn.problems.binpack"><code class="xref">miplearn.problems.binpack</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/problems/#module-miplearn.problems.multiknapsack"><code class="xref">miplearn.problems.multiknapsack</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/problems/#module-miplearn.problems.pmedian"><code class="xref">miplearn.problems.pmedian</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/problems/#module-miplearn.problems.setcover"><code class="xref">miplearn.problems.setcover</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/problems/#module-miplearn.problems.setpack"><code class="xref">miplearn.problems.setpack</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/problems/#module-miplearn.problems.stab"><code class="xref">miplearn.problems.stab</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/problems/#module-miplearn.problems.tsp"><code class="xref">miplearn.problems.tsp</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/problems/#module-miplearn.problems.uc"><code class="xref">miplearn.problems.uc</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/problems/#module-miplearn.problems.vertexcover"><code class="xref">miplearn.problems.vertexcover</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/solvers/#module-miplearn.solvers.abstract"><code class="xref">miplearn.solvers.abstract</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/solvers/#module-miplearn.solvers.gurobi"><code class="xref">miplearn.solvers.gurobi</code></a></td><td>
<em></em></td></tr>
<tr class="cg-1">
<td></td>
<td>&#160;&#160;&#160;
<a href="../api/solvers/#module-miplearn.solvers.learning"><code class="xref">miplearn.solvers.learning</code></a></td><td>
<em></em></td></tr>
</table>
</div>
<div class='prev-next-bottom'>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

@ -0,0 +1,272 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Search &#8212; MIPLearn 0.4</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" type="text/css" href="../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../_static/doctools.js?v=888ff710"></script>
<script src="../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<script src="../_static/searchtools.js"></script>
<script src="../_static/language_data.js"></script>
<link rel="index" title="Index" href="../genindex/" />
<link rel="search" title="Search" href="#" />
<script src="../searchindex.js" defer></script>
<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.4</h1>
</a>
</div><form class="bd-search d-flex align-items-center" action="#" 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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../tutorials/getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../tutorials/getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../tutorials/getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../tutorials/cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../guide/problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../guide/collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../guide/features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../guide/primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../guide/solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../api/problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../api/collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../api/components/">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../api/solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../api/helpers/">
14. 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>
<!-- 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>
</div>
</div>
<div id="main-content" class="row">
<div class="col-12 col-md-9 pl-md-3 pr-md-0">
<div>
<h1 id="search-documentation">Search</h1>
<noscript>
<div class="admonition warning">
<p>
Please activate JavaScript to enable the search
functionality.
</p>
</div>
</noscript>
<p>
Searching for multiple words only shows matches that contain
all words.
</p>
<form action="" method="get">
<input type="text" name="q" aria-labelledby="search-documentation" value="" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="search" />
<span id="search-progress" style="padding-left: 10px"></span>
</form>
<div id="search-results">
</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-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

@ -0,0 +1,541 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "b4bd8bd6-3ce9-4932-852f-f98a44120a3e",
"metadata": {},
"source": [
"# User cuts and lazy constraints\n",
"\n",
"User cuts and lazy constraints are two advanced mixed-integer programming techniques that can accelerate solver performance. User cuts are additional constraints, derived from the constraints already in the model, that can tighten the feasible region and eliminate fractional solutions, thus reducing the size of the branch-and-bound tree. Lazy constraints, on the other hand, are constraints that are potentially part of the problem formulation but are omitted from the initial model to reduce its size; these constraints are added to the formulation only once the solver finds a solution that violates them. While both techniques have been successful, significant computational effort may still be required to generate strong user cuts and to identify violated lazy constraints, which can reduce their effectiveness.\n",
"\n",
"MIPLearn is able to predict which user cuts and which lazy constraints to enforce at the beginning of the optimization process, using machine learning. In this tutorial, we will use the framework to predict subtour elimination constraints for the **traveling salesman problem** using Gurobipy. We assume that MIPLearn has already been correctly installed.\n",
"\n",
"<div class=\"alert alert-info\">\n",
"\n",
"Solver Compatibility\n",
"\n",
"User cuts and lazy constraints are also supported in the Python/Pyomo and Julia/JuMP versions of the package. See the source code of <code>build_tsp_model_pyomo</code> and <code>build_tsp_model_jump</code> for more details. Note, however, the following limitations:\n",
"\n",
"- Python/Pyomo: Only `gurobi_persistent` is currently supported. PRs implementing callbacks for other persistent solvers are welcome.\n",
"- Julia/JuMP: Only solvers supporting solver-independent callbacks are supported. As of JuMP 1.19, this includes Gurobi, CPLEX, XPRESS, SCIP and GLPK. Note that HiGHS and Cbc are not supported. As newer versions of JuMP implement further callback support, MIPLearn should become automatically compatible with these solvers.\n",
"\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "72229e1f-cbd8-43f0-82ee-17d6ec9c3b7d",
"metadata": {},
"source": [
"## Modeling the traveling salesman problem\n",
"\n",
"Given a list of cities and the distances between them, the **traveling salesman problem (TSP)** asks for the shortest route starting at the first city, visiting each other city exactly once, then returning to the first city. This problem is a generalization of the Hamiltonian path problem, one of Karp's 21 NP-complete problems, and has many practical applications, including routing delivery trucks and scheduling airline routes.\n",
"\n",
"To describe an instance of TSP, we need to specify the number of cities $n$, and an $n \\times n$ matrix of distances. The class `TravelingSalesmanData`, in the `miplearn.problems.tsp` package, can hold this data:"
]
},
{
"cell_type": "markdown",
"id": "4598a1bc-55b6-48cc-a050-2262786c203a",
"metadata": {},
"source": [
"```python\n",
"@dataclass\r\n",
"class TravelingSalesmanData:\r\n",
" n_cities: int\r\n",
" distances: np.ndarray\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "3a43cc12-1207-4247-bdb2-69a6a2910738",
"metadata": {},
"source": [
"MIPLearn also provides `TravelingSalesmandGenerator`, a random generator for TSP instances, and `build_tsp_model_gurobipy`, a function which converts `TravelingSalesmanData` into an actual gurobipy optimization model, and which uses lazy constraints to enforce subtour elimination.\n",
"\n",
"The example below is a simplified and annotated version of `build_tsp_model_gurobipy`, illustrating the usage of callbacks with MIPLearn. Compared the the previous tutorial examples, note that, in addition to defining the variables, objective function and constraints of our problem, we also define two callback functions `lazy_separate` and `lazy_enforce`."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "e4712a85-0327-439c-8889-933e1ff714e7",
"metadata": {},
"outputs": [],
"source": [
"import gurobipy as gp\n",
"from gurobipy import quicksum, GRB, tuplelist\n",
"from miplearn.solvers.gurobi import GurobiModel\n",
"import networkx as nx\n",
"import numpy as np\n",
"from miplearn.problems.tsp import (\n",
" TravelingSalesmanData,\n",
" TravelingSalesmanGenerator,\n",
")\n",
"from scipy.stats import uniform, randint\n",
"from miplearn.io import write_pkl_gz, read_pkl_gz\n",
"from miplearn.collectors.basic import BasicCollector\n",
"from miplearn.solvers.learning import LearningSolver\n",
"from miplearn.components.lazy.mem import MemorizingLazyComponent\n",
"from miplearn.extractors.fields import H5FieldsExtractor\n",
"from sklearn.neighbors import KNeighborsClassifier\n",
"\n",
"# Set up random seed to make example more reproducible\n",
"np.random.seed(42)\n",
"\n",
"# Set up Python logging\n",
"import logging\n",
"\n",
"logging.basicConfig(level=logging.WARNING)\n",
"\n",
"\n",
"def build_tsp_model_gurobipy_simplified(data):\n",
" # Read data from file if a filename is provided\n",
" if isinstance(data, str):\n",
" data = read_pkl_gz(data)\n",
"\n",
" # Create empty gurobipy model\n",
" model = gp.Model()\n",
"\n",
" # Create set of edges between every pair of cities, for convenience\n",
" edges = tuplelist(\n",
" (i, j) for i in range(data.n_cities) for j in range(i + 1, data.n_cities)\n",
" )\n",
"\n",
" # Add binary variable x[e] for each edge e\n",
" x = model.addVars(edges, vtype=GRB.BINARY, name=\"x\")\n",
"\n",
" # Add objective function\n",
" model.setObjective(quicksum(x[(i, j)] * data.distances[i, j] for (i, j) in edges))\n",
"\n",
" # Add constraint: must choose two edges adjacent to each city\n",
" model.addConstrs(\n",
" (\n",
" quicksum(x[min(i, j), max(i, j)] for j in range(data.n_cities) if i != j)\n",
" == 2\n",
" for i in range(data.n_cities)\n",
" ),\n",
" name=\"eq_degree\",\n",
" )\n",
"\n",
" def lazy_separate(m: GurobiModel):\n",
" \"\"\"\n",
" Callback function that finds subtours in the current solution.\n",
" \"\"\"\n",
" # Query current value of the x variables\n",
" x_val = m.inner.cbGetSolution(x)\n",
"\n",
" # Initialize empty set of violations\n",
" violations = []\n",
"\n",
" # Build set of edges we have currently selected\n",
" selected_edges = [e for e in edges if x_val[e] > 0.5]\n",
"\n",
" # Build a graph containing the selected edges, using networkx\n",
" graph = nx.Graph()\n",
" graph.add_edges_from(selected_edges)\n",
"\n",
" # For each component of the graph\n",
" for component in list(nx.connected_components(graph)):\n",
"\n",
" # If the component is not the entire graph, we found a\n",
" # subtour. Add the edge cut to the list of violations.\n",
" if len(component) < data.n_cities:\n",
" cut_edges = [\n",
" [e[0], e[1]]\n",
" for e in edges\n",
" if (e[0] in component and e[1] not in component)\n",
" or (e[0] not in component and e[1] in component)\n",
" ]\n",
" violations.append(cut_edges)\n",
"\n",
" # Return the list of violations\n",
" return violations\n",
"\n",
" def lazy_enforce(m: GurobiModel, violations) -> None:\n",
" \"\"\"\n",
" Callback function that, given a list of subtours, adds lazy\n",
" constraints to remove them from the feasible region.\n",
" \"\"\"\n",
" print(f\"Enforcing {len(violations)} subtour elimination constraints\")\n",
" for violation in violations:\n",
" m.add_constr(quicksum(x[e[0], e[1]] for e in violation) >= 2)\n",
"\n",
" return GurobiModel(\n",
" model,\n",
" lazy_separate=lazy_separate,\n",
" lazy_enforce=lazy_enforce,\n",
" )"
]
},
{
"cell_type": "markdown",
"id": "58875042-d6ac-4f93-b3cc-9a5822b11dad",
"metadata": {},
"source": [
"The `lazy_separate` function starts by querying the current fractional solution value through `m.inner.cbGetSolution` (recall that `m.inner` is a regular gurobipy model), then finds the set of violated lazy constraints. Unlike a regular lazy constraint solver callback, note that `lazy_separate` does not add the violated constraints to the model; it simply returns a list of objects that uniquely identifies the set of lazy constraints that should be generated. Enforcing the constraints is the responsbility of the second callback function, `lazy_enforce`. This function takes as input the model and the list of violations found by `lazy_separate`, converts them into actual constraints, and adds them to the model through `m.add_constr`.\n",
"\n",
"During training data generation, MIPLearn calls `lazy_separate` and `lazy_enforce` in sequence, inside a regular solver callback. However, once the machine learning models are trained, MIPLearn calls `lazy_enforce` directly, before the optimization process starts, with a list of **predicted** violations, as we will see in the example below."
]
},
{
"cell_type": "markdown",
"id": "5839728e-406c-4be2-ba81-83f2b873d4b2",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
"\n",
"Constraint Representation\n",
"\n",
"How should user cuts and lazy constraints be represented is a decision that the user can make; MIPLearn is representation agnostic. The objects returned by `lazy_separate`, however, are serialized as JSON and stored in the HDF5 training data files. Therefore, it is recommended to use only simple objects, such as lists, tuples and dictionaries.\n",
"\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "847ae32e-fad7-406a-8797-0d79065a07fd",
"metadata": {},
"source": [
"## Generating training data\n",
"\n",
"To test the callback defined above, we generate a small set of TSP instances, using the provided random instance generator. As in the previous tutorial, we generate some test instances and some training instances, then solve them using `BasicCollector`. Input problem data is stored in `tsp/train/00000.pkl.gz, ...`, whereas solver training data (including list of required lazy constraints) is stored in `tsp/train/00000.h5, ...`."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "eb63154a-1fa6-4eac-aa46-6838b9c201f6",
"metadata": {},
"outputs": [],
"source": [
"# Configure generator to produce instances with 50 cities located\n",
"# in the 1000 x 1000 square, and with slightly perturbed distances.\n",
"gen = TravelingSalesmanGenerator(\n",
" x=uniform(loc=0.0, scale=1000.0),\n",
" y=uniform(loc=0.0, scale=1000.0),\n",
" n=randint(low=50, high=51),\n",
" gamma=uniform(loc=1.0, scale=0.25),\n",
" fix_cities=True,\n",
" round=True,\n",
")\n",
"\n",
"# Generate 500 instances and store input data file to .pkl.gz files\n",
"data = gen.generate(500)\n",
"train_data = write_pkl_gz(data[0:450], \"tsp/train\")\n",
"test_data = write_pkl_gz(data[450:500], \"tsp/test\")\n",
"\n",
"# Solve the training instances in parallel, collecting the required lazy\n",
"# constraints, in addition to other information, such as optimal solution.\n",
"bc = BasicCollector()\n",
"bc.collect(train_data, build_tsp_model_gurobipy_simplified, n_jobs=10)"
]
},
{
"cell_type": "markdown",
"id": "6903c26c-dbe0-4a2e-bced-fdbf93513dde",
"metadata": {},
"source": [
"## Training and solving new instances"
]
},
{
"cell_type": "markdown",
"id": "57cd724a-2d27-4698-a1e6-9ab8345ef31f",
"metadata": {},
"source": [
"After producing the training dataset, we can train the machine learning models to predict which lazy constraints are necessary. In this tutorial, we use the following ML strategy: given a new instance, find the 50 most similar ones in the training dataset and verify how often each lazy constraint was required. If a lazy constraint was required for the majority of the 50 most-similar instances, enforce it ahead-of-time for the current instance. To measure instance similarity, use the objective function only. This ML strategy can be implemented using `MemorizingLazyComponent` with `H5FieldsExtractor` and `KNeighborsClassifier`, as shown below."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "43779e3d-4174-4189-bc75-9f564910e212",
"metadata": {},
"outputs": [],
"source": [
"solver = LearningSolver(\n",
" components=[\n",
" MemorizingLazyComponent(\n",
" extractor=H5FieldsExtractor(instance_fields=[\"static_var_obj_coeffs\"]),\n",
" clf=KNeighborsClassifier(n_neighbors=100),\n",
" ),\n",
" ],\n",
")\n",
"solver.fit(train_data)"
]
},
{
"cell_type": "markdown",
"id": "12480712-9d3d-4cbc-a6d7-d6c1e2f950f4",
"metadata": {},
"source": [
"Next, we solve one of the test instances using the trained solver. In the run below, we can see that MIPLearn adds many lazy constraints ahead-of-time, before the optimization starts. During the optimization process itself, some additional lazy constraints are required, but very few."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "23f904ad-f1a8-4b5a-81ae-c0b9e813a4b2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter Threads to value 1\n",
"Restricted license - for non-production use only - expires 2024-10-28\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Optimize a model with 50 rows, 1225 columns and 2450 nonzeros\n",
"Model fingerprint: 0x04d7bec1\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+00]\n",
" Objective range [1e+01, 1e+03]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+00, 2e+00]\n",
"Presolve time: 0.00s\n",
"Presolved: 50 rows, 1225 columns, 2450 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 4.0600000e+02 9.700000e+01 0.000000e+00 0s\n",
" 66 5.5880000e+03 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 66 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 5.588000000e+03\n",
"\n",
"User-callback calls 107, time in user-callback 0.00 sec\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:miplearn.components.cuts.mem:Predicting violated lazy constraints...\n",
"INFO:miplearn.components.lazy.mem:Enforcing 19 constraints ahead-of-time...\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Enforcing 19 subtour elimination constraints\n",
"Set parameter PreCrush to value 1\n",
"Set parameter LazyConstraints to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Optimize a model with 69 rows, 1225 columns and 6091 nonzeros\n",
"Model fingerprint: 0x09bd34d6\n",
"Variable types: 0 continuous, 1225 integer (1225 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+00]\n",
" Objective range [1e+01, 1e+03]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+00, 2e+00]\n",
"Found heuristic solution: objective 29853.000000\n",
"Presolve time: 0.00s\n",
"Presolved: 69 rows, 1225 columns, 6091 nonzeros\n",
"Variable types: 0 continuous, 1225 integer (1225 binary)\n",
"\n",
"Root relaxation: objective 6.139000e+03, 93 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 6139.00000 0 6 29853.0000 6139.00000 79.4% - 0s\n",
"H 0 0 6390.0000000 6139.00000 3.93% - 0s\n",
" 0 0 6165.50000 0 10 6390.00000 6165.50000 3.51% - 0s\n",
"Enforcing 3 subtour elimination constraints\n",
" 0 0 6165.50000 0 6 6390.00000 6165.50000 3.51% - 0s\n",
" 0 0 6198.50000 0 16 6390.00000 6198.50000 3.00% - 0s\n",
"* 0 0 0 6219.0000000 6219.00000 0.00% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 11\n",
" MIR: 1\n",
" Zero half: 4\n",
" Lazy constraints: 3\n",
"\n",
"Explored 1 nodes (222 simplex iterations) in 0.03 seconds (0.02 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"\n",
"Solution count 3: 6219 6390 29853 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 6.219000000000e+03, best bound 6.219000000000e+03, gap 0.0000%\n",
"\n",
"User-callback calls 141, time in user-callback 0.00 sec\n"
]
}
],
"source": [
"# Increase log verbosity, so that we can see what is MIPLearn doing\n",
"logging.getLogger(\"miplearn\").setLevel(logging.INFO)\n",
"\n",
"# Solve a new test instance\n",
"solver.optimize(test_data[0], build_tsp_model_gurobipy_simplified);"
]
},
{
"cell_type": "markdown",
"id": "79cc3e61-ee2b-4f18-82cb-373d55d67de6",
"metadata": {},
"source": [
"Finally, we solve the same instance, but using a regular solver, without ML prediction. We can see that a much larger number of lazy constraints are added during the optimization process itself. Additionally, the solver requires a larger number of iterations to find the optimal solution. There is not a significant difference in running time because of the small size of these instances."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "a015c51c-091a-43b6-b761-9f3577fc083e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Optimize a model with 50 rows, 1225 columns and 2450 nonzeros\n",
"Model fingerprint: 0x04d7bec1\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+00]\n",
" Objective range [1e+01, 1e+03]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+00, 2e+00]\n",
"Presolve time: 0.00s\n",
"Presolved: 50 rows, 1225 columns, 2450 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 4.0600000e+02 9.700000e+01 0.000000e+00 0s\n",
" 66 5.5880000e+03 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 66 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 5.588000000e+03\n",
"\n",
"User-callback calls 107, time in user-callback 0.00 sec\n",
"Set parameter PreCrush to value 1\n",
"Set parameter LazyConstraints to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 1 threads\n",
"\n",
"Optimize a model with 50 rows, 1225 columns and 2450 nonzeros\n",
"Model fingerprint: 0x77a94572\n",
"Variable types: 0 continuous, 1225 integer (1225 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+00]\n",
" Objective range [1e+01, 1e+03]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [2e+00, 2e+00]\n",
"Found heuristic solution: objective 29695.000000\n",
"Presolve time: 0.00s\n",
"Presolved: 50 rows, 1225 columns, 2450 nonzeros\n",
"Variable types: 0 continuous, 1225 integer (1225 binary)\n",
"\n",
"Root relaxation: objective 5.588000e+03, 68 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 5588.00000 0 12 29695.0000 5588.00000 81.2% - 0s\n",
"Enforcing 9 subtour elimination constraints\n",
"Enforcing 11 subtour elimination constraints\n",
"H 0 0 27241.000000 5588.00000 79.5% - 0s\n",
" 0 0 5898.00000 0 8 27241.0000 5898.00000 78.3% - 0s\n",
"Enforcing 4 subtour elimination constraints\n",
"Enforcing 3 subtour elimination constraints\n",
" 0 0 6066.00000 0 - 27241.0000 6066.00000 77.7% - 0s\n",
"Enforcing 2 subtour elimination constraints\n",
" 0 0 6128.00000 0 - 27241.0000 6128.00000 77.5% - 0s\n",
" 0 0 6139.00000 0 6 27241.0000 6139.00000 77.5% - 0s\n",
"H 0 0 6368.0000000 6139.00000 3.60% - 0s\n",
" 0 0 6154.75000 0 15 6368.00000 6154.75000 3.35% - 0s\n",
"Enforcing 2 subtour elimination constraints\n",
" 0 0 6154.75000 0 6 6368.00000 6154.75000 3.35% - 0s\n",
" 0 0 6165.75000 0 11 6368.00000 6165.75000 3.18% - 0s\n",
"Enforcing 3 subtour elimination constraints\n",
" 0 0 6204.00000 0 6 6368.00000 6204.00000 2.58% - 0s\n",
"* 0 0 0 6219.0000000 6219.00000 0.00% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 5\n",
" MIR: 1\n",
" Zero half: 4\n",
" Lazy constraints: 4\n",
"\n",
"Explored 1 nodes (224 simplex iterations) in 0.10 seconds (0.03 work units)\n",
"Thread count was 1 (of 20 available processors)\n",
"\n",
"Solution count 4: 6219 6368 27241 29695 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 6.219000000000e+03, best bound 6.219000000000e+03, gap 0.0000%\n",
"\n",
"User-callback calls 170, time in user-callback 0.01 sec\n"
]
}
],
"source": [
"solver = LearningSolver(components=[]) # empty set of ML components\n",
"solver.optimize(test_data[0], build_tsp_model_gurobipy_simplified);"
]
},
{
"cell_type": "markdown",
"id": "432c99b2-67fe-409b-8224-ccef91de96d1",
"metadata": {},
"source": [
"## Learning user cuts\n",
"\n",
"The example above focused on lazy constraints. To enforce user cuts instead, the procedure is very similar, with the following changes:\n",
"\n",
"- Instead of `lazy_separate` and `lazy_enforce`, use `cuts_separate` and `cuts_enforce`\n",
"- Instead of `m.inner.cbGetSolution`, use `m.inner.cbGetNodeRel`\n",
"\n",
"For a complete example, see `build_stab_model_gurobipy`, `build_stab_model_pyomo` and `build_stab_model_jump`, which solves the maximum-weight stable set problem using user cut callbacks."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e6cb694d-8c43-410f-9a13-01bf9e0763b7",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,714 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<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" />
<title>4. User cuts and lazy constraints &#8212; MIPLearn 0.4</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" type="text/css" href="../../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css?v=2aa19091" />
<link rel="stylesheet" type="text/css" href="../../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script>
<script>window.MathJax = {"tex": {"inlineMath": [["$", "$"], ["\\(", "\\)"]], "processEscapes": true}, "options": {"ignoreHtmlClass": "tex2jax_ignore|mathjax_ignore|document", "processHtmlClass": "tex2jax_process|mathjax_process|math|output_area"}}</script>
<script defer="defer" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<link rel="index" title="Index" href="../../genindex/" />
<link rel="search" title="Search" href="../../search/" />
<link rel="next" title="5. Benchmark Problems" href="../../guide/problems/" />
<link rel="prev" title="3. Getting started (JuMP)" href="../getting-started-jump/" />
<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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="current nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1 current active">
<a class="current reference internal" href="#">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../guide/problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../api/problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/components/">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/helpers/">
14. 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/cuts-gurobipy.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="#Modeling-the-traveling-salesman-problem">
4.1. Modeling the traveling salesman problem
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Generating-training-data">
4.2. Generating training data
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Training-and-solving-new-instances">
4.3. Training and solving new instances
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Learning-user-cuts">
4.4. Learning user cuts
</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>
<section id="User-cuts-and-lazy-constraints">
<h1><span class="section-number">4. </span>User cuts and lazy constraints<a class="headerlink" href="#User-cuts-and-lazy-constraints" title="Link to this heading"></a></h1>
<p>User cuts and lazy constraints are two advanced mixed-integer programming techniques that can accelerate solver performance. User cuts are additional constraints, derived from the constraints already in the model, that can tighten the feasible region and eliminate fractional solutions, thus reducing the size of the branch-and-bound tree. Lazy constraints, on the other hand, are constraints that are potentially part of the problem formulation but are omitted from the initial model to reduce its
size; these constraints are added to the formulation only once the solver finds a solution that violates them. While both techniques have been successful, significant computational effort may still be required to generate strong user cuts and to identify violated lazy constraints, which can reduce their effectiveness.</p>
<p>MIPLearn is able to predict which user cuts and which lazy constraints to enforce at the beginning of the optimization process, using machine learning. In this tutorial, we will use the framework to predict subtour elimination constraints for the <strong>traveling salesman problem</strong> using Gurobipy. We assume that MIPLearn has already been correctly installed.</p>
<div class="admonition note">
<p class="admonition-title">Solver Compatibility</p>
<p>User cuts and lazy constraints are also supported in the Python/Pyomo and Julia/JuMP versions of the package. See the source code of build_tsp_model_pyomo and build_tsp_model_jump for more details. Note, however, the following limitations:</p>
<ul class="simple">
<li><p>Python/Pyomo: Only <code class="docutils literal notranslate"><span class="pre">gurobi_persistent</span></code> is currently supported. PRs implementing callbacks for other persistent solvers are welcome.</p></li>
<li><p>Julia/JuMP: Only solvers supporting solver-independent callbacks are supported. As of JuMP 1.19, this includes Gurobi, CPLEX, XPRESS, SCIP and GLPK. Note that HiGHS and Cbc are not supported. As newer versions of JuMP implement further callback support, MIPLearn should become automatically compatible with these solvers.</p></li>
</ul>
</div>
<section id="Modeling-the-traveling-salesman-problem">
<h2><span class="section-number">4.1. </span>Modeling the traveling salesman problem<a class="headerlink" href="#Modeling-the-traveling-salesman-problem" title="Link to this heading"></a></h2>
<p>Given a list of cities and the distances between them, the <strong>traveling salesman problem (TSP)</strong> asks for the shortest route starting at the first city, visiting each other city exactly once, then returning to the first city. This problem is a generalization of the Hamiltonian path problem, one of Karps 21 NP-complete problems, and has many practical applications, including routing delivery trucks and scheduling airline routes.</p>
<p>To describe an instance of TSP, we need to specify the number of cities <span class="math notranslate nohighlight">\(n\)</span>, and an <span class="math notranslate nohighlight">\(n \times n\)</span> matrix of distances. The class <code class="docutils literal notranslate"><span class="pre">TravelingSalesmanData</span></code>, in the <code class="docutils literal notranslate"><span class="pre">miplearn.problems.tsp</span></code> package, can hold this data:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="nd">@dataclass</span>
<span class="k">class</span> <span class="nc">TravelingSalesmanData</span><span class="p">:</span>
<span class="n">n_cities</span><span class="p">:</span> <span class="nb">int</span>
<span class="n">distances</span><span class="p">:</span> <span class="n">np</span><span class="o">.</span><span class="n">ndarray</span>
</pre></div>
</div>
<p>MIPLearn also provides <code class="docutils literal notranslate"><span class="pre">TravelingSalesmandGenerator</span></code>, a random generator for TSP instances, and <code class="docutils literal notranslate"><span class="pre">build_tsp_model_gurobipy</span></code>, a function which converts <code class="docutils literal notranslate"><span class="pre">TravelingSalesmanData</span></code> into an actual gurobipy optimization model, and which uses lazy constraints to enforce subtour elimination.</p>
<p>The example below is a simplified and annotated version of <code class="docutils literal notranslate"><span class="pre">build_tsp_model_gurobipy</span></code>, illustrating the usage of callbacks with MIPLearn. Compared the the previous tutorial examples, note that, in addition to defining the variables, objective function and constraints of our problem, we also define two callback functions <code class="docutils literal notranslate"><span class="pre">lazy_separate</span></code> and <code class="docutils literal notranslate"><span class="pre">lazy_enforce</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="kn">import</span> <span class="nn">gurobipy</span> <span class="k">as</span> <span class="nn">gp</span>
<span class="kn">from</span> <span class="nn">gurobipy</span> <span class="kn">import</span> <span class="n">quicksum</span><span class="p">,</span> <span class="n">GRB</span><span class="p">,</span> <span class="n">tuplelist</span>
<span class="kn">from</span> <span class="nn">miplearn.solvers.gurobi</span> <span class="kn">import</span> <span class="n">GurobiModel</span>
<span class="kn">import</span> <span class="nn">networkx</span> <span class="k">as</span> <span class="nn">nx</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</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">TravelingSalesmanData</span><span class="p">,</span>
<span class="n">TravelingSalesmanGenerator</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">scipy.stats</span> <span class="kn">import</span> <span class="n">uniform</span><span class="p">,</span> <span class="n">randint</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="p">,</span> <span class="n">read_pkl_gz</span>
<span class="kn">from</span> <span class="nn">miplearn.collectors.basic</span> <span class="kn">import</span> <span class="n">BasicCollector</span>
<span class="kn">from</span> <span class="nn">miplearn.solvers.learning</span> <span class="kn">import</span> <span class="n">LearningSolver</span>
<span class="kn">from</span> <span class="nn">miplearn.components.lazy.mem</span> <span class="kn">import</span> <span class="n">MemorizingLazyComponent</span>
<span class="kn">from</span> <span class="nn">miplearn.extractors.fields</span> <span class="kn">import</span> <span class="n">H5FieldsExtractor</span>
<span class="kn">from</span> <span class="nn">sklearn.neighbors</span> <span class="kn">import</span> <span class="n">KNeighborsClassifier</span>
<span class="c1"># Set up random seed to make example more reproducible</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="mi">42</span><span class="p">)</span>
<span class="c1"># Set up Python logging</span>
<span class="kn">import</span> <span class="nn">logging</span>
<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">logging</span><span class="o">.</span><span class="n">WARNING</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">build_tsp_model_gurobipy_simplified</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
<span class="c1"># Read data from file if a filename is provided</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">read_pkl_gz</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="c1"># Create empty gurobipy model</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">gp</span><span class="o">.</span><span class="n">Model</span><span class="p">()</span>
<span class="c1"># Create set of edges between every pair of cities, for convenience</span>
<span class="n">edges</span> <span class="o">=</span> <span class="n">tuplelist</span><span class="p">(</span>
<span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">j</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">data</span><span class="o">.</span><span class="n">n_cities</span><span class="p">)</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">data</span><span class="o">.</span><span class="n">n_cities</span><span class="p">)</span>
<span class="p">)</span>
<span class="c1"># Add binary variable x[e] for each edge e</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">addVars</span><span class="p">(</span><span class="n">edges</span><span class="p">,</span> <span class="n">vtype</span><span class="o">=</span><span class="n">GRB</span><span class="o">.</span><span class="n">BINARY</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;x&quot;</span><span class="p">)</span>
<span class="c1"># Add objective function</span>
<span class="n">model</span><span class="o">.</span><span class="n">setObjective</span><span class="p">(</span><span class="n">quicksum</span><span class="p">(</span><span class="n">x</span><span class="p">[(</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">)]</span> <span class="o">*</span> <span class="n">data</span><span class="o">.</span><span class="n">distances</span><span class="p">[</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">]</span> <span class="k">for</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">)</span> <span class="ow">in</span> <span class="n">edges</span><span class="p">))</span>
<span class="c1"># Add constraint: must choose two edges adjacent to each city</span>
<span class="n">model</span><span class="o">.</span><span class="n">addConstrs</span><span class="p">(</span>
<span class="p">(</span>
<span class="n">quicksum</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="nb">min</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">),</span> <span class="nb">max</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">)]</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">n_cities</span><span class="p">)</span> <span class="k">if</span> <span class="n">i</span> <span class="o">!=</span> <span class="n">j</span><span class="p">)</span>
<span class="o">==</span> <span class="mi">2</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">data</span><span class="o">.</span><span class="n">n_cities</span><span class="p">)</span>
<span class="p">),</span>
<span class="n">name</span><span class="o">=</span><span class="s2">&quot;eq_degree&quot;</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">lazy_separate</span><span class="p">(</span><span class="n">m</span><span class="p">:</span> <span class="n">GurobiModel</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Callback function that finds subtours in the current solution.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="c1"># Query current value of the x variables</span>
<span class="n">x_val</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">inner</span><span class="o">.</span><span class="n">cbGetSolution</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="c1"># Initialize empty set of violations</span>
<span class="n">violations</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># Build set of edges we have currently selected</span>
<span class="n">selected_edges</span> <span class="o">=</span> <span class="p">[</span><span class="n">e</span> <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">edges</span> <span class="k">if</span> <span class="n">x_val</span><span class="p">[</span><span class="n">e</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mf">0.5</span><span class="p">]</span>
<span class="c1"># Build a graph containing the selected edges, using networkx</span>
<span class="n">graph</span> <span class="o">=</span> <span class="n">nx</span><span class="o">.</span><span class="n">Graph</span><span class="p">()</span>
<span class="n">graph</span><span class="o">.</span><span class="n">add_edges_from</span><span class="p">(</span><span class="n">selected_edges</span><span class="p">)</span>
<span class="c1"># For each component of the graph</span>
<span class="k">for</span> <span class="n">component</span> <span class="ow">in</span> <span class="nb">list</span><span class="p">(</span><span class="n">nx</span><span class="o">.</span><span class="n">connected_components</span><span class="p">(</span><span class="n">graph</span><span class="p">)):</span>
<span class="c1"># If the component is not the entire graph, we found a</span>
<span class="c1"># subtour. Add the edge cut to the list of violations.</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">component</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">data</span><span class="o">.</span><span class="n">n_cities</span><span class="p">:</span>
<span class="n">cut_edges</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">[</span><span class="n">e</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">e</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span>
<span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">edges</span>
<span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">in</span> <span class="n">component</span> <span class="ow">and</span> <span class="n">e</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">component</span><span class="p">)</span>
<span class="ow">or</span> <span class="p">(</span><span class="n">e</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">component</span> <span class="ow">and</span> <span class="n">e</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="ow">in</span> <span class="n">component</span><span class="p">)</span>
<span class="p">]</span>
<span class="n">violations</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">cut_edges</span><span class="p">)</span>
<span class="c1"># Return the list of violations</span>
<span class="k">return</span> <span class="n">violations</span>
<span class="k">def</span> <span class="nf">lazy_enforce</span><span class="p">(</span><span class="n">m</span><span class="p">:</span> <span class="n">GurobiModel</span><span class="p">,</span> <span class="n">violations</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Callback function that, given a list of subtours, adds lazy</span>
<span class="sd"> constraints to remove them from the feasible region.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Enforcing </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">violations</span><span class="p">)</span><span class="si">}</span><span class="s2"> subtour elimination constraints&quot;</span><span class="p">)</span>
<span class="k">for</span> <span class="n">violation</span> <span class="ow">in</span> <span class="n">violations</span><span class="p">:</span>
<span class="n">m</span><span class="o">.</span><span class="n">add_constr</span><span class="p">(</span><span class="n">quicksum</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="n">e</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">e</span><span class="p">[</span><span class="mi">1</span><span class="p">]]</span> <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">violation</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mi">2</span><span class="p">)</span>
<span class="k">return</span> <span class="n">GurobiModel</span><span class="p">(</span>
<span class="n">model</span><span class="p">,</span>
<span class="n">lazy_separate</span><span class="o">=</span><span class="n">lazy_separate</span><span class="p">,</span>
<span class="n">lazy_enforce</span><span class="o">=</span><span class="n">lazy_enforce</span><span class="p">,</span>
<span class="p">)</span>
</pre></div>
</div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">lazy_separate</span></code> function starts by querying the current fractional solution value through <code class="docutils literal notranslate"><span class="pre">m.inner.cbGetSolution</span></code> (recall that <code class="docutils literal notranslate"><span class="pre">m.inner</span></code> is a regular gurobipy model), then finds the set of violated lazy constraints. Unlike a regular lazy constraint solver callback, note that <code class="docutils literal notranslate"><span class="pre">lazy_separate</span></code> does not add the violated constraints to the model; it simply returns a list of objects that uniquely identifies the set of lazy constraints that should be generated. Enforcing the constraints is
the responsbility of the second callback function, <code class="docutils literal notranslate"><span class="pre">lazy_enforce</span></code>. This function takes as input the model and the list of violations found by <code class="docutils literal notranslate"><span class="pre">lazy_separate</span></code>, converts them into actual constraints, and adds them to the model through <code class="docutils literal notranslate"><span class="pre">m.add_constr</span></code>.</p>
<p>During training data generation, MIPLearn calls <code class="docutils literal notranslate"><span class="pre">lazy_separate</span></code> and <code class="docutils literal notranslate"><span class="pre">lazy_enforce</span></code> in sequence, inside a regular solver callback. However, once the machine learning models are trained, MIPLearn calls <code class="docutils literal notranslate"><span class="pre">lazy_enforce</span></code> directly, before the optimization process starts, with a list of <strong>predicted</strong> violations, as we will see in the example below.</p>
<div class="admonition note">
<p class="admonition-title">Constraint Representation</p>
<p>How should user cuts and lazy constraints be represented is a decision that the user can make; MIPLearn is representation agnostic. The objects returned by <code class="docutils literal notranslate"><span class="pre">lazy_separate</span></code>, however, are serialized as JSON and stored in the HDF5 training data files. Therefore, it is recommended to use only simple objects, such as lists, tuples and dictionaries.</p>
</div>
</section>
<section id="Generating-training-data">
<h2><span class="section-number">4.2. </span>Generating training data<a class="headerlink" href="#Generating-training-data" title="Link to this heading"></a></h2>
<p>To test the callback defined above, we generate a small set of TSP instances, using the provided random instance generator. As in the previous tutorial, we generate some test instances and some training instances, then solve them using <code class="docutils literal notranslate"><span class="pre">BasicCollector</span></code>. Input problem data is stored in <code class="docutils literal notranslate"><span class="pre">tsp/train/00000.pkl.gz,</span> <span class="pre">...</span></code>, whereas solver training data (including list of required lazy constraints) is stored in <code class="docutils literal notranslate"><span class="pre">tsp/train/00000.h5,</span> <span class="pre">...</span></code>.</p>
<div class="nbinput nblast 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="c1"># Configure generator to produce instances with 50 cities located</span>
<span class="c1"># in the 1000 x 1000 square, and with slightly perturbed distances.</span>
<span class="n">gen</span> <span class="o">=</span> <span class="n">TravelingSalesmanGenerator</span><span class="p">(</span>
<span class="n">x</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.0</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mf">1000.0</span><span class="p">),</span>
<span class="n">y</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.0</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="mf">1000.0</span><span class="p">),</span>
<span class="n">n</span><span class="o">=</span><span class="n">randint</span><span class="p">(</span><span class="n">low</span><span class="o">=</span><span class="mi">50</span><span class="p">,</span> <span class="n">high</span><span class="o">=</span><span class="mi">51</span><span class="p">),</span>
<span class="n">gamma</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.0</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="n">fix_cities</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="nb">round</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># Generate 500 instances and store input data file to .pkl.gz files</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">gen</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="mi">500</span><span class="p">)</span>
<span class="n">train_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="mi">0</span><span class="p">:</span><span class="mi">450</span><span class="p">],</span> <span class="s2">&quot;tsp/train&quot;</span><span class="p">)</span>
<span class="n">test_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="mi">450</span><span class="p">:</span><span class="mi">500</span><span class="p">],</span> <span class="s2">&quot;tsp/test&quot;</span><span class="p">)</span>
<span class="c1"># Solve the training instances in parallel, collecting the required lazy</span>
<span class="c1"># constraints, in addition to other information, such as optimal solution.</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_tsp_model_gurobipy_simplified</span><span class="p">,</span> <span class="n">n_jobs</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
</pre></div>
</div>
</div>
</section>
<section id="Training-and-solving-new-instances">
<h2><span class="section-number">4.3. </span>Training and solving new instances<a class="headerlink" href="#Training-and-solving-new-instances" title="Link to this heading"></a></h2>
<p>After producing the training dataset, we can train the machine learning models to predict which lazy constraints are necessary. In this tutorial, we use the following ML strategy: given a new instance, find the 50 most similar ones in the training dataset and verify how often each lazy constraint was required. If a lazy constraint was required for the majority of the 50 most-similar instances, enforce it ahead-of-time for the current instance. To measure instance similarity, use the objective
function only. This ML strategy can be implemented using <code class="docutils literal notranslate"><span class="pre">MemorizingLazyComponent</span></code> with <code class="docutils literal notranslate"><span class="pre">H5FieldsExtractor</span></code> and <code class="docutils literal notranslate"><span class="pre">KNeighborsClassifier</span></code>, as shown below.</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="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">MemorizingLazyComponent</span><span class="p">(</span>
<span class="n">extractor</span><span class="o">=</span><span class="n">H5FieldsExtractor</span><span class="p">(</span><span class="n">instance_fields</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;static_var_obj_coeffs&quot;</span><span class="p">]),</span>
<span class="n">clf</span><span class="o">=</span><span class="n">KNeighborsClassifier</span><span class="p">(</span><span class="n">n_neighbors</span><span class="o">=</span><span class="mi">100</span><span class="p">),</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">fit</span><span class="p">(</span><span class="n">train_data</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>Next, we solve one of the test instances using the trained solver. In the run below, we can see that MIPLearn adds many lazy constraints ahead-of-time, before the optimization starts. During the optimization process itself, some additional lazy constraints are required, but very few.</p>
<div class="nbinput 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="c1"># Increase log verbosity, so that we can see what is MIPLearn doing</span>
<span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">&quot;miplearn&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">)</span>
<span class="c1"># Solve a new 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_gurobipy_simplified</span><span class="p">);</span>
</pre></div>
</div>
</div>
<div class="nboutput 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
Restricted license - for non-production use only - expires 2024-10-28
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 1 threads
Optimize a model with 50 rows, 1225 columns and 2450 nonzeros
Model fingerprint: 0x04d7bec1
Coefficient statistics:
Matrix range [1e+00, 1e+00]
Objective range [1e+01, 1e+03]
Bounds range [1e+00, 1e+00]
RHS range [2e+00, 2e+00]
Presolve time: 0.00s
Presolved: 50 rows, 1225 columns, 2450 nonzeros
Iteration Objective Primal Inf. Dual Inf. Time
0 4.0600000e+02 9.700000e+01 0.000000e+00 0s
66 5.5880000e+03 0.000000e+00 0.000000e+00 0s
Solved in 66 iterations and 0.01 seconds (0.00 work units)
Optimal objective 5.588000000e+03
User-callback calls 107, time in user-callback 0.00 sec
</pre></div></div>
</div>
<div class="nboutput docutils container">
<div class="prompt empty docutils container">
</div>
<div class="output_area stderr docutils container">
<div class="highlight"><pre>
INFO:miplearn.components.cuts.mem:Predicting violated lazy constraints...
INFO:miplearn.components.lazy.mem:Enforcing 19 constraints ahead-of-time...
</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>
Enforcing 19 subtour elimination constraints
Set parameter PreCrush to value 1
Set parameter LazyConstraints to value 1
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 1 threads
Optimize a model with 69 rows, 1225 columns and 6091 nonzeros
Model fingerprint: 0x09bd34d6
Variable types: 0 continuous, 1225 integer (1225 binary)
Coefficient statistics:
Matrix range [1e+00, 1e+00]
Objective range [1e+01, 1e+03]
Bounds range [1e+00, 1e+00]
RHS range [2e+00, 2e+00]
Found heuristic solution: objective 29853.000000
Presolve time: 0.00s
Presolved: 69 rows, 1225 columns, 6091 nonzeros
Variable types: 0 continuous, 1225 integer (1225 binary)
Root relaxation: objective 6.139000e+03, 93 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 6139.00000 0 6 29853.0000 6139.00000 79.4% - 0s
H 0 0 6390.0000000 6139.00000 3.93% - 0s
0 0 6165.50000 0 10 6390.00000 6165.50000 3.51% - 0s
Enforcing 3 subtour elimination constraints
0 0 6165.50000 0 6 6390.00000 6165.50000 3.51% - 0s
0 0 6198.50000 0 16 6390.00000 6198.50000 3.00% - 0s
* 0 0 0 6219.0000000 6219.00000 0.00% - 0s
Cutting planes:
Gomory: 11
MIR: 1
Zero half: 4
Lazy constraints: 3
Explored 1 nodes (222 simplex iterations) in 0.03 seconds (0.02 work units)
Thread count was 1 (of 20 available processors)
Solution count 3: 6219 6390 29853
Optimal solution found (tolerance 1.00e-04)
Best objective 6.219000000000e+03, best bound 6.219000000000e+03, gap 0.0000%
User-callback calls 141, time in user-callback 0.00 sec
</pre></div></div>
</div>
<p>Finally, we solve the same instance, but using a regular solver, without ML prediction. We can see that a much larger number of lazy constraints are added during the optimization process itself. Additionally, the solver requires a larger number of iterations to find the optimal solution. There is not a significant difference in running time because of the small size of these instances.</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">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="c1"># empty set of ML components</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_gurobipy_simplified</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>
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 1 threads
Optimize a model with 50 rows, 1225 columns and 2450 nonzeros
Model fingerprint: 0x04d7bec1
Coefficient statistics:
Matrix range [1e+00, 1e+00]
Objective range [1e+01, 1e+03]
Bounds range [1e+00, 1e+00]
RHS range [2e+00, 2e+00]
Presolve time: 0.00s
Presolved: 50 rows, 1225 columns, 2450 nonzeros
Iteration Objective Primal Inf. Dual Inf. Time
0 4.0600000e+02 9.700000e+01 0.000000e+00 0s
66 5.5880000e+03 0.000000e+00 0.000000e+00 0s
Solved in 66 iterations and 0.01 seconds (0.00 work units)
Optimal objective 5.588000000e+03
User-callback calls 107, time in user-callback 0.00 sec
Set parameter PreCrush to value 1
Set parameter LazyConstraints to value 1
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 1 threads
Optimize a model with 50 rows, 1225 columns and 2450 nonzeros
Model fingerprint: 0x77a94572
Variable types: 0 continuous, 1225 integer (1225 binary)
Coefficient statistics:
Matrix range [1e+00, 1e+00]
Objective range [1e+01, 1e+03]
Bounds range [1e+00, 1e+00]
RHS range [2e+00, 2e+00]
Found heuristic solution: objective 29695.000000
Presolve time: 0.00s
Presolved: 50 rows, 1225 columns, 2450 nonzeros
Variable types: 0 continuous, 1225 integer (1225 binary)
Root relaxation: objective 5.588000e+03, 68 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 5588.00000 0 12 29695.0000 5588.00000 81.2% - 0s
Enforcing 9 subtour elimination constraints
Enforcing 11 subtour elimination constraints
H 0 0 27241.000000 5588.00000 79.5% - 0s
0 0 5898.00000 0 8 27241.0000 5898.00000 78.3% - 0s
Enforcing 4 subtour elimination constraints
Enforcing 3 subtour elimination constraints
0 0 6066.00000 0 - 27241.0000 6066.00000 77.7% - 0s
Enforcing 2 subtour elimination constraints
0 0 6128.00000 0 - 27241.0000 6128.00000 77.5% - 0s
0 0 6139.00000 0 6 27241.0000 6139.00000 77.5% - 0s
H 0 0 6368.0000000 6139.00000 3.60% - 0s
0 0 6154.75000 0 15 6368.00000 6154.75000 3.35% - 0s
Enforcing 2 subtour elimination constraints
0 0 6154.75000 0 6 6368.00000 6154.75000 3.35% - 0s
0 0 6165.75000 0 11 6368.00000 6165.75000 3.18% - 0s
Enforcing 3 subtour elimination constraints
0 0 6204.00000 0 6 6368.00000 6204.00000 2.58% - 0s
* 0 0 0 6219.0000000 6219.00000 0.00% - 0s
Cutting planes:
Gomory: 5
MIR: 1
Zero half: 4
Lazy constraints: 4
Explored 1 nodes (224 simplex iterations) in 0.10 seconds (0.03 work units)
Thread count was 1 (of 20 available processors)
Solution count 4: 6219 6368 27241 29695
Optimal solution found (tolerance 1.00e-04)
Best objective 6.219000000000e+03, best bound 6.219000000000e+03, gap 0.0000%
User-callback calls 170, time in user-callback 0.01 sec
</pre></div></div>
</div>
</section>
<section id="Learning-user-cuts">
<h2><span class="section-number">4.4. </span>Learning user cuts<a class="headerlink" href="#Learning-user-cuts" title="Link to this heading"></a></h2>
<p>The example above focused on lazy constraints. To enforce user cuts instead, the procedure is very similar, with the following changes:</p>
<ul class="simple">
<li><p>Instead of <code class="docutils literal notranslate"><span class="pre">lazy_separate</span></code> and <code class="docutils literal notranslate"><span class="pre">lazy_enforce</span></code>, use <code class="docutils literal notranslate"><span class="pre">cuts_separate</span></code> and <code class="docutils literal notranslate"><span class="pre">cuts_enforce</span></code></p></li>
<li><p>Instead of <code class="docutils literal notranslate"><span class="pre">m.inner.cbGetSolution</span></code>, use <code class="docutils literal notranslate"><span class="pre">m.inner.cbGetNodeRel</span></code></p></li>
</ul>
<p>For a complete example, see <code class="docutils literal notranslate"><span class="pre">build_stab_model_gurobipy</span></code>, <code class="docutils literal notranslate"><span class="pre">build_stab_model_pyomo</span></code> and <code class="docutils literal notranslate"><span class="pre">build_stab_model_jump</span></code>, which solves the maximum-weight stable set problem using user cut callbacks.</p>
<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>
</section>
</section>
</div>
<div class='prev-next-bottom'>
<a class='left-prev' id="prev-link" href="../getting-started-jump/" title="previous page"><span class="section-number">3. </span>Getting started (JuMP)</a>
<a class='right-next' id="next-link" href="../../guide/problems/" title="next page"><span class="section-number">5. </span>Benchmark Problems</a>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

@ -0,0 +1,837 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "6b8983b1",
"metadata": {
"tags": []
},
"source": [
"# Getting started (Gurobipy)\n",
"\n",
"## Introduction\n",
"\n",
"**MIPLearn** is an open source framework that uses machine learning (ML) to accelerate the performance of mixed-integer programming solvers (e.g. Gurobi, CPLEX, XPRESS). In this tutorial, we will:\n",
"\n",
"1. Install the Python/Gurobipy version of MIPLearn\n",
"2. Model a simple optimization problem using Gurobipy\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/Gurobipy version of MIPLearn is only compatible with the Gurobi Optimizer. For broader solver compatibility, see the Python/Pyomo and Julia/JuMP versions 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"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "02f0a927",
"metadata": {},
"source": [
"## Installation\n",
"\n",
"MIPLearn is available in two versions:\n",
"\n",
"- Python version, compatible with the Pyomo and Gurobipy modeling languages,\n",
"- Julia version, compatible with the JuMP modeling language.\n",
"\n",
"In this tutorial, we will demonstrate how to use and install the Python/Gurobipy version of the package. The first step is to install Python 3.8+ in your computer. See the [official Python website for more instructions](https://www.python.org/downloads/). After Python is installed, we proceed to install MIPLearn using `pip`:\n",
"\n",
"```\n",
"$ pip install MIPLearn==0.3\n",
"```\n",
"\n",
"In addition to MIPLearn itself, we will also install Gurobi 10.0, a state-of-the-art commercial MILP solver. This step also install a demo license for Gurobi, which should able to solve the small optimization problems in this tutorial. A license is required for solving larger-scale problems.\n",
"\n",
"```\n",
"$ pip install 'gurobipy>=10,<10.1'\n",
"```"
]
},
{
"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 a utility company needs to decide which electrical generators should be online at each hour of the day, as well as how much power should each generator produce. More specifically, assume that the 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 the 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. The total amount of power to be produced needs to be exactly equal to the total demand $d$ (in megawatts).\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:"
]
},
{
"cell_type": "markdown",
"id": "f12c3702",
"metadata": {},
"source": [
"$$\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",
"$$"
]
},
{
"cell_type": "markdown",
"id": "be3989ed",
"metadata": {},
"source": [
"<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.\n",
"\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "a5fd33f6",
"metadata": {},
"source": [
"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": 1,
"id": "22a67170-10b4-43d3-8708-014d91141e73",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:18:25.442346786Z",
"start_time": "2023-06-06T20:18:25.329017476Z"
},
"tags": []
},
"outputs": [],
"source": [
"from dataclasses import dataclass\n",
"from typing import List\n",
"\n",
"import numpy as np\n",
"\n",
"\n",
"@dataclass\n",
"class UnitCommitmentData:\n",
" demand: float\n",
" pmin: List[float]\n",
" pmax: List[float]\n",
" cfix: List[float]\n",
" cvar: List[float]"
]
},
{
"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. The function accepts `UnitCommitmentData`, the data structure we previously defined, or the path to a compressed pickle file containing this data."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "2f67032f-0d74-4317-b45c-19da0ec859e9",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:48:05.953902842Z",
"start_time": "2023-06-06T20:48:05.909747925Z"
}
},
"outputs": [],
"source": [
"import gurobipy as gp\n",
"from gurobipy import GRB, quicksum\n",
"from typing import Union\n",
"from miplearn.io import read_pkl_gz\n",
"from miplearn.solvers.gurobi import GurobiModel\n",
"\n",
"\n",
"def build_uc_model(data: Union[str, UnitCommitmentData]) -> GurobiModel:\n",
" if isinstance(data, str):\n",
" data = read_pkl_gz(data)\n",
"\n",
" model = gp.Model()\n",
" n = len(data.pmin)\n",
" x = model._x = model.addVars(n, vtype=GRB.BINARY, name=\"x\")\n",
" y = model._y = model.addVars(n, name=\"y\")\n",
" model.setObjective(\n",
" quicksum(data.cfix[i] * x[i] + data.cvar[i] * y[i] for i in range(n))\n",
" )\n",
" model.addConstrs(y[i] <= data.pmax[i] * x[i] for i in range(n))\n",
" model.addConstrs(y[i] >= data.pmin[i] * x[i] for i in range(n))\n",
" model.addConstr(quicksum(y[i] for i in range(n)) == data.demand)\n",
" return GurobiModel(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": 3,
"id": "2a896f47",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:49:14.266758244Z",
"start_time": "2023-06-06T20:49:14.223514806Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Restricted license - for non-production use only - expires 2024-10-28\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 7 rows, 6 columns and 15 nonzeros\n",
"Model fingerprint: 0x58dfdd53\n",
"Variable types: 3 continuous, 3 integer (3 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 7e+01]\n",
" Objective range [2e+00, 7e+02]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [1e+02, 1e+02]\n",
"Presolve removed 2 rows and 1 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 5 rows, 5 columns, 13 nonzeros\n",
"Variable types: 0 continuous, 5 integer (3 binary)\n",
"Found heuristic solution: objective 1400.0000000\n",
"\n",
"Root relaxation: objective 1.035000e+03, 3 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 1035.00000 0 1 1400.00000 1035.00000 26.1% - 0s\n",
" 0 0 1105.71429 0 1 1400.00000 1105.71429 21.0% - 0s\n",
"* 0 0 0 1320.0000000 1320.00000 0.00% - 0s\n",
"\n",
"Explored 1 nodes (5 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 2: 1320 1400 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 1.320000000000e+03, best bound 1.320000000000e+03, gap 0.0000%\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",
"model.optimize()\n",
"print(\"obj =\", model.inner.objVal)\n",
"print(\"x =\", [model.inner._x[i].x for i in range(3)])\n",
"print(\"y =\", [model.inner._y[i].x 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": "01f576e1-1790-425e-9e5c-9fa07b6f4c26",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
" \n",
"Note\n",
"\n",
"- In the example above, `GurobiModel` is just a thin wrapper around a standard Gurobi model. This wrapper allows MIPLearn to be solver- and modeling-language-agnostic. The wrapper provides only a few basic methods, such as `optimize`. For more control, and to query the solution, the original Gurobi model can be accessed through `model.inner`, as illustrated above.\n",
"- To ensure training data consistency, MIPLearn requires all decision variables to have names.\n",
"</div>"
]
},
{
"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** solver, which can optimize 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": 4,
"id": "5eb09fab",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:49:22.758192368Z",
"start_time": "2023-06-06T20:49:22.724784572Z"
}
},
"outputs": [],
"source": [
"from scipy.stats import uniform\n",
"from typing import List\n",
"import random\n",
"\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 _ 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 on multiple machines. 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": 5,
"id": "6156752c",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:49:24.811192929Z",
"start_time": "2023-06-06T20:49:24.575639142Z"
}
},
"outputs": [],
"source": [
"from miplearn.io import write_pkl_gz\n",
"\n",
"data = random_uc_data(samples=500, n=500)\n",
"train_data = write_pkl_gz(data[0:450], \"uc/train\")\n",
"test_data = write_pkl_gz(data[450:500], \"uc/test\")"
]
},
{
"cell_type": "markdown",
"id": "b17af877",
"metadata": {},
"source": [
"Finally, we use `BasicCollector` to collect the optimal solutions and other useful training data for all training instances. The data is stored in HDF5 files `uc/train/00000.h5`, `uc/train/00001.h5`, etc. The optimization models are also exported to compressed MPS files `uc/train/00000.mps.gz`, `uc/train/00001.mps.gz`, etc."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "7623f002",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:49:34.936729253Z",
"start_time": "2023-06-06T20:49:25.936126612Z"
}
},
"outputs": [],
"source": [
"from miplearn.collectors.basic import BasicCollector\n",
"\n",
"bc = BasicCollector()\n",
"bc.collect(train_data, build_uc_model, n_jobs=4)"
]
},
{
"cell_type": "markdown",
"id": "c42b1be1-9723-4827-82d8-974afa51ef9f",
"metadata": {},
"source": [
"## Training and solving test instances"
]
},
{
"cell_type": "markdown",
"id": "a33c6aa4-f0b8-4ccb-9935-01f7d7de2a1c",
"metadata": {},
"source": [
"With training data in hand, we can now design and train a machine learning model to accelerate solver performance. In this tutorial, for illustration purposes, we will use ML to generate a good warm start using $k$-nearest neighbors. More specifically, the strategy is to:\n",
"\n",
"1. Memorize the optimal solutions of all training instances;\n",
"2. Given a test instance, find the 25 most similar training instances, based on constraint right-hand sides;\n",
"3. Merge their optimal solutions into a single partial solution; specifically, only assign values to the binary variables that agree unanimously.\n",
"4. Provide this partial solution to the solver as a warm start.\n",
"\n",
"This simple strategy can be implemented as shown below, using `MemorizingPrimalComponent`. For more advanced strategies, and for the usage of more advanced classifiers, see the user guide."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "435f7bf8-4b09-4889-b1ec-b7b56e7d8ed2",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:49:38.997939600Z",
"start_time": "2023-06-06T20:49:38.968261432Z"
}
},
"outputs": [],
"source": [
"from sklearn.neighbors import KNeighborsClassifier\n",
"from miplearn.components.primal.actions import SetWarmStart\n",
"from miplearn.components.primal.mem import (\n",
" MemorizingPrimalComponent,\n",
" MergeTopSolutions,\n",
")\n",
"from miplearn.extractors.fields import H5FieldsExtractor\n",
"\n",
"comp = MemorizingPrimalComponent(\n",
" clf=KNeighborsClassifier(n_neighbors=25),\n",
" extractor=H5FieldsExtractor(\n",
" instance_fields=[\"static_constr_rhs\"],\n",
" ),\n",
" constructor=MergeTopSolutions(25, [0.0, 1.0]),\n",
" action=SetWarmStart(),\n",
")"
]
},
{
"cell_type": "markdown",
"id": "9536e7e4-0b0d-49b0-bebd-4a848f839e94",
"metadata": {},
"source": [
"Having defined the ML strategy, we next construct `LearningSolver`, train the ML component and optimize one of the test instances."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "9d13dd50-3dcf-4673-a757-6f44dcc0dedf",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:49:42.072345411Z",
"start_time": "2023-06-06T20:49:41.294040974Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0xa8b70287\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 [3e+08, 3e+08]\n",
"Presolve removed 1000 rows and 500 columns\n",
"Presolve time: 0.01s\n",
"Presolved: 1 rows, 500 columns, 500 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 6.6166537e+09 5.648803e+04 0.000000e+00 0s\n",
" 1 8.2906219e+09 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.290621916e+09\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0xcf27855a\n",
"Variable types: 500 continuous, 500 integer (500 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 [3e+08, 3e+08]\n",
"\n",
"User MIP start produced solution with objective 8.29153e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.29153e+09 (0.01s)\n",
"Loaded user MIP start with objective 8.29153e+09\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"\n",
"Root relaxation: objective 8.290622e+09, 512 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 8.2906e+09 0 1 8.2915e+09 8.2906e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 3 8.2915e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2915e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 2 8.2915e+09 8.2907e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 1\n",
" Flow cover: 2\n",
"\n",
"Explored 1 nodes (565 simplex iterations) in 0.03 seconds (0.01 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 1: 8.29153e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.291528276179e+09, best bound 8.290733258025e+09, gap 0.0096%\n"
]
},
{
"data": {
"text/plain": [
"{'WS: Count': 1, 'WS: Number of variables set': 482.0}"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from miplearn.solvers.learning import LearningSolver\n",
"\n",
"solver_ml = LearningSolver(components=[comp])\n",
"solver_ml.fit(train_data)\n",
"solver_ml.optimize(test_data[0], build_uc_model)"
]
},
{
"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 very close to the optimal solution to the problem. Now let us repeat the code above, but a solver which does not apply any ML strategies. Note that our previously-defined component is not provided."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "2ff391ed-e855-4228-aa09-a7641d8c2893",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:49:44.012782276Z",
"start_time": "2023-06-06T20:49:43.813974362Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0xa8b70287\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 [3e+08, 3e+08]\n",
"Presolve removed 1000 rows and 500 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1 rows, 500 columns, 500 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 6.6166537e+09 5.648803e+04 0.000000e+00 0s\n",
" 1 8.2906219e+09 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.290621916e+09\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x4cbbf7c7\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"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 [3e+08, 3e+08]\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"Found heuristic solution: objective 9.757128e+09\n",
"\n",
"Root relaxation: objective 8.290622e+09, 512 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 8.2906e+09 0 1 9.7571e+09 8.2906e+09 15.0% - 0s\n",
"H 0 0 8.298273e+09 8.2906e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 4 8.2983e+09 8.2907e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2983e+09 8.2907e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 4 8.2983e+09 8.2907e+09 0.09% - 0s\n",
"H 0 0 8.293980e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 5 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 2 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 1 8.2940e+09 8.2908e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 4 8.2940e+09 8.2908e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 4 8.2940e+09 8.2908e+09 0.04% - 0s\n",
"H 0 0 8.291465e+09 8.2908e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 2\n",
" MIR: 1\n",
"\n",
"Explored 1 nodes (1031 simplex iterations) in 0.15 seconds (0.03 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 4: 8.29147e+09 8.29398e+09 8.29827e+09 9.75713e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.291465302389e+09, best bound 8.290781665333e+09, gap 0.0082%\n"
]
},
{
"data": {
"text/plain": [
"{}"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"solver_baseline = LearningSolver(components=[])\n",
"solver_baseline.fit(train_data)\n",
"solver_baseline.optimize(test_data[0], build_uc_model)"
]
},
{
"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, but the difference can be significant for larger problems."
]
},
{
"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. 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": 10,
"id": "67a6cd18",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:50:12.869892930Z",
"start_time": "2023-06-06T20:50:12.509410473Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x19042f12\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 [3e+08, 3e+08]\n",
"Presolve removed 1000 rows and 500 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1 rows, 500 columns, 500 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 6.5917580e+09 5.627453e+04 0.000000e+00 0s\n",
" 1 8.2535968e+09 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.253596777e+09\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0xf97cde91\n",
"Variable types: 500 continuous, 500 integer (500 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 [3e+08, 3e+08]\n",
"\n",
"User MIP start produced solution with objective 8.25814e+09 (0.00s)\n",
"User MIP start produced solution with objective 8.25512e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25483e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25483e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25483e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25459e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25459e+09 (0.01s)\n",
"Loaded user MIP start with objective 8.25459e+09\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"\n",
"Root relaxation: objective 8.253597e+09, 512 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 8.2536e+09 0 1 8.2546e+09 8.2536e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 3 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 1 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 4 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 4 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2538e+09 0 4 8.2546e+09 8.2538e+09 0.01% - 0s\n",
" 0 0 8.2538e+09 0 5 8.2546e+09 8.2538e+09 0.01% - 0s\n",
" 0 0 8.2538e+09 0 6 8.2546e+09 8.2538e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Cover: 1\n",
" MIR: 2\n",
" StrongCG: 1\n",
" Flow cover: 1\n",
"\n",
"Explored 1 nodes (575 simplex iterations) in 0.05 seconds (0.01 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 4: 8.25459e+09 8.25483e+09 8.25512e+09 8.25814e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.254590409970e+09, best bound 8.253768093811e+09, gap 0.0100%\n",
"obj = 8254590409.969726\n",
"x = [1.0, 1.0, 0.0]\n",
"y = [935662.0949262811, 1604270.0218116897, 0.0]\n"
]
}
],
"source": [
"data = random_uc_data(samples=1, n=500)[0]\n",
"model = build_uc_model(data)\n",
"solver_ml.optimize(model)\n",
"print(\"obj =\", model.inner.objVal)\n",
"print(\"x =\", [model.inner._x[i].x for i in range(3)])\n",
"print(\"y =\", [model.inner._y[i].x for i in range(3)])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5593d23a-83bd-4e16-8253-6300f5e3f63b",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,888 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<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" />
<title>2. Getting started (Gurobipy) &#8212; MIPLearn 0.4</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" type="text/css" href="../../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css?v=2aa19091" />
<link rel="stylesheet" type="text/css" href="../../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script>
<script>window.MathJax = {"tex": {"inlineMath": [["$", "$"], ["\\(", "\\)"]], "processEscapes": true}, "options": {"ignoreHtmlClass": "tex2jax_ignore|mathjax_ignore|document", "processHtmlClass": "tex2jax_process|mathjax_process|math|output_area"}}</script>
<script defer="defer" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<link rel="index" title="Index" href="../../genindex/" />
<link rel="search" title="Search" href="../../search/" />
<link rel="next" title="3. Getting started (JuMP)" href="../getting-started-jump/" />
<link rel="prev" title="1. Getting started (Pyomo)" href="../getting-started-pyomo/" />
<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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="current nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1 current active">
<a class="current reference internal" href="#">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../guide/problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../api/problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/components/">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/helpers/">
14. 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-gurobipy.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">
2.1. Introduction
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Installation">
2.2. Installation
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Modeling-a-simple-optimization-problem">
2.3. 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">
2.4. Generating training data
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Training-and-solving-test-instances">
2.5. Training and solving test instances
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Accessing-the-solution">
2.6. 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>
<section id="Getting-started-(Gurobipy)">
<h1><span class="section-number">2. </span>Getting started (Gurobipy)<a class="headerlink" href="#Getting-started-(Gurobipy)" title="Link to this heading"></a></h1>
<section id="Introduction">
<h2><span class="section-number">2.1. </span>Introduction<a class="headerlink" href="#Introduction" title="Link to this heading"></a></h2>
<p><strong>MIPLearn</strong> is an open source framework that uses machine learning (ML) to accelerate the performance of mixed-integer programming solvers (e.g. Gurobi, CPLEX, XPRESS). In this tutorial, we will:</p>
<ol class="arabic simple">
<li><p>Install the Python/Gurobipy version of MIPLearn</p></li>
<li><p>Model a simple optimization problem using Gurobipy</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/Gurobipy version of MIPLearn is only compatible with the Gurobi Optimizer. For broader solver compatibility, see the Python/Pyomo and Julia/JuMP versions 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>
</section>
<section id="Installation">
<h2><span class="section-number">2.2. </span>Installation<a class="headerlink" href="#Installation" title="Link to this heading"></a></h2>
<p>MIPLearn is available in two versions:</p>
<ul class="simple">
<li><p>Python version, compatible with the Pyomo and Gurobipy modeling languages,</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/Gurobipy 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="highlight-none notranslate"><div class="highlight"><pre><span></span>$ pip install MIPLearn==0.3
</pre></div>
</div>
<p>In addition to MIPLearn itself, we will also install Gurobi 10.0, a state-of-the-art commercial MILP solver. This step also install a demo license for Gurobi, which should able to solve the small optimization problems in this tutorial. A license is required for solving larger-scale problems.</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>$ pip install &#39;gurobipy&gt;=10,&lt;10.1&#39;
</pre></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>
</section>
<section id="Modeling-a-simple-optimization-problem">
<h2><span class="section-number">2.3. </span>Modeling a simple optimization problem<a class="headerlink" href="#Modeling-a-simple-optimization-problem" title="Link to this heading"></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 a utility company needs to decide which electrical generators should be online at each hour of the day, as well as how much power should each generator produce. More specifically, assume that the 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 the 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. 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).</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.</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>[1]:
</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">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">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">List</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span>
<span class="n">pmax</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span>
<span class="n">cfix</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span>
<span class="n">cvar</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">float</span><span class="p">]</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. The function accepts <code class="docutils literal notranslate"><span class="pre">UnitCommitmentData</span></code>, the data structure we previously defined, or the path to a compressed pickle file containing this data.</p>
<div class="nbinput nblast 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="kn">import</span> <span class="nn">gurobipy</span> <span class="k">as</span> <span class="nn">gp</span>
<span class="kn">from</span> <span class="nn">gurobipy</span> <span class="kn">import</span> <span class="n">GRB</span><span class="p">,</span> <span class="n">quicksum</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Union</span>
<span class="kn">from</span> <span class="nn">miplearn.io</span> <span class="kn">import</span> <span class="n">read_pkl_gz</span>
<span class="kn">from</span> <span class="nn">miplearn.solvers.gurobi</span> <span class="kn">import</span> <span class="n">GurobiModel</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">Union</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">UnitCommitmentData</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">GurobiModel</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">read_pkl_gz</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">gp</span><span class="o">.</span><span class="n">Model</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">x</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">_x</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">addVars</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">vtype</span><span class="o">=</span><span class="n">GRB</span><span class="o">.</span><span class="n">BINARY</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;x&quot;</span><span class="p">)</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">_y</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">addVars</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">name</span><span class="o">=</span><span class="s2">&quot;y&quot;</span><span class="p">)</span>
<span class="n">model</span><span class="o">.</span><span class="n">setObjective</span><span class="p">(</span>
<span class="n">quicksum</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">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">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="n">model</span><span class="o">.</span><span class="n">addConstrs</span><span class="p">(</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">x</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="n">model</span><span class="o">.</span><span class="n">addConstrs</span><span class="p">(</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">x</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="n">model</span><span class="o">.</span><span class="n">addConstr</span><span class="p">(</span><span class="n">quicksum</span><span class="p">(</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="k">return</span> <span class="n">GurobiModel</span><span class="p">(</span><span class="n">model</span><span class="p">)</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>[3]:
</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">model</span><span class="o">.</span><span class="n">optimize</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">inner</span><span class="o">.</span><span class="n">objVal</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">inner</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">x</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">inner</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">x</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>
Restricted license - for non-production use only - expires 2024-10-28
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 7 rows, 6 columns and 15 nonzeros
Model fingerprint: 0x58dfdd53
Variable types: 3 continuous, 3 integer (3 binary)
Coefficient statistics:
Matrix range [1e+00, 7e+01]
Objective range [2e+00, 7e+02]
Bounds range [1e+00, 1e+00]
RHS range [1e+02, 1e+02]
Presolve removed 2 rows and 1 columns
Presolve time: 0.00s
Presolved: 5 rows, 5 columns, 13 nonzeros
Variable types: 0 continuous, 5 integer (3 binary)
Found heuristic solution: objective 1400.0000000
Root relaxation: objective 1.035000e+03, 3 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 1035.00000 0 1 1400.00000 1035.00000 26.1% - 0s
0 0 1105.71429 0 1 1400.00000 1105.71429 21.0% - 0s
* 0 0 0 1320.0000000 1320.00000 0.00% - 0s
Explored 1 nodes (5 simplex iterations) in 0.01 seconds (0.00 work units)
Thread count was 20 (of 20 available processors)
Solution count 2: 1320 1400
Optimal solution found (tolerance 1.00e-04)
Best objective 1.320000000000e+03, best bound 1.320000000000e+03, gap 0.0000%
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 class="admonition note">
<p class="admonition-title">Note</p>
<ul class="simple">
<li><p>In the example above, <code class="docutils literal notranslate"><span class="pre">GurobiModel</span></code> is just a thin wrapper around a standard Gurobi model. This wrapper allows MIPLearn to be solver- and modeling-language-agnostic. The wrapper provides only a few basic methods, such as <code class="docutils literal notranslate"><span class="pre">optimize</span></code>. For more control, and to query the solution, the original Gurobi model can be accessed through <code class="docutils literal notranslate"><span class="pre">model.inner</span></code>, as illustrated above.</p></li>
<li><p>To ensure training data consistency, MIPLearn requires all decision variables to have names.</p></li>
</ul>
</div>
</section>
<section id="Generating-training-data">
<h2><span class="section-number">2.4. </span>Generating training data<a class="headerlink" href="#Generating-training-data" title="Link to this heading"></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> solver, which can optimize 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>[4]:
</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">_</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 on multiple
machines. 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>[5]:
</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.io</span> <span class="kn">import</span> <span class="n">write_pkl_gz</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">500</span><span class="p">)</span>
<span class="n">train_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="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_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="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">BasicCollector</span></code> to collect the optimal solutions and other useful training data for all training instances. The data is 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. The optimization models are also exported to compressed MPS files <code class="docutils literal notranslate"><span class="pre">uc/train/00000.mps.gz</span></code>, <code class="docutils literal notranslate"><span class="pre">uc/train/00001.mps.gz</span></code>, etc.</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">miplearn.collectors.basic</span> <span class="kn">import</span> <span class="n">BasicCollector</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_uc_model</span><span class="p">,</span> <span class="n">n_jobs</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
</pre></div>
</div>
</div>
</section>
<section id="Training-and-solving-test-instances">
<h2><span class="section-number">2.5. </span>Training and solving test instances<a class="headerlink" href="#Training-and-solving-test-instances" title="Link to this heading"></a></h2>
<p>With training data in hand, we can now design and train a machine learning model to accelerate solver performance. In this tutorial, for illustration purposes, we will use ML to generate a good warm start using <span class="math notranslate nohighlight">\(k\)</span>-nearest neighbors. More specifically, the strategy is to:</p>
<ol class="arabic simple">
<li><p>Memorize the optimal solutions of all training instances;</p></li>
<li><p>Given a test instance, find the 25 most similar training instances, based on constraint right-hand sides;</p></li>
<li><p>Merge their optimal solutions into a single partial solution; specifically, only assign values to the binary variables that agree unanimously.</p></li>
<li><p>Provide this partial solution to the solver as a warm start.</p></li>
</ol>
<p>This simple strategy can be implemented as shown below, using <code class="docutils literal notranslate"><span class="pre">MemorizingPrimalComponent</span></code>. For more advanced strategies, and for the usage of more advanced classifiers, see the user guide.</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">sklearn.neighbors</span> <span class="kn">import</span> <span class="n">KNeighborsClassifier</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.mem</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">MemorizingPrimalComponent</span><span class="p">,</span>
<span class="n">MergeTopSolutions</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">miplearn.extractors.fields</span> <span class="kn">import</span> <span class="n">H5FieldsExtractor</span>
<span class="n">comp</span> <span class="o">=</span> <span class="n">MemorizingPrimalComponent</span><span class="p">(</span>
<span class="n">clf</span><span class="o">=</span><span class="n">KNeighborsClassifier</span><span class="p">(</span><span class="n">n_neighbors</span><span class="o">=</span><span class="mi">25</span><span class="p">),</span>
<span class="n">extractor</span><span class="o">=</span><span class="n">H5FieldsExtractor</span><span class="p">(</span>
<span class="n">instance_fields</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;static_constr_rhs&quot;</span><span class="p">],</span>
<span class="p">),</span>
<span class="n">constructor</span><span class="o">=</span><span class="n">MergeTopSolutions</span><span class="p">(</span><span class="mi">25</span><span class="p">,</span> <span class="p">[</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">]),</span>
<span class="n">action</span><span class="o">=</span><span class="n">SetWarmStart</span><span class="p">(),</span>
<span class="p">)</span>
</pre></div>
</div>
</div>
<p>Having defined the ML strategy, we next construct <code class="docutils literal notranslate"><span class="pre">LearningSolver</span></code>, train the ML component and optimize one of the test instances.</p>
<div class="nbinput docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[8]:
</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.solvers.learning</span> <span class="kn">import</span> <span class="n">LearningSolver</span>
<span class="n">solver_ml</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">comp</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_data</span><span class="p">)</span>
<span class="n">solver_ml</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_uc_model</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="nboutput docutils container">
<div class="prompt empty docutils container">
</div>
<div class="output_area docutils container">
<div class="highlight"><pre>
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros
Model fingerprint: 0xa8b70287
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [3e+08, 3e+08]
Presolve removed 1000 rows and 500 columns
Presolve time: 0.01s
Presolved: 1 rows, 500 columns, 500 nonzeros
Iteration Objective Primal Inf. Dual Inf. Time
0 6.6166537e+09 5.648803e+04 0.000000e+00 0s
1 8.2906219e+09 0.000000e+00 0.000000e+00 0s
Solved in 1 iterations and 0.01 seconds (0.00 work units)
Optimal objective 8.290621916e+09
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros
Model fingerprint: 0xcf27855a
Variable types: 500 continuous, 500 integer (500 binary)
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [3e+08, 3e+08]
User MIP start produced solution with objective 8.29153e+09 (0.01s)
User MIP start produced solution with objective 8.29153e+09 (0.01s)
Loaded user MIP start with objective 8.29153e+09
Presolve time: 0.00s
Presolved: 1001 rows, 1000 columns, 2500 nonzeros
Variable types: 500 continuous, 500 integer (500 binary)
Root relaxation: objective 8.290622e+09, 512 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 8.2906e+09 0 1 8.2915e+09 8.2906e+09 0.01% - 0s
0 0 8.2907e+09 0 3 8.2915e+09 8.2907e+09 0.01% - 0s
0 0 8.2907e+09 0 1 8.2915e+09 8.2907e+09 0.01% - 0s
0 0 8.2907e+09 0 2 8.2915e+09 8.2907e+09 0.01% - 0s
Cutting planes:
Gomory: 1
Flow cover: 2
Explored 1 nodes (565 simplex iterations) in 0.03 seconds (0.01 work units)
Thread count was 20 (of 20 available processors)
Solution count 1: 8.29153e+09
Optimal solution found (tolerance 1.00e-04)
Best objective 8.291528276179e+09, best bound 8.290733258025e+09, gap 0.0096%
</pre></div></div>
</div>
<div class="nboutput nblast docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[8]:
</pre></div>
</div>
<div class="output_area docutils container">
<div class="highlight"><pre>
{&#39;WS: Count&#39;: 1, &#39;WS: Number of variables set&#39;: 482.0}
</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 very close to the optimal solution to the problem. Now let us repeat the code above, but a solver which does not apply any ML strategies. Note that our previously-defined component is not provided.</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_baseline</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">solver_baseline</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_baseline</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_uc_model</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="nboutput docutils container">
<div class="prompt empty docutils container">
</div>
<div class="output_area docutils container">
<div class="highlight"><pre>
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros
Model fingerprint: 0xa8b70287
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [3e+08, 3e+08]
Presolve removed 1000 rows and 500 columns
Presolve time: 0.00s
Presolved: 1 rows, 500 columns, 500 nonzeros
Iteration Objective Primal Inf. Dual Inf. Time
0 6.6166537e+09 5.648803e+04 0.000000e+00 0s
1 8.2906219e+09 0.000000e+00 0.000000e+00 0s
Solved in 1 iterations and 0.01 seconds (0.00 work units)
Optimal objective 8.290621916e+09
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros
Model fingerprint: 0x4cbbf7c7
Variable types: 500 continuous, 500 integer (500 binary)
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [3e+08, 3e+08]
Presolve time: 0.00s
Presolved: 1001 rows, 1000 columns, 2500 nonzeros
Variable types: 500 continuous, 500 integer (500 binary)
Found heuristic solution: objective 9.757128e+09
Root relaxation: objective 8.290622e+09, 512 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 8.2906e+09 0 1 9.7571e+09 8.2906e+09 15.0% - 0s
H 0 0 8.298273e+09 8.2906e+09 0.09% - 0s
0 0 8.2907e+09 0 4 8.2983e+09 8.2907e+09 0.09% - 0s
0 0 8.2907e+09 0 1 8.2983e+09 8.2907e+09 0.09% - 0s
0 0 8.2907e+09 0 4 8.2983e+09 8.2907e+09 0.09% - 0s
H 0 0 8.293980e+09 8.2907e+09 0.04% - 0s
0 0 8.2907e+09 0 5 8.2940e+09 8.2907e+09 0.04% - 0s
0 0 8.2907e+09 0 1 8.2940e+09 8.2907e+09 0.04% - 0s
0 0 8.2907e+09 0 2 8.2940e+09 8.2907e+09 0.04% - 0s
0 0 8.2908e+09 0 1 8.2940e+09 8.2908e+09 0.04% - 0s
0 0 8.2908e+09 0 4 8.2940e+09 8.2908e+09 0.04% - 0s
0 0 8.2908e+09 0 4 8.2940e+09 8.2908e+09 0.04% - 0s
H 0 0 8.291465e+09 8.2908e+09 0.01% - 0s
Cutting planes:
Gomory: 2
MIR: 1
Explored 1 nodes (1031 simplex iterations) in 0.15 seconds (0.03 work units)
Thread count was 20 (of 20 available processors)
Solution count 4: 8.29147e+09 8.29398e+09 8.29827e+09 9.75713e+09
Optimal solution found (tolerance 1.00e-04)
Best objective 8.291465302389e+09, best bound 8.290781665333e+09, gap 0.0082%
</pre></div></div>
</div>
<div class="nboutput nblast docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[9]:
</pre></div>
</div>
<div class="output_area docutils container">
<div class="highlight"><pre>
{}
</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, but the difference can be significant for larger problems.</p>
</section>
<section id="Accessing-the-solution">
<h2><span class="section-number">2.6. </span>Accessing the solution<a class="headerlink" href="#Accessing-the-solution" title="Link to this heading"></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. 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>[10]:
</pre></div>
</div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></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">500</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="n">solver_ml</span><span class="o">.</span><span class="n">optimize</span><span class="p">(</span><span class="n">model</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">inner</span><span class="o">.</span><span class="n">objVal</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">inner</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">x</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">inner</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">x</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>
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros
Model fingerprint: 0x19042f12
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [3e+08, 3e+08]
Presolve removed 1000 rows and 500 columns
Presolve time: 0.00s
Presolved: 1 rows, 500 columns, 500 nonzeros
Iteration Objective Primal Inf. Dual Inf. Time
0 6.5917580e+09 5.627453e+04 0.000000e+00 0s
1 8.2535968e+09 0.000000e+00 0.000000e+00 0s
Solved in 1 iterations and 0.01 seconds (0.00 work units)
Optimal objective 8.253596777e+09
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros
Model fingerprint: 0xf97cde91
Variable types: 500 continuous, 500 integer (500 binary)
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [3e+08, 3e+08]
User MIP start produced solution with objective 8.25814e+09 (0.00s)
User MIP start produced solution with objective 8.25512e+09 (0.01s)
User MIP start produced solution with objective 8.25483e+09 (0.01s)
User MIP start produced solution with objective 8.25483e+09 (0.01s)
User MIP start produced solution with objective 8.25483e+09 (0.01s)
User MIP start produced solution with objective 8.25459e+09 (0.01s)
User MIP start produced solution with objective 8.25459e+09 (0.01s)
Loaded user MIP start with objective 8.25459e+09
Presolve time: 0.00s
Presolved: 1001 rows, 1000 columns, 2500 nonzeros
Variable types: 500 continuous, 500 integer (500 binary)
Root relaxation: objective 8.253597e+09, 512 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 8.2536e+09 0 1 8.2546e+09 8.2536e+09 0.01% - 0s
0 0 8.2537e+09 0 3 8.2546e+09 8.2537e+09 0.01% - 0s
0 0 8.2537e+09 0 1 8.2546e+09 8.2537e+09 0.01% - 0s
0 0 8.2537e+09 0 4 8.2546e+09 8.2537e+09 0.01% - 0s
0 0 8.2537e+09 0 4 8.2546e+09 8.2537e+09 0.01% - 0s
0 0 8.2538e+09 0 4 8.2546e+09 8.2538e+09 0.01% - 0s
0 0 8.2538e+09 0 5 8.2546e+09 8.2538e+09 0.01% - 0s
0 0 8.2538e+09 0 6 8.2546e+09 8.2538e+09 0.01% - 0s
Cutting planes:
Cover: 1
MIR: 2
StrongCG: 1
Flow cover: 1
Explored 1 nodes (575 simplex iterations) in 0.05 seconds (0.01 work units)
Thread count was 20 (of 20 available processors)
Solution count 4: 8.25459e+09 8.25483e+09 8.25512e+09 8.25814e+09
Optimal solution found (tolerance 1.00e-04)
Best objective 8.254590409970e+09, best bound 8.253768093811e+09, gap 0.0100%
obj = 8254590409.969726
x = [1.0, 1.0, 0.0]
y = [935662.0949262811, 1604270.0218116897, 0.0]
</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>
</section>
</section>
</div>
<div class='prev-next-bottom'>
<a class='left-prev' id="prev-link" href="../getting-started-pyomo/" title="previous page"><span class="section-number">1. </span>Getting started (Pyomo)</a>
<a class='right-next' id="next-link" href="../getting-started-jump/" title="next page"><span class="section-number">3. </span>Getting started (JuMP)</a>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

@ -0,0 +1,680 @@
{
"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 mixed-integer programming solvers (e.g. Gurobi, CPLEX, XPRESS). In this tutorial, we will:\n",
"\n",
"1. Install the Julia/JuMP version of MIPLearn\n",
"2. Model a simple optimization problem using JuMP\n",
"3. Generate training data and train the ML models\n",
"4. Use the ML models together Gurobi to solve new instances\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 and Gurobipy modeling languages,\n",
"- Julia version, compatible with the JuMP modeling language.\n",
"\n",
"In this tutorial, we will demonstrate how to use and install the Python/Pyomo version of the package. The first step is to install Julia in your machine. See the [official Julia website for more instructions](https://julialang.org/downloads/). After Julia is installed, launch the Julia REPL, type `]` to enter package mode, then install MIPLearn:\n",
"\n",
"```\n",
"pkg> add MIPLearn@0.3\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "e8274543",
"metadata": {},
"source": [
"In addition to MIPLearn itself, we will also install:\n",
"\n",
"- the JuMP modeling language\n",
"- Gurobi, a state-of-the-art commercial MILP solver\n",
"- Distributions, to generate random data\n",
"- PyCall, to access ML model from Scikit-Learn\n",
"- Suppressor, to make the output cleaner\n",
"\n",
"```\n",
"pkg> add JuMP@1, Gurobi@1, Distributions@0.25, PyCall@1, Suppressor@0.2\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "a14e4550",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
" \n",
"Note\n",
"\n",
"- If you do not have a Gurobi license available, you can also follow the tutorial by installing an open-source solver, such as `HiGHS`, and replacing `Gurobi.Optimizer` by `HiGHS.Optimizer` in all the code examples.\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 Julia 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 a utility company needs to decide which electrical generators should be online at each hour of the day, as well as how much power should each generator produce. More specifically, assume that the 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 the 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. The total amount of power to be produced needs to be exactly equal to the total demand $d$ (in megawatts).\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:"
]
},
{
"cell_type": "markdown",
"id": "f12c3702",
"metadata": {},
"source": [
"$$\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",
"$$"
]
},
{
"cell_type": "markdown",
"id": "be3989ed",
"metadata": {},
"source": [
"<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.\n",
"\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "a5fd33f6",
"metadata": {},
"source": [
"Next, let us convert this abstract mathematical formulation into a concrete optimization model, using Julia and JuMP. We start by defining a data class `UnitCommitmentData`, which holds all the input data."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "c62ebff1-db40-45a1-9997-d121837f067b",
"metadata": {},
"outputs": [],
"source": [
"struct UnitCommitmentData\n",
" demand::Float64\n",
" pmin::Vector{Float64}\n",
" pmax::Vector{Float64}\n",
" cfix::Vector{Float64}\n",
" cvar::Vector{Float64}\n",
"end;"
]
},
{
"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 JuMP model. The function accepts `UnitCommitmentData`, the data structure we previously defined, or the path to a JLD2 file containing this data."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "79ef7775-18ca-4dfa-b438-49860f762ad0",
"metadata": {},
"outputs": [],
"source": [
"using MIPLearn\n",
"using JuMP\n",
"using Gurobi\n",
"\n",
"function build_uc_model(data)\n",
" if data isa String\n",
" data = read_jld2(data)\n",
" end\n",
" model = Model(Gurobi.Optimizer)\n",
" G = 1:length(data.pmin)\n",
" @variable(model, x[G], Bin)\n",
" @variable(model, y[G] >= 0)\n",
" @objective(model, Min, sum(data.cfix[g] * x[g] + data.cvar[g] * y[g] for g in G))\n",
" @constraint(model, eq_max_power[g in G], y[g] <= data.pmax[g] * x[g])\n",
" @constraint(model, eq_min_power[g in G], y[g] >= data.pmin[g] * x[g])\n",
" @constraint(model, eq_demand, sum(y[g] for g in G) == data.demand)\n",
" return JumpModel(model)\n",
"end;"
]
},
{
"cell_type": "markdown",
"id": "c22714a3",
"metadata": {},
"source": [
"At this point, we can already use Gurobi 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": 3,
"id": "dd828d68-fd43-4d2a-a058-3e2628d99d9e",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:01:10.993801745Z",
"start_time": "2023-06-06T20:01:10.887580927Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)\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 7 rows, 6 columns and 15 nonzeros\n",
"Model fingerprint: 0x55e33a07\n",
"Variable types: 3 continuous, 3 integer (3 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 7e+01]\n",
" Objective range [2e+00, 7e+02]\n",
" Bounds range [0e+00, 0e+00]\n",
" RHS range [1e+02, 1e+02]\n",
"Presolve removed 2 rows and 1 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 5 rows, 5 columns, 13 nonzeros\n",
"Variable types: 0 continuous, 5 integer (3 binary)\n",
"Found heuristic solution: objective 1400.0000000\n",
"\n",
"Root relaxation: objective 1.035000e+03, 3 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 1035.00000 0 1 1400.00000 1035.00000 26.1% - 0s\n",
" 0 0 1105.71429 0 1 1400.00000 1105.71429 21.0% - 0s\n",
"* 0 0 0 1320.0000000 1320.00000 0.00% - 0s\n",
"\n",
"Explored 1 nodes (5 simplex iterations) in 0.00 seconds (0.00 work units)\n",
"Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 2: 1320 1400 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 1.320000000000e+03, best bound 1.320000000000e+03, gap 0.0000%\n",
"\n",
"User-callback calls 371, time in user-callback 0.00 sec\n",
"objective_value(model.inner) = 1320.0\n",
"Vector(value.(model.inner[:x])) = [-0.0, 1.0, 1.0]\n",
"Vector(value.(model.inner[:y])) = [0.0, 60.0, 40.0]\n"
]
}
],
"source": [
"model = build_uc_model(\n",
" UnitCommitmentData(\n",
" 100.0, # demand\n",
" [10, 20, 30], # pmin\n",
" [50, 60, 70], # pmax\n",
" [700, 600, 500], # cfix\n",
" [1.5, 2.0, 2.5], # cvar\n",
" )\n",
")\n",
"model.optimize()\n",
"@show objective_value(model.inner)\n",
"@show Vector(value.(model.inner[:x]))\n",
"@show Vector(value.(model.inner[:y]));"
]
},
{
"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": "01f576e1-1790-425e-9e5c-9fa07b6f4c26",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
" \n",
"Notes\n",
" \n",
"- In the example above, `JumpModel` is just a thin wrapper around a standard JuMP model. This wrapper allows MIPLearn to be solver- and modeling-language-agnostic. The wrapper provides only a few basic methods, such as `optimize`. For more control, and to query the solution, the original JuMP model can be accessed through `model.inner`, as illustrated above.\n",
"</div>"
]
},
{
"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** solver, which can optimize 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": 4,
"id": "1326efd7-3869-4137-ab6b-df9cb609a7e0",
"metadata": {},
"outputs": [],
"source": [
"using Distributions\n",
"using Random\n",
"\n",
"function random_uc_data(; samples::Int, n::Int, seed::Int=42)::Vector\n",
" Random.seed!(seed)\n",
" pmin = rand(Uniform(100_000, 500_000), n)\n",
" pmax = pmin .* rand(Uniform(2, 2.5), n)\n",
" cfix = pmin .* rand(Uniform(100, 125), n)\n",
" cvar = rand(Uniform(1.25, 1.50), n)\n",
" return [\n",
" UnitCommitmentData(\n",
" sum(pmax) * rand(Uniform(0.5, 0.75)),\n",
" pmin,\n",
" pmax,\n",
" cfix,\n",
" cvar,\n",
" )\n",
" for _ in 1:samples\n",
" ]\n",
"end;"
]
},
{
"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 on multiple machines. The code below generates the files `uc/train/00001.jld2`, `uc/train/00002.jld2`, etc., which contain the input data in JLD2 format."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "6156752c",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:03:04.782830561Z",
"start_time": "2023-06-06T20:03:04.530421396Z"
}
},
"outputs": [],
"source": [
"data = random_uc_data(samples=500, n=500)\n",
"train_data = write_jld2(data[1:450], \"uc/train\")\n",
"test_data = write_jld2(data[451:500], \"uc/test\");"
]
},
{
"cell_type": "markdown",
"id": "b17af877",
"metadata": {},
"source": [
"Finally, we use `BasicCollector` to collect the optimal solutions and other useful training data for all training instances. The data is stored in HDF5 files `uc/train/00001.h5`, `uc/train/00002.h5`, etc. The optimization models are also exported to compressed MPS files `uc/train/00001.mps.gz`, `uc/train/00002.mps.gz`, etc."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "7623f002",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:03:35.571497019Z",
"start_time": "2023-06-06T20:03:25.804104036Z"
}
},
"outputs": [],
"source": [
"using Suppressor\n",
"@suppress_out begin\n",
" bc = BasicCollector()\n",
" bc.collect(train_data, build_uc_model)\n",
"end"
]
},
{
"cell_type": "markdown",
"id": "c42b1be1-9723-4827-82d8-974afa51ef9f",
"metadata": {},
"source": [
"## Training and solving test instances"
]
},
{
"cell_type": "markdown",
"id": "a33c6aa4-f0b8-4ccb-9935-01f7d7de2a1c",
"metadata": {},
"source": [
"With training data in hand, we can now design and train a machine learning model to accelerate solver performance. In this tutorial, for illustration purposes, we will use ML to generate a good warm start using $k$-nearest neighbors. More specifically, the strategy is to:\n",
"\n",
"1. Memorize the optimal solutions of all training instances;\n",
"2. Given a test instance, find the 25 most similar training instances, based on constraint right-hand sides;\n",
"3. Merge their optimal solutions into a single partial solution; specifically, only assign values to the binary variables that agree unanimously.\n",
"4. Provide this partial solution to the solver as a warm start.\n",
"\n",
"This simple strategy can be implemented as shown below, using `MemorizingPrimalComponent`. For more advanced strategies, and for the usage of more advanced classifiers, see the user guide."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "435f7bf8-4b09-4889-b1ec-b7b56e7d8ed2",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:05:20.497772794Z",
"start_time": "2023-06-06T20:05:20.484821405Z"
}
},
"outputs": [],
"source": [
"# Load kNN classifier from Scikit-Learn\n",
"using PyCall\n",
"KNeighborsClassifier = pyimport(\"sklearn.neighbors\").KNeighborsClassifier\n",
"\n",
"# Build the MIPLearn component\n",
"comp = MemorizingPrimalComponent(\n",
" clf=KNeighborsClassifier(n_neighbors=25),\n",
" extractor=H5FieldsExtractor(\n",
" instance_fields=[\"static_constr_rhs\"],\n",
" ),\n",
" constructor=MergeTopSolutions(25, [0.0, 1.0]),\n",
" action=SetWarmStart(),\n",
");"
]
},
{
"cell_type": "markdown",
"id": "9536e7e4-0b0d-49b0-bebd-4a848f839e94",
"metadata": {},
"source": [
"Having defined the ML strategy, we next construct `LearningSolver`, train the ML component and optimize one of the test instances."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "9d13dd50-3dcf-4673-a757-6f44dcc0dedf",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:05:22.672002339Z",
"start_time": "2023-06-06T20:05:21.447466634Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)\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 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0xd2378195\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+06]\n",
" Objective range [1e+00, 6e+07]\n",
" Bounds range [0e+00, 0e+00]\n",
" RHS range [2e+08, 2e+08]\n",
"\n",
"User MIP start produced solution with objective 1.02165e+10 (0.00s)\n",
"Loaded user MIP start with objective 1.02165e+10\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"\n",
"Root relaxation: objective 1.021568e+10, 510 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 1.0216e+10 0 1 1.0217e+10 1.0216e+10 0.01% - 0s\n",
"\n",
"Explored 1 nodes (510 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 1: 1.02165e+10 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 1.021651058978e+10, best bound 1.021567971257e+10, gap 0.0081%\n",
"\n",
"User-callback calls 169, time in user-callback 0.00 sec\n"
]
}
],
"source": [
"solver_ml = LearningSolver(components=[comp])\n",
"solver_ml.fit(train_data)\n",
"solver_ml.optimize(test_data[1], build_uc_model);"
]
},
{
"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 very close to the optimal solution to the problem. Now let us repeat the code above, but a solver which does not apply any ML strategies. Note that our previously-defined component is not provided."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "2ff391ed-e855-4228-aa09-a7641d8c2893",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:05:46.969575966Z",
"start_time": "2023-06-06T20:05:46.420803286Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)\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 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0xb45c0594\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+06]\n",
" Objective range [1e+00, 6e+07]\n",
" Bounds range [0e+00, 0e+00]\n",
" RHS range [2e+08, 2e+08]\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"Found heuristic solution: objective 1.071463e+10\n",
"\n",
"Root relaxation: objective 1.021568e+10, 510 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 1.0216e+10 0 1 1.0715e+10 1.0216e+10 4.66% - 0s\n",
"H 0 0 1.025162e+10 1.0216e+10 0.35% - 0s\n",
" 0 0 1.0216e+10 0 1 1.0252e+10 1.0216e+10 0.35% - 0s\n",
"H 0 0 1.023090e+10 1.0216e+10 0.15% - 0s\n",
"H 0 0 1.022335e+10 1.0216e+10 0.07% - 0s\n",
"H 0 0 1.022281e+10 1.0216e+10 0.07% - 0s\n",
"H 0 0 1.021753e+10 1.0216e+10 0.02% - 0s\n",
"H 0 0 1.021752e+10 1.0216e+10 0.02% - 0s\n",
" 0 0 1.0216e+10 0 3 1.0218e+10 1.0216e+10 0.02% - 0s\n",
" 0 0 1.0216e+10 0 1 1.0218e+10 1.0216e+10 0.02% - 0s\n",
"H 0 0 1.021651e+10 1.0216e+10 0.01% - 0s\n",
"\n",
"Explored 1 nodes (764 simplex iterations) in 0.03 seconds (0.02 work units)\n",
"Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 7: 1.02165e+10 1.02175e+10 1.02228e+10 ... 1.07146e+10\n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 1.021651058978e+10, best bound 1.021573363741e+10, gap 0.0076%\n",
"\n",
"User-callback calls 204, time in user-callback 0.00 sec\n"
]
}
],
"source": [
"solver_baseline = LearningSolver(components=[])\n",
"solver_baseline.fit(train_data)\n",
"solver_baseline.optimize(test_data[1], build_uc_model);"
]
},
{
"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, but the difference can be significant for larger problems."
]
},
{
"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. In the following example, we show how to build and solve a JuMP model entirely in-memory, using our trained solver."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "67a6cd18",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:06:26.913448568Z",
"start_time": "2023-06-06T20:06:26.169047914Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)\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 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x974a7fba\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 1e+06]\n",
" Objective range [1e+00, 6e+07]\n",
" Bounds range [0e+00, 0e+00]\n",
" RHS range [2e+08, 2e+08]\n",
"\n",
"User MIP start produced solution with objective 9.86729e+09 (0.00s)\n",
"User MIP start produced solution with objective 9.86675e+09 (0.00s)\n",
"User MIP start produced solution with objective 9.86654e+09 (0.01s)\n",
"User MIP start produced solution with objective 9.8661e+09 (0.01s)\n",
"Loaded user MIP start with objective 9.8661e+09\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"\n",
"Root relaxation: objective 9.865344e+09, 510 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 9.8653e+09 0 1 9.8661e+09 9.8653e+09 0.01% - 0s\n",
"\n",
"Explored 1 nodes (510 simplex iterations) in 0.02 seconds (0.01 work units)\n",
"Thread count was 32 (of 32 available processors)\n",
"\n",
"Solution count 4: 9.8661e+09 9.86654e+09 9.86675e+09 9.86729e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 9.866096485614e+09, best bound 9.865343669936e+09, gap 0.0076%\n",
"\n",
"User-callback calls 182, time in user-callback 0.00 sec\n",
"objective_value(model.inner) = 9.866096485613789e9\n"
]
}
],
"source": [
"data = random_uc_data(samples=1, n=500)[1]\n",
"model = build_uc_model(data)\n",
"solver_ml.optimize(model)\n",
"@show objective_value(model.inner);"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Julia 1.9.0",
"language": "julia",
"name": "julia-1.9"
},
"language_info": {
"file_extension": ".jl",
"mimetype": "application/julia",
"name": "julia",
"version": "1.9.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,755 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<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" />
<title>3. Getting started (JuMP) &#8212; MIPLearn 0.4</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" type="text/css" href="../../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css?v=2aa19091" />
<link rel="stylesheet" type="text/css" href="../../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script>
<script>window.MathJax = {"tex": {"inlineMath": [["$", "$"], ["\\(", "\\)"]], "processEscapes": true}, "options": {"ignoreHtmlClass": "tex2jax_ignore|mathjax_ignore|document", "processHtmlClass": "tex2jax_process|mathjax_process|math|output_area"}}</script>
<script defer="defer" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<link rel="index" title="Index" href="../../genindex/" />
<link rel="search" title="Search" href="../../search/" />
<link rel="next" title="4. User cuts and lazy constraints" href="../cuts-gurobipy/" />
<link rel="prev" title="2. Getting started (Gurobipy)" href="../getting-started-gurobipy/" />
<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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="current nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../getting-started-pyomo/">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1 current active">
<a class="current reference internal" href="#">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../guide/problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../api/problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/components/">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/helpers/">
14. 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-jump.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">
3.1. Introduction
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Installation">
3.2. Installation
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Modeling-a-simple-optimization-problem">
3.3. 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">
3.4. Generating training data
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Training-and-solving-test-instances">
3.5. Training and solving test instances
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Accessing-the-solution">
3.6. 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>
<section id="Getting-started-(JuMP)">
<h1><span class="section-number">3. </span>Getting started (JuMP)<a class="headerlink" href="#Getting-started-(JuMP)" title="Link to this heading"></a></h1>
<section id="Introduction">
<h2><span class="section-number">3.1. </span>Introduction<a class="headerlink" href="#Introduction" title="Link to this heading"></a></h2>
<p><strong>MIPLearn</strong> is an open source framework that uses machine learning (ML) to accelerate the performance of mixed-integer programming solvers (e.g. Gurobi, CPLEX, XPRESS). In this tutorial, we will:</p>
<ol class="arabic simple">
<li><p>Install the Julia/JuMP 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 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>
</section>
<section id="Installation">
<h2><span class="section-number">3.2. </span>Installation<a class="headerlink" href="#Installation" title="Link to this heading"></a></h2>
<p>MIPLearn is available in two versions:</p>
<ul class="simple">
<li><p>Python version, compatible with the Pyomo and Gurobipy modeling languages,</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 Julia in your machine. See the <a class="reference external" href="https://julialang.org/downloads/">official Julia website for more instructions</a>. After Julia is installed, launch the Julia REPL, type <code class="docutils literal notranslate"><span class="pre">]</span></code> to enter package mode, then install MIPLearn:</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>pkg&gt; add MIPLearn@0.3
</pre></div>
</div>
<p>In addition to MIPLearn itself, we will also install:</p>
<ul class="simple">
<li><p>the JuMP modeling language</p></li>
<li><p>Gurobi, a state-of-the-art commercial MILP solver</p></li>
<li><p>Distributions, to generate random data</p></li>
<li><p>PyCall, to access ML model from Scikit-Learn</p></li>
<li><p>Suppressor, to make the output cleaner</p></li>
</ul>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>pkg&gt; add JuMP@1, Gurobi@1, Distributions@0.25, PyCall@1, Suppressor@0.2
</pre></div>
</div>
<div class="admonition note">
<p class="admonition-title">Note</p>
<ul class="simple">
<li><p>If you do not have a Gurobi license available, you can also follow the tutorial by installing an open-source solver, such as <code class="docutils literal notranslate"><span class="pre">HiGHS</span></code>, and replacing <code class="docutils literal notranslate"><span class="pre">Gurobi.Optimizer</span></code> by <code class="docutils literal notranslate"><span class="pre">HiGHS.Optimizer</span></code> in all the code examples.</p></li>
<li><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 Julia projects.</p></li>
</ul>
</div>
</section>
<section id="Modeling-a-simple-optimization-problem">
<h2><span class="section-number">3.3. </span>Modeling a simple optimization problem<a class="headerlink" href="#Modeling-a-simple-optimization-problem" title="Link to this heading"></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 a utility company needs to decide which electrical generators should be online at each hour of the day, as well as how much power should each generator produce. More specifically, assume that the 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 the 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. 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).</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.</p>
</div>
<p>Next, let us convert this abstract mathematical formulation into a concrete optimization model, using Julia and JuMP. 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>[1]:
</pre></div>
</div>
<div class="input_area highlight-julia notranslate"><div class="highlight"><pre><span></span><span class="k">struct</span> <span class="kt">UnitCommitmentData</span>
<span class="w"> </span><span class="n">demand</span><span class="o">::</span><span class="kt">Float64</span>
<span class="w"> </span><span class="n">pmin</span><span class="o">::</span><span class="kt">Vector</span><span class="p">{</span><span class="kt">Float64</span><span class="p">}</span>
<span class="w"> </span><span class="n">pmax</span><span class="o">::</span><span class="kt">Vector</span><span class="p">{</span><span class="kt">Float64</span><span class="p">}</span>
<span class="w"> </span><span class="n">cfix</span><span class="o">::</span><span class="kt">Vector</span><span class="p">{</span><span class="kt">Float64</span><span class="p">}</span>
<span class="w"> </span><span class="n">cvar</span><span class="o">::</span><span class="kt">Vector</span><span class="p">{</span><span class="kt">Float64</span><span class="p">}</span>
<span class="k">end</span><span class="p">;</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 JuMP model. The function accepts <code class="docutils literal notranslate"><span class="pre">UnitCommitmentData</span></code>, the data structure we previously defined, or the path to a JLD2 file containing this data.</p>
<div class="nbinput nblast docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[2]:
</pre></div>
</div>
<div class="input_area highlight-julia notranslate"><div class="highlight"><pre><span></span><span class="k">using</span><span class="w"> </span><span class="n">MIPLearn</span>
<span class="k">using</span><span class="w"> </span><span class="n">JuMP</span>
<span class="k">using</span><span class="w"> </span><span class="n">Gurobi</span>
<span class="k">function</span><span class="w"> </span><span class="n">build_uc_model</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="k">isa</span><span class="w"> </span><span class="kt">String</span>
<span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">read_jld2</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="w"> </span><span class="k">end</span>
<span class="w"> </span><span class="n">model</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Model</span><span class="p">(</span><span class="n">Gurobi</span><span class="o">.</span><span class="n">Optimizer</span><span class="p">)</span>
<span class="w"> </span><span class="n">G</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="o">:</span><span class="n">length</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="w"> </span><span class="nd">@variable</span><span class="p">(</span><span class="n">model</span><span class="p">,</span><span class="w"> </span><span class="n">x</span><span class="p">[</span><span class="n">G</span><span class="p">],</span><span class="w"> </span><span class="n">Bin</span><span class="p">)</span>
<span class="w"> </span><span class="nd">@variable</span><span class="p">(</span><span class="n">model</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">[</span><span class="n">G</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span>
<span class="w"> </span><span class="nd">@objective</span><span class="p">(</span><span class="n">model</span><span class="p">,</span><span class="w"> </span><span class="n">Min</span><span class="p">,</span><span class="w"> </span><span class="n">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">g</span><span class="p">]</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">x</span><span class="p">[</span><span class="n">g</span><span class="p">]</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">data</span><span class="o">.</span><span class="n">cvar</span><span class="p">[</span><span class="n">g</span><span class="p">]</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">y</span><span class="p">[</span><span class="n">g</span><span class="p">]</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">g</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">G</span><span class="p">))</span>
<span class="w"> </span><span class="nd">@constraint</span><span class="p">(</span><span class="n">model</span><span class="p">,</span><span class="w"> </span><span class="n">eq_max_power</span><span class="p">[</span><span class="n">g</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">G</span><span class="p">],</span><span class="w"> </span><span class="n">y</span><span class="p">[</span><span class="n">g</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="n">data</span><span class="o">.</span><span class="n">pmax</span><span class="p">[</span><span class="n">g</span><span class="p">]</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">x</span><span class="p">[</span><span class="n">g</span><span class="p">])</span>
<span class="w"> </span><span class="nd">@constraint</span><span class="p">(</span><span class="n">model</span><span class="p">,</span><span class="w"> </span><span class="n">eq_min_power</span><span class="p">[</span><span class="n">g</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">G</span><span class="p">],</span><span class="w"> </span><span class="n">y</span><span class="p">[</span><span class="n">g</span><span class="p">]</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="n">data</span><span class="o">.</span><span class="n">pmin</span><span class="p">[</span><span class="n">g</span><span class="p">]</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">x</span><span class="p">[</span><span class="n">g</span><span class="p">])</span>
<span class="w"> </span><span class="nd">@constraint</span><span class="p">(</span><span class="n">model</span><span class="p">,</span><span class="w"> </span><span class="n">eq_demand</span><span class="p">,</span><span class="w"> </span><span class="n">sum</span><span class="p">(</span><span class="n">y</span><span class="p">[</span><span class="n">g</span><span class="p">]</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">g</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">G</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">data</span><span class="o">.</span><span class="n">demand</span><span class="p">)</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">JumpModel</span><span class="p">(</span><span class="n">model</span><span class="p">)</span>
<span class="k">end</span><span class="p">;</span>
</pre></div>
</div>
</div>
<p>At this point, we can already use Gurobi 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>[3]:
</pre></div>
</div>
<div class="input_area highlight-julia notranslate"><div class="highlight"><pre><span></span><span class="n">model</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">build_uc_model</span><span class="p">(</span>
<span class="w"> </span><span class="n">UnitCommitmentData</span><span class="p">(</span>
<span class="w"> </span><span class="mf">100.0</span><span class="p">,</span><span class="w"> </span><span class="c"># demand</span>
<span class="w"> </span><span class="p">[</span><span class="mi">10</span><span class="p">,</span><span class="w"> </span><span class="mi">20</span><span class="p">,</span><span class="w"> </span><span class="mi">30</span><span class="p">],</span><span class="w"> </span><span class="c"># pmin</span>
<span class="w"> </span><span class="p">[</span><span class="mi">50</span><span class="p">,</span><span class="w"> </span><span class="mi">60</span><span class="p">,</span><span class="w"> </span><span class="mi">70</span><span class="p">],</span><span class="w"> </span><span class="c"># pmax</span>
<span class="w"> </span><span class="p">[</span><span class="mi">700</span><span class="p">,</span><span class="w"> </span><span class="mi">600</span><span class="p">,</span><span class="w"> </span><span class="mi">500</span><span class="p">],</span><span class="w"> </span><span class="c"># cfix</span>
<span class="w"> </span><span class="p">[</span><span class="mf">1.5</span><span class="p">,</span><span class="w"> </span><span class="mf">2.0</span><span class="p">,</span><span class="w"> </span><span class="mf">2.5</span><span class="p">],</span><span class="w"> </span><span class="c"># cvar</span>
<span class="w"> </span><span class="p">)</span>
<span class="p">)</span>
<span class="n">model</span><span class="o">.</span><span class="n">optimize</span><span class="p">()</span>
<span class="nd">@show</span><span class="w"> </span><span class="n">objective_value</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">inner</span><span class="p">)</span>
<span class="nd">@show</span><span class="w"> </span><span class="kt">Vector</span><span class="p">(</span><span class="n">value</span><span class="o">.</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">inner</span><span class="p">[</span><span class="ss">:x</span><span class="p">]))</span>
<span class="nd">@show</span><span class="w"> </span><span class="kt">Vector</span><span class="p">(</span><span class="n">value</span><span class="o">.</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">inner</span><span class="p">[</span><span class="ss">:y</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>
Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)
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 7 rows, 6 columns and 15 nonzeros
Model fingerprint: 0x55e33a07
Variable types: 3 continuous, 3 integer (3 binary)
Coefficient statistics:
Matrix range [1e+00, 7e+01]
Objective range [2e+00, 7e+02]
Bounds range [0e+00, 0e+00]
RHS range [1e+02, 1e+02]
Presolve removed 2 rows and 1 columns
Presolve time: 0.00s
Presolved: 5 rows, 5 columns, 13 nonzeros
Variable types: 0 continuous, 5 integer (3 binary)
Found heuristic solution: objective 1400.0000000
Root relaxation: objective 1.035000e+03, 3 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 1035.00000 0 1 1400.00000 1035.00000 26.1% - 0s
0 0 1105.71429 0 1 1400.00000 1105.71429 21.0% - 0s
* 0 0 0 1320.0000000 1320.00000 0.00% - 0s
Explored 1 nodes (5 simplex iterations) in 0.00 seconds (0.00 work units)
Thread count was 32 (of 32 available processors)
Solution count 2: 1320 1400
Optimal solution found (tolerance 1.00e-04)
Best objective 1.320000000000e+03, best bound 1.320000000000e+03, gap 0.0000%
User-callback calls 371, time in user-callback 0.00 sec
objective_value(model.inner) = 1320.0
Vector(value.(model.inner[:x])) = [-0.0, 1.0, 1.0]
Vector(value.(model.inner[: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 class="admonition note">
<p class="admonition-title">Notes</p>
<ul class="simple">
<li><p>In the example above, <code class="docutils literal notranslate"><span class="pre">JumpModel</span></code> is just a thin wrapper around a standard JuMP model. This wrapper allows MIPLearn to be solver- and modeling-language-agnostic. The wrapper provides only a few basic methods, such as <code class="docutils literal notranslate"><span class="pre">optimize</span></code>. For more control, and to query the solution, the original JuMP model can be accessed through <code class="docutils literal notranslate"><span class="pre">model.inner</span></code>, as illustrated above.</p></li>
</ul>
</div>
</section>
<section id="Generating-training-data">
<h2><span class="section-number">3.4. </span>Generating training data<a class="headerlink" href="#Generating-training-data" title="Link to this heading"></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> solver, which can optimize 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>[4]:
</pre></div>
</div>
<div class="input_area highlight-julia notranslate"><div class="highlight"><pre><span></span><span class="k">using</span><span class="w"> </span><span class="n">Distributions</span>
<span class="k">using</span><span class="w"> </span><span class="n">Random</span>
<span class="k">function</span><span class="w"> </span><span class="n">random_uc_data</span><span class="p">(;</span><span class="w"> </span><span class="n">samples</span><span class="o">::</span><span class="kt">Int</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="o">::</span><span class="kt">Int</span><span class="p">,</span><span class="w"> </span><span class="n">seed</span><span class="o">::</span><span class="kt">Int</span><span class="o">=</span><span class="mi">42</span><span class="p">)</span><span class="o">::</span><span class="kt">Vector</span>
<span class="w"> </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="w"> </span><span class="n">pmin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rand</span><span class="p">(</span><span class="n">Uniform</span><span class="p">(</span><span class="mi">100_000</span><span class="p">,</span><span class="w"> </span><span class="mi">500_000</span><span class="p">),</span><span class="w"> </span><span class="n">n</span><span class="p">)</span>
<span class="w"> </span><span class="n">pmax</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pmin</span><span class="w"> </span><span class="o">.*</span><span class="w"> </span><span class="n">rand</span><span class="p">(</span><span class="n">Uniform</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mf">2.5</span><span class="p">),</span><span class="w"> </span><span class="n">n</span><span class="p">)</span>
<span class="w"> </span><span class="n">cfix</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pmin</span><span class="w"> </span><span class="o">.*</span><span class="w"> </span><span class="n">rand</span><span class="p">(</span><span class="n">Uniform</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span><span class="w"> </span><span class="mi">125</span><span class="p">),</span><span class="w"> </span><span class="n">n</span><span class="p">)</span>
<span class="w"> </span><span class="n">cvar</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rand</span><span class="p">(</span><span class="n">Uniform</span><span class="p">(</span><span class="mf">1.25</span><span class="p">,</span><span class="w"> </span><span class="mf">1.50</span><span class="p">),</span><span class="w"> </span><span class="n">n</span><span class="p">)</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">[</span>
<span class="w"> </span><span class="n">UnitCommitmentData</span><span class="p">(</span>
<span class="w"> </span><span class="n">sum</span><span class="p">(</span><span class="n">pmax</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">rand</span><span class="p">(</span><span class="n">Uniform</span><span class="p">(</span><span class="mf">0.5</span><span class="p">,</span><span class="w"> </span><span class="mf">0.75</span><span class="p">)),</span>
<span class="w"> </span><span class="n">pmin</span><span class="p">,</span>
<span class="w"> </span><span class="n">pmax</span><span class="p">,</span>
<span class="w"> </span><span class="n">cfix</span><span class="p">,</span>
<span class="w"> </span><span class="n">cvar</span><span class="p">,</span>
<span class="w"> </span><span class="p">)</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">1</span><span class="o">:</span><span class="n">samples</span>
<span class="w"> </span><span class="p">]</span>
<span class="k">end</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 on multiple
machines. The code below generates the files <code class="docutils literal notranslate"><span class="pre">uc/train/00001.jld2</span></code>, <code class="docutils literal notranslate"><span class="pre">uc/train/00002.jld2</span></code>, etc., which contain the input data in JLD2 format.</p>
<div class="nbinput nblast docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[5]:
</pre></div>
</div>
<div class="input_area highlight-julia notranslate"><div class="highlight"><pre><span></span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="n">n</span><span class="o">=</span><span class="mi">500</span><span class="p">)</span>
<span class="n">train_data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">write_jld2</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">1</span><span class="o">:</span><span class="mi">450</span><span class="p">],</span><span class="w"> </span><span class="s">&quot;uc/train&quot;</span><span class="p">)</span>
<span class="n">test_data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">write_jld2</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">451</span><span class="o">:</span><span class="mi">500</span><span class="p">],</span><span class="w"> </span><span class="s">&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">BasicCollector</span></code> to collect the optimal solutions and other useful training data for all training instances. The data is stored in HDF5 files <code class="docutils literal notranslate"><span class="pre">uc/train/00001.h5</span></code>, <code class="docutils literal notranslate"><span class="pre">uc/train/00002.h5</span></code>, etc. The optimization models are also exported to compressed MPS files <code class="docutils literal notranslate"><span class="pre">uc/train/00001.mps.gz</span></code>, <code class="docutils literal notranslate"><span class="pre">uc/train/00002.mps.gz</span></code>, etc.</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-julia notranslate"><div class="highlight"><pre><span></span><span class="k">using</span><span class="w"> </span><span class="n">Suppressor</span>
<span class="nd">@suppress_out</span><span class="w"> </span><span class="k">begin</span>
<span class="w"> </span><span class="n">bc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BasicCollector</span><span class="p">()</span>
<span class="w"> </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="w"> </span><span class="n">build_uc_model</span><span class="p">)</span>
<span class="k">end</span>
</pre></div>
</div>
</div>
</section>
<section id="Training-and-solving-test-instances">
<h2><span class="section-number">3.5. </span>Training and solving test instances<a class="headerlink" href="#Training-and-solving-test-instances" title="Link to this heading"></a></h2>
<p>With training data in hand, we can now design and train a machine learning model to accelerate solver performance. In this tutorial, for illustration purposes, we will use ML to generate a good warm start using <span class="math notranslate nohighlight">\(k\)</span>-nearest neighbors. More specifically, the strategy is to:</p>
<ol class="arabic simple">
<li><p>Memorize the optimal solutions of all training instances;</p></li>
<li><p>Given a test instance, find the 25 most similar training instances, based on constraint right-hand sides;</p></li>
<li><p>Merge their optimal solutions into a single partial solution; specifically, only assign values to the binary variables that agree unanimously.</p></li>
<li><p>Provide this partial solution to the solver as a warm start.</p></li>
</ol>
<p>This simple strategy can be implemented as shown below, using <code class="docutils literal notranslate"><span class="pre">MemorizingPrimalComponent</span></code>. For more advanced strategies, and for the usage of more advanced classifiers, see the user guide.</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-julia notranslate"><div class="highlight"><pre><span></span><span class="c"># Load kNN classifier from Scikit-Learn</span>
<span class="k">using</span><span class="w"> </span><span class="n">PyCall</span>
<span class="n">KNeighborsClassifier</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">pyimport</span><span class="p">(</span><span class="s">&quot;sklearn.neighbors&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">KNeighborsClassifier</span>
<span class="c"># Build the MIPLearn component</span>
<span class="n">comp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">MemorizingPrimalComponent</span><span class="p">(</span>
<span class="w"> </span><span class="n">clf</span><span class="o">=</span><span class="n">KNeighborsClassifier</span><span class="p">(</span><span class="n">n_neighbors</span><span class="o">=</span><span class="mi">25</span><span class="p">),</span>
<span class="w"> </span><span class="n">extractor</span><span class="o">=</span><span class="n">H5FieldsExtractor</span><span class="p">(</span>
<span class="w"> </span><span class="n">instance_fields</span><span class="o">=</span><span class="p">[</span><span class="s">&quot;static_constr_rhs&quot;</span><span class="p">],</span>
<span class="w"> </span><span class="p">),</span>
<span class="w"> </span><span class="n">constructor</span><span class="o">=</span><span class="n">MergeTopSolutions</span><span class="p">(</span><span class="mi">25</span><span class="p">,</span><span class="w"> </span><span class="p">[</span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="mf">1.0</span><span class="p">]),</span>
<span class="w"> </span><span class="n">action</span><span class="o">=</span><span class="n">SetWarmStart</span><span class="p">(),</span>
<span class="p">);</span>
</pre></div>
</div>
</div>
<p>Having defined the ML strategy, we next construct <code class="docutils literal notranslate"><span class="pre">LearningSolver</span></code>, train the ML component and optimize one of the test instances.</p>
<div class="nbinput docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[8]:
</pre></div>
</div>
<div class="input_area highlight-julia notranslate"><div class="highlight"><pre><span></span><span class="n">solver_ml</span><span class="w"> </span><span class="o">=</span><span class="w"> </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">comp</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_data</span><span class="p">)</span>
<span class="n">solver_ml</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">1</span><span class="p">],</span><span class="w"> </span><span class="n">build_uc_model</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>
Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)
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 1001 rows, 1000 columns and 2500 nonzeros
Model fingerprint: 0xd2378195
Variable types: 500 continuous, 500 integer (500 binary)
Coefficient statistics:
Matrix range [1e+00, 1e+06]
Objective range [1e+00, 6e+07]
Bounds range [0e+00, 0e+00]
RHS range [2e+08, 2e+08]
User MIP start produced solution with objective 1.02165e+10 (0.00s)
Loaded user MIP start with objective 1.02165e+10
Presolve time: 0.00s
Presolved: 1001 rows, 1000 columns, 2500 nonzeros
Variable types: 500 continuous, 500 integer (500 binary)
Root relaxation: objective 1.021568e+10, 510 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 1.0216e+10 0 1 1.0217e+10 1.0216e+10 0.01% - 0s
Explored 1 nodes (510 simplex iterations) in 0.01 seconds (0.00 work units)
Thread count was 32 (of 32 available processors)
Solution count 1: 1.02165e+10
Optimal solution found (tolerance 1.00e-04)
Best objective 1.021651058978e+10, best bound 1.021567971257e+10, gap 0.0081%
User-callback calls 169, time in user-callback 0.00 sec
</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 very close to the optimal solution to the problem. Now let us repeat the code above, but a solver which does not apply any ML strategies. Note that our previously-defined component is not provided.</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-julia notranslate"><div class="highlight"><pre><span></span><span class="n">solver_baseline</span><span class="w"> </span><span class="o">=</span><span class="w"> </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">solver_baseline</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_baseline</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">1</span><span class="p">],</span><span class="w"> </span><span class="n">build_uc_model</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>
Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)
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 1001 rows, 1000 columns and 2500 nonzeros
Model fingerprint: 0xb45c0594
Variable types: 500 continuous, 500 integer (500 binary)
Coefficient statistics:
Matrix range [1e+00, 1e+06]
Objective range [1e+00, 6e+07]
Bounds range [0e+00, 0e+00]
RHS range [2e+08, 2e+08]
Presolve time: 0.00s
Presolved: 1001 rows, 1000 columns, 2500 nonzeros
Variable types: 500 continuous, 500 integer (500 binary)
Found heuristic solution: objective 1.071463e+10
Root relaxation: objective 1.021568e+10, 510 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 1.0216e+10 0 1 1.0715e+10 1.0216e+10 4.66% - 0s
H 0 0 1.025162e+10 1.0216e+10 0.35% - 0s
0 0 1.0216e+10 0 1 1.0252e+10 1.0216e+10 0.35% - 0s
H 0 0 1.023090e+10 1.0216e+10 0.15% - 0s
H 0 0 1.022335e+10 1.0216e+10 0.07% - 0s
H 0 0 1.022281e+10 1.0216e+10 0.07% - 0s
H 0 0 1.021753e+10 1.0216e+10 0.02% - 0s
H 0 0 1.021752e+10 1.0216e+10 0.02% - 0s
0 0 1.0216e+10 0 3 1.0218e+10 1.0216e+10 0.02% - 0s
0 0 1.0216e+10 0 1 1.0218e+10 1.0216e+10 0.02% - 0s
H 0 0 1.021651e+10 1.0216e+10 0.01% - 0s
Explored 1 nodes (764 simplex iterations) in 0.03 seconds (0.02 work units)
Thread count was 32 (of 32 available processors)
Solution count 7: 1.02165e+10 1.02175e+10 1.02228e+10 ... 1.07146e+10
Optimal solution found (tolerance 1.00e-04)
Best objective 1.021651058978e+10, best bound 1.021573363741e+10, gap 0.0076%
User-callback calls 204, time in user-callback 0.00 sec
</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, but the difference can be significant for larger problems.</p>
</section>
<section id="Accessing-the-solution">
<h2><span class="section-number">3.6. </span>Accessing the solution<a class="headerlink" href="#Accessing-the-solution" title="Link to this heading"></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. In the following example, we show how to build and solve a JuMP 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>[10]:
</pre></div>
</div>
<div class="input_area highlight-julia notranslate"><div class="highlight"><pre><span></span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </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="w"> </span><span class="n">n</span><span class="o">=</span><span class="mi">500</span><span class="p">)[</span><span class="mi">1</span><span class="p">]</span>
<span class="n">model</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">build_uc_model</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="n">solver_ml</span><span class="o">.</span><span class="n">optimize</span><span class="p">(</span><span class="n">model</span><span class="p">)</span>
<span class="nd">@show</span><span class="w"> </span><span class="n">objective_value</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">inner</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>
Gurobi Optimizer version 10.0.1 build v10.0.1rc0 (linux64)
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 1001 rows, 1000 columns and 2500 nonzeros
Model fingerprint: 0x974a7fba
Variable types: 500 continuous, 500 integer (500 binary)
Coefficient statistics:
Matrix range [1e+00, 1e+06]
Objective range [1e+00, 6e+07]
Bounds range [0e+00, 0e+00]
RHS range [2e+08, 2e+08]
User MIP start produced solution with objective 9.86729e+09 (0.00s)
User MIP start produced solution with objective 9.86675e+09 (0.00s)
User MIP start produced solution with objective 9.86654e+09 (0.01s)
User MIP start produced solution with objective 9.8661e+09 (0.01s)
Loaded user MIP start with objective 9.8661e+09
Presolve time: 0.00s
Presolved: 1001 rows, 1000 columns, 2500 nonzeros
Variable types: 500 continuous, 500 integer (500 binary)
Root relaxation: objective 9.865344e+09, 510 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 9.8653e+09 0 1 9.8661e+09 9.8653e+09 0.01% - 0s
Explored 1 nodes (510 simplex iterations) in 0.02 seconds (0.01 work units)
Thread count was 32 (of 32 available processors)
Solution count 4: 9.8661e+09 9.86654e+09 9.86675e+09 9.86729e+09
Optimal solution found (tolerance 1.00e-04)
Best objective 9.866096485614e+09, best bound 9.865343669936e+09, gap 0.0076%
User-callback calls 182, time in user-callback 0.00 sec
objective_value(model.inner) = 9.866096485613789e9
</pre></div></div>
</div>
</section>
</section>
</div>
<div class='prev-next-bottom'>
<a class='left-prev' id="prev-link" href="../getting-started-gurobipy/" title="previous page"><span class="section-number">2. </span>Getting started (Gurobipy)</a>
<a class='right-next' id="next-link" href="../cuts-gurobipy/" title="next page"><span class="section-number">4. </span>User cuts and lazy constraints</a>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

@ -0,0 +1,858 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "6b8983b1",
"metadata": {
"tags": []
},
"source": [
"# Getting started (Pyomo)\n",
"\n",
"## Introduction\n",
"\n",
"**MIPLearn** is an open source framework that uses machine learning (ML) to accelerate the performance of mixed-integer programming solvers (e.g. Gurobi, CPLEX, XPRESS). In this tutorial, we will:\n",
"\n",
"1. Install the Python/Pyomo version of MIPLearn\n",
"2. Model a simple optimization problem using Pyomo\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 Pyomo persistent solvers (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"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "02f0a927",
"metadata": {},
"source": [
"## Installation\n",
"\n",
"MIPLearn is available in two versions:\n",
"\n",
"- Python version, compatible with the Pyomo and Gurobipy modeling languages,\n",
"- Julia version, compatible with the JuMP modeling language.\n",
"\n",
"In this tutorial, we will demonstrate how to use and install the Python/Pyomo version of the package. The first step is to install Python 3.8+ in your computer. See the [official Python website for more instructions](https://www.python.org/downloads/). After Python is installed, we proceed to install MIPLearn using `pip`:\n",
"\n",
"```\n",
"$ pip install MIPLearn==0.3\n",
"```\n",
"\n",
"In addition to MIPLearn itself, we will also install Gurobi 10.0, a state-of-the-art commercial MILP solver. This step also install a demo license for Gurobi, which should able to solve the small optimization problems in this tutorial. A license is required for solving larger-scale problems.\n",
"\n",
"```\n",
"$ pip install 'gurobipy>=10,<10.1'\n",
"```"
]
},
{
"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 a utility company needs to decide which electrical generators should be online at each hour of the day, as well as how much power should each generator produce. More specifically, assume that the 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 the 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. The total amount of power to be produced needs to be exactly equal to the total demand $d$ (in megawatts).\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:"
]
},
{
"cell_type": "markdown",
"id": "f12c3702",
"metadata": {},
"source": [
"$$\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",
"$$"
]
},
{
"cell_type": "markdown",
"id": "be3989ed",
"metadata": {},
"source": [
"<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.\n",
"\n",
"</div>"
]
},
{
"cell_type": "markdown",
"id": "a5fd33f6",
"metadata": {},
"source": [
"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": 1,
"id": "22a67170-10b4-43d3-8708-014d91141e73",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:00:03.278853343Z",
"start_time": "2023-06-06T20:00:03.123324067Z"
},
"tags": []
},
"outputs": [],
"source": [
"from dataclasses import dataclass\n",
"from typing import List\n",
"\n",
"import numpy as np\n",
"\n",
"\n",
"@dataclass\n",
"class UnitCommitmentData:\n",
" demand: float\n",
" pmin: List[float]\n",
" pmax: List[float]\n",
" cfix: List[float]\n",
" cvar: List[float]"
]
},
{
"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. The function accepts `UnitCommitmentData`, the data structure we previously defined, or the path to a compressed pickle file containing this data."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "2f67032f-0d74-4317-b45c-19da0ec859e9",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:00:45.890126754Z",
"start_time": "2023-06-06T20:00:45.637044282Z"
}
},
"outputs": [],
"source": [
"import pyomo.environ as pe\n",
"from typing import Union\n",
"from miplearn.io import read_pkl_gz\n",
"from miplearn.solvers.pyomo import PyomoModel\n",
"\n",
"\n",
"def build_uc_model(data: Union[str, UnitCommitmentData]) -> PyomoModel:\n",
" if isinstance(data, str):\n",
" data = read_pkl_gz(data)\n",
"\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] + data.cvar[i] * model.y[i] 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 PyomoModel(model, \"gurobi_persistent\")"
]
},
{
"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": 3,
"id": "2a896f47",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:01:10.993801745Z",
"start_time": "2023-06-06T20:01:10.887580927Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Restricted license - for non-production use only - expires 2024-10-28\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 7 rows, 6 columns and 15 nonzeros\n",
"Model fingerprint: 0x15c7a953\n",
"Variable types: 3 continuous, 3 integer (3 binary)\n",
"Coefficient statistics:\n",
" Matrix range [1e+00, 7e+01]\n",
" Objective range [2e+00, 7e+02]\n",
" Bounds range [1e+00, 1e+00]\n",
" RHS range [1e+02, 1e+02]\n",
"Presolve removed 2 rows and 1 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 5 rows, 5 columns, 13 nonzeros\n",
"Variable types: 0 continuous, 5 integer (3 binary)\n",
"Found heuristic solution: objective 1400.0000000\n",
"\n",
"Root relaxation: objective 1.035000e+03, 3 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 1035.00000 0 1 1400.00000 1035.00000 26.1% - 0s\n",
" 0 0 1105.71429 0 1 1400.00000 1105.71429 21.0% - 0s\n",
"* 0 0 0 1320.0000000 1320.00000 0.00% - 0s\n",
"\n",
"Explored 1 nodes (5 simplex iterations) in 0.01 seconds (0.00 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 2: 1320 1400 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 1.320000000000e+03, best bound 1.320000000000e+03, gap 0.0000%\n",
"WARNING: Cannot get reduced costs for MIP.\n",
"WARNING: Cannot get duals for MIP.\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",
"model.optimize()\n",
"print(\"obj =\", model.inner.obj())\n",
"print(\"x =\", [model.inner.x[i].value for i in range(3)])\n",
"print(\"y =\", [model.inner.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": "01f576e1-1790-425e-9e5c-9fa07b6f4c26",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
" \n",
"Notes\n",
" \n",
"- In the example above, `PyomoModel` is just a thin wrapper around a standard Pyomo model. This wrapper allows MIPLearn to be solver- and modeling-language-agnostic. The wrapper provides only a few basic methods, such as `optimize`. For more control, and to query the solution, the original Pyomo model can be accessed through `model.inner`, as illustrated above. \n",
"- To use CPLEX or XPRESS, instead of Gurobi, replace `gurobi_persistent` by `cplex_persistent` or `xpress_persistent` in the `build_uc_model`. Note that only persistent Pyomo solvers are currently supported. Pull requests adding support for other types of solver are very welcome.\n",
"</div>"
]
},
{
"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** solver, which can optimize 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": 4,
"id": "5eb09fab",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:02:27.324208900Z",
"start_time": "2023-06-06T20:02:26.990044230Z"
}
},
"outputs": [],
"source": [
"from scipy.stats import uniform\n",
"from typing import List\n",
"import random\n",
"\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 _ 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 on multiple machines. 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": 5,
"id": "6156752c",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:03:04.782830561Z",
"start_time": "2023-06-06T20:03:04.530421396Z"
}
},
"outputs": [],
"source": [
"from miplearn.io import write_pkl_gz\n",
"\n",
"data = random_uc_data(samples=500, n=500)\n",
"train_data = write_pkl_gz(data[0:450], \"uc/train\")\n",
"test_data = write_pkl_gz(data[450:500], \"uc/test\")"
]
},
{
"cell_type": "markdown",
"id": "b17af877",
"metadata": {},
"source": [
"Finally, we use `BasicCollector` to collect the optimal solutions and other useful training data for all training instances. The data is stored in HDF5 files `uc/train/00000.h5`, `uc/train/00001.h5`, etc. The optimization models are also exported to compressed MPS files `uc/train/00000.mps.gz`, `uc/train/00001.mps.gz`, etc."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "7623f002",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:03:35.571497019Z",
"start_time": "2023-06-06T20:03:25.804104036Z"
}
},
"outputs": [],
"source": [
"from miplearn.collectors.basic import BasicCollector\n",
"\n",
"bc = BasicCollector()\n",
"bc.collect(train_data, build_uc_model, n_jobs=4)"
]
},
{
"cell_type": "markdown",
"id": "c42b1be1-9723-4827-82d8-974afa51ef9f",
"metadata": {},
"source": [
"## Training and solving test instances"
]
},
{
"cell_type": "markdown",
"id": "a33c6aa4-f0b8-4ccb-9935-01f7d7de2a1c",
"metadata": {},
"source": [
"With training data in hand, we can now design and train a machine learning model to accelerate solver performance. In this tutorial, for illustration purposes, we will use ML to generate a good warm start using $k$-nearest neighbors. More specifically, the strategy is to:\n",
"\n",
"1. Memorize the optimal solutions of all training instances;\n",
"2. Given a test instance, find the 25 most similar training instances, based on constraint right-hand sides;\n",
"3. Merge their optimal solutions into a single partial solution; specifically, only assign values to the binary variables that agree unanimously.\n",
"4. Provide this partial solution to the solver as a warm start.\n",
"\n",
"This simple strategy can be implemented as shown below, using `MemorizingPrimalComponent`. For more advanced strategies, and for the usage of more advanced classifiers, see the user guide."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "435f7bf8-4b09-4889-b1ec-b7b56e7d8ed2",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:05:20.497772794Z",
"start_time": "2023-06-06T20:05:20.484821405Z"
}
},
"outputs": [],
"source": [
"from sklearn.neighbors import KNeighborsClassifier\n",
"from miplearn.components.primal.actions import SetWarmStart\n",
"from miplearn.components.primal.mem import (\n",
" MemorizingPrimalComponent,\n",
" MergeTopSolutions,\n",
")\n",
"from miplearn.extractors.fields import H5FieldsExtractor\n",
"\n",
"comp = MemorizingPrimalComponent(\n",
" clf=KNeighborsClassifier(n_neighbors=25),\n",
" extractor=H5FieldsExtractor(\n",
" instance_fields=[\"static_constr_rhs\"],\n",
" ),\n",
" constructor=MergeTopSolutions(25, [0.0, 1.0]),\n",
" action=SetWarmStart(),\n",
")"
]
},
{
"cell_type": "markdown",
"id": "9536e7e4-0b0d-49b0-bebd-4a848f839e94",
"metadata": {},
"source": [
"Having defined the ML strategy, we next construct `LearningSolver`, train the ML component and optimize one of the test instances."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "9d13dd50-3dcf-4673-a757-6f44dcc0dedf",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:05:22.672002339Z",
"start_time": "2023-06-06T20:05:21.447466634Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x5e67c6ee\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 [3e+08, 3e+08]\n",
"Presolve removed 1000 rows and 500 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1 rows, 500 columns, 500 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 6.6166537e+09 5.648803e+04 0.000000e+00 0s\n",
" 1 8.2906219e+09 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.290621916e+09\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x4a7cfe2b\n",
"Variable types: 500 continuous, 500 integer (500 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 [3e+08, 3e+08]\n",
"\n",
"User MIP start produced solution with objective 8.29153e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.29153e+09 (0.01s)\n",
"Loaded user MIP start with objective 8.29153e+09\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"\n",
"Root relaxation: objective 8.290622e+09, 512 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 8.2906e+09 0 1 8.2915e+09 8.2906e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 3 8.2915e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2915e+09 8.2907e+09 0.01% - 0s\n",
" 0 0 8.2907e+09 0 2 8.2915e+09 8.2907e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 1\n",
" Flow cover: 2\n",
"\n",
"Explored 1 nodes (565 simplex iterations) in 0.04 seconds (0.01 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 1: 8.29153e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.291528276179e+09, best bound 8.290733258025e+09, gap 0.0096%\n",
"WARNING: Cannot get reduced costs for MIP.\n",
"WARNING: Cannot get duals for MIP.\n"
]
},
{
"data": {
"text/plain": [
"{}"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from miplearn.solvers.learning import LearningSolver\n",
"\n",
"solver_ml = LearningSolver(components=[comp])\n",
"solver_ml.fit(train_data)\n",
"solver_ml.optimize(test_data[0], build_uc_model)"
]
},
{
"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 very close to the optimal solution to the problem. Now let us repeat the code above, but a solver which does not apply any ML strategies. Note that our previously-defined component is not provided."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "2ff391ed-e855-4228-aa09-a7641d8c2893",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:05:46.969575966Z",
"start_time": "2023-06-06T20:05:46.420803286Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x5e67c6ee\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 [3e+08, 3e+08]\n",
"Presolve removed 1000 rows and 500 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1 rows, 500 columns, 500 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 6.6166537e+09 5.648803e+04 0.000000e+00 0s\n",
" 1 8.2906219e+09 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.290621916e+09\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x8a0f9587\n",
"Variable types: 500 continuous, 500 integer (500 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 [3e+08, 3e+08]\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"Found heuristic solution: objective 9.757128e+09\n",
"\n",
"Root relaxation: objective 8.290622e+09, 512 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 8.2906e+09 0 1 9.7571e+09 8.2906e+09 15.0% - 0s\n",
"H 0 0 8.298273e+09 8.2906e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 4 8.2983e+09 8.2907e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2983e+09 8.2907e+09 0.09% - 0s\n",
" 0 0 8.2907e+09 0 4 8.2983e+09 8.2907e+09 0.09% - 0s\n",
"H 0 0 8.293980e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 5 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 1 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2907e+09 0 2 8.2940e+09 8.2907e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 1 8.2940e+09 8.2908e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 4 8.2940e+09 8.2908e+09 0.04% - 0s\n",
" 0 0 8.2908e+09 0 4 8.2940e+09 8.2908e+09 0.04% - 0s\n",
"H 0 0 8.291465e+09 8.2908e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Gomory: 2\n",
" MIR: 1\n",
"\n",
"Explored 1 nodes (1025 simplex iterations) in 0.12 seconds (0.03 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 4: 8.29147e+09 8.29398e+09 8.29827e+09 9.75713e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.291465302389e+09, best bound 8.290781665333e+09, gap 0.0082%\n",
"WARNING: Cannot get reduced costs for MIP.\n",
"WARNING: Cannot get duals for MIP.\n"
]
},
{
"data": {
"text/plain": [
"{}"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"solver_baseline = LearningSolver(components=[])\n",
"solver_baseline.fit(train_data)\n",
"solver_baseline.optimize(test_data[0], build_uc_model)"
]
},
{
"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, but the difference can be significant for larger problems."
]
},
{
"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. 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": 10,
"id": "67a6cd18",
"metadata": {
"ExecuteTime": {
"end_time": "2023-06-06T20:06:26.913448568Z",
"start_time": "2023-06-06T20:06:26.169047914Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x2dfe4e1c\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 [3e+08, 3e+08]\n",
"Presolve removed 1000 rows and 500 columns\n",
"Presolve time: 0.00s\n",
"Presolved: 1 rows, 500 columns, 500 nonzeros\n",
"\n",
"Iteration Objective Primal Inf. Dual Inf. Time\n",
" 0 6.5917580e+09 5.627453e+04 0.000000e+00 0s\n",
" 1 8.2535968e+09 0.000000e+00 0.000000e+00 0s\n",
"\n",
"Solved in 1 iterations and 0.01 seconds (0.00 work units)\n",
"Optimal objective 8.253596777e+09\n",
"Set parameter QCPDual to value 1\n",
"Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)\n",
"\n",
"CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]\n",
"Thread count: 10 physical cores, 20 logical processors, using up to 20 threads\n",
"\n",
"Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros\n",
"Model fingerprint: 0x0f0924a1\n",
"Variable types: 500 continuous, 500 integer (500 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 [3e+08, 3e+08]\n",
"\n",
"User MIP start produced solution with objective 8.25814e+09 (0.00s)\n",
"User MIP start produced solution with objective 8.25512e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25483e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25483e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25483e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25459e+09 (0.01s)\n",
"User MIP start produced solution with objective 8.25459e+09 (0.01s)\n",
"Loaded user MIP start with objective 8.25459e+09\n",
"\n",
"Presolve time: 0.00s\n",
"Presolved: 1001 rows, 1000 columns, 2500 nonzeros\n",
"Variable types: 500 continuous, 500 integer (500 binary)\n",
"\n",
"Root relaxation: objective 8.253597e+09, 512 iterations, 0.00 seconds (0.00 work units)\n",
"\n",
" Nodes | Current Node | Objective Bounds | Work\n",
" Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time\n",
"\n",
" 0 0 8.2536e+09 0 1 8.2546e+09 8.2536e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 3 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 1 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 4 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2537e+09 0 4 8.2546e+09 8.2537e+09 0.01% - 0s\n",
" 0 0 8.2538e+09 0 4 8.2546e+09 8.2538e+09 0.01% - 0s\n",
" 0 0 8.2538e+09 0 5 8.2546e+09 8.2538e+09 0.01% - 0s\n",
" 0 0 8.2538e+09 0 6 8.2546e+09 8.2538e+09 0.01% - 0s\n",
"\n",
"Cutting planes:\n",
" Cover: 1\n",
" MIR: 2\n",
" StrongCG: 1\n",
" Flow cover: 1\n",
"\n",
"Explored 1 nodes (575 simplex iterations) in 0.09 seconds (0.01 work units)\n",
"Thread count was 20 (of 20 available processors)\n",
"\n",
"Solution count 4: 8.25459e+09 8.25483e+09 8.25512e+09 8.25814e+09 \n",
"\n",
"Optimal solution found (tolerance 1.00e-04)\n",
"Best objective 8.254590409970e+09, best bound 8.253768093811e+09, gap 0.0100%\n",
"WARNING: Cannot get reduced costs for MIP.\n",
"WARNING: Cannot get duals for MIP.\n",
"obj = 8254590409.96973\n",
" x = [1.0, 1.0, 0.0, 1.0, 1.0]\n",
" y = [935662.0949262811, 1604270.0218116897, 0.0, 1369560.835229226, 602828.5321028307]\n"
]
}
],
"source": [
"data = random_uc_data(samples=1, n=500)[0]\n",
"model = build_uc_model(data)\n",
"solver_ml.optimize(model)\n",
"print(\"obj =\", model.inner.obj())\n",
"print(\" x =\", [model.inner.x[i].value for i in range(5)])\n",
"print(\" y =\", [model.inner.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 (ipykernel)",
"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.11.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,909 @@
<!DOCTYPE html>
<html lang="en" data-content_root="../../">
<head>
<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" />
<title>1. Getting started (Pyomo) &#8212; MIPLearn 0.4</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" type="text/css" href="../../_static/pygments.css?v=362ab14a" />
<link rel="stylesheet" type="text/css" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css?v=b0dfe17c" />
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css?v=2aa19091" />
<link rel="stylesheet" type="text/css" href="../../_static/custom.css?v=f8244a84" />
<link rel="preload" as="script" href="../../_static/js/index.1c5a1a01449ed65a7b51.js">
<script src="../../_static/documentation_options.js?v=751a5dd3"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></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?v=7c4c3336"></script>
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script>
<script>window.MathJax = {"tex": {"inlineMath": [["$", "$"], ["\\(", "\\)"]], "processEscapes": true}, "options": {"ignoreHtmlClass": "tex2jax_ignore|mathjax_ignore|document", "processHtmlClass": "tex2jax_process|mathjax_process|math|output_area"}}</script>
<script defer="defer" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<link rel="index" title="Index" href="../../genindex/" />
<link rel="search" title="Search" href="../../search/" />
<link rel="next" title="2. Getting started (Gurobipy)" href="../getting-started-gurobipy/" />
<link rel="prev" title="MIPLearn" href="../../" />
<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.4</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" role="heading">
<span class="caption-text">
Tutorials
</span>
</p>
<ul class="current nav bd-sidenav">
<li class="toctree-l1 current active">
<a class="current reference internal" href="#">
1. Getting started (Pyomo)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../getting-started-gurobipy/">
2. Getting started (Gurobipy)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../getting-started-jump/">
3. Getting started (JuMP)
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../cuts-gurobipy/">
4. User cuts and lazy constraints
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
User Guide
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../guide/problems/">
5. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/collectors/">
6. Training Data Collectors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/features/">
7. Feature Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/primal/">
8. Primal Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../guide/solvers/">
9. Learning Solver
</a>
</li>
</ul>
<p class="caption" role="heading">
<span class="caption-text">
Python API Reference
</span>
</p>
<ul class="nav bd-sidenav">
<li class="toctree-l1">
<a class="reference internal" href="../../api/problems/">
10. Benchmark Problems
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/collectors/">
11. Collectors &amp; Extractors
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/components/">
12. Components
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/solvers/">
13. Solvers
</a>
</li>
<li class="toctree-l1">
<a class="reference internal" href="../../api/helpers/">
14. 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-pyomo.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">
1.1. Introduction
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Installation">
1.2. Installation
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Modeling-a-simple-optimization-problem">
1.3. 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">
1.4. Generating training data
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Training-and-solving-test-instances">
1.5. Training and solving test instances
</a>
</li>
<li class="toc-h2 nav-item toc-entry">
<a class="reference internal nav-link" href="#Accessing-the-solution">
1.6. 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>
<section id="Getting-started-(Pyomo)">
<h1><span class="section-number">1. </span>Getting started (Pyomo)<a class="headerlink" href="#Getting-started-(Pyomo)" title="Link to this heading"></a></h1>
<section id="Introduction">
<h2><span class="section-number">1.1. </span>Introduction<a class="headerlink" href="#Introduction" title="Link to this heading"></a></h2>
<p><strong>MIPLearn</strong> is an open source framework that uses machine learning (ML) to accelerate the performance of mixed-integer programming solvers (e.g. Gurobi, CPLEX, XPRESS). 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 Pyomo</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 Pyomo persistent solvers (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>
</section>
<section id="Installation">
<h2><span class="section-number">1.2. </span>Installation<a class="headerlink" href="#Installation" title="Link to this heading"></a></h2>
<p>MIPLearn is available in two versions:</p>
<ul class="simple">
<li><p>Python version, compatible with the Pyomo and Gurobipy modeling languages,</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="highlight-none notranslate"><div class="highlight"><pre><span></span>$ pip install MIPLearn==0.3
</pre></div>
</div>
<p>In addition to MIPLearn itself, we will also install Gurobi 10.0, a state-of-the-art commercial MILP solver. This step also install a demo license for Gurobi, which should able to solve the small optimization problems in this tutorial. A license is required for solving larger-scale problems.</p>
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>$ pip install &#39;gurobipy&gt;=10,&lt;10.1&#39;
</pre></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>
</section>
<section id="Modeling-a-simple-optimization-problem">
<h2><span class="section-number">1.3. </span>Modeling a simple optimization problem<a class="headerlink" href="#Modeling-a-simple-optimization-problem" title="Link to this heading"></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 a utility company needs to decide which electrical generators should be online at each hour of the day, as well as how much power should each generator produce. More specifically, assume that the 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 the 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. 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).</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.</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>[1]:
</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">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">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">List</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span>
<span class="n">pmax</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span>
<span class="n">cfix</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">float</span><span class="p">]</span>
<span class="n">cvar</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">float</span><span class="p">]</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. The function accepts <code class="docutils literal notranslate"><span class="pre">UnitCommitmentData</span></code>, the data structure we previously defined, or the path to a compressed pickle file containing this data.</p>
<div class="nbinput nblast 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="kn">import</span> <span class="nn">pyomo.environ</span> <span class="k">as</span> <span class="nn">pe</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Union</span>
<span class="kn">from</span> <span class="nn">miplearn.io</span> <span class="kn">import</span> <span class="n">read_pkl_gz</span>
<span class="kn">from</span> <span class="nn">miplearn.solvers.pyomo</span> <span class="kn">import</span> <span class="n">PyomoModel</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">Union</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">UnitCommitmentData</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">PyomoModel</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">read_pkl_gz</span><span class="p">(</span><span class="n">data</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">PyomoModel</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="s2">&quot;gurobi_persistent&quot;</span><span class="p">)</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>[3]:
</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">model</span><span class="o">.</span><span class="n">optimize</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">inner</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">inner</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">inner</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>
Restricted license - for non-production use only - expires 2024-10-28
Set parameter QCPDual to value 1
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 7 rows, 6 columns and 15 nonzeros
Model fingerprint: 0x15c7a953
Variable types: 3 continuous, 3 integer (3 binary)
Coefficient statistics:
Matrix range [1e+00, 7e+01]
Objective range [2e+00, 7e+02]
Bounds range [1e+00, 1e+00]
RHS range [1e+02, 1e+02]
Presolve removed 2 rows and 1 columns
Presolve time: 0.00s
Presolved: 5 rows, 5 columns, 13 nonzeros
Variable types: 0 continuous, 5 integer (3 binary)
Found heuristic solution: objective 1400.0000000
Root relaxation: objective 1.035000e+03, 3 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 1035.00000 0 1 1400.00000 1035.00000 26.1% - 0s
0 0 1105.71429 0 1 1400.00000 1105.71429 21.0% - 0s
* 0 0 0 1320.0000000 1320.00000 0.00% - 0s
Explored 1 nodes (5 simplex iterations) in 0.01 seconds (0.00 work units)
Thread count was 20 (of 20 available processors)
Solution count 2: 1320 1400
Optimal solution found (tolerance 1.00e-04)
Best objective 1.320000000000e+03, best bound 1.320000000000e+03, gap 0.0000%
WARNING: Cannot get reduced costs for MIP.
WARNING: Cannot get duals for MIP.
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 class="admonition note">
<p class="admonition-title">Notes</p>
<ul class="simple">
<li><p>In the example above, <code class="docutils literal notranslate"><span class="pre">PyomoModel</span></code> is just a thin wrapper around a standard Pyomo model. This wrapper allows MIPLearn to be solver- and modeling-language-agnostic. The wrapper provides only a few basic methods, such as <code class="docutils literal notranslate"><span class="pre">optimize</span></code>. For more control, and to query the solution, the original Pyomo model can be accessed through <code class="docutils literal notranslate"><span class="pre">model.inner</span></code>, as illustrated above.</p></li>
<li><p>To use CPLEX or XPRESS, instead of Gurobi, replace <code class="docutils literal notranslate"><span class="pre">gurobi_persistent</span></code> by <code class="docutils literal notranslate"><span class="pre">cplex_persistent</span></code> or <code class="docutils literal notranslate"><span class="pre">xpress_persistent</span></code> in the <code class="docutils literal notranslate"><span class="pre">build_uc_model</span></code>. Note that only persistent Pyomo solvers are currently supported. Pull requests adding support for other types of solver are very welcome.</p></li>
</ul>
</div>
</section>
<section id="Generating-training-data">
<h2><span class="section-number">1.4. </span>Generating training data<a class="headerlink" href="#Generating-training-data" title="Link to this heading"></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> solver, which can optimize 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>[4]:
</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">_</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 on multiple
machines. 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>[5]:
</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.io</span> <span class="kn">import</span> <span class="n">write_pkl_gz</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">500</span><span class="p">)</span>
<span class="n">train_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="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_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="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">BasicCollector</span></code> to collect the optimal solutions and other useful training data for all training instances. The data is 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. The optimization models are also exported to compressed MPS files <code class="docutils literal notranslate"><span class="pre">uc/train/00000.mps.gz</span></code>, <code class="docutils literal notranslate"><span class="pre">uc/train/00001.mps.gz</span></code>, etc.</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">miplearn.collectors.basic</span> <span class="kn">import</span> <span class="n">BasicCollector</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_uc_model</span><span class="p">,</span> <span class="n">n_jobs</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
</pre></div>
</div>
</div>
</section>
<section id="Training-and-solving-test-instances">
<h2><span class="section-number">1.5. </span>Training and solving test instances<a class="headerlink" href="#Training-and-solving-test-instances" title="Link to this heading"></a></h2>
<p>With training data in hand, we can now design and train a machine learning model to accelerate solver performance. In this tutorial, for illustration purposes, we will use ML to generate a good warm start using <span class="math notranslate nohighlight">\(k\)</span>-nearest neighbors. More specifically, the strategy is to:</p>
<ol class="arabic simple">
<li><p>Memorize the optimal solutions of all training instances;</p></li>
<li><p>Given a test instance, find the 25 most similar training instances, based on constraint right-hand sides;</p></li>
<li><p>Merge their optimal solutions into a single partial solution; specifically, only assign values to the binary variables that agree unanimously.</p></li>
<li><p>Provide this partial solution to the solver as a warm start.</p></li>
</ol>
<p>This simple strategy can be implemented as shown below, using <code class="docutils literal notranslate"><span class="pre">MemorizingPrimalComponent</span></code>. For more advanced strategies, and for the usage of more advanced classifiers, see the user guide.</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">sklearn.neighbors</span> <span class="kn">import</span> <span class="n">KNeighborsClassifier</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.mem</span> <span class="kn">import</span> <span class="p">(</span>
<span class="n">MemorizingPrimalComponent</span><span class="p">,</span>
<span class="n">MergeTopSolutions</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">from</span> <span class="nn">miplearn.extractors.fields</span> <span class="kn">import</span> <span class="n">H5FieldsExtractor</span>
<span class="n">comp</span> <span class="o">=</span> <span class="n">MemorizingPrimalComponent</span><span class="p">(</span>
<span class="n">clf</span><span class="o">=</span><span class="n">KNeighborsClassifier</span><span class="p">(</span><span class="n">n_neighbors</span><span class="o">=</span><span class="mi">25</span><span class="p">),</span>
<span class="n">extractor</span><span class="o">=</span><span class="n">H5FieldsExtractor</span><span class="p">(</span>
<span class="n">instance_fields</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;static_constr_rhs&quot;</span><span class="p">],</span>
<span class="p">),</span>
<span class="n">constructor</span><span class="o">=</span><span class="n">MergeTopSolutions</span><span class="p">(</span><span class="mi">25</span><span class="p">,</span> <span class="p">[</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">]),</span>
<span class="n">action</span><span class="o">=</span><span class="n">SetWarmStart</span><span class="p">(),</span>
<span class="p">)</span>
</pre></div>
</div>
</div>
<p>Having defined the ML strategy, we next construct <code class="docutils literal notranslate"><span class="pre">LearningSolver</span></code>, train the ML component and optimize one of the test instances.</p>
<div class="nbinput docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[8]:
</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.solvers.learning</span> <span class="kn">import</span> <span class="n">LearningSolver</span>
<span class="n">solver_ml</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">comp</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_data</span><span class="p">)</span>
<span class="n">solver_ml</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_uc_model</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="nboutput docutils container">
<div class="prompt empty docutils container">
</div>
<div class="output_area docutils container">
<div class="highlight"><pre>
Set parameter QCPDual to value 1
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros
Model fingerprint: 0x5e67c6ee
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [3e+08, 3e+08]
Presolve removed 1000 rows and 500 columns
Presolve time: 0.00s
Presolved: 1 rows, 500 columns, 500 nonzeros
Iteration Objective Primal Inf. Dual Inf. Time
0 6.6166537e+09 5.648803e+04 0.000000e+00 0s
1 8.2906219e+09 0.000000e+00 0.000000e+00 0s
Solved in 1 iterations and 0.01 seconds (0.00 work units)
Optimal objective 8.290621916e+09
Set parameter QCPDual to value 1
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros
Model fingerprint: 0x4a7cfe2b
Variable types: 500 continuous, 500 integer (500 binary)
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [3e+08, 3e+08]
User MIP start produced solution with objective 8.29153e+09 (0.01s)
User MIP start produced solution with objective 8.29153e+09 (0.01s)
Loaded user MIP start with objective 8.29153e+09
Presolve time: 0.00s
Presolved: 1001 rows, 1000 columns, 2500 nonzeros
Variable types: 500 continuous, 500 integer (500 binary)
Root relaxation: objective 8.290622e+09, 512 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 8.2906e+09 0 1 8.2915e+09 8.2906e+09 0.01% - 0s
0 0 8.2907e+09 0 3 8.2915e+09 8.2907e+09 0.01% - 0s
0 0 8.2907e+09 0 1 8.2915e+09 8.2907e+09 0.01% - 0s
0 0 8.2907e+09 0 2 8.2915e+09 8.2907e+09 0.01% - 0s
Cutting planes:
Gomory: 1
Flow cover: 2
Explored 1 nodes (565 simplex iterations) in 0.04 seconds (0.01 work units)
Thread count was 20 (of 20 available processors)
Solution count 1: 8.29153e+09
Optimal solution found (tolerance 1.00e-04)
Best objective 8.291528276179e+09, best bound 8.290733258025e+09, gap 0.0096%
WARNING: Cannot get reduced costs for MIP.
WARNING: Cannot get duals for MIP.
</pre></div></div>
</div>
<div class="nboutput nblast docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[8]:
</pre></div>
</div>
<div class="output_area docutils container">
<div class="highlight"><pre>
{}
</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 very close to the optimal solution to the problem. Now let us repeat the code above, but a solver which does not apply any ML strategies. Note that our previously-defined component is not provided.</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_baseline</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">solver_baseline</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_baseline</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_uc_model</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="nboutput docutils container">
<div class="prompt empty docutils container">
</div>
<div class="output_area docutils container">
<div class="highlight"><pre>
Set parameter QCPDual to value 1
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros
Model fingerprint: 0x5e67c6ee
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [3e+08, 3e+08]
Presolve removed 1000 rows and 500 columns
Presolve time: 0.00s
Presolved: 1 rows, 500 columns, 500 nonzeros
Iteration Objective Primal Inf. Dual Inf. Time
0 6.6166537e+09 5.648803e+04 0.000000e+00 0s
1 8.2906219e+09 0.000000e+00 0.000000e+00 0s
Solved in 1 iterations and 0.01 seconds (0.00 work units)
Optimal objective 8.290621916e+09
Set parameter QCPDual to value 1
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros
Model fingerprint: 0x8a0f9587
Variable types: 500 continuous, 500 integer (500 binary)
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [3e+08, 3e+08]
Presolve time: 0.00s
Presolved: 1001 rows, 1000 columns, 2500 nonzeros
Variable types: 500 continuous, 500 integer (500 binary)
Found heuristic solution: objective 9.757128e+09
Root relaxation: objective 8.290622e+09, 512 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 8.2906e+09 0 1 9.7571e+09 8.2906e+09 15.0% - 0s
H 0 0 8.298273e+09 8.2906e+09 0.09% - 0s
0 0 8.2907e+09 0 4 8.2983e+09 8.2907e+09 0.09% - 0s
0 0 8.2907e+09 0 1 8.2983e+09 8.2907e+09 0.09% - 0s
0 0 8.2907e+09 0 4 8.2983e+09 8.2907e+09 0.09% - 0s
H 0 0 8.293980e+09 8.2907e+09 0.04% - 0s
0 0 8.2907e+09 0 5 8.2940e+09 8.2907e+09 0.04% - 0s
0 0 8.2907e+09 0 1 8.2940e+09 8.2907e+09 0.04% - 0s
0 0 8.2907e+09 0 2 8.2940e+09 8.2907e+09 0.04% - 0s
0 0 8.2908e+09 0 1 8.2940e+09 8.2908e+09 0.04% - 0s
0 0 8.2908e+09 0 4 8.2940e+09 8.2908e+09 0.04% - 0s
0 0 8.2908e+09 0 4 8.2940e+09 8.2908e+09 0.04% - 0s
H 0 0 8.291465e+09 8.2908e+09 0.01% - 0s
Cutting planes:
Gomory: 2
MIR: 1
Explored 1 nodes (1025 simplex iterations) in 0.12 seconds (0.03 work units)
Thread count was 20 (of 20 available processors)
Solution count 4: 8.29147e+09 8.29398e+09 8.29827e+09 9.75713e+09
Optimal solution found (tolerance 1.00e-04)
Best objective 8.291465302389e+09, best bound 8.290781665333e+09, gap 0.0082%
WARNING: Cannot get reduced costs for MIP.
WARNING: Cannot get duals for MIP.
</pre></div></div>
</div>
<div class="nboutput nblast docutils container">
<div class="prompt highlight-none notranslate"><div class="highlight"><pre><span></span>[9]:
</pre></div>
</div>
<div class="output_area docutils container">
<div class="highlight"><pre>
{}
</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, but the difference can be significant for larger problems.</p>
</section>
<section id="Accessing-the-solution">
<h2><span class="section-number">1.6. </span>Accessing the solution<a class="headerlink" href="#Accessing-the-solution" title="Link to this heading"></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. 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>[10]:
</pre></div>
</div>
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></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">500</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="n">solver_ml</span><span class="o">.</span><span class="n">optimize</span><span class="p">(</span><span class="n">model</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">inner</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">inner</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">inner</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>
Set parameter QCPDual to value 1
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros
Model fingerprint: 0x2dfe4e1c
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [3e+08, 3e+08]
Presolve removed 1000 rows and 500 columns
Presolve time: 0.00s
Presolved: 1 rows, 500 columns, 500 nonzeros
Iteration Objective Primal Inf. Dual Inf. Time
0 6.5917580e+09 5.627453e+04 0.000000e+00 0s
1 8.2535968e+09 0.000000e+00 0.000000e+00 0s
Solved in 1 iterations and 0.01 seconds (0.00 work units)
Optimal objective 8.253596777e+09
Set parameter QCPDual to value 1
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (linux64)
CPU model: 13th Gen Intel(R) Core(TM) i7-13800H, instruction set [SSE2|AVX|AVX2]
Thread count: 10 physical cores, 20 logical processors, using up to 20 threads
Optimize a model with 1001 rows, 1000 columns and 2500 nonzeros
Model fingerprint: 0x0f0924a1
Variable types: 500 continuous, 500 integer (500 binary)
Coefficient statistics:
Matrix range [1e+00, 2e+06]
Objective range [1e+00, 6e+07]
Bounds range [1e+00, 1e+00]
RHS range [3e+08, 3e+08]
User MIP start produced solution with objective 8.25814e+09 (0.00s)
User MIP start produced solution with objective 8.25512e+09 (0.01s)
User MIP start produced solution with objective 8.25483e+09 (0.01s)
User MIP start produced solution with objective 8.25483e+09 (0.01s)
User MIP start produced solution with objective 8.25483e+09 (0.01s)
User MIP start produced solution with objective 8.25459e+09 (0.01s)
User MIP start produced solution with objective 8.25459e+09 (0.01s)
Loaded user MIP start with objective 8.25459e+09
Presolve time: 0.00s
Presolved: 1001 rows, 1000 columns, 2500 nonzeros
Variable types: 500 continuous, 500 integer (500 binary)
Root relaxation: objective 8.253597e+09, 512 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 8.2536e+09 0 1 8.2546e+09 8.2536e+09 0.01% - 0s
0 0 8.2537e+09 0 3 8.2546e+09 8.2537e+09 0.01% - 0s
0 0 8.2537e+09 0 1 8.2546e+09 8.2537e+09 0.01% - 0s
0 0 8.2537e+09 0 4 8.2546e+09 8.2537e+09 0.01% - 0s
0 0 8.2537e+09 0 4 8.2546e+09 8.2537e+09 0.01% - 0s
0 0 8.2538e+09 0 4 8.2546e+09 8.2538e+09 0.01% - 0s
0 0 8.2538e+09 0 5 8.2546e+09 8.2538e+09 0.01% - 0s
0 0 8.2538e+09 0 6 8.2546e+09 8.2538e+09 0.01% - 0s
Cutting planes:
Cover: 1
MIR: 2
StrongCG: 1
Flow cover: 1
Explored 1 nodes (575 simplex iterations) in 0.09 seconds (0.01 work units)
Thread count was 20 (of 20 available processors)
Solution count 4: 8.25459e+09 8.25483e+09 8.25512e+09 8.25814e+09
Optimal solution found (tolerance 1.00e-04)
Best objective 8.254590409970e+09, best bound 8.253768093811e+09, gap 0.0100%
WARNING: Cannot get reduced costs for MIP.
WARNING: Cannot get duals for MIP.
obj = 8254590409.96973
x = [1.0, 1.0, 0.0, 1.0, 1.0]
y = [935662.0949262811, 1604270.0218116897, 0.0, 1369560.835229226, 602828.5321028307]
</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>
</section>
</section>
</div>
<div class='prev-next-bottom'>
<a class='left-prev' id="prev-link" href="../../" title="previous page">MIPLearn</a>
<a class='right-next' id="next-link" href="../getting-started-gurobipy/" title="next page"><span class="section-number">2. </span>Getting started (Gurobipy)</a>
</div>
</div>
</div>
<footer class="footer mt-5 mt-md-0">
<div class="container">
<p>
&copy; Copyright 2020-2023, UChicago Argonne, LLC.<br/>
</p>
</div>
</footer>
</main>
</div>
</div>
<script src="../../_static/js/index.1c5a1a01449ed65a7b51.js"></script>
</body>
</html>

@ -1 +1 @@
<meta http-equiv="Refresh" content="0; url='/MIPLearn/0.3'" /> <meta http-equiv="Refresh" content="0; url='/MIPLearn/0.4'" />

Loading…
Cancel
Save