Expressions¶
An Expr is a list of term frames, each carrying (*dims, col_id,
coef). Three primitives shape Exprs: Sum, Where, Lag.
Sum¶
Sum(expr, over=("j",)) group-by-sums each term over the listed
dims. The dims listed disappear; the rest become the term's open
dims, ready to be matched against a constraint's over= frame.
flow_per_i = Sum(v, over=("j",)) # open dim: i
total_flow = Sum(v, over=None) # open dims: () — collapses to a scalar
Sum(..., over=None) collapses every open dim — useful for
objective terms or single-row constraints.
Sum(..., where=frame) pre-filters term frames before the
group-by-sum (inner-join on shared columns).
Where¶
Where(expr, frame) inner-joins an Expr with a frame, doing two
things at once:
- Filter — rows of the term whose join keys don't appear in
frameare dropped (e.g.Where(v_flow, wind_only)keeps only the wind rows). - Map — columns of
framethat the term doesn't already carry become new open dims. This is how you remap a(p, source, sink)flow variable into a constraint indexed by(n, t):
# flow_to_n maps each (p, source, sink) to the destination node n
v_flow_at_n = Where(v_flow, flow_to_n)
# v_flow_at_n now has open dims (p, source, sink, n) — once Sum'd over
# (p, source, sink) it leaves just (n,) for a per-node balance.
Lag¶
Lag(var, lag_frame, time_dim, lag_col) references var at a
shifted time index, joined via lag_frame:
# Storage state-change: v_state[t] - v_state[t_prev]
state_diff = v_state - Lag(v_state, dt_frame,
time_dim="t", lag_col="t_prev")
The lag frame is responsible for defining the temporal mapping —
typically (d, t, t_prev) for within-period lag, or (d, t,
d_prev, t_prev) for cross-period lag. This pushes the modelling
choice (cyclic? acyclic? cross-period?) out of the engine and into
data the user supplies.
Arithmetic¶
expr1 + expr2 # term-list concatenation
expr * 2.5 # scale every term's coef
expr + 5.0 # adds a constant — engine handles via obj_offset / RHS shift
param * var # see vars-and-params.md
Var + Var, Var - Var, Expr + Expr all return Exprs. Adding a
scalar to an Expr defers the constant to the right-hand side at
constraint-binding time.
Open vs closed dims¶
- A term's open dims are the columns of the term frame that are
not
col_id/coef. - When you call
add_cstr(..., over=row_index, lhs_terms=…, rhs_terms=…), the engine matches each term's open dims againstrow_index's columns. Terms must have a subset ofrow_index's columns as open dims. Sumreduces open dims,Whereadds open dims via the join frame.
This is the load-bearing invariant of the modelling layer: get your open dims right and constraints just compose. Get them wrong and you'll see "missing dim" errors or row-count mismatches.
See also¶
- Vars and Params — what
Param * Varreturns. - Problem and Solve — how
over=matches open dims to row dims.