Skip to content

Commit a0165ec

Browse files
Joao-DionisioOpt-Muccammghannam
authored
New matrix variable functionality (#957)
* Create branch * start of tests * Added extra dimension * Update tests * First working test * First working test * Expr from matrix variables test * Working version of ExprCons with le * Can now add linear matrix constraints * More robust Matrix classes * add todo * Clean up addMatrixCons function * Add MatrixVar methods * Update TODOs * Add getter functions to matrixConstraint class@ git push * Add checks for missing numpy * Add matrix API to readthedocs * Add minor grammar stuff. Add GCG to similar software * Remove universal build. Add type asserts * Update pipelines to include numpy * Add numpy as a dependency * Remove cimports for now * Support adding MatrixExprCons in addConss * Changed tests a bit * Support querying solution values using MatrixExpr * Add a test * Remove SCIP bug from tests. Already reported * Update CHANGELOG * Add info for 5.4.0 release * Make numpy a dependency (not just at build time) * Most comments addressed * Add test for documentation * Fix documentation * Add solution value accessing to docs * Format test file * Update docs/tutorials/matrix.rst Co-authored-by: Mohammed Ghannam <ghannam@zib.de> * Update docs/tutorials/matrix.rst Co-authored-by: Mohammed Ghannam <ghannam@zib.de> --------- Co-authored-by: Mark Turner <turner@zib.de> Co-authored-by: Mohammed Ghannam <mohammad.m.ghannam@gmail.com> Co-authored-by: Mark Turner <64978342+Opt-Mucca@users.noreply.github.com> Co-authored-by: Mohammed Ghannam <ghannam@zib.de>
1 parent 2e9380e commit a0165ec

16 files changed

+1389
-25
lines changed

.github/workflows/coverage.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
- name: Prepare python environment
3535
run: |
3636
python -m pip install --upgrade pip
37-
python -m pip install networkx cython pytest-cov
37+
python -m pip install networkx cython pytest-cov numpy
3838
3939
- name: Install PySCIPOpt
4040
run: |

.github/workflows/integration-test.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
- name: Prepare python environment
3535
run: |
3636
python -m pip install --upgrade pip
37-
python -m pip install networkx pytest-cov
37+
python -m pip install networkx pytest-cov numpy
3838
3939
- name: Install PySCIPOpt
4040
run: python -m pip install .
@@ -70,7 +70,7 @@ jobs:
7070
shell: powershell
7171
run: |
7272
python -m pip install --upgrade pip
73-
python -m pip install networkx pytest-cov
73+
python -m pip install networkx pytest-cov numpy
7474
7575
- name: Install PySCIPOpt
7676
shell: powershell
@@ -107,7 +107,7 @@ jobs:
107107
- name: Prepare python environment
108108
run: |
109109
python -m pip install --upgrade pip
110-
python -m pip install networkx pytest-cov pytest
110+
python -m pip install networkx pytest-cov pytest numpy
111111
112112
- name: Install PySCIPOpt
113113
run: |

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,22 @@
22

33
## Unreleased
44
### Added
5+
### Fixed
6+
### Changed
7+
### Removed
8+
9+
## 5.4.0 - 2024.02.24
10+
### Added
511
- Added option to get Lhs, Rhs of nonlinear constraints
612
- Added cutoffNode and test
713
- Added getMajorVersion, getMinorVersion, and getTechVersion
14+
- Added addMatrixVar and addMatriCons
15+
- Added MatrixVariable, MatrixConstraint, MatrixExpr, and MatrixExprCons
816
### Fixed
17+
- Warning at Model initialisation now uses new version calls
918
### Changed
1019
### Removed
20+
- Removed universal wheel type from setup.cfg (support for Python 2)
1121

1222
## 5.3.0 - 2025.02.07
1323
### Added

docs/build.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ To download SCIP please either use the pre-built SCIP Optimization Suite availab
2222
* - SCIP
2323
- PySCIPOpt
2424
* - 9.2
25-
- 5.3
25+
- 5.3, 5.4+
2626
* - 9.1
2727
- 5.1, 5.2.x
2828
* - 9.0

docs/similarsoftware.rst

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ Software using PySCIPOpt
8585

8686
This is software that is built on PySCIPOpt
8787

88+
- `PyGCGOpt <https://github.com/scipopt/PyGCGOpt>`_: An extension of SCIP, using generic decompositions for solving MIPs
8889
- `GeCO <https://github.com/CharJon/GeCO>`_: Generators for Combinatorial Optimization
8990
- `scip-routing <https://github.com/mmghannam/scip-routing>`_: An exact VRPTW solver in Python
9091
- `PySCIPOpt-ML <https://github.com/Opt-Mucca/PySCIPOpt-ML>`_: Python interface to automatically formulate Machine Learning models into Mixed-Integer Programs

docs/tutorials/index.rst

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ more detailed information see `this page <https://www.scipopt.org/doc/html/index
1414
vartypes
1515
constypes
1616
expressions
17+
matrix
1718
readwrite
1819
logfile
1920
branchrule
@@ -22,4 +23,5 @@ more detailed information see `this page <https://www.scipopt.org/doc/html/index
2223
heuristic
2324
nodeselector
2425
lazycons
25-
eventhandler
26+
eventhandler
27+
scipdex

docs/tutorials/matrix.rst

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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

docs/tutorials/scipdex.rst

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
###################
2+
Hands On Tutorials
3+
###################
4+
5+
For more tutorials with PySCIPOpt, especially interactive ones, please see
6+
`SCIPDex <https://github.com/mmghannam/scipdex>`_. SCIPDex is a collection of
7+
interactive exercises to get you started (and more) with PySCIPOpt

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ description = "Python interface and modeling environment for SCIP"
88
authors = [
99
{name = "Zuse Institute Berlin", email = "scip@zib.de"},
1010
]
11-
dependencies = []
11+
dependencies = ['numpy >=1.16.0']
1212
requires-python = ">=3.8"
1313
readme = "README.md"
1414
license = {text = "MIT"}

setup.cfg

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
[bdist_wheel]
2-
universal = 1
1+

setup.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from setuptools import find_packages, setup, Extension
2-
import os, platform, sys, re
2+
import os, platform, sys
33

44
# look for environment variable that specifies path to SCIP
55
scipoptdir = os.environ.get("SCIPOPTDIR", "").strip('"')
@@ -19,7 +19,6 @@
1919
print("Assuming that SCIP is installed globally, because SCIPOPTDIR is undefined.\n")
2020

2121
else:
22-
2322
# check whether SCIP is installed in the given directory
2423
if os.path.exists(os.path.join(scipoptdir, "include")):
2524
includedir = os.path.abspath(os.path.join(scipoptdir, "include"))
@@ -109,7 +108,7 @@
109108

110109
setup(
111110
name="PySCIPOpt",
112-
version="5.3.0",
111+
version="5.4.0",
113112
description="Python interface and modeling environment for SCIP",
114113
long_description=long_description,
115114
long_description_content_type="text/markdown",

src/pyscipopt/_version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '5.3.0'
1+
__version__ = '5.4.0'

0 commit comments

Comments
 (0)