Errors and limits#
What to_spec checks#
to_spec binds no data. Before it returns a Spec, it parses the file,
resolves every name, checks every dimension rule and every degree, and reads
every where string and every macro template, including the templates that
nothing calls. A piecewise: block is checked as written, against every rule
its expansion would be held to, and stays a block.
Anything the language refuses is refused there, so a repository of models validates in CI with no data and no solver. An array that does not bind, or a solver exception, comes from the tool that builds and solves the model.
Every message names what went wrong and what to do about it:
Constraint 'balance', equation 0: 'p_charge' not found.
Variables: ['dispatch', 'soc']
Parameters: ['capacity', 'load', 'efficiency']
Check for typos, or ensure 'p_charge' is declared.
What advice warns about#
ms.advice(model) returns a tuple of ms.Advice, one per warning, and
python -m math_spec check model.yaml prints them. Advice is a warning: the file
loads.
kind |
The file has… | The advice says… |
|---|---|---|
never-an-axis |
a dimension nothing is indexed by, nothing aggregates into and no relation targets | remove it, or keep it knowingly if its declarations are still to come |
unbounded |
a variable that no constraint, set or curve uses, whose objective term pushes it towards a bound it does not have | give it a finite bound, or the constraint that was meant to define it |
Variable 'slack' makes this model unbounded: no constraint names it, and
bounds.lower is -inf, which is the direction a +slack term improves a minimize
objective in. No data can change that, so the solve would answer `unbounded`
and name nothing.
Give it a finite bounds.lower, or the constraint that was meant to define it.
advice is silent where the answer depends on the data: an objective
coefficient that is a parameter, or a where: that leaves one slice of a
variable with no constraint row.
Which error you get#
MathSpecError |
The root. Everything below is an instance of it |
LanguageError |
Something in the model: a construct outside the language, a dimension set that does not compose, or a name that nothing declares |
SchemaError |
Something in the file: an unknown key, a malformed declaration, or a bad symbol table |
DimensionError |
Dimensions that disagree, such as a constraint whose expression does not equal its dims |
Every one of these is reproducible from the YAML alone. An engine that binds
numbers or calls a solver adds its own errors below MathSpecError.
What the language will not express#
Each of these was asked for and refused, and the limits gives the reasons.
| Not in the language | Instead |
|---|---|
variable * variable in a bound or a piecewise: link |
The objective and the constraints take it. Everywhere else, use a parameter coefficient (expressions) |
sum(x, over=d) * sum(y, over=d) |
Multiply before you reduce, or constrain a variable to equal the reduction |
degree 3 (x * y * z) |
A variable constrained to equal one product, multiplied by the third |
** with a variable in it |
x * x for a square. Over variable-free operands ** is in the language |
arithmetic in bounds: |
A name or a number. Ship the derived column as data |
| time-series processing (resample, cluster, interpolate, align), file IO, units | Data preparation. Pass a parameter |
| indicator constraints | sos: is where that landed (piecewise) |
| multi-objective | There is one objective: block. Weight the goals into one expression |
arbitrary array operations (merge, reindex, apply_ufunc) |
Data preparation |
filling a missing value (.fillna) |
Data preparation, or a where if the coordinate should not exist. Inside the language, only shift(..., edge=) fills (absence) |
The language has no escape hatch. Math it cannot express is a gap in the language, and a gap closes as a macro, a primitive or a formulation (the limits).