Skip to content

The language

A model is one YAML file. It declares the axes the model runs over, the data it expects, the decisions the solver makes, and the rules those decisions obey — and nothing else: no Python state changes what a file means, and the same file means the same model whichever solver takes it.

dispatch.yaml
description: Least-cost dispatch of a generator fleet against an hourly load.

dimensions:
  snapshot: {dtype: int}
  generator: {values: [wind, solar, gas]}

parameters:
  load: {dims: [snapshot]}
  cost: {dims: [generator]}
  p_max: {dims: [generator]}

variables:
  p:
    foreach: [snapshot, generator]
    where: "p_max > 0"
    bounds: {lower: 0, upper: p_max}

constraints:
  power_balance:
    foreach: [snapshot]
    expression: sum(p, over=generator) == load

objective:
  sense: minimize
  expression: p * cost  # an objective sums every dim it carries

That file is a complete model. Write a model walks through the ideas behind it; the pages here are the exact rules.

Ten rules the language reduces to

Nothing is guessed. Where a file does not determine the answer, loading fails and the message names the rewrite. Every rule below is that one principle in a different position, and each links to the page that spells it out.

# Rule
1 Ten declaration keys plus version and description, and the schema is closed at every level — an unknown key is an error naming the near miss. Booleans are YAML 1.2, so no / on / off stay labels. File shape
2 Everything decidable without data is decided without data. Errors
3 One flat namespace, no shadowing — a collision is a load error naming both declarations. Names
4 Position decides which kinds of name are legal, and a name's kind is fixed at load time. A dimension is never legal in a value position: it is a coordinate space, not data. Names
5 Dim sets compose by union. A constraint must equal its foreach; a where or a bound must not exceed its frame. Dim algebra
6 Four constructs create absence, and nothing else does. It is a state of a variable; a constraint's own where: deletes its row directly. Absence
7 Through arithmetic absence spreads, taking the row with it. Out of a reduction it does not — so sum(x + y) and sum(x) + sum(y) are different questions. Absence
8 A missing value reads as the identity of its position — zero as a coefficient, false in a where. Where the position has no identity it is refused, rather than guessed: a divisor, a bound. Absence, Operators
9 Degree 1, always: * needs a variable-free factor, / a variable-free divisor, ** is refused. Bounds are narrower still — a name or a number, never arithmetic. Expressions
10 The operator set is closed. Compositions go in macros:. Operators

The pages

File shape the ten keys, version, description, and how the YAML is read
Dimensions and lookups the axes, and the maps their members carry
Parameters, variables, constraints the four blocks that make up the math
Expressions the two grammars — arithmetic and where — what a name may mean where, and how dims compose
Operators sum, at, shift — the closed set
Absence and where what a mask means: which rows are built, and which are not
Piecewise curves and SOS piecewise: and sos:
Data binding what sources accepts, and how coordinates are resolved
Errors and limits what fails when, and what the language will not say

Running a model — building, solving, reading an answer back — is the Python API. Nothing there changes what a file means.