Note

You can download this example as a Jupyter notebook or start it in interactive mode.

# Migrating from Pyomo#

Similar to the implementation in Pyomo, expressions and constraints can be created using a combination of a function and a set of coordinates to iterate over. For creating expressions, the function itself has to return a ScalarLinearExpression which can be obtained by selecting single values of the variables are combining them:

:

import linopy
import pandas as pd

m = linopy.Model()
coords = pd.RangeIndex(10), ["a", "b"]
x = m.add_variables(0, 100, coords, name='x')
x

:

Variable (dim_0: 10, dim_1: 2)
------------------------------
[0, a]: x[0, a] ∈ [0, 100]
[0, b]: x[0, b] ∈ [0, 100]
[1, a]: x[1, a] ∈ [0, 100]
[1, b]: x[1, b] ∈ [0, 100]
[2, a]: x[2, a] ∈ [0, 100]
[2, b]: x[2, b] ∈ [0, 100]
[3, a]: x[3, a] ∈ [0, 100]
...
[6, b]: x[6, b] ∈ [0, 100]
[7, a]: x[7, a] ∈ [0, 100]
[7, b]: x[7, b] ∈ [0, 100]
[8, a]: x[8, a] ∈ [0, 100]
[8, b]: x[8, b] ∈ [0, 100]
[9, a]: x[9, a] ∈ [0, 100]
[9, b]: x[9, b] ∈ [0, 100]

:

x[0, 'a']

:

ScalarVariable: x[0, a]


Such a ScalarVariable is very light-weight and can be used in functions in order to create expressions, just like you know it from Pyomo. The following function shows how:

:

def bound(m, i, j):
if i % 2:
return (i / 2) * x[i, j]
else:
return i * x[i, j]

expr = m.linexpr(bound, coords)
expr

:

LinearExpression (dim_0: 10, dim_1: 2):
---------------------------------------
[0, a]: +0 x[0, a]
[0, b]: +0 x[0, b]
[1, a]: +0.5 x[1, a]
[1, b]: +0.5 x[1, b]
[2, a]: +2 x[2, a]
[2, b]: +2 x[2, b]
[3, a]: +1.5 x[3, a]
...
[6, b]: +6 x[6, b]
[7, a]: +3.5 x[7, a]
[7, b]: +3.5 x[7, b]
[8, a]: +8 x[8, a]
[8, b]: +8 x[8, b]
[9, a]: +4.5 x[9, a]
[9, b]: +4.5 x[9, b]


Note that the function’s first argument has to be the model itself, even though it might not be used in the function.

This functionality is also supported by the .add_constraints function. When passing a function as a first argument, .add_constraints expects coords to by non-empty. The function itself has to return a AnonymousScalarConstraint, as done by

:

x[0, 'a'] <= 3

:

AnonymousScalarConstraint: +1 x[0, a] <= 3

:

def bound(m, i, j):
if i % 2:
return (i / 2) * x[i, j] >= i
else:
return i * x[i, j]  == 0.

con

:

Constraint con0 (dim_0: 10, dim_1: 2):
----------------------------------------
[0, a]: +0 x[0, a]   = 0.0
[0, b]: +0 x[0, b]   = 0.0
[1, a]: +0.5 x[1, a] ≥ 1.0
[1, b]: +0.5 x[1, b] ≥ 1.0
[2, a]: +2 x[2, a]   = 0.0
[2, b]: +2 x[2, b]   = 0.0
[3, a]: +1.5 x[3, a] ≥ 3.0
...
[6, b]: +6 x[6, b]   = 0.0
[7, a]: +3.5 x[7, a] ≥ 7.0
[7, b]: +3.5 x[7, b] ≥ 7.0
[8, a]: +8 x[8, a]   = 0.0
[8, b]: +8 x[8, b]   = 0.0
[9, a]: +4.5 x[9, a] ≥ 9.0
[9, b]: +4.5 x[9, b] ≥ 9.0