math_spec.composition
Several files into one model, before any of them is validated.
Two verbs, and they answer different questions. :func:merge composes
peers: templates that each own part of the math, where a name two of them
declare is a collision and the order they are given in means nothing.
:func:override layers a base and its patches: what a framework ships and a
project extends, where a name the patch declares is the point. Neither is a
mode of the other, and they compose —
override(merge({...}), {...}) builds the model and then configures the run.
A patch says only what it changes, because declarations are laid over a field at a time::
constraints:
ramp: {dims: [snapshot, generator, investment_period]}
A patch is not a :class:~math_spec.model.Spec. It is read before
validation, so it may carry null where a declaration would go and may name
what only its base declares. Nothing here resolves a name or checks a dim: the
laid mapping goes through :func:~math_spec.validation.to_spec like any other
file, and every rule the language has applies to it there and nowhere else.
The verb is designed to collide, so what keeps it predictable is that a collision is refused everywhere the caller did not ask for one:
- A partial entry edits, and a whole one creates. An entry that does not validate as a declaration on its own has to land on one the base declares, named with the near miss. A mistyped name then refuses instead of quietly inventing a declaration nothing refers to.
- Sibling patches are disjoint. Two patches writing one field is refused,
both named, so the order they are given in never decides a model. Layering
is written out —
override(override(base, …), …)— where it is on the page rather than in an argument's position. - A patch adjusts the math, not the axes. A
dimensionsorrelationsentry may be added or restated exactly; changing one under the expressions already written over it is refused.
A declaration the patch sets to null is removed, which is the one
thing an ordered list of files cannot say for itself: a declaration a patch
does not mention is left alone, so without a marker a deletion has no spelling.
The marker is positional and means nothing deeper down — constraints: {ramp:
null} removes the constraint, where variables: {p: {where: null}} sets
that variable's mask to none, which is a value the schema already takes.
GIVEN_SECTIONS = ('given_variables',)
module-attribute
#
IRREGULAR = {'piecewise': 'piecewise curve', 'sos': 'special-ordered set', 'objective': 'objective', 'given_variables': 'given variable'}
module-attribute
#
OWNED_SECTIONS = ('parameters', 'variables', 'constraints', 'expressions', 'macros', 'piecewise', 'sos')
module-attribute
#
SECTIONS = (*SHARED_SECTIONS, *OWNED_SECTIONS, *GIVEN_SECTIONS)
module-attribute
#
SHARED_SECTIONS = ('dimensions', 'relations')
module-attribute
#
merge(fragments, description=None)
#
fragments composed as peers, each owning the math it declares.
A component library is a set of templates that agree on a coupling surface — one flow per port, one balance per bus — and wiring a system is rows in a table rather than generated YAML. This is what takes the templates and hands back one model.
| PARAMETER | DESCRIPTION |
|---|---|
fragments
|
What each fragment is called, to the fragment. The name is what an error calls it, so it is the template's name rather than a path. The order they are given in does not reach the result. |
description
|
What the composed model is. A fragment's own
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict[str, Any]
|
One mapping, ready for :func: |
dict[str, Any]
|
in it has been resolved, name-checked or lowered. |
| RAISES | DESCRIPTION |
|---|---|
LanguageError
|
Two fragments declare one name; two fragments say different things about one dimension or relation; two fragments pin different language versions; or their objectives run opposite ways. |
FileNotFoundError
|
A |
Source code in src/math_spec/composition.py
override(base, patches)
#
base with each patch laid over it, and nothing laid over another patch.
| PARAMETER | DESCRIPTION |
|---|---|
base
|
The model being extended — whatever every other verb takes. |
patches
|
What each patch is called, to the patch. The name is what an error calls it, so it is the patch's own name rather than a path. The patches must write disjoint fields, which is why the order they are given in cannot change the result. |
| RETURNS | DESCRIPTION |
|---|---|
dict[str, Any]
|
One mapping, ready for :func: |
dict[str, Any]
|
in it has been resolved, name-checked or lowered. |
| RAISES | DESCRIPTION |
|---|---|
LanguageError
|
A patch edits or removes a declaration its base does not declare; a patch creates one that is not whole; a patch redeclares a dimension or a relation as something else; or two patches write one field. |
FileNotFoundError
|
A |