One construct per spec#
This page has a section for each call form of the four
operators, sum, at, shift and
sum_back, and of dual. Each section
contains the smallest spec that uses the form, and the equation that
the typesetter prints from that spec. The
operators page
lists the same equations in one table, without the files.
sum(array)#
examples/operators/sum_all.yaml
description: >-
Every dimension at once: `sum(array)` lists no dimension and collapses every
one.
dimensions:
snapshot: { dtype: int }
generator: { dtype: str }
parameters:
budget: { dims: [] }
variables:
p:
dims: [snapshot, generator]
bounds: { lower: 0 }
constraints:
fleet_budget:
dims: []
expression: sum(p) <= budget
objective: { sense: minimize, expression: sum(p) }
\(\sum_{g \in \mathcal{G},\ t \in \mathcal{T}} p_{g,t} \le \mathrm{budget}\)
sum(array, over=dim)#
examples/operators/sum.yaml
description: >-
The plain reduction: `sum(array, over=dim)` collapses one dimension.
dimensions:
snapshot: { dtype: int }
generator: { dtype: str }
parameters:
limit: { dims: [snapshot] }
variables:
p:
dims: [snapshot, generator]
bounds: { lower: 0 }
constraints:
fleet_total:
dims: [snapshot]
expression: sum(p, over=generator) <= limit
objective: { sense: minimize, expression: sum(p) }
\(\sum_{g \in \mathcal{G}} p_{g,t} \le \mathrm{limit}_{t} \qquad \forall\, t \in \mathcal{T}\)
sum(array, over=[a, …])#
examples/operators/sum_list.yaml
description: >-
Several dimensions at once: `sum(array, over=[a, b])` collapses each
dimension in the list.
dimensions:
snapshot: { dtype: int }
generator: { dtype: str }
carrier: { dtype: str }
parameters:
limit: { dims: [carrier] }
variables:
p:
dims: [snapshot, generator, carrier]
bounds: { lower: 0 }
constraints:
carrier_total:
dims: [carrier]
expression: sum(p, over=[snapshot, generator]) <= limit
objective: { sense: minimize, expression: sum(p) }
\(\sum_{g \in \mathcal{G},\ t \in \mathcal{T}} p_{c,g,t} \le \mathrm{limit}_{c} \qquad \forall\, c \in \mathcal{C}\)
sum(array, over=dim, by=subject[m])#
examples/operators/sum_by.yaml
description: >-
A sum by group: `sum(array, over=dim, by=subject[m])` groups the result by
the label that map `m` gives each row. The data of the map, not the file,
sets which generator sits at which bus.
dimensions:
snapshot: { dtype: int }
generator: { dtype: str }
bus: { dtype: str }
maps:
generator: { bus: bus }
parameters:
limit: { dims: [snapshot, bus] }
variables:
p:
dims: [snapshot, generator]
bounds: { lower: 0 }
constraints:
bus_total:
dims: [snapshot, bus]
expression: sum(p, over=generator, by=generator[bus]) <= limit
objective: { sense: minimize, expression: sum(p) }
\(\sum_{g \in \mathcal{G} \,:\, \mathrm{bus}(g) = b} p_{g,t} \le \mathrm{limit}_{b,t} \qquad \forall\, b \in \mathcal{B},\ t \in \mathcal{T}\)
sum(array, over=dim, by=subject[m]), joining on the rest of the key#
examples/operators/sum_by_columns.yaml
description: >-
A sum that keeps a key column: `sum(array, over=dim, by=subject[m])` joins
on the column over `dim` and groups by map `m`. The sum also joins on the
key column that the call does not list. It keeps that column, so each zone
gets one total per period.
dimensions:
generator: { dtype: str }
period: { dtype: int }
zone: { dtype: str }
relations:
zone_of: { dims: [generator, period] }
maps:
zone_of: { zone: zone }
parameters:
demand: { dims: [zone, period] }
variables:
p:
dims: [generator, period]
bounds: { lower: 0 }
constraints:
zone_balance:
dims: [zone, period]
expression: sum(p, over=generator, by=zone_of[zone]) >= demand
objective: { sense: minimize, expression: sum(p) }
\(\sum_{g \in \mathcal{G} \,:\, \mathrm{zone}(g,\ e) = z} p_{g,e} \ge \mathrm{demand}_{e,z} \qquad \forall\, e \in \mathcal{E},\ z \in \mathcal{Z}\)
sum(array, over=[dim, …], by=subject[m, …])#
examples/operators/sum_by_column_lists.yaml
description: >-
Several columns and maps: `sum(array, over=[dim, …], by=subject[m, …])`
joins on both columns of the relation and groups by both of its maps.
dimensions:
generator: { dtype: str }
period: { dtype: int }
bus: { dtype: str }
technology: { dtype: str }
relations:
slot_of: { dims: [generator, period] }
maps:
slot_of: { bus: bus, technology: technology }
parameters:
cap: { dims: [bus, technology] }
variables:
p:
dims: [generator, period]
bounds: { lower: 0 }
constraints:
slot_cap:
dims: [bus, technology]
expression: sum(p, over=[generator, period], by=slot_of[bus, technology]) <= cap
objective: { sense: minimize, expression: sum(p) }
\(\sum_{g \in \mathcal{G},\ e \in \mathcal{E} \,:\, \mathrm{bus}(g,\ e) = b \wedge \mathrm{technology}(g,\ e) = t} p_{g,e} \le \mathrm{cap}_{b,t} \qquad \forall\, b \in \mathcal{B},\ t \in \mathcal{T}\)
at(array, by=subject[m])#
examples/operators/at.yaml
description: >-
The join without a grouping: `at(array, by=subject[m])` takes, for each
`snapshot`, the value at the `period` that the map gives that snapshot.
dimensions:
snapshot: { dtype: int }
period: { dtype: int }
maps:
snapshot: { period: period }
parameters:
cap: { dims: [period] }
variables:
p:
dims: [snapshot]
bounds: { lower: 0 }
constraints:
within_cap:
dims: [snapshot]
expression: p <= at(cap, by=snapshot[period])
objective: { sense: minimize, expression: sum(p) }
\(p_{t} \le \mathrm{cap}_{\mathrm{period}(t)} \qquad \forall\, t \in \mathcal{T}\)
at(array, by=subject[m]), two maps into one dimension#
examples/operators/at_columns.yaml
description: >-
One of two maps: where a subject has two maps into one dimension,
`at(array, by=subject[m])` takes the value at the label of map `m`. Here `m`
is `bus0`, the sending end of a line.
dimensions:
line: { dtype: str }
bus: { dtype: str }
maps:
line: { bus0: bus, bus1: bus }
parameters:
cap: { dims: [bus] }
variables:
f:
dims: [line]
bounds: { lower: 0 }
constraints:
sending_cap:
dims: [line]
expression: f <= at(cap, by=line[bus0])
objective: { sense: minimize, expression: sum(f) }
\(f_{l} \le \mathrm{cap}_{\mathrm{bus0}(l)} \qquad \forall\, l \in \mathcal{L}\)
shift(array, along=dim, offset=n)#
examples/operators/shift.yaml
description: >-
A shift with no `edge=`: the vacated position is absent, so the constraint
has no row there.
dimensions:
snapshot: { dtype: int, ordered: true }
variables:
p:
dims: [snapshot]
bounds: { lower: 0 }
constraints:
no_faster_than_before:
dims: [snapshot]
expression: p <= shift(p, along=snapshot, offset=1)
objective: { sense: minimize, expression: sum(p) }
\(p_{t} \le p_{t - 1} \qquad \forall\, t \in \mathcal{T}\)
shift(array, along=dim, offset=n, edge='wrap')#
examples/operators/shift_wrap.yaml
description: >-
A cyclic shift: the horizon wraps, so the first position takes the value of
the last and no position is vacated.
dimensions:
snapshot: { dtype: int, ordered: true }
variables:
p:
dims: [snapshot]
bounds: { lower: 0 }
constraints:
no_faster_than_before:
dims: [snapshot]
expression: p <= shift(p, along=snapshot, offset=1, edge='wrap')
objective: { sense: minimize, expression: sum(p) }
\(p_{t} \le p_{t \ominus 1} \qquad \forall\, t \in \mathcal{T}\)
shift(array, along=dim, offset=n, edge=v)#
examples/operators/shift_edge.yaml
description: >-
A shift with a value at the edge: the vacated position contributes the
`edge=` value, here 0, instead of being absent. The constraint has a row
there.
dimensions:
snapshot: { dtype: int, ordered: true }
variables:
p:
dims: [snapshot]
bounds: { lower: 0 }
constraints:
no_faster_than_before:
dims: [snapshot]
expression: p <= shift(p, along=snapshot, offset=1, edge=0)
objective: { sense: minimize, expression: sum(p) }
\(p_{t} \le p_{t \boxminus_{0} 1} \qquad \forall\, t \in \mathcal{T}\)
shift(array, along=dim, offset=p, edge=…)#
examples/operators/shift_by_parameter.yaml
description: >-
A shift by an offset that differs per entity: `offset=` is an integer
parameter. Each technology is shifted by its own lead time.
dimensions:
technology: { dtype: str }
month: { dtype: int, ordered: true }
parameters:
lead: { dims: [technology], dtype: int }
demand: { dims: [technology, month] }
variables:
order:
dims: [technology, month]
bounds: { lower: 0 }
constraints:
arrives_after_its_lead:
dims: [technology, month]
expression: shift(order, along=month, offset=lead, edge=0) >= demand
objective: { sense: minimize, expression: sum(order) }
\(\mathit{order}_{m \boxminus_{0} \mathrm{lead},t} \ge \mathrm{demand}_{m,t} \qquad \forall\, m \in \mathcal{M},\ t \in \mathcal{T}\)
shift(array, along=dim, offset=n, within=subject[m])#
examples/operators/shift_partitioned.yaml
description: >-
A shift inside a group: each season wraps on itself. A season's first
snapshot takes the value of that season's last, and no value crosses from one
season into the next.
dimensions:
snapshot: { dtype: int, ordered: true }
season: { dtype: str }
maps:
snapshot: { season: season }
variables:
p:
dims: [snapshot]
bounds: { lower: 0 }
constraints:
no_faster_than_before_in_season:
dims: [snapshot]
expression: p <= shift(p, along=snapshot, offset=1, edge='wrap', within=snapshot[season])
objective: { sense: minimize, expression: sum(p) }
\(p_{t} \le p_{t \ominus^{\mathrm{season}(t)} 1} \qquad \forall\, t \in \mathcal{T}\)
sum_back(array, along=dim, window=n)#
examples/operators/sum_back.yaml
description: >-
A trailing window of a fixed width: a unit that started in the last three
hours is still on.
dimensions:
unit: { dtype: str }
hour: { dtype: int, ordered: true }
parameters:
min_up: { dims: [unit], dtype: int }
variables:
started:
dims: [unit, hour]
domain: binary
on:
dims: [unit, hour]
domain: binary
constraints:
stays_up_its_own_time:
dims: [unit, hour]
expression: sum_back(started, along=hour, window=3) <= on
objective: { sense: minimize, expression: sum(on) }
\(\sum_{h' \in \mathcal{H} \,:\, 0 \le h - h' < 3} \mathit{started}_{h',u} \le \mathit{on}_{h,u} \qquad \forall\, h \in \mathcal{H},\ u \in \mathcal{U}\)
sum_back(array, along=dim, window=p)#
examples/operators/sum_back_by_parameter.yaml
description: >-
A trailing window whose width is data: `window=` is an integer parameter. A
unit stays up for its own minimum up time.
dimensions:
unit: { dtype: str }
hour: { dtype: int, ordered: true }
parameters:
min_up: { dims: [unit], dtype: int }
variables:
started:
dims: [unit, hour]
domain: binary
on:
dims: [unit, hour]
domain: binary
constraints:
stays_up_its_own_time:
dims: [unit, hour]
expression: sum_back(started, along=hour, window=min_up) <= on
objective: { sense: minimize, expression: sum(on) }
\(\sum_{h' \in \mathcal{H} \,:\, 0 \le h - h' < \mathrm{min\_up}} \mathit{started}_{h',u} \le \mathit{on}_{h,u} \qquad \forall\, h \in \mathcal{H},\ u \in \mathcal{U}\)
sum_back(array, along=dim, window=p, edge='wrap')#
examples/operators/sum_back_wrap.yaml
description: >-
A trailing window on a day that repeats, so the window at the first hour
reaches back into the last.
dimensions:
unit: { dtype: str }
hour: { dtype: int, ordered: true }
parameters:
min_up: { dims: [unit], dtype: int }
variables:
started:
dims: [unit, hour]
domain: binary
on:
dims: [unit, hour]
domain: binary
constraints:
stays_up_its_own_time:
dims: [unit, hour]
expression: sum_back(started, along=hour, window=min_up, edge='wrap') <= on
objective: { sense: minimize, expression: sum(on) }
\(\sum_{h' \in \mathcal{H} \,:\, 0 \le h \ominus h' < \mathrm{min\_up}} \mathit{started}_{h',u} \le \mathit{on}_{h,u} \qquad \forall\, h \in \mathcal{H},\ u \in \mathcal{U}\)
sum_back(array, along=dim, window=n, within=subject[m])#
examples/operators/sum_back_partitioned.yaml
description: >-
A window inside a group: the window stops at the first hour of each day.
dimensions:
unit: { dtype: str }
hour: { dtype: int, ordered: true }
day: { dtype: str }
maps:
hour: { day: day }
variables:
started:
dims: [unit, hour]
domain: binary
on:
dims: [unit, hour]
domain: binary
constraints:
stays_up_inside_its_day:
dims: [unit, hour]
expression: sum_back(started, along=hour, window=3, within=hour[day]) <= on
objective: { sense: minimize, expression: sum(on) }
\(\sum_{h' \in \mathcal{H} \,:\, 0 \le h -^{\mathrm{day}(h)} h' < 3} \mathit{started}_{h',u} \le \mathit{on}_{h,u} \qquad \forall\, h \in \mathcal{H},\ u \in \mathcal{U}\)
dual(constraint)#
examples/operators/dual.yaml
description: >-
After the solver runs, `dual(constraint)` has the constraint's dimensions,
and each value is the marginal value of the constraint at that
coordinate (its shadow price).
dimensions:
snapshot: { dtype: int }
parameters:
load: { dims: [snapshot] }
variables:
p:
dims: [snapshot]
bounds: { lower: 0 }
constraints:
balance:
dims: [snapshot]
expression: p >= load
expressions:
price: dual(balance)
objective: { sense: minimize, expression: sum(p) }
\(\mathit{price}_{t} = \lambda_{\mathrm{balance},t} \qquad \forall\, t \in \mathcal{T}\)
See also
- Operators, for what each operator accepts and what mathspec refuses
dual, for the entries that may calldual- Typeset output, for the three formats the typesetter prints