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. Every tool that reads the file reads the same model.

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

dimensions:
  snapshot: { dtype: int }
  generator: { dtype: str }

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

variables:
  dispatch:
    dims: [snapshot, generator]
    where: "capacity > 0"
    bounds: { lower: 0, upper: capacity }

constraints:
  power_balance:
    dims: [snapshot]
    expression: sum(dispatch, over=generator) == load

objective:
  sense: minimize
  expression: sum(dispatch * cost) # an objective is one number, so the sum is written

That file is a complete model. The pages below give the exact rules.

The ten rules#

to_spec checks everything it can without data, and refuses the file with a message that names the fix. These ten rules are what it checks.

# Rule
1 A file has ten declaration keys, plus version and description. A key the schema does not know is refused, with the nearest valid key named: boundz → bounds. File shape
2 Everything that can be checked without data is checked when the file loads. Errors
3 Every name is declared once. A parameter and a dimension both called snapshot is refused, and the message names both lines. Names
4 Where a name may stand depends on what it is. A dimension may follow over= or along=, and may not be multiplied: dispatch * snapshot is refused, because snapshot is an axis and not a column of numbers. Names
5 a + b carries the dimensions of a and of b together. A constraint's expression must carry exactly its dims. The objective must carry none. A where or a bound may carry fewer dimensions than its declaration, never more. How dimensions combine
6 A variable's where: deletes the variable at the masked coordinates. There is no column there, not a column fixed at zero. A constraint's where: deletes the row. Absence
7 A deleted variable takes its row with it: x + y >= 1 has no row where y is deleted. Inside a sum it is one term fewer, and the row stays. So sum(x + y) and sum(x) + sum(y) are different constraints. Absence
8 A parameter row that is missing from the table reads as 0 in arithmetic and as false in a where. Where 0 would change the model, as in a divisor or a bound, the missing row is refused instead. Absence
9 The objective and the constraints may multiply two variables: dispatch * dispatch * wear. A bound and a piecewise: link may not. x / y needs y free of variables, and a ** b needs both a and b free of them. Expressions
10 The operators are sum, sum_back, at, shift, and dual in a reported expression. There are no others, and a file cannot add one. Write a composition of them as a macro. Operators

The pages#

File shape the ten keys, version, description, and how the YAML is read
Dimensions and relations the axes, and the maps from one axis onto another
Parameters, variables, constraints and the objective the four blocks that carry the math
Expressions the arithmetic grammar and the where grammar, where each kind of name may stand, and how dimensions combine
Reported expressions named quantities that no constraint or objective uses, which you read back after a solve
Operators sum, sum_back, at and shift
Absence and where which rows are built, and which are not
Piecewise curves and SOS piecewise: and sos:
Reading a loaded model what a tool gets when it loads a model
Errors and limits what fails when, and what the language will not express

Building the model, solving it and reading the answer back are the work of the tool that reads the file, such as an engine or a renderer. Nothing that tool does changes what the file means.