Skip to content

Every construct, as math#

Every construct the language has is printed here, beside the math the typesetter gives it. Look up the notation for one construct, or read the whole notation at once: two constructs that mean different things print differently, and every symbol below appears first in the legend that defines it.

The page is generated by pixi run python -m tools.notation, almost all of it from one model, tests/typesetting/golden/model.yaml. That model is not a sensible optimisation problem. It is the one file that carries every construct at once, and tests in tests/typesetting/ hold it to the language: every operator a format spells, every kind of node the parsers produce, and every line of the code that walks them. So a construct added to the language either arrives on this page, or CI fails. The curves are the exception, and use one real model per method:.

For what an operator does, read Operators, which prints the same math with one row per call. For models written to be read, start with the examples.

The symbols below are derived from the names in the file, which is what a model prints with no setup, so you see \(\mathit{load}_{t}\) rather than \(\ell_t\). A symbol table replaces every symbol, and changes nothing else on this page.

The legend#

A dimension, a lookup and a parameter declare no equation; what they print is the legend every model opens with.

dimensions:
  snapshot: { dtype: int }
  generator: { dtype: str }
  bus: { dtype: str }
  zone: { dtype: str }
  season: { dtype: str }
  technology: { dtype: str }

lookups:
  gen_bus: { over: generator, into: bus }
  gen_tech: { over: generator, into: technology } # a second map out of `generator`, to group through both at once
  zone_of: { over: bus, into: zone }
  area_of: { over: bus, into: zone } # a second map into the same set, to compare against
  season_of: { over: snapshot, into: season }

parameters:
  p_max: { dims: [generator] }
  p_min: { dims: [generator] }
  cost: { dims: [generator] }
  load: { dims: [snapshot, bus] }
  is_flexible: { dims: [generator], dtype: bool }
  zone_cap: { dims: [zone] }
  tech_cap: { dims: [bus, technology] }
  min_up: { dims: [generator], dtype: int }
  eta: { dims: [generator] } # a Greek name that is *given*, so the rule wins and it prints as the word
  lead: { dims: [generator], dtype: int }
  budget: { dims: [] } # scalar: the legend says so rather than printing an empty product
  growth: { dims: [] } # the base of a power; the exponent is `lead`, a column

Sets#

Symbol Meaning
\(\mathcal{T}\) index \(t\) — snapshot (int coordinates) with \(\mathrm{season\_of}: \mathcal{T} \to \mathcal{S}\)
\(\mathcal{G}\) index \(g\) — generator with \(\mathrm{gen\_bus}: \mathcal{G} \to \mathcal{B},\ \mathrm{gen\_tech}: \mathcal{G} \to \mathcal{E}\)
\(\mathcal{B}\) index \(b\) — bus with \(\mathrm{zone\_of}: \mathcal{B} \to \mathcal{Z},\ \mathrm{area\_of}: \mathcal{B} \to \mathcal{Z}\)
\(\mathcal{Z}\) index \(z\) — zone
\(\mathcal{S}\) index \(s\) — season
\(\mathcal{E}\) index \(e\) — technology

Parameters#

Symbol Meaning
\(\mathrm{p}^{\mathrm{max}}\) p_max over \(\mathcal{G}\)
\(\mathrm{p}^{\mathrm{min}}\) p_min over \(\mathcal{G}\)
\(\mathrm{cost}\) cost over \(\mathcal{G}\)
\(\mathrm{load}\) load over \(\mathcal{T} \times \mathcal{B}\)
\(\mathrm{is\_flexible}\) is_flexible over \(\mathcal{G}\)
\(\mathrm{zone\_cap}\) zone_cap over \(\mathcal{Z}\)
\(\mathrm{tech\_cap}\) tech_cap over \(\mathcal{B} \times \mathcal{E}\)
\(\mathrm{min\_up}\) min_up over \(\mathcal{G}\)
\(\mathrm{eta}\) eta over \(\mathcal{G}\)
\(\mathrm{lead}\) lead over \(\mathcal{G}\)
\(\mathrm{budget}\) budget (scalar)
\(\mathrm{growth}\) growth (scalar)

Variables#

Symbol Meaning
\(p\) p over \(\mathcal{T} \times \mathcal{G}\)
\(\mathit{spill}\) spill over \(\mathcal{T}\)
\(\mathit{slack}\) slack over \(\mathcal{T}\)
\(\theta\) theta over \(\mathcal{B}\)
\(\mathit{on}\) on over \(\mathcal{T} \times \mathcal{G}\)
\(\mathit{units}\) units over \(\mathcal{G}\)
\(\mathit{spare}\) spare over \(\mathcal{G}\)
\(\mathit{reserve}\) reserve (scalar)
\(\mathit{headroom}\) headroom (scalar)
\(\mathit{weight}\) weight over \(\mathcal{T} \times \mathcal{G}\)
\(\mathit{imported}\) imported over \(\mathcal{T} \times \mathcal{Z}\) — energy brought into a zone, built by the model this file is laid over

Definitions#

Symbol Meaning
\(\mathit{spend}\) spend over \(\mathcal{T}\) — what a snapshot's dispatch costs
\(\mathit{lcoe}\) lcoe (scalar)
\(\mathit{marginal\_price}\) marginal_price over \(\mathcal{T} \times \mathcal{B}\)
\(\mathit{congestion\_rent}\) congestion_rent over \(\mathcal{T} \times \mathcal{Z}\)
\(\mathit{net\_import}\) net_import over \(\mathcal{T}\)
\(\mathrm{startup\_cost}\) startup_cost over \(\mathcal{T} \times \mathcal{G}\) — what starting a unit in this snapshot costs, which the horizon's edge changes

Upright is what the model is given — a parameter such as \(\mathrm{p}^{\mathrm{max}}\), a coordinate map, a label — and italic is what the solver chooses, such as \(p\). An index is italic too, being what a quantifier chooses, and a set is script.

\(t \ominus k\) denotes cyclic translation: index \(t-k\) taken modulo the size of the dimension (roll). Plain \(t-k\) (shift) has no wraparound — terms translated past the edge are simply absent.

\(t \boxminus_{v} k\) denotes translation with \(v\) standing where index \(t-k\) leaves the dimension (shift(edge=v)), so the row at that boundary is built and carries \(v\) rather than being dropped.

\(t \ominus^{\mathrm{lookup}(t)} k\) denotes a translation counted inside the group a lookup puts \(t\) in (shift(by=lookup)), so a term never crosses out of its own group. The two modifiers take different slots — the group above, the fill below — so \(t \boxminus_{v}^{\mathrm{lookup}(t)} k\) is both at once.

\(\mathrm{pos}(t)\) denotes where index \(t\) sits along its dimension's own order — the order shift walks, not the order labels sort in — counted from \(0\). The index itself stays the coordinate, so \(t\) compares against labels and \(\mathrm{pos}(t)\) against positions.

\(\mathrm{pos}_{\mathrm{lookup}(t)}(t)\) counts within the group a lookup puts \(t\) in: the subscript names the map, \(\mathcal{T}_{\mathrm{lookup}(t)}\) is the group it lands in, and that group has a first position of its own.

\(\lvert \mathcal{T} \rvert\) denotes the size of the set being counted along, and a position counted from the end prints against it — \(\lvert \mathcal{T} \rvert - 1\) is the last position, one less than the size because the first is \(0\).

The objective#

objective#

a sense, a product of two variables, a power over two parameters, a power of one of those, and the summations a scalar objective spells out beside two scalar terms

sense: maximize
expression: sum(p * cost) + sum(p * p * cost) + sum(p * cost * growth ** lead) + sum(p * (growth ** lead) ** 2) + sum(p * p_max) - reserve + -headroom
\[ \max \sum_{t \in \mathcal{T},\ g \in \mathcal{G}} p_{t,g} \cdot \mathrm{cost}_{g} + \sum_{t \in \mathcal{T},\ g \in \mathcal{G}} p_{t,g} \cdot p_{t,g} \cdot \mathrm{cost}_{g} + \sum_{t \in \mathcal{T},\ g \in \mathcal{G}} p_{t,g} \cdot \mathrm{cost}_{g} \cdot \mathrm{growth}^{\mathrm{lead}_{g}} + \sum_{t \in \mathcal{T},\ g \in \mathcal{G}} p_{t,g} \cdot \left( \mathrm{growth}^{\mathrm{lead}_{g}} \right)^{2} + \sum_{t \in \mathcal{T},\ g \in \mathcal{G}} p_{t,g} \cdot \mathrm{p}^{\mathrm{max}}_{g} - \mathit{reserve} - \mathit{headroom} \]

Constraints#

budgeted#

names the plain expression: its symbol prints here, its definition once below

budgeted:
  foreach: [snapshot]
  expression: spend <= budget
\[ \mathit{spend}_{t} \le \mathrm{budget} \qquad \forall\, t \in \mathcal{T} \]

starts#

names the cased expression: its symbol prints here, its block once below

starts:
  foreach: [snapshot, generator]
  expression: p <= startup_cost
\[ p_{t,g} \le \mathrm{startup\_cost}_{t,g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

balance#

sum over a lookup

balance:
  foreach: [snapshot, bus]
  expression: sum(p, by=gen_bus) + spill - slack == load
\[ \sum_{g \in \mathcal{G} \,:\, \mathrm{gen\_bus}(g) = b} p_{t,g} + \mathit{spill}_{t} - \mathit{slack}_{t} = \mathrm{load}_{t,b} \qquad \forall\, t \in \mathcal{T},\ b \in \mathcal{B} \]

ramp#

roll (cyclic) and shift (acyclic) in one equation

ramp:
  foreach: [snapshot, generator]
  expression: p - shift(p, over=snapshot, offset=1, edge='wrap') <= shift(p, over=snapshot, offset=1) + p_max
\[ p_{t,g} - p_{t \ominus 1,g} \le p_{t - 1,g} + \mathrm{p}^{\mathrm{max}}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

edges#

the two translations ramp leaves out: a fill, and forwards

edges:
  foreach: [snapshot, generator]
  expression: >-
    shift(p, over=snapshot, offset=1, edge=0)
    <= shift(p, over=snapshot, offset=-1, edge=0) + p_max
\[ p_{t \boxminus_{0} 1,g} \le p_{t \boxplus_{0} 1,g} + \mathrm{p}^{\mathrm{max}}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

ahead#

the cyclic translation forwards, which is a fourth symbol again

ahead:
  foreach: [snapshot, generator]
  expression: p <= shift(p, over=snapshot, offset=-1, edge='wrap')
\[ p_{t,g} \le p_{t \oplus 1,g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

composed#

two steps of one policy are one step; a zero step is none at all

composed:
  foreach: [snapshot, generator]
  expression: shift(shift(p, over=snapshot, offset=1), over=snapshot, offset=1) <= shift(p_max, over=generator, offset=0)
\[ p_{t - 2,g} \le \mathrm{p}^{\mathrm{max}}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

uncomposed#

a named offset under a numbered one stays two steps, not their sum

uncomposed:
  foreach: [snapshot, generator]
  expression: shift(shift(p, over=snapshot, offset=lead, edge=0), over=snapshot, offset=1) <= p_max
\[ p_{\left( t - 1 \right) \boxminus_{0} \mathrm{lead},g} \le \mathrm{p}^{\mathrm{max}}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

crossed#

two dimensions translated at one leaf, each with its own policy

crossed:
  foreach: [snapshot, generator]
  expression: shift(shift(p, over=snapshot, offset=1, edge='wrap'), over=generator, offset=-1) <= p_max
\[ p_{t \ominus 1,g + 1} \le \mathrm{p}^{\mathrm{max}}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

lead_time#

an offset the data carries, so it prints as a symbol rather than a number

lead_time:
  foreach: [snapshot, generator]
  expression: shift(p, over=snapshot, offset=lead, edge=0) <= p_max
\[ p_{t \boxminus_{0} \mathrm{lead},g} \le \mathrm{p}^{\mathrm{max}}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

in_season#

a translation partitioned by a lookup: the group rides on the operator

in_season:
  foreach: [snapshot, generator]
  expression: p <= shift(p, over=snapshot, offset=1, edge='wrap', by=season_of)
\[ p_{t,g} \le p_{t \ominus^{\mathrm{season\_of}(t)} 1,g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

held_in_season#

the same group, with a fill: each season's opening row is kept and given a zero

held_in_season:
  foreach: [snapshot, generator]
  expression: p <= shift(p, over=snapshot, offset=1, edge=0, by=season_of)
\[ p_{t,g} \le p_{t \boxminus_{0}^{\mathrm{season\_of}(t)} 1,g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

window#

a trailing window of fixed width

window:
  foreach: [snapshot, generator]
  expression: sum_back(on, over=snapshot, within=3) <= units
\[ \sum_{t' \in \mathcal{T} \,:\, 0 \le t - t' < 3} \mathit{on}_{t',g} \le \mathit{units}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

history#

the same window, its width in the data and its edge wrapped

history:
  foreach: [snapshot, generator]
  expression: sum_back(on, over=snapshot, within=min_up, edge='wrap') <= units
\[ \sum_{t' \in \mathcal{T} \,:\, 0 \le t \ominus t' < \mathrm{min\_up}} \mathit{on}_{t',g} \le \mathit{units}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

seasonal_window#

a window partitioned by a lookup: the group rides on the operator

seasonal_window:
  foreach: [snapshot, generator]
  expression: sum_back(on, over=snapshot, within=3, by=season_of) <= units
\[ \sum_{t' \in \mathcal{T} \,:\, 0 \le t -^{\mathrm{season\_of}(t)} t' < 3} \mathit{on}_{t',g} \le \mathit{units}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

pullback#

at(), which re-indexes through a lookup instead of an offset

pullback:
  foreach: [snapshot, bus]
  expression: spill <= at(zone_cap, by=zone_of)
\[ \mathit{spill}_{t} \le \mathrm{zone\_cap}_{\mathrm{zone\_of}(b)} \qquad \forall\, t \in \mathcal{T},\ b \in \mathcal{B} \]

grouped_twice#

one grouping through two maps: the domain carries both conditions

grouped_twice:
  foreach: [snapshot, bus, technology]
  expression: sum(p, by=[gen_bus, gen_tech]) <= tech_cap
\[ \sum_{g \in \mathcal{G} \,:\, \mathrm{gen\_bus}(g) = b \wedge \mathrm{gen\_tech}(g) = e} p_{t,g} \le \mathrm{tech\_cap}_{b,e} \qquad \forall\, t \in \mathcal{T},\ b \in \mathcal{B},\ e \in \mathcal{E} \]

pulled_back_twice#

its adjoint, reading one slot through a pair of labels

pulled_back_twice:
  foreach: [generator]
  expression: units <= at(tech_cap, by=[gen_bus, gen_tech])
\[ \mathit{units}_{g} \le \mathrm{tech\_cap}_{\mathrm{gen\_bus}(g),\mathrm{gen\_tech}(g)} \qquad \forall\, g \in \mathcal{G} \]

arithmetic#

division, both unary signs, a sign beside a sign, floats with and without an exponent, bracketing

arithmetic:
  foreach: [snapshot]
  expression: >-
    sum(p / 2 + -cost - -1e-5 * p + 2.5e-7 * cost + 0.5 * p, over=generator)
    >= -sum(+p, over=generator) * -3
\[ \sum_{g \in \mathcal{G}} \left( \frac{p_{t,g}}{2} - \mathrm{cost}_{g} + 10^{-5} \cdot p_{t,g} + 2.5 \times 10^{-7} \cdot \mathrm{cost}_{g} + 0.5 \cdot p_{t,g} \right) \ge -\left( \sum_{g \in \mathcal{G}} p_{t,g} \right) \cdot \left( -3 \right) \qquad \forall\, t \in \mathcal{T} \]

total#

a sum naming no dim, whose domain is the one place the dims it took are said

total:
  foreach: []
  expression: sum(p) <= budget
\[ \sum_{t \in \mathcal{T},\ g \in \mathcal{G}} p_{t,g} \le \mathrm{budget} \]

scalar#

a parameter over nothing, and a mask that is a bare parameter

scalar:
  foreach: [generator]
  where: "cost"
  expression: units <= budget
\[ \mathit{units}_{g} \le \mathrm{budget} \qquad \forall\, g \in \mathcal{G} \,:\, \mathrm{cost}_{g} \text{ is defined} \]

running#

a mask on a variable's existence, and one on a dimension's label

running:
  foreach: [snapshot, bus]
  where: "theta AND snapshot >= 3"
  expression: theta <= load
\[ \theta_{b} \le \mathrm{load}_{t,b} \qquad \forall\, t \in \mathcal{T},\ b \in \mathcal{B} \,:\, \theta_{b} \text{ exists} \wedge t \ge 3 \]

first#

a position in a dimension, and the same position within a group

first:
  foreach: [snapshot, generator]
  where: "position(snapshot) == 0 OR position(snapshot, by=season_of) == 0"
  expression: on == 1
\[ \mathit{on}_{t,g} = 1 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{pos}(t) = 0 \vee \mathrm{pos}_{\mathrm{season\_of}(t)}(t) = 0 \]

last#

the same two counted from the end, which print against a size rather than as themselves

last:
  foreach: [snapshot, generator]
  where: "position(snapshot) == -1 OR position(snapshot, by=season_of) == -1"
  expression: on == 0
\[ \mathit{on}_{t,g} = 0 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{pos}(t) = \lvert \mathcal{T} \rvert - 1 \vee \mathrm{pos}_{\mathrm{season\_of}(t)}(t) = \lvert \mathcal{T}_{\mathrm{season\_of}(t)} \rvert - 1 \]

northern#

a lookup compared to a label, to another lookup, and to nothing

northern:
  foreach: [snapshot, bus]
  where: "zone_of == 'north' AND zone_of != area_of AND zone_of"
  expression: slack <= load
\[ \mathit{slack}_{t} \le \mathrm{load}_{t,b} \qquad \forall\, t \in \mathcal{T},\ b \in \mathcal{B} \,:\, \mathrm{zone\_of}(b) = \text{'}\mathrm{north}\text{'} \wedge \mathrm{zone\_of}(b) \neq \mathrm{area\_of}(b) \wedge \mathrm{zone\_of}(b) \text{ is defined} \]

efficiency#

a Greek-named parameter, which is given — so the convention wins and it prints as the word

efficiency:
  foreach: [snapshot, generator]
  expression: p <= eta * p_max
\[ p_{t,g} \le \mathrm{eta}_{g} \cdot \mathrm{p}^{\mathrm{max}}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

ceiling#

the infinity literal, which is the one way infinity prints

ceiling:
  foreach: [bus]
  expression: theta <= inf
\[ \theta_{b} \le \infty \qquad \forall\, b \in \mathcal{B} \]

always#

a mask that is only the constant true, which the language says is no mask at all — so none prints

always:
  foreach: [snapshot]
  where: "true"
  expression: spill >= 0
\[ \mathit{spill}_{t} \ge 0 \qquad \forall\, t \in \mathcal{T} \]

redundant#

the same constant inside a mask, where it is what the file says and prints

redundant:
  foreach: [snapshot]
  where: "True AND spill"
  expression: spill >= 0
\[ \mathit{spill}_{t} \ge 0 \qquad \forall\, t \in \mathcal{T} \,:\, \mathit{spill}_{t} \text{ exists} \]

never#

the other constant mask, which says the rows are none and is worth seeing

never:
  foreach: [snapshot]
  where: "false"
  expression: slack >= 0
\[ \mathit{slack}_{t} \ge 0 \qquad \forall\, t \in \mathcal{T} \,:\, \bot \]

Definitions#

spend#

a plain named expression: its symbol prints where it is used, its body once as a definition

spend:
  expression: sum(p * cost, over=generator)
\[ \mathit{spend}_{t} = \sum_{g \in \mathcal{G}} p_{t,g} \cdot \mathrm{cost}_{g} \qquad \forall\, t \in \mathcal{T} \]

lcoe#

nothing in the math reads it, so its divisor may carry a variable

lcoe: sum(p * cost) / sum(p)
\[ \mathit{lcoe} = \frac{\sum_{t \in \mathcal{T},\ g \in \mathcal{G}} p_{t,g} \cdot \mathrm{cost}_{g}}{\sum_{t \in \mathcal{T},\ g \in \mathcal{G}} p_{t,g}} \]

marginal_price#

the row dual of a constraint, the one builtin only an entry the math never reads may call

marginal_price: dual(balance)
\[ \mathit{marginal\_price}_{t,b} = \lambda_{\mathrm{balance},t,b} \qquad \forall\, t \in \mathcal{T},\ b \in \mathcal{B} \]

congestion_rent#

the dual of a row family this file is given rather than builds

congestion_rent: dual(import_limit)
\[ \mathit{congestion\_rent}_{t,z} = \lambda_{\mathrm{import\_limit},t,z} \qquad \forall\, t \in \mathcal{T},\ z \in \mathcal{Z} \]

net_import#

a given column read like any other

net_import: sum(imported, over=zone)
\[ \mathit{net\_import}_{t} = \sum_{z \in \mathcal{Z}} \mathit{imported}_{t,z} \qquad \forall\, t \in \mathcal{T} \]

startup_cost#

a quantity defined by region: no two cases overlap, and otherwise is the rest

startup_cost:
  foreach: [snapshot, generator]
  cases:
    opening: { when: "position(snapshot) == 0", expression: cost }
    winter: { when: "position(snapshot) > 0 and season_of == 'winter'", expression: cost * 2 }
  otherwise: 0
\[ \mathrm{startup\_cost}_{t,g} = \begin{cases} \mathrm{cost}_{g} & \text{if } \mathrm{pos}(t) = 0 \\ \mathrm{cost}_{g} \cdot 2 & \text{if } \mathrm{pos}(t) > 0 \wedge \mathrm{season\_of}(t) = \text{'}\mathrm{winter}\text{'} \\ 0 & \text{otherwise} \end{cases} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

Variable domains#

p#

both bounds, and a where with all three connectives

p:
  foreach: [snapshot, generator]
  where: "p_max > 0 AND NOT is_flexible OR p_min > 0"
  bounds: { lower: p_min, upper: p_max }
\[ \mathrm{p}^{\mathrm{min}}_{g} \le p_{t,g} \le \mathrm{p}^{\mathrm{max}}_{g} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \,:\, \mathrm{p}^{\mathrm{max}}_{g} > 0 \wedge \neg \mathrm{is\_flexible}_{g} \vee \mathrm{p}^{\mathrm{min}}_{g} > 0 \]

spill#

lower only

spill:
  foreach: [snapshot]
  bounds: { lower: 0 }
\[ \mathit{spill}_{t} \ge 0 \qquad \forall\, t \in \mathcal{T} \]

slack#

upper only

slack:
  foreach: [snapshot]
  bounds: { upper: 100 }
\[ \mathit{slack}_{t} \le 100 \qquad \forall\, t \in \mathcal{T} \]

theta#

unbounded

theta:
  foreach: [bus]
\[ \theta_{b} \in \mathbb{R} \qquad \forall\, b \in \mathcal{B} \]

on#

a binary domain, which is a set rather than a pair of bounds

on:
  foreach: [snapshot, generator]
  domain: binary
\[ \mathit{on}_{t,g} \in \{0, 1\} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

units#

an integer domain, which is both: bounds, and where the values live

units:
  foreach: [generator]
  domain: integer
  bounds: { lower: 0, upper: 10 }
\[ 0 \le \mathit{units}_{g} \le 10, \mathit{units}_{g} \in \mathbb{Z} \qquad \forall\, g \in \mathcal{G} \]

spare#

integer with neither bound: the domain is the whole line

spare:
  foreach: [generator]
  domain: integer
\[ \mathit{spare}_{g} \in \mathbb{Z} \qquad \forall\, g \in \mathcal{G} \]

reserve#

an empty foreach: a scalar declaration, whose line carries no quantifier

reserve:
  foreach: []
  bounds: { lower: 0 }
\[ \mathit{reserve} \ge 0 \]

headroom#

scalar too, but masked, so the condition stands with no set beside it

headroom:
  foreach: []
  where: "budget"
  bounds: { lower: 0 }
\[ \mathit{headroom} \ge 0 \qquad \text{where } \mathrm{budget} \text{ is defined} \]

weight#

the family a sos runs along

weight:
  foreach: [snapshot, generator]
  bounds: { lower: 0, upper: 1 }
\[ 0 \le \mathit{weight}_{t,g} \le 1 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

Curves, as what they expand to#

A curve is sugar: what prints is the formulation it expands to, which is the math the solver receives. One row per method:, each from the model named under it, so the symbols in this section are that model's.

economies_of_scale#

method: adjacency — a binary per segment, and a row making the two nonzero weights neighbours, in examples/ports/transport_pwl.yaml.

Rendered with the sidecar symbol table examples/symbols/transport_pwl.yaml, which is what the weights print as:

notation: latex

names:
  economies_of_scale_lam: "\\lambda"
  economies_of_scale_seg: "\\delta"
  bp_x: "\\mathrm{x}"
  bp_y: "\\mathrm{y}"
economies_of_scale:
  over: bp
  links:
    - [shipment, bp_x]
    - [scaled, bp_y]
\[ \sum_{b \in \mathcal{B}} \lambda_{p,m,b} = 1 \qquad \forall\, p \in \mathcal{P},\ m \in \mathcal{M} \]
\[ \mathit{shipment}_{p,m} = \sum_{b \in \mathcal{B}} \lambda_{p,m,b} \cdot \mathrm{x}_{b} \qquad \forall\, p \in \mathcal{P},\ m \in \mathcal{M} \]
\[ \mathit{scaled}_{p,m} = \sum_{b \in \mathcal{B}} \lambda_{p,m,b} \cdot \mathrm{y}_{b} \qquad \forall\, p \in \mathcal{P},\ m \in \mathcal{M} \]
\[ \sum_{b \in \mathcal{B}} \delta_{p,m,b} = 1 \qquad \forall\, p \in \mathcal{P},\ m \in \mathcal{M} \]
\[ \lambda_{p,m,b} \le \delta_{p,m,b} + \delta_{p,m,b \boxminus_{0} 1} \qquad \forall\, p \in \mathcal{P},\ m \in \mathcal{M},\ b \in \mathcal{B} \]
\[ 0 \le \lambda_{p,m,b} \le 1 \qquad \forall\, p \in \mathcal{P},\ m \in \mathcal{M},\ b \in \mathcal{B} \]
\[ \delta_{p,m,b} \in \{0, 1\} \qquad \forall\, p \in \mathcal{P},\ m \in \mathcal{M},\ b \in \mathcal{B} \]

cost_curve#

method: sos2 — the same weights, restricted by a set the solver branches on (the sos rules), in examples/sos.yaml.

Rendered with the sidecar symbol table examples/symbols/sos.yaml, which is what the weights print as:

notation: latex

names:
  cost_curve_lam: "\\lambda"
  bp_x: "\\mathrm{x}"
  bp_y: "\\mathrm{y}"
cost_curve:
  over: bp
  links:
    - [p, bp_x]
    - [op_cost, bp_y]
  method: sos2
\[ \sum_{b \in \mathcal{B}} \lambda_{t,g,b} = 1 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]
\[ p_{t,g} = \sum_{b \in \mathcal{B}} \lambda_{t,g,b} \cdot \mathrm{x}_{g,b} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]
\[ \mathit{op\_cost}_{t,g} = \sum_{b \in \mathcal{B}} \lambda_{t,g,b} \cdot \mathrm{y}_{g,b} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]
\[ 0 \le \lambda_{t,g,b} \le 1 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G},\ b \in \mathcal{B} \]
\[ \left( \lambda_{t,g,b} \right)_{b \in \mathcal{B}} \in \mathrm{SOS}2 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]

cost_curve#

method: convex — nothing — the weights range over the hull, which is a pure LP, in examples/piecewise.yaml.

Rendered with the sidecar symbol table examples/symbols/piecewise.yaml, which is what the weights print as:

notation: latex

names:
  cost_curve_lam: "\\lambda"
  bp_x: "\\mathrm{x}"
  bp_y: "\\mathrm{y}"
cost_curve:
  over: bp
  links:
    - [p, bp_x]
    - [op_cost, bp_y]
  method: convex
\[ \sum_{b \in \mathcal{B}} \lambda_{t,g,b} = 1 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]
\[ p_{t,g} = \sum_{b \in \mathcal{B}} \lambda_{t,g,b} \cdot \mathrm{x}_{g,b} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]
\[ \mathit{op\_cost}_{t,g} = \sum_{b \in \mathcal{B}} \lambda_{t,g,b} \cdot \mathrm{y}_{g,b} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G} \]
\[ 0 \le \lambda_{t,g,b} \le 1 \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G},\ b \in \mathcal{B} \]

cost_curve#

method: lp — no weights at all — one row per segment line, plus the two rows holding the domain, in examples/piecewise_lp.yaml.

Rendered with the sidecar symbol table examples/symbols/piecewise_lp.yaml, which is what the weights print as:

notation: latex

names:
  bp_x: "\\mathrm{x}"
  bp_y: "\\mathrm{y}"
cost_curve:
  over: bp
  links:
    - [p, bp_x]
    - [op_cost, bp_y, ">="]
  method: lp
\[ \mathit{op\_cost}_{t,g} \cdot \left( \mathrm{x}_{g,b} - \mathrm{x}_{g,b \boxminus_{0} 1} \right) \ge \left( \mathrm{y}_{g,b} - \mathrm{y}_{g,b \boxminus_{0} 1} \right) \cdot \left( p_{t,g} - \mathrm{x}_{g,b} \right) + \mathrm{y}_{g,b} \cdot \left( \mathrm{x}_{g,b} - \mathrm{x}_{g,b \boxminus_{0} 1} \right) \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G},\ b \in \mathcal{B} \,:\, \mathrm{pos}(b) \neq 0 \]
\[ p_{t,g} \ge \mathrm{x}_{g,b} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G},\ b \in \mathcal{B} \,:\, \mathrm{pos}(b) = 0 \]
\[ p_{t,g} \le \mathrm{x}_{g,b} \qquad \forall\, t \in \mathcal{T},\ g \in \mathcal{G},\ b \in \mathcal{B} \,:\, \mathrm{pos}(b) = \lvert \mathcal{B} \rvert - 1 \]

Sets carried to the solver#

adjacent#

at most two adjacent members nonzero, one set per snapshot

adjacent:
  variable: weight
  over: generator
  type: 2
\[ \left( \mathit{weight}_{t,g} \right)_{g \in \mathcal{G}} \in \mathrm{SOS}2 \qquad \forall\, t \in \mathcal{T} \]