Skip to main content

Lags

Time-coupled constraints reference the previous period. A storage balance relates the stored energy at t to the stored energy at t-1. A ramp limit bounds the change in output between consecutive periods. T - 1 is the set T lagged by one member, and x[T - 1] references the variable at the previous member.

A lag that drops the boundary row​

import numpy as np
from nimopt import Model, Set

T = Set("T", np.array(["t0", "t1", "t2"]))

m = Model("schedule")
x = m.var("x", (T,))
rows = m.constraint("carry", x[T] - x[T - 1] <= 0.0)

print(rows.n_rows, rows.nnz)
print(m.assemble().to_dense())
Output
2 4
[[-1. 1. 0.]
[ 0. -1. 1.]]

Each row references its own column and the previous one. The first member has no predecessor, and its row is not produced. Three members give two rows.

T + 1 references the following member by the same rule.

A lag that wraps​

T.cyclic lags with wrap-around: the member before the first is the last. No row is dropped.

import numpy as np
from nimopt import Model, Set

T = Set("T", np.array(["t0", "t1", "t2"]))

m = Model("schedule")
x = m.var("x", (T,))
rows = m.constraint("carry", x[T] - x[T.cyclic - 1] <= 0.0)

print(rows.n_rows, rows.nnz)
print(m.assemble().to_dense())
Output
3 6
[[ 1. 0. -1.]
[-1. 1. 0.]
[ 0. -1. 1.]]

Three rows, and the first references the last column: the -1 in row 0 is in the final position. A storage balance over a repeating horizon is written this way, and the level at the end of the horizon enters the row of the first period.

A lag applies to a reference, not to a sum​

Sum runs over the members of a set and takes the set itself. Passing a lagged set raises ValueError.

import numpy as np
from nimopt import Model, Set, Sum

T = Set("T", np.array(["t0", "t1", "t2"]))

m = Model("schedule")
x = m.var("x", (T,))

Sum(T - 1, x[T])
Raises ValueError
ValueError: a sum is over the members of ['T'] and takes the set, not a lag of it; write the lag at the variable's reference

The lag belongs on the variable reference: Sum(T, x[T - 1]).

A lag applies to a variable, not to a parameter​

Reading a parameter at a lag raises ValueError. A coefficient is indexed by the row it appears in, and a lag selects which column a row references. rate[T] * x[T - 1] applies the rate at t to the variable at t-1.

import numpy as np
from nimopt import Param, Set

T = Set("T", np.array(["t0", "t1", "t2"]))
rate = Param.from_dense("rate", (T,), np.array([1.0, 2.0, 3.0]))

rate[T - 1]
Raises ValueError
ValueError: parameter 'rate' is read at a lag ['T']; write the lag at the variable's reference

A lag is an integer number of members. A fractional lag raises ValueError and is not truncated to a different lag.

import numpy as np
from nimopt import Set

T = Set("T", np.arange(3))
T - 1.7
Raises ValueError
ValueError: a lag is a whole number of members; got 1.7