mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 17:38:51 -06:00
Update docs
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en" data-content_root="../../">
|
||||
<html>
|
||||
<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" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>4. User cuts and lazy constraints — MIPLearn 0.4</title>
|
||||
|
||||
<link href="../../_static/css/theme.css" rel="stylesheet" />
|
||||
@@ -22,21 +22,26 @@
|
||||
|
||||
|
||||
|
||||
<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="stylesheet" href="../../_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css" type="text/css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/custom.css" />
|
||||
|
||||
<link rel="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 id="documentation_options" data-url_root="../../" src="../../_static/documentation_options.js"></script>
|
||||
<script src="../../_static/jquery.js"></script>
|
||||
<script src="../../_static/underscore.js"></script>
|
||||
<script src="../../_static/doctools.js"></script>
|
||||
<script crossorigin="anonymous" integrity="sha256-Ae2Vz/4ePdIu6ZyI/5ZGsYnb+m0JlOmKPjt6XZ9JJkA=" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js"></script>
|
||||
<script src="../../_static/sphinx-book-theme.12a9622fbb08dcb3a2a40b2c02b83a57.js?v=7c4c3336"></script>
|
||||
<script src="../../_static/sphinx-book-theme.12a9622fbb08dcb3a2a40b2c02b83a57.js"></script>
|
||||
<script async="async" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script>
|
||||
<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/" />
|
||||
@@ -68,7 +73,7 @@
|
||||
<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">
|
||||
<p class="caption">
|
||||
<span class="caption-text">
|
||||
Tutorials
|
||||
</span>
|
||||
@@ -95,7 +100,7 @@
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="caption" role="heading">
|
||||
<p class="caption">
|
||||
<span class="caption-text">
|
||||
User Guide
|
||||
</span>
|
||||
@@ -127,7 +132,7 @@
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="caption" role="heading">
|
||||
<p class="caption">
|
||||
<span class="caption-text">
|
||||
Python API Reference
|
||||
</span>
|
||||
@@ -256,8 +261,8 @@
|
||||
|
||||
<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>
|
||||
<div class="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="Permalink to this headline">¶</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>
|
||||
@@ -269,8 +274,8 @@ size; these constraints are added to the formulation only once the solver finds
|
||||
<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>
|
||||
<div class="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="Permalink to this headline">¶</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 Karp’s 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>
|
||||
@@ -285,109 +290,109 @@ size; these constraints are added to the formulation only once the solver finds
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>import gurobipy as gp
|
||||
from gurobipy import quicksum, GRB, tuplelist
|
||||
from miplearn.solvers.gurobi import GurobiModel
|
||||
import networkx as nx
|
||||
import numpy as np
|
||||
from miplearn.problems.tsp import (
|
||||
TravelingSalesmanData,
|
||||
TravelingSalesmanGenerator,
|
||||
)
|
||||
from scipy.stats import uniform, randint
|
||||
from miplearn.io import write_pkl_gz, read_pkl_gz
|
||||
from miplearn.collectors.basic import BasicCollector
|
||||
from miplearn.solvers.learning import LearningSolver
|
||||
from miplearn.components.lazy.mem import MemorizingLazyComponent
|
||||
from miplearn.extractors.fields import H5FieldsExtractor
|
||||
from sklearn.neighbors import KNeighborsClassifier
|
||||
|
||||
<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>
|
||||
# Set up random seed to make example more reproducible
|
||||
np.random.seed(42)
|
||||
|
||||
<span class="c1"># Set up Python logging</span>
|
||||
<span class="kn">import</span> <span class="nn">logging</span>
|
||||
# Set up Python logging
|
||||
import logging
|
||||
|
||||
<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>
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
|
||||
|
||||
<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>
|
||||
def build_tsp_model_gurobipy_simplified(data):
|
||||
# Read data from file if a filename is provided
|
||||
if isinstance(data, str):
|
||||
data = read_pkl_gz(data)
|
||||
|
||||
<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>
|
||||
# Create empty gurobipy model
|
||||
model = gp.Model()
|
||||
|
||||
<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>
|
||||
# Create set of edges between every pair of cities, for convenience
|
||||
edges = tuplelist(
|
||||
(i, j) for i in range(data.n_cities) for j in range(i + 1, data.n_cities)
|
||||
)
|
||||
|
||||
<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">"x"</span><span class="p">)</span>
|
||||
# Add binary variable x[e] for each edge e
|
||||
x = model.addVars(edges, vtype=GRB.BINARY, name="x")
|
||||
|
||||
<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>
|
||||
# Add objective function
|
||||
model.setObjective(quicksum(x[(i, j)] * data.distances[i, j] for (i, j) in edges))
|
||||
|
||||
<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">"eq_degree"</span><span class="p">,</span>
|
||||
<span class="p">)</span>
|
||||
# Add constraint: must choose two edges adjacent to each city
|
||||
model.addConstrs(
|
||||
(
|
||||
quicksum(x[min(i, j), max(i, j)] for j in range(data.n_cities) if i != j)
|
||||
== 2
|
||||
for i in range(data.n_cities)
|
||||
),
|
||||
name="eq_degree",
|
||||
)
|
||||
|
||||
<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">"""</span>
|
||||
<span class="sd"> Callback function that finds subtours in the current solution.</span>
|
||||
<span class="sd"> """</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>
|
||||
def lazy_separate(m: GurobiModel):
|
||||
"""
|
||||
Callback function that finds subtours in the current solution.
|
||||
"""
|
||||
# Query current value of the x variables
|
||||
x_val = m.inner.cbGetSolution(x)
|
||||
|
||||
<span class="c1"># Initialize empty set of violations</span>
|
||||
<span class="n">violations</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
# Initialize empty set of violations
|
||||
violations = []
|
||||
|
||||
<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">></span> <span class="mf">0.5</span><span class="p">]</span>
|
||||
# Build set of edges we have currently selected
|
||||
selected_edges = [e for e in edges if x_val[e] > 0.5]
|
||||
|
||||
<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>
|
||||
# Build a graph containing the selected edges, using networkx
|
||||
graph = nx.Graph()
|
||||
graph.add_edges_from(selected_edges)
|
||||
|
||||
<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>
|
||||
# For each component of the graph
|
||||
for component in list(nx.connected_components(graph)):
|
||||
|
||||
<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"><</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>
|
||||
# If the component is not the entire graph, we found a
|
||||
# subtour. Add the edge cut to the list of violations.
|
||||
if len(component) < data.n_cities:
|
||||
cut_edges = [
|
||||
[e[0], e[1]]
|
||||
for e in edges
|
||||
if (e[0] in component and e[1] not in component)
|
||||
or (e[0] not in component and e[1] in component)
|
||||
]
|
||||
violations.append(cut_edges)
|
||||
|
||||
<span class="c1"># Return the list of violations</span>
|
||||
<span class="k">return</span> <span class="n">violations</span>
|
||||
# Return the list of violations
|
||||
return violations
|
||||
|
||||
<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">-></span> <span class="kc">None</span><span class="p">:</span>
|
||||
<span class="w"> </span><span class="sd">"""</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"> """</span>
|
||||
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"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"</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">>=</span> <span class="mi">2</span><span class="p">)</span>
|
||||
def lazy_enforce(m: GurobiModel, violations) -> None:
|
||||
"""
|
||||
Callback function that, given a list of subtours, adds lazy
|
||||
constraints to remove them from the feasible region.
|
||||
"""
|
||||
print(f"Enforcing {len(violations)} subtour elimination constraints")
|
||||
for violation in violations:
|
||||
m.add_constr(quicksum(x[e[0], e[1]] for e in violation) >= 2)
|
||||
|
||||
<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>
|
||||
return GurobiModel(
|
||||
model,
|
||||
lazy_separate=lazy_separate,
|
||||
lazy_enforce=lazy_enforce,
|
||||
)
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -398,55 +403,55 @@ the responsbility of the second callback function, <code class="docutils literal
|
||||
<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>
|
||||
</div>
|
||||
<div class="section" id="Generating-training-data">
|
||||
<h2><span class="section-number">4.2. </span>Generating training data<a class="headerlink" href="#Generating-training-data" title="Permalink to this headline">¶</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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span># Configure generator to produce instances with 50 cities located
|
||||
# in the 1000 x 1000 square, and with slightly perturbed distances.
|
||||
gen = TravelingSalesmanGenerator(
|
||||
x=uniform(loc=0.0, scale=1000.0),
|
||||
y=uniform(loc=0.0, scale=1000.0),
|
||||
n=randint(low=50, high=51),
|
||||
gamma=uniform(loc=1.0, scale=0.25),
|
||||
fix_cities=True,
|
||||
round=True,
|
||||
)
|
||||
|
||||
<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">"tsp/train"</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">"tsp/test"</span><span class="p">)</span>
|
||||
# Generate 500 instances and store input data file to .pkl.gz files
|
||||
data = gen.generate(500)
|
||||
train_data = write_pkl_gz(data[0:450], "tsp/train")
|
||||
test_data = write_pkl_gz(data[450:500], "tsp/test")
|
||||
|
||||
<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>
|
||||
# Solve the training instances in parallel, collecting the required lazy
|
||||
# constraints, in addition to other information, such as optimal solution.
|
||||
bc = BasicCollector()
|
||||
bc.collect(train_data, build_tsp_model_gurobipy_simplified, n_jobs=10)
|
||||
</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>
|
||||
</div>
|
||||
<div class="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="Permalink to this headline">¶</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">"static_var_obj_coeffs"</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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>solver = LearningSolver(
|
||||
components=[
|
||||
MemorizingLazyComponent(
|
||||
extractor=H5FieldsExtractor(instance_fields=["static_var_obj_coeffs"]),
|
||||
clf=KNeighborsClassifier(n_neighbors=100),
|
||||
),
|
||||
],
|
||||
)
|
||||
solver.fit(train_data)
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -455,11 +460,11 @@ function only. This ML strategy can be implemented using <code class="docutils l
|
||||
<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">"miplearn"</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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span># Increase log verbosity, so that we can see what is MIPLearn doing
|
||||
logging.getLogger("miplearn").setLevel(logging.INFO)
|
||||
|
||||
<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>
|
||||
# Solve a new test instance
|
||||
solver.optimize(test_data[0], build_tsp_model_gurobipy_simplified);
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -565,8 +570,8 @@ User-callback calls 141, time in user-callback 0.00 sec
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>solver = LearningSolver(components=[]) # empty set of ML components
|
||||
solver.optimize(test_data[0], build_tsp_model_gurobipy_simplified);
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -660,9 +665,9 @@ 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>
|
||||
</div>
|
||||
<div class="section" id="Learning-user-cuts">
|
||||
<h2><span class="section-number">4.4. </span>Learning user cuts<a class="headerlink" href="#Learning-user-cuts" title="Permalink to this headline">¶</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>
|
||||
@@ -677,8 +682,8 @@ User-callback calls 170, time in user-callback 0.01 sec
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@@ -45,16 +45,10 @@
|
||||
"- Python version, compatible with the Pyomo and Gurobipy modeling languages,\n",
|
||||
"- Julia version, compatible with the JuMP modeling language.\n",
|
||||
"\n",
|
||||
"In this tutorial, we will demonstrate how to use and install the Python/Gurobipy version of the package. The first step is to install Python 3.8+ in your computer. See the [official Python website for more instructions](https://www.python.org/downloads/). After Python is installed, we proceed to install MIPLearn using `pip`:\n",
|
||||
"In this tutorial, we will demonstrate how to use and install the Python/Gurobipy version of the package. The first step is to install Python 3.9+ in your computer. See the [official Python website for more instructions](https://www.python.org/downloads/). After Python is installed, we proceed to install MIPLearn using `pip`:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"$ pip install MIPLearn==0.4\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"In addition to MIPLearn itself, we will also install Gurobi 10.0, a state-of-the-art commercial MILP solver. This step also install a demo license for Gurobi, which should able to solve the small optimization problems in this tutorial. A license is required for solving larger-scale problems.\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"$ pip install 'gurobipy>=10,<10.1'\n",
|
||||
"$ pip install MIPLearn~=0.4\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en" data-content_root="../../">
|
||||
<html>
|
||||
<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" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>2. Getting started (Gurobipy) — MIPLearn 0.4</title>
|
||||
|
||||
<link href="../../_static/css/theme.css" rel="stylesheet" />
|
||||
@@ -22,21 +22,27 @@
|
||||
|
||||
|
||||
|
||||
<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="stylesheet" href="../../_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css" type="text/css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/custom.css" />
|
||||
|
||||
<link rel="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 id="documentation_options" data-url_root="../../" src="../../_static/documentation_options.js"></script>
|
||||
<script src="../../_static/jquery.js"></script>
|
||||
<script src="../../_static/underscore.js"></script>
|
||||
<script src="../../_static/doctools.js"></script>
|
||||
<script crossorigin="anonymous" integrity="sha256-Ae2Vz/4ePdIu6ZyI/5ZGsYnb+m0JlOmKPjt6XZ9JJkA=" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js"></script>
|
||||
<script src="../../_static/sphinx-book-theme.12a9622fbb08dcb3a2a40b2c02b83a57.js?v=7c4c3336"></script>
|
||||
<script src="../../_static/sphinx-book-theme.12a9622fbb08dcb3a2a40b2c02b83a57.js"></script>
|
||||
<script async="async" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script>
|
||||
<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/" />
|
||||
@@ -68,7 +74,7 @@
|
||||
<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">
|
||||
<p class="caption">
|
||||
<span class="caption-text">
|
||||
Tutorials
|
||||
</span>
|
||||
@@ -95,7 +101,7 @@
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="caption" role="heading">
|
||||
<p class="caption">
|
||||
<span class="caption-text">
|
||||
User Guide
|
||||
</span>
|
||||
@@ -127,7 +133,7 @@
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="caption" role="heading">
|
||||
<p class="caption">
|
||||
<span class="caption-text">
|
||||
Python API Reference
|
||||
</span>
|
||||
@@ -266,10 +272,10 @@
|
||||
|
||||
<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>
|
||||
<div class="section" id="Getting-started-(Gurobipy)">
|
||||
<h1><span class="section-number">2. </span>Getting started (Gurobipy)<a class="headerlink" href="#Getting-started-(Gurobipy)" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="section" id="Introduction">
|
||||
<h2><span class="section-number">2.1. </span>Introduction<a class="headerlink" href="#Introduction" title="Permalink to this headline">¶</a></h2>
|
||||
<p><strong>MIPLearn</strong> is an open source framework that uses machine learning (ML) to accelerate the performance of 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>
|
||||
@@ -285,29 +291,25 @@
|
||||
<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>
|
||||
</div>
|
||||
<div class="section" id="Installation">
|
||||
<h2><span class="section-number">2.2. </span>Installation<a class="headerlink" href="#Installation" title="Permalink to this headline">¶</a></h2>
|
||||
<p>MIPLearn is available in two versions:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Python version, compatible with the Pyomo 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.4
|
||||
</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 'gurobipy>=10,<10.1'
|
||||
<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.9+ 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.4
|
||||
</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>
|
||||
</div>
|
||||
<div class="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="Permalink to this headline">¶</a></h2>
|
||||
<p>To illustrate how can MIPLearn be used, we will model and solve a small optimization problem related to power systems optimization. The problem we discuss below is a simplification of the <strong>unit commitment problem,</strong> a practical optimization problem solved daily by electric grid operators around the world.</p>
|
||||
<p>Suppose that 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>
|
||||
@@ -330,19 +332,19 @@
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
|
||||
import numpy as np
|
||||
|
||||
|
||||
<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>
|
||||
@dataclass
|
||||
class UnitCommitmentData:
|
||||
demand: float
|
||||
pmin: List[float]
|
||||
pmax: List[float]
|
||||
cfix: List[float]
|
||||
cvar: List[float]
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -351,28 +353,28 @@
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>import gurobipy as gp
|
||||
from gurobipy import GRB, quicksum
|
||||
from typing import Union
|
||||
from miplearn.io import read_pkl_gz
|
||||
from miplearn.solvers.gurobi import GurobiModel
|
||||
|
||||
|
||||
<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">-></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>
|
||||
def build_uc_model(data: Union[str, UnitCommitmentData]) -> GurobiModel:
|
||||
if isinstance(data, str):
|
||||
data = read_pkl_gz(data)
|
||||
|
||||
<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">"x"</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">"y"</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"><=</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">>=</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>
|
||||
model = gp.Model()
|
||||
n = len(data.pmin)
|
||||
x = model._x = model.addVars(n, vtype=GRB.BINARY, name="x")
|
||||
y = model._y = model.addVars(n, name="y")
|
||||
model.setObjective(
|
||||
quicksum(data.cfix[i] * x[i] + data.cvar[i] * y[i] for i in range(n))
|
||||
)
|
||||
model.addConstrs(y[i] <= data.pmax[i] * x[i] for i in range(n))
|
||||
model.addConstrs(y[i] >= data.pmin[i] * x[i] for i in range(n))
|
||||
model.addConstr(quicksum(y[i] for i in range(n)) == data.demand)
|
||||
return GurobiModel(model)
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -381,20 +383,20 @@
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>model = build_uc_model(
|
||||
UnitCommitmentData(
|
||||
demand=100.0,
|
||||
pmin=[10, 20, 30],
|
||||
pmax=[50, 60, 70],
|
||||
cfix=[700, 600, 500],
|
||||
cvar=[1.5, 2.0, 2.5],
|
||||
)
|
||||
)
|
||||
|
||||
<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">"obj ="</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">"x ="</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">"y ="</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>
|
||||
model.optimize()
|
||||
print("obj =", model.inner.objVal)
|
||||
print("x =", [model.inner._x[i].x for i in range(3)])
|
||||
print("y =", [model.inner._y[i].x for i in range(3)])
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -455,9 +457,9 @@ y = [0.0, 60.0, 40.0]
|
||||
<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>
|
||||
</div>
|
||||
<div class="section" id="Generating-training-data">
|
||||
<h2><span class="section-number">2.4. </span>Generating training data<a class="headerlink" href="#Generating-training-data" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Although Gurobi could solve the small example above in a fraction of a second, it gets slower for larger and more complex versions of the problem. If this is a problem that needs to be solved frequently, as it is often the case in practice, it could make sense to spend some time upfront generating a <strong>trained</strong> 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>
|
||||
@@ -465,28 +467,28 @@ random instance generator:</p>
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>from scipy.stats import uniform
|
||||
from typing import List
|
||||
import random
|
||||
|
||||
|
||||
<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">-></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>
|
||||
def random_uc_data(samples: int, n: int, seed: int = 42) -> List[UnitCommitmentData]:
|
||||
random.seed(seed)
|
||||
np.random.seed(seed)
|
||||
pmin = uniform(loc=100_000.0, scale=400_000.0).rvs(n)
|
||||
pmax = pmin * uniform(loc=2.0, scale=2.5).rvs(n)
|
||||
cfix = pmin * uniform(loc=100.0, scale=25.0).rvs(n)
|
||||
cvar = uniform(loc=1.25, scale=0.25).rvs(n)
|
||||
return [
|
||||
UnitCommitmentData(
|
||||
demand=pmax.sum() * uniform(loc=0.5, scale=0.25).rvs(),
|
||||
pmin=pmin,
|
||||
pmax=pmax,
|
||||
cfix=cfix,
|
||||
cvar=cvar,
|
||||
)
|
||||
for _ in range(samples)
|
||||
]
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -497,11 +499,11 @@ machines. The code below generates the files <code class="docutils literal notra
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>from miplearn.io import write_pkl_gz
|
||||
|
||||
<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">"uc/train"</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">"uc/test"</span><span class="p">)</span>
|
||||
data = random_uc_data(samples=500, n=500)
|
||||
train_data = write_pkl_gz(data[0:450], "uc/train")
|
||||
test_data = write_pkl_gz(data[450:500], "uc/test")
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -510,16 +512,16 @@ machines. The code below generates the files <code class="docutils literal notra
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>from miplearn.collectors.basic import BasicCollector
|
||||
|
||||
<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>
|
||||
bc = BasicCollector()
|
||||
bc.collect(train_data, build_uc_model)
|
||||
</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>
|
||||
</div>
|
||||
<div class="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="Permalink to this headline">¶</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>
|
||||
@@ -532,22 +534,22 @@ machines. The code below generates the files <code class="docutils literal notra
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>from sklearn.neighbors import KNeighborsClassifier
|
||||
from miplearn.components.primal.actions import SetWarmStart
|
||||
from miplearn.components.primal.mem import (
|
||||
MemorizingPrimalComponent,
|
||||
MergeTopSolutions,
|
||||
)
|
||||
from miplearn.extractors.fields import H5FieldsExtractor
|
||||
|
||||
<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">"static_constr_rhs"</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>
|
||||
comp = MemorizingPrimalComponent(
|
||||
clf=KNeighborsClassifier(n_neighbors=25),
|
||||
extractor=H5FieldsExtractor(
|
||||
instance_fields=["static_constr_rhs"],
|
||||
),
|
||||
constructor=MergeTopSolutions(25, [0.0, 1.0]),
|
||||
action=SetWarmStart(),
|
||||
)
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -556,11 +558,11 @@ machines. The code below generates the files <code class="docutils literal notra
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>from miplearn.solvers.learning import LearningSolver
|
||||
|
||||
<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>
|
||||
solver_ml = LearningSolver(components=[comp])
|
||||
solver_ml.fit(train_data)
|
||||
solver_ml.optimize(test_data[0], build_uc_model)
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -654,9 +656,9 @@ User-callback calls 193, time in user-callback 0.00 sec
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>solver_baseline = LearningSolver(components=[])
|
||||
solver_baseline.fit(train_data)
|
||||
solver_baseline.optimize(test_data[0], build_uc_model);
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -747,20 +749,20 @@ User-callback calls 708, 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">2.6. </span>Accessing the solution<a class="headerlink" href="#Accessing-the-solution" title="Link to this heading">¶</a></h2>
|
||||
</div>
|
||||
<div class="section" id="Accessing-the-solution">
|
||||
<h2><span class="section-number">2.6. </span>Accessing the solution<a class="headerlink" href="#Accessing-the-solution" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In the example above, we used <code class="docutils literal notranslate"><span class="pre">LearningSolver.solve</span></code> together with data files to solve both the training and the test instances. 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">"obj ="</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">"x ="</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">"y ="</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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>data = random_uc_data(samples=1, n=500)[0]
|
||||
model = build_uc_model(data)
|
||||
solver_ml.optimize(model)
|
||||
print("obj =", model.inner.objVal)
|
||||
print("x =", [model.inner._x[i].x for i in range(3)])
|
||||
print("y =", [model.inner._y[i].x for i in range(3)])
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -855,8 +857,8 @@ y = [935662.0949262811, 1604270.0218116897, 0.0]
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en" data-content_root="../../">
|
||||
<html>
|
||||
<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" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>3. Getting started (JuMP) — MIPLearn 0.4</title>
|
||||
|
||||
<link href="../../_static/css/theme.css" rel="stylesheet" />
|
||||
@@ -22,21 +22,28 @@
|
||||
|
||||
|
||||
|
||||
<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="stylesheet" href="../../_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css" type="text/css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/custom.css" />
|
||||
|
||||
<link rel="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 id="documentation_options" data-url_root="../../" src="../../_static/documentation_options.js"></script>
|
||||
<script src="../../_static/jquery.js"></script>
|
||||
<script src="../../_static/underscore.js"></script>
|
||||
<script src="../../_static/doctools.js"></script>
|
||||
<script crossorigin="anonymous" integrity="sha256-Ae2Vz/4ePdIu6ZyI/5ZGsYnb+m0JlOmKPjt6XZ9JJkA=" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js"></script>
|
||||
<script src="../../_static/sphinx-book-theme.12a9622fbb08dcb3a2a40b2c02b83a57.js?v=7c4c3336"></script>
|
||||
<script src="../../_static/sphinx-book-theme.12a9622fbb08dcb3a2a40b2c02b83a57.js"></script>
|
||||
<script async="async" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script>
|
||||
<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/" />
|
||||
@@ -68,7 +75,7 @@
|
||||
<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">
|
||||
<p class="caption">
|
||||
<span class="caption-text">
|
||||
Tutorials
|
||||
</span>
|
||||
@@ -95,7 +102,7 @@
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="caption" role="heading">
|
||||
<p class="caption">
|
||||
<span class="caption-text">
|
||||
User Guide
|
||||
</span>
|
||||
@@ -127,7 +134,7 @@
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="caption" role="heading">
|
||||
<p class="caption">
|
||||
<span class="caption-text">
|
||||
Python API Reference
|
||||
</span>
|
||||
@@ -266,10 +273,10 @@
|
||||
|
||||
<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>
|
||||
<div class="section" id="Getting-started-(JuMP)">
|
||||
<h1><span class="section-number">3. </span>Getting started (JuMP)<a class="headerlink" href="#Getting-started-(JuMP)" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="section" id="Introduction">
|
||||
<h2><span class="section-number">3.1. </span>Introduction<a class="headerlink" href="#Introduction" title="Permalink to this headline">¶</a></h2>
|
||||
<p><strong>MIPLearn</strong> is an open source framework that uses machine learning (ML) to accelerate the performance of 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>
|
||||
@@ -281,9 +288,9 @@
|
||||
<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>
|
||||
</div>
|
||||
<div class="section" id="Installation">
|
||||
<h2><span class="section-number">3.2. </span>Installation<a class="headerlink" href="#Installation" title="Permalink to this headline">¶</a></h2>
|
||||
<p>MIPLearn is available in two versions:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Python version, compatible with the Pyomo and Gurobipy modeling languages,</p></li>
|
||||
@@ -311,9 +318,9 @@
|
||||
<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>
|
||||
</div>
|
||||
<div class="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="Permalink to this headline">¶</a></h2>
|
||||
<p>To illustrate how can MIPLearn be used, we will model and solve a small optimization problem related to power systems optimization. The problem we discuss below is a simplification of the <strong>unit commitment problem,</strong> a practical optimization problem solved daily by electric grid operators around the world.</p>
|
||||
<p>Suppose that 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>
|
||||
@@ -447,9 +454,9 @@ Vector(value.(model.inner[:y])) = [0.0, 60.0, 40.0]
|
||||
<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>
|
||||
</div>
|
||||
<div class="section" id="Generating-training-data">
|
||||
<h2><span class="section-number">3.4. </span>Generating training data<a class="headerlink" href="#Generating-training-data" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Although Gurobi could solve the small example above in a fraction of a second, it gets slower for larger and more complex versions of the problem. If this is a problem that needs to be solved frequently, as it is often the case in practice, it could make sense to spend some time upfront generating a <strong>trained</strong> 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>
|
||||
@@ -506,9 +513,9 @@ machines. The code below generates the files <code class="docutils literal notra
|
||||
</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>
|
||||
</div>
|
||||
<div class="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="Permalink to this headline">¶</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>
|
||||
@@ -655,9 +662,9 @@ 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>
|
||||
</div>
|
||||
<div class="section" id="Accessing-the-solution">
|
||||
<h2><span class="section-number">3.6. </span>Accessing the solution<a class="headerlink" href="#Accessing-the-solution" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In the example above, we used <code class="docutils literal notranslate"><span class="pre">LearningSolver.solve</span></code> together with data files to solve both the training and the test instances. 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]:
|
||||
@@ -718,8 +725,8 @@ 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>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@@ -45,16 +45,10 @@
|
||||
"- Python version, compatible with the Pyomo and Gurobipy modeling languages,\n",
|
||||
"- Julia version, compatible with the JuMP modeling language.\n",
|
||||
"\n",
|
||||
"In this tutorial, we will demonstrate how to use and install the Python/Pyomo version of the package. The first step is to install Python 3.8+ in your computer. See the [official Python website for more instructions](https://www.python.org/downloads/). After Python is installed, we proceed to install MIPLearn using `pip`:\n",
|
||||
"In this tutorial, we will demonstrate how to use and install the Python/Pyomo version of the package. The first step is to install Python 3.9+ in your computer. See the [official Python website for more instructions](https://www.python.org/downloads/). After Python is installed, we proceed to install MIPLearn using `pip`:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"$ pip install MIPLearn==0.4\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"In addition to MIPLearn itself, we will also install Gurobi 10.0, a state-of-the-art commercial MILP solver. This step also install a demo license for Gurobi, which should able to solve the small optimization problems in this tutorial. A license is required for solving larger-scale problems.\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"$ pip install 'gurobipy>=10,<10.1'\n",
|
||||
"$ pip install MIPLearn~=0.4\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en" data-content_root="../../">
|
||||
<html>
|
||||
<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" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>1. Getting started (Pyomo) — MIPLearn 0.4</title>
|
||||
|
||||
<link href="../../_static/css/theme.css" rel="stylesheet" />
|
||||
@@ -22,21 +22,29 @@
|
||||
|
||||
|
||||
|
||||
<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="stylesheet" href="../../_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../../_static/sphinx-book-theme.acff12b8f9c144ce68a297486a2fa670.css" type="text/css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/nbsphinx-code-cells.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../_static/custom.css" />
|
||||
|
||||
<link rel="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 id="documentation_options" data-url_root="../../" src="../../_static/documentation_options.js"></script>
|
||||
<script src="../../_static/jquery.js"></script>
|
||||
<script src="../../_static/underscore.js"></script>
|
||||
<script src="../../_static/doctools.js"></script>
|
||||
<script crossorigin="anonymous" integrity="sha256-Ae2Vz/4ePdIu6ZyI/5ZGsYnb+m0JlOmKPjt6XZ9JJkA=" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js"></script>
|
||||
<script src="../../_static/sphinx-book-theme.12a9622fbb08dcb3a2a40b2c02b83a57.js?v=7c4c3336"></script>
|
||||
<script src="../../_static/sphinx-book-theme.12a9622fbb08dcb3a2a40b2c02b83a57.js"></script>
|
||||
<script async="async" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script>
|
||||
<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/" />
|
||||
@@ -68,7 +76,7 @@
|
||||
<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">
|
||||
<p class="caption">
|
||||
<span class="caption-text">
|
||||
Tutorials
|
||||
</span>
|
||||
@@ -95,7 +103,7 @@
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="caption" role="heading">
|
||||
<p class="caption">
|
||||
<span class="caption-text">
|
||||
User Guide
|
||||
</span>
|
||||
@@ -127,7 +135,7 @@
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="caption" role="heading">
|
||||
<p class="caption">
|
||||
<span class="caption-text">
|
||||
Python API Reference
|
||||
</span>
|
||||
@@ -266,10 +274,10 @@
|
||||
|
||||
<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>
|
||||
<div class="section" id="Getting-started-(Pyomo)">
|
||||
<h1><span class="section-number">1. </span>Getting started (Pyomo)<a class="headerlink" href="#Getting-started-(Pyomo)" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="section" id="Introduction">
|
||||
<h2><span class="section-number">1.1. </span>Introduction<a class="headerlink" href="#Introduction" title="Permalink to this headline">¶</a></h2>
|
||||
<p><strong>MIPLearn</strong> is an open source framework that uses machine learning (ML) to accelerate the performance of 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>
|
||||
@@ -285,29 +293,25 @@
|
||||
<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>
|
||||
</div>
|
||||
<div class="section" id="Installation">
|
||||
<h2><span class="section-number">1.2. </span>Installation<a class="headerlink" href="#Installation" title="Permalink to this headline">¶</a></h2>
|
||||
<p>MIPLearn is available in two versions:</p>
|
||||
<ul class="simple">
|
||||
<li><p>Python version, compatible with the Pyomo 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.4
|
||||
</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 'gurobipy>=10,<10.1'
|
||||
<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.9+ 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.4
|
||||
</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>
|
||||
</div>
|
||||
<div class="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="Permalink to this headline">¶</a></h2>
|
||||
<p>To illustrate how can MIPLearn be used, we will model and solve a small optimization problem related to power systems optimization. The problem we discuss below is a simplification of the <strong>unit commitment problem,</strong> a practical optimization problem solved daily by electric grid operators around the world.</p>
|
||||
<p>Suppose that 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>
|
||||
@@ -330,19 +334,19 @@
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
|
||||
import numpy as np
|
||||
|
||||
|
||||
<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>
|
||||
@dataclass
|
||||
class UnitCommitmentData:
|
||||
demand: float
|
||||
pmin: List[float]
|
||||
pmax: List[float]
|
||||
cfix: List[float]
|
||||
cvar: List[float]
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -351,34 +355,34 @@
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>import pyomo.environ as pe
|
||||
from typing import Union
|
||||
from miplearn.io import read_pkl_gz
|
||||
from miplearn.solvers.pyomo import PyomoModel
|
||||
|
||||
|
||||
<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">-></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>
|
||||
def build_uc_model(data: Union[str, UnitCommitmentData]) -> PyomoModel:
|
||||
if isinstance(data, str):
|
||||
data = read_pkl_gz(data)
|
||||
|
||||
<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"><=</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">>=</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">"gurobi_persistent"</span><span class="p">)</span>
|
||||
model = pe.ConcreteModel()
|
||||
n = len(data.pmin)
|
||||
model.x = pe.Var(range(n), domain=pe.Binary)
|
||||
model.y = pe.Var(range(n), domain=pe.NonNegativeReals)
|
||||
model.obj = pe.Objective(
|
||||
expr=sum(
|
||||
data.cfix[i] * model.x[i] + data.cvar[i] * model.y[i] for i in range(n)
|
||||
)
|
||||
)
|
||||
model.eq_max_power = pe.ConstraintList()
|
||||
model.eq_min_power = pe.ConstraintList()
|
||||
for i in range(n):
|
||||
model.eq_max_power.add(model.y[i] <= data.pmax[i] * model.x[i])
|
||||
model.eq_min_power.add(model.y[i] >= data.pmin[i] * model.x[i])
|
||||
model.eq_demand = pe.Constraint(
|
||||
expr=sum(model.y[i] for i in range(n)) == data.demand,
|
||||
)
|
||||
return PyomoModel(model, "gurobi_persistent")
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -387,20 +391,20 @@
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>model = build_uc_model(
|
||||
UnitCommitmentData(
|
||||
demand=100.0,
|
||||
pmin=[10, 20, 30],
|
||||
pmax=[50, 60, 70],
|
||||
cfix=[700, 600, 500],
|
||||
cvar=[1.5, 2.0, 2.5],
|
||||
)
|
||||
)
|
||||
|
||||
<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">"obj ="</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">"x ="</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">"y ="</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>
|
||||
model.optimize()
|
||||
print("obj =", model.inner.obj())
|
||||
print("x =", [model.inner.x[i].value for i in range(3)])
|
||||
print("y =", [model.inner.y[i].value for i in range(3)])
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -461,9 +465,9 @@ y = [0.0, 60.0, 40.0]
|
||||
<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>
|
||||
</div>
|
||||
<div class="section" id="Generating-training-data">
|
||||
<h2><span class="section-number">1.4. </span>Generating training data<a class="headerlink" href="#Generating-training-data" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Although Gurobi could solve the small example above in a fraction of a second, it gets slower for larger and more complex versions of the problem. If this is a problem that needs to be solved frequently, as it is often the case in practice, it could make sense to spend some time upfront generating a <strong>trained</strong> 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>
|
||||
@@ -471,28 +475,28 @@ random instance generator:</p>
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>from scipy.stats import uniform
|
||||
from typing import List
|
||||
import random
|
||||
|
||||
|
||||
<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">-></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>
|
||||
def random_uc_data(samples: int, n: int, seed: int = 42) -> List[UnitCommitmentData]:
|
||||
random.seed(seed)
|
||||
np.random.seed(seed)
|
||||
pmin = uniform(loc=100_000.0, scale=400_000.0).rvs(n)
|
||||
pmax = pmin * uniform(loc=2.0, scale=2.5).rvs(n)
|
||||
cfix = pmin * uniform(loc=100.0, scale=25.0).rvs(n)
|
||||
cvar = uniform(loc=1.25, scale=0.25).rvs(n)
|
||||
return [
|
||||
UnitCommitmentData(
|
||||
demand=pmax.sum() * uniform(loc=0.5, scale=0.25).rvs(),
|
||||
pmin=pmin,
|
||||
pmax=pmax,
|
||||
cfix=cfix,
|
||||
cvar=cvar,
|
||||
)
|
||||
for _ in range(samples)
|
||||
]
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -503,11 +507,11 @@ machines. The code below generates the files <code class="docutils literal notra
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>from miplearn.io import write_pkl_gz
|
||||
|
||||
<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">"uc/train"</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">"uc/test"</span><span class="p">)</span>
|
||||
data = random_uc_data(samples=500, n=500)
|
||||
train_data = write_pkl_gz(data[0:450], "uc/train")
|
||||
test_data = write_pkl_gz(data[450:500], "uc/test")
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -516,16 +520,16 @@ machines. The code below generates the files <code class="docutils literal notra
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>from miplearn.collectors.basic import BasicCollector
|
||||
|
||||
<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>
|
||||
bc = BasicCollector()
|
||||
bc.collect(train_data, build_uc_model, n_jobs=4)
|
||||
</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>
|
||||
</div>
|
||||
<div class="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="Permalink to this headline">¶</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>
|
||||
@@ -538,22 +542,22 @@ machines. The code below generates the files <code class="docutils literal notra
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>from sklearn.neighbors import KNeighborsClassifier
|
||||
from miplearn.components.primal.actions import SetWarmStart
|
||||
from miplearn.components.primal.mem import (
|
||||
MemorizingPrimalComponent,
|
||||
MergeTopSolutions,
|
||||
)
|
||||
from miplearn.extractors.fields import H5FieldsExtractor
|
||||
|
||||
<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">"static_constr_rhs"</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>
|
||||
comp = MemorizingPrimalComponent(
|
||||
clf=KNeighborsClassifier(n_neighbors=25),
|
||||
extractor=H5FieldsExtractor(
|
||||
instance_fields=["static_constr_rhs"],
|
||||
),
|
||||
constructor=MergeTopSolutions(25, [0.0, 1.0]),
|
||||
action=SetWarmStart(),
|
||||
)
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -562,11 +566,11 @@ machines. The code below generates the files <code class="docutils literal notra
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>from miplearn.solvers.learning import LearningSolver
|
||||
|
||||
<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>
|
||||
solver_ml = LearningSolver(components=[comp])
|
||||
solver_ml.fit(train_data)
|
||||
solver_ml.optimize(test_data[0], build_uc_model)
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -660,9 +664,9 @@ WARNING: Cannot get duals for MIP.
|
||||
<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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>solver_baseline = LearningSolver(components=[])
|
||||
solver_baseline.fit(train_data)
|
||||
solver_baseline.optimize(test_data[0], build_uc_model)
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -757,20 +761,20 @@ WARNING: Cannot get duals for MIP.
|
||||
</pre></div></div>
|
||||
</div>
|
||||
<p>In the log above, the <code class="docutils literal notranslate"><span class="pre">MIP</span> <span class="pre">start</span></code> line is missing, and Gurobi had to start with a significantly inferior initial solution. The solver was still able to find the optimal solution at the end, but it required using its own internal heuristic procedures. In this example, because we solve very small optimization problems, there was almost no difference in terms of running time, 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>
|
||||
</div>
|
||||
<div class="section" id="Accessing-the-solution">
|
||||
<h2><span class="section-number">1.6. </span>Accessing the solution<a class="headerlink" href="#Accessing-the-solution" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In the example above, we used <code class="docutils literal notranslate"><span class="pre">LearningSolver.solve</span></code> together with data files to solve both the training and the test instances. 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">"obj ="</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">" x ="</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">" y ="</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>
|
||||
<div class="input_area highlight-ipython3 notranslate"><div class="highlight"><pre><span></span>data = random_uc_data(samples=1, n=500)[0]
|
||||
model = build_uc_model(data)
|
||||
solver_ml.optimize(model)
|
||||
print("obj =", model.inner.obj())
|
||||
print(" x =", [model.inner.x[i].value for i in range(5)])
|
||||
print(" y =", [model.inner.y[i].value for i in range(5)])
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -872,8 +876,8 @@ obj = 8254590409.96973
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user