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: fragments 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 lays patches over a base: what a framework ships and a
project extends, where a name the patch declares is the point. They compose as
override(merge({...}), {...}), which builds the model and then configures
the run.
What :func:merge does with each section:
- A dimension or a relation every fragment may declare, and the ones that do have to say the same thing about it. Prose is not a claim, so two descriptions of one dimension agree, and the first fragment's is carried.
- Every other declaration is owned. A name two fragments declare is refused, both named.
- The objectives are summed, each term in parentheses, in the fragments' name order, and the senses have to agree.
- A given declaration is folded into the declaration that introduces the
name, once the reader is checked to say the same as the introducer or less.
Two fragments that both only read a name have to read it the same way, and a
fragment that declares a name and reads it as well is refused. What no
fragment introduces stays under
given:for a consumer to bind.
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.
What a patch may say, and what is refused:
- 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, and a miss is refused with the near miss named.
- 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 as
override(override(base, …), …). - A patch adjusts the math, not the coordinate space. A
dimensionsorrelationsentry may be added or restated word for word, never changed and never removed. - A declaration set to
nullis removed, and a removal of what the base does not declare is refused. The marker is positional:constraints: {ramp: null}removes the constraint, wherevariables: {p: {where: null}}sets that variable's mask to none, which is a value the schema takes. A whole section set tonullis refused, because it removes nothing. given:is laid over one kind at a time, by the same rules as any owned section.
GIVEN_KINDS = {'variables': 'given variable', 'constraints': 'given constraint'}
module-attribute
#
IRREGULAR = {'piecewise': 'piecewise curve', 'sos': 'special-ordered set', 'objective': 'objective'}
module-attribute
#
OWNED_SECTIONS = ('parameters', 'variables', 'constraints', 'expressions', 'macros', 'piecewise', 'sos')
module-attribute
#
SECTIONS = (*SHARED_SECTIONS, *OWNED_SECTIONS, 'given')
module-attribute
#
SHARED_SECTIONS = ('dimensions', 'relations')
module-attribute
#
merge(fragments, description=None)
#
fragments composed as peers, each owning the math it declares.
| PARAMETER | DESCRIPTION |
|---|---|
fragments
|
What each fragment is called, to the fragment: a YAML path,
YAML text, a mapping, or a loaded :class: |
description
|
What the composed model is. A fragment's own
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict[str, object]
|
One mapping, ready for :func: |
dict[str, object]
|
in it has been resolved, name-checked or lowered, and it shares no |
dict[str, object]
|
object with any fragment. A given declaration a sibling introduces is |
dict[str, object]
|
folded away; one nothing introduces stays under |
| RAISES | DESCRIPTION |
|---|---|
LanguageError
|
Two fragments declare one name; two fragments say different things about one dimension, relation or given declaration; a fragment reads a name as something other than what its sibling introduces; a fragment declares a name and reads it as well; 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: a YAML path, YAML text, a mapping, or a
loaded :class: |
patches
|
What each patch is called, to the patch. The name is what an error calls it. The patches must write disjoint fields, so the order they are given in cannot change the result. |
| RETURNS | DESCRIPTION |
|---|---|
dict[str, object]
|
One mapping, ready for :func: |
dict[str, object]
|
in it has been resolved, name-checked or lowered, and it shares no |
dict[str, object]
|
object with base or any patch. |
| 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
or removes a dimension or a relation; a patch sets a whole section
to |
FileNotFoundError
|
A |