.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_userguide/example_simple.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_userguide_example_simple.py: Usage Example ============= In this page, we provide a simple example of using the Gurobi Machine Learning package. The example is entirely abstract. Its aim is only to illustrate the basic functionalities of the package in the most simple way. For some more realistic applications, please refer to the notebooks in the `examples <../auto_examples/index.html>`__ section. Before proceeding to the example itself, we need to import a number of packages. Here, we will use Scikit-learn to train regression models. We generate random data for the regression using the `make_regression `__ function. For the regression model, we use a `multi-layer perceptron regressor `__ neural network. We import the corresponding objects. .. GENERATED FROM PYTHON SOURCE LINES 22-30 .. code-block:: Python import gurobipy as gp import numpy as np from sklearn.datasets import make_regression from sklearn.neural_network import MLPRegressor from gurobi_ml import add_predictor_constr .. GENERATED FROM PYTHON SOURCE LINES 31-36 Certainly, we need gurobipy to build an optimization model and from the gurobi_ml package we need the :func:`add_predictor_constr `. function. We also need numpy. .. GENERATED FROM PYTHON SOURCE LINES 39-42 We start by building artificial data to train our regressions. To do so, we use *make_regression* to obtain data with 10 features. .. GENERATED FROM PYTHON SOURCE LINES 42-46 .. code-block:: Python X, y = make_regression(n_features=10, noise=1.0) .. GENERATED FROM PYTHON SOURCE LINES 47-49 Now, create the *MLPRegressor* object and fit it. .. GENERATED FROM PYTHON SOURCE LINES 49-55 .. code-block:: Python nn = MLPRegressor([20] * 2, max_iter=10000, random_state=1) nn.fit(X, y) .. raw:: html
MLPRegressor(hidden_layer_sizes=[20, 20], max_iter=10000, random_state=1)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.


.. GENERATED FROM PYTHON SOURCE LINES 56-84 We now turn to the optimization model. In the spirit of adversarial machine learning examples, we use some training examples. We pick :math:`n` training examples randomly. For each of the examples, we want to find an input that is in a small neighborhood of it that leads to the output that is closer to :math:`0` with the regression. Denoting by :math:`X^E` our set of examples and by :math:`g` the prediction function of our regression model, our optimization problem reads: .. math:: \begin{aligned} &\min \sum_{i=1}^n y_i^2 \\ &\text{s.t.:}\\ &y_i = g(X_i) & & i = 1, \ldots, n,\\ &X^E - \delta \leq X \leq X^E + \delta,\\ \end{aligned} where :math:`X` is a matrix of variables of dimension :math:`n \times 10` (the number of examples we consider and number of features in the regression respectively), :math:`y` is a vector of free (unbounded) variables and :math:`\delta` a small positive constant. First, let’s pick randomly 2 training examples using numpy, and create our :external+gurobi:py:class:`Gurobi model`. .. GENERATED FROM PYTHON SOURCE LINES 84-93 .. code-block:: Python n = 2 index = np.random.choice(X.shape[0], n, replace=False) X_examples = X[index, :] y_examples = y[index] m = gp.Model() .. GENERATED FROM PYTHON SOURCE LINES 94-106 Our only decision variables in this case, are the five inputs and outputs for the regression. We use :external+gurobi:py:class:`MVar` matrix variables that are most convenient in this case. The input variables have the same shape as ``X_examples``. Their lower bound is ``X_examples - delta`` and their upper bound ``X_examples + delta``. The output variables have the shape of ``y_examples`` and are unbounded. By default, in Gurobi variables are non-negative, we therefore need to set an infinite lower bound. .. GENERATED FROM PYTHON SOURCE LINES 106-111 .. code-block:: Python input_vars = m.addMVar(X_examples.shape, lb=X_examples - 0.2, ub=X_examples + 0.2) output_vars = m.addMVar(y_examples.shape, lb=-gp.GRB.INFINITY) .. GENERATED FROM PYTHON SOURCE LINES 112-123 The constraints linking ``input_vars`` and ``output_vars`` can now be added with the function :func:`add_predictor_constr `. Note that because of the shape of the variables this will add the 5 different constraints. The function returns an instance of a :class:`modeling object ` that we can use later on. .. GENERATED FROM PYTHON SOURCE LINES 123-127 .. code-block:: Python pred_constr = add_predictor_constr(m, nn, input_vars, output_vars) .. GENERATED FROM PYTHON SOURCE LINES 128-133 The method :func:`print_stats ` of the modeling object outputs the details of the regression model that was added to the Gurobi. .. GENERATED FROM PYTHON SOURCE LINES 133-137 .. code-block:: Python pred_constr.print_stats() .. rst-class:: sphx-glr-script-out .. code-block:: none Model for mlpregressor: 160 variables 82 constraints 80 general constraints Input has shape (2, 10) Output has shape (2, 1) -------------------------------------------------------------------------------- Layer Output Shape Variables Constraints Linear Quadratic General ================================================================================ dense (2, 20) 80 40 0 40 (relu) dense0 (2, 20) 80 40 0 40 (relu) dense1 (2, 1) 0 2 0 0 -------------------------------------------------------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 138-140 To finish the model, we set the objective, and then we can optimize it. .. GENERATED FROM PYTHON SOURCE LINES 140-146 .. code-block:: Python m.setObjective(output_vars @ output_vars, gp.GRB.MINIMIZE) m.optimize() .. rst-class:: sphx-glr-script-out .. code-block:: none Gurobi Optimizer version 12.0.2 build v12.0.2rc0 (linux64 - "Ubuntu 24.04 LTS") CPU model: AMD EPYC 9R14, instruction set [SSE2|AVX|AVX2|AVX512] Thread count: 2 physical cores, 2 logical processors, using up to 2 threads Optimize a model with 82 rows, 182 columns and 1322 nonzeros Model fingerprint: 0x84e7506e Model has 2 quadratic objective terms Model has 80 simple general constraints 80 MAX Variable types: 182 continuous, 0 integer (0 binary) Coefficient statistics: Matrix range [5e-05, 2e+00] Objective range [0e+00, 0e+00] QObjective range [2e+00, 2e+00] Bounds range [7e-02, 2e+00] RHS range [3e-02, 1e+00] Presolve removed 13 rows and 103 columns Presolve time: 0.01s Presolved: 69 rows, 79 columns, 547 nonzeros Presolved model has 1 quadratic objective terms Variable types: 60 continuous, 19 integer (19 binary) Root relaxation: objective 4.520392e+04, 141 iterations, 0.00 seconds (0.00 work units) Nodes | Current Node | Objective Bounds | Work Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time 0 0 45203.9244 0 14 - 45203.9244 - - 0s H 0 0 45745.583577 45203.9244 1.18% - 0s 0 0 45522.8667 0 14 45745.5836 45522.8667 0.49% - 0s 0 0 45654.3534 0 2 45745.5836 45654.3534 0.20% - 0s 0 0 45689.9618 0 2 45745.5836 45689.9618 0.12% - 0s 0 0 45689.9618 0 2 45745.5836 45689.9618 0.12% - 0s H 0 0 45705.102836 45689.9618 0.03% - 0s 0 0 - 0 45705.1028 45703.3081 0.00% - 0s Cutting planes: Implied bound: 1 MIR: 2 Flow cover: 2 Relax-and-lift: 1 Explored 1 nodes (357 simplex iterations) in 0.02 seconds (0.02 work units) Thread count was 2 (of 2 available processors) Solution count 2: 45705.1 45745.6 Optimal solution found (tolerance 1.00e-04) Best objective 4.570510283560e+04, best bound 4.570330807468e+04, gap 0.0039% .. GENERATED FROM PYTHON SOURCE LINES 147-158 The method :func:`get_error ` is useful to check that the solution computed by Gurobi is correct with respect to the regression model we use. Let :math:`(\bar X, \bar y)` be the values of the input and output variables in the computed solution. The function returns :math:`g(\bar X) - y` using the original regression object. Normally, all values should be small and below Gurobi’s tolerances in this example. .. GENERATED FROM PYTHON SOURCE LINES 158-162 .. code-block:: Python pred_constr.get_error() .. rst-class:: sphx-glr-script-out .. code-block:: none array([[2.84217094e-14], [1.77635684e-14]]) .. GENERATED FROM PYTHON SOURCE LINES 163-166 We can look at the computed values for the output variables and compare them with the original target values. .. GENERATED FROM PYTHON SOURCE LINES 166-170 .. code-block:: Python print("Computed values") print(pred_constr.output_values.flatten()) .. rst-class:: sphx-glr-script-out .. code-block:: none Computed values [212.61214556 22.38701405] .. GENERATED FROM PYTHON SOURCE LINES 171-176 .. code-block:: Python print("Original values") print(y_examples) .. rst-class:: sphx-glr-script-out .. code-block:: none Original values [327.88114958 149.09302163] .. GENERATED FROM PYTHON SOURCE LINES 177-180 Finally, we can remove ``pred_constr`` with the method :func:`remove() `. .. GENERATED FROM PYTHON SOURCE LINES 180-182 .. code-block:: Python pred_constr.remove() .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 1.380 seconds) .. _sphx_glr_download_auto_userguide_example_simple.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: example_simple.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: example_simple.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: example_simple.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_