|
| 1 | +############## |
| 2 | +Matrix API |
| 3 | +############## |
| 4 | + |
| 5 | +In this overview of the matrix variable and constraint API in PySCIPOpt |
| 6 | +we'll walk through best practices for modelling them and the various information that |
| 7 | +can be extracted from them. |
| 8 | + |
| 9 | +For the following let us assume that a Model object is available, which is created as follows: |
| 10 | + |
| 11 | +.. code-block:: python |
| 12 | +
|
| 13 | + from pyscipopt import Model, quicksum |
| 14 | +
|
| 15 | + scip = Model() |
| 16 | +
|
| 17 | +
|
| 18 | +This tutorial should only be read after having an understanding of both the ``Variable`` |
| 19 | +object (see :doc:`the constraint tutorial </tutorials/vartypes>`) and the ``Constraint`` |
| 20 | +object (see :doc:`the constraint tutorial </tutorials/constypes>`). |
| 21 | + |
| 22 | +.. note:: |
| 23 | + |
| 24 | + The matrix API is built heavily on `numpy <https://numpy.org/>`_. This means that users can |
| 25 | + use all standard ``numpy`` operations that they are familiar with when handling matrix |
| 26 | + variables and expressions. For example, using the ``@``, ``matmul``, ``*``, |
| 27 | + ``+``, ``hstack``, ``vstack``, and ``**`` operations work exactly as they do |
| 28 | + when handling any standard ``numpy`` array. |
| 29 | + |
| 30 | +.. contents:: Contents |
| 31 | + |
| 32 | +What is a Matrix API? |
| 33 | +====================== |
| 34 | + |
| 35 | +The standard approach explained in the variable and constraint tutorials, is to |
| 36 | +build each variable yourself (storing them in some data structure, e.g., a list or dict, |
| 37 | +with some loop), and to construct each constraint in a similar manner. That means building |
| 38 | +up each constraint yourself term by term. This approach is flexible, and still remains the standard, |
| 39 | +but an increasingly common trend is to view the modelling approach from a vector, matrix, |
| 40 | +and tensor perspective. That is, directly operate on larger sets of variables and expressions, |
| 41 | +letting python handle the interaction for each term. For such cases, it is encouraged |
| 42 | +that users now use the new matrix API! |
| 43 | + |
| 44 | +Matrix Variables |
| 45 | +================= |
| 46 | + |
| 47 | +Matrix variables are added via a single function call. It is important beforehand |
| 48 | +to know the ``shape`` of the new set of variables you want to create, where ``shape`` |
| 49 | +is some ``tuple`` or ``int``. Below is an example for creating a 2x2 matrix variable |
| 50 | +of type continuous with an ub of 8. |
| 51 | + |
| 52 | +.. code-block:: python |
| 53 | +
|
| 54 | + x = scip.addMatrixVar(shape, vtype='C', name='x', ub=8) |
| 55 | +
|
| 56 | +.. note:: |
| 57 | + |
| 58 | + The ``name`` of each variable in the example above becomes ``x_(indices)`` |
| 59 | + |
| 60 | +In the case of each ``kwarg``, e.g., ``vtype`` and ``ub``, a ``np.array`` of explicit |
| 61 | +values can be passed. In the example above, this means that each variable within the |
| 62 | +matrix variable can have its own custom information. For example: |
| 63 | + |
| 64 | +.. code-block:: python |
| 65 | +
|
| 66 | + x = scip.addMatrixVar(shape, vtype='C', name='x', ub=np.array([[5, 6], [2, 8]])) |
| 67 | +
|
| 68 | +Matrix Constraints |
| 69 | +=================== |
| 70 | + |
| 71 | +Matrix constraints follow the same logic as matrix variables. They can be constructed quickly |
| 72 | +and added all at once. The standard variable operators, like ``sin``, ``exp``, ``sqrt``, etc., |
| 73 | +are applied element-wise. Some examples are provided below (these examples are nonsensical, |
| 74 | +and there to purely understand the API): |
| 75 | + |
| 76 | +.. code-block:: python |
| 77 | +
|
| 78 | + x = scip.addMatrixVar(shape=(2, 2), vtype="B", name="x") |
| 79 | + y = scip.addMatrixVar(shape=(2, 2), vtype="C", name="y", ub=5) |
| 80 | + z = scip.addVar(vtype="C", name="z", ub=7) |
| 81 | +
|
| 82 | + scip.addMatrixCons(x + y <= z) |
| 83 | + scip.addMatrixCons(exp(x) + sin(sqrt(y)) == z + y) |
| 84 | + scip.addMatrixCons(y <= x @ y) |
| 85 | + scip.addMatrixCons(x @ y <= x) |
| 86 | + scip.addCons(x.sum() <= 2) # Matrix variables can also appear in standard constraints, if the result expression is type Expr |
| 87 | +
|
| 88 | +.. note:: |
| 89 | + |
| 90 | + When creating constraints, one can mix standard variables and values in the same |
| 91 | + expressions. ``numpy`` will then handle this, and broadcast the correct operations. |
| 92 | + In general this can be viewed as creating an imaginary ``np.array`` of the appropriate |
| 93 | + shape and populating it with the variable / value. |
| 94 | + |
| 95 | +Class Properties |
| 96 | +================= |
| 97 | + |
| 98 | +A ``MatrixVariable`` and ``MatrixConstraint`` object have all the same getter |
| 99 | +functions that are in general available for the standard equivalent. An example |
| 100 | +is provided below for ``vtype``. |
| 101 | + |
| 102 | +.. code-block:: python |
| 103 | +
|
| 104 | + x = scip.addVar() |
| 105 | + matrix_x = scip.addMatrixVar(shape=(2,2)) |
| 106 | +
|
| 107 | + x.vtype() |
| 108 | + matrix_x.vtype() |
| 109 | +
|
| 110 | +The objects are not interchangeable however, when being passed into functions |
| 111 | +derived from the ``Model`` class. That is, there is currently no global support, |
| 112 | +that the following code runs: |
| 113 | + |
| 114 | +.. code-block:: python |
| 115 | +
|
| 116 | + scip.imaginary_function(x) # will always work |
| 117 | + scip.imaginary_function(matrix_x) # may have to access each variable manually |
| 118 | +
|
| 119 | +Accessing Variables and Constraints |
| 120 | +=================================== |
| 121 | + |
| 122 | +After creating the matrix variables and matrix constraints, |
| 123 | +one can always access the individual variables or constraints via their index. |
| 124 | + |
| 125 | +.. code-block:: python |
| 126 | +
|
| 127 | + x = scip.addMatrixVar(shape=(2, 2)) |
| 128 | + assert(isinstance(x, MatrixVariable)) |
| 129 | + assert(isinstance(x[0][0], Variable)) |
| 130 | + cons = x <= 2 |
| 131 | + assert(isinstance(cons, MatrixExprCons)) |
| 132 | + assert(isinstance(cons[0][0]), ExprCons) |
| 133 | +
|
| 134 | +Accessing Solution Values |
| 135 | +=========================== |
| 136 | + |
| 137 | +After optimizing a model, the solution values of a matrix variable can be |
| 138 | +accessed in an identical manner to the standard variant. There are two |
| 139 | +recommended ways to do this. |
| 140 | + |
| 141 | +.. code-block:: python |
| 142 | +
|
| 143 | + matrix_var_vals = scip.getVal(x) |
| 144 | +
|
| 145 | +.. code-block:: python |
| 146 | +
|
| 147 | + sol = scip.getBestSol() |
| 148 | + matrix_var_vals = sol[x] # returns a numpy array of values |
0 commit comments