Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,14 @@ stale anchor fails it. `pixi run docs-serve` builds the site and serves it at
The same construct passes through three layers, and each names it in full. The
suffix says which layer:

| Layer | Suffix | Example |
| ------------------------------- | -------------------- | ------------------------------------------ |
| YAML block (`math_spec.model`) | `Block` | `VariableBlock`, `PiecewiseBlock` |
| Core AST (`math_spec.*_parser`) | `Node` | `VariableNode`, `UnresolvedComparisonNode` |
| Program (`math_spec.program`) | none / `Declaration` | `Variable`, `VariableDeclaration` |
| Layer | Suffix | Example |
| ------------------------------ | -------------------- | -------------------------------------- |
| YAML block (`math_spec.model`) | `Block` | `VariableBlock`, `PiecewiseBlock` |
| Syntax (`math_spec.*_parser`) | `Node` | `NameNode`, `UnresolvedComparisonNode` |
| Program (`math_spec.program`) | none / `Declaration` | `Variable`, `VariableDeclaration` |

A node names the operation, not the verb a file writes. One verb can lower to
two nodes, so the file's spelling cannot decide the name.
A node names the operation, not the verb a file writes. One verb can resolve
to two nodes, so the file's spelling cannot decide the name.

| File verb | Node | What the node names |
| ------------------ | ----------- | ------------------------------ |
Expand All @@ -121,11 +121,10 @@ Nothing is abbreviated.

Start with the grammar, which is usually free because `f(x, k=v)` already
parses. Then declare the signature in `operators.BUILTINS`. It holds the number
of arguments and says which arguments name dimensions, and resolution,
validation and lowering all read it from there. Then write the dimension rule in
`dimensions.py`, the degree verdict in `degree.py`, the node it lowers to in
`program.py`, and the entry in the
[language reference](reference/language/operators.md).
of arguments and says which arguments name dimensions, and resolution reads it
from there. Then write the node in `program.py` and how resolution builds it,
the dimension rule in `dimensions.py`, the degree verdict in `degree.py`, and
the entry in the [language reference](reference/language/operators.md).

## Submitting changes

Expand Down
13 changes: 6 additions & 7 deletions docs/reference/language/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,12 @@ 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` |
| `PiecewiseExpansionError` | A `piecewise:` block that cannot be expanded |
| | |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `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`.
Expand Down
10 changes: 7 additions & 3 deletions docs/reference/reading.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,17 @@ refusal quotes. The engine, which has the numbers, runs each one and raises
`assumption_message` where it fails:

```python
from math_spec.program import Holds, assumption_message
from math_spec.program import Assumption, assumption_message

sorted(program.assumptions) # ['cost_is_never_negative', 'curve_complete', 'curve_curvature', 'curve_increasing']
isinstance(program.assumptions['curve_increasing'], Holds) # True
isinstance(program.assumptions['curve_increasing'], Assumption) # True
message = assumption_message('curve_increasing', program.assumptions['curve_increasing'])
message # "assumption 'curve_increasing' does not hold for the data bound to 'bp_x' — piecewise 'curve': method: convex requires strictly increasing breakpoints in 'bp_x' along 'bp'"
written = assumption_message('cost_is_never_negative', program.assumptions['cost_is_never_negative'])
written # "assumption 'cost_is_never_negative' does not hold for the data bound to 'bp_y' — a negative cost is a gain the objective would chase"
```

One kind stands in that mapping. A `Holds` carries a predicate as two masks —
One kind stands in that mapping. An `Assumption` carries a predicate as two masks —
`predicate`, and the `where` it is checked under — and the sentence a refusal
trails under `description`. What a `piecewise:` block's method implies about
its breakpoints is written in the same language and stands beside what the
Expand All @@ -138,6 +138,10 @@ node's operands, and `where_children()` walks a predicate's. `walk()` yields
every node under an expression, parents first. `walk_regions()` yields each node
with the `cases:` regions it stands inside, outermost first.

`Named` is the one node no program carries. A `Spec.resolved` tree holds it
where an `expressions:` entry is used, and lowering inlines the entry's body
there before the program is built, so `Expression` does not name it.

Every `where` arrives as a `Mask`. Its `.root` is the resolved predicate. The
mask also answers four questions:

Expand Down
2 changes: 1 addition & 1 deletion schema/math-spec.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@
},
"MacroBlock": {
"additionalProperties": false,
"description": "A parameterised expression template, defined in the YAML itself.\n\nLanguage, not code: formals (``args`` positional, ``kwargs`` keyword)\nshadow model names inside the template, and every call site expands into\ncore AST before either backend sees the expression.",
"description": "A parameterised expression template, defined in the YAML itself.\n\nLanguage, not code: formals (``args`` positional, ``kwargs`` keyword)\nshadow model names inside the template, and every call site expands in\nthe syntax tree before resolution reads the expression.",
"properties": {
"args": {
"default": [],
Expand Down
2 changes: 0 additions & 2 deletions src/math_spec/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
DimensionError,
LanguageError,
MathSpecError,
PiecewiseExpansionError,
SchemaError,
did_you_mean,
schema_error,
Expand Down Expand Up @@ -65,7 +64,6 @@
'DimensionError',
'LanguageError',
'MathSpecError',
'PiecewiseExpansionError',
'SchemaError',
'SosBlock',
'Spec',
Expand Down
Loading
Loading