Program API#
mathspec.program contains what a file means. It has the typed entries, the
expression and condition nodes, and the reports that a program has. It also has
each warning that advice returns (an
Advice). This page lists every name that the module exports,
in alphabetical order.
The program: what a file declares, with names resolved and shapes fixed.
The second public state, and the one a consumer reads. A Program is
the file typed, section for section: every entry it makes, with names
resolved, shapes fixed and every rule decidable without data checked, and no
data at all. Lowering, as a Spec loads, is the only
thing that builds one, so nothing here re-checks a hand-built one.
Node and entry classes are matched with isinstance. The rules a
node's structure does not show is children; the questions over the walk
are walk_regions, walk and the filters beside them. A
resolved where arrives as a Mask, and what
advice says about a program as Advice. Frozen dataclasses only — no
execution logic, and nothing imported from a consumer. How a consumer reads
one: docs/reference/reading.md.
AdviceKind = Literal['never-an-axis', 'given', 'unbounded']
module-attribute
#
The checks that an Advice comes from. The set is closed, so a tool that filters on it can list every value.
ComparisonOperator = Literal['<=', '>=', '==', '!=', '<', '>']
module-attribute
#
Condition = BooleanLiteral | TypedCondition | Connective | ConditionReference
module-attribute
#
The union of every condition node in a program. The parser's Unresolved* nodes are not part of it, because mathspec
replaces them at load.
Connective = Not | And | Or
module-attribute
#
The boolean connectives. With ConditionReference, they are the only condition nodes that contain other condition
nodes, so a walk over a condition recurses only into these.
ConstraintSense = Sense
module-attribute
#
DeclaredDtype = ParameterDtype | DimensionDtype
module-attribute
#
The dtype of a name that a comparison in a condition tests. A parameter and a dimension declare it, and a relation column has the dtype of its dimension.
DimensionDtype = Literal['float', 'int', 'str', 'datetime']
module-attribute
#
The dtypes a dimension may declare, which set what its labels are. Only a dimension may declare datetime.
Expression = Constant | ParameterReference | VariableReference | Dual | Negate | Add | Multiply | Power | Divide | Sum | Join | Translate | WindowSum | Cases | ExpressionReference
module-attribute
#
The union of every expression node, which a walk over an expression takes. The set is closed, so a walk can end in
assert_never. Every node has dims, the dimensions it is indexed over. Lowering sorts them by name, and a node
built by hand has the dims it was given. An
ExpressionReference has the dims of its body. Program.roots, a bound and a named expression that is
in_math are affine, except that at a QuadraticPosition a Multiply may have a variable on both sides.
ObjectiveSense = Literal['minimize', 'maximize']
module-attribute
#
The directions an objective may declare.
ParameterDtype = Literal['float', 'int', 'bool', 'str']
module-attribute
#
The dtypes a parameter may declare, which set the dtype of its data column. Only a parameter may declare bool.
ParameterMissing = RelationMissing | bool | float
module-attribute
#
The values of missing: on a parameter: a value of RelationMissing, or the value that a missing row takes.
PiecewiseMethod = Literal['adjacency', 'sos2', 'incremental', 'convex', 'lp']
module-attribute
#
The methods a piecewise: entry may declare to restrict its interpolation weights.
PIECEWISE_METHODS lists what each method adds.
QuadraticPosition = Literal['objective', 'constraint']
module-attribute
#
Where the math may contain a product of two variables: the objective or a constraint. A bound and a piecewise: link
are affine.
RelationMissing = Literal['refused', 'absent']
module-attribute
#
The values of missing: on a relation. Under refused, a tool refuses data that leaves out a combination of the
key. Under absent, that combination has no row.
SosType = Literal[1, 2]
module-attribute
#
The types of special ordered set: 1 or 2.
TypedCondition = ParameterComparison | ExpressionComparison | ParameterDefined | VariableDefined | DimensionComparison | DimensionPosition | RelationComparison | RelationPairComparison | RelationDefined | CountComparison | TranslatedCondition | JoinedCondition
module-attribute
#
The condition nodes that refer to an entry whose kind mathspec knows at load.
VariableDomain = Literal['continuous', 'integer', 'binary']
module-attribute
#
The domains a variable may declare.
VariableMissing = Literal['absent'] | float
module-attribute
#
The values of missing: on a variable. Under absent, the default, a term that contains the variable has no row
where its where is false. A number is the value of the variable there, so the term takes that number and keeps its
row.
Add(left, right, *, dims)
dataclass
#
Advice(kind, subject, text)
dataclass
#
One thing the language advises about a file it accepts.
Never an error: each is what a half-written spec looks like too. A
consumer prints it, or filters on kind and subject; the text is the
language's, so no consumer writes its own.
| ATTRIBUTE | DESCRIPTION |
|---|---|
kind |
The pass that said it.
TYPE:
|
subject |
The entry it is about — a dimension name, a variable name.
TYPE:
|
text |
The sentence, naming the rewrite.
TYPE:
|
Assumption(holds, where=None, description=None)
dataclass
#
A condition the file states of its data, under the name it wrote in assumptions:.
holds is true at every coordinate of its frame — the product of
every dim the two masks name — that where admits, a missing row
reading as false as it does in any mask. Nothing here is decidable at
load: both sides are the data's, which is why the consumer attaching it
checks.
description = None
class-attribute
instance-attribute
#
The description: that the file sets, or the sentence that a piecewise: method implies. A tool quotes it in a
refusal, after the names of the columns, to say why the rule exists.
holds
instance-attribute
#
where = None
class-attribute
instance-attribute
#
Axis(dimension, column=None)
dataclass
#
A position in a frame: the dimension whose labels it runs over, and the relation column it stands for.
A declared dimension is the axis Axis(dimension). A Join
opens Axis(dimension, column) for each column it drops, so a column
dropped and a column added over one dimension are two axes of one frame.
Two axes are equal where both fields are, so a frame holds a dimension's
own axis at most once however many join axes run over the same labels.
Cases(regions, *, dims)
dataclass
#
A value defined by region — exactly one region applies at each coordinate.
The regions are disjoint and total, so a consumer adds them rather than ranking them. Not a shape operator: every region spans the dims the expression does.
Column(relation, name)
dataclass
#
ConditionReference(name, body)
dataclass
#
A use of a conditions: entry, standing where its name was written, with the entry's condition under it.
Its truth is its body's: a consumer steps through it, as
condition_children does. It is kept as a node rather than written in so
the typesetter can print the symbol where the name stood and define it
once, as ExpressionReference does for an expression.
Constant(value, *, dims)
dataclass
#
Constraint(dims, lhs, sense, rhs, where=None, description=None)
dataclass
#
lhs sense rhs for each coord combination of dims.
Either side may carry variables and constants alike; which side a
consumer gathers them onto is its own arrangement and not stated here.
where masks out coord combinations (row absence, like variables).
CountComparison(condition, over, op, value, dims)
dataclass
#
How many coordinates condition admits along over, against a literal — count(points, over=bp) >= 2.
The count is one number per coordinate of dims, which is every dim
condition reads minus over, so a claim about each curve is written
without saying "each curve". A condition a leaf reads arrives as a
Mask, where a connective's operand is a bare Condition:
a walk recurses through the second and stops at the first.
Dimension(dtype='str', one_of=None, ordered=False, description=None)
dataclass
#
A dimension, as the file declares it.
description = None
class-attribute
instance-attribute
#
dtype = 'str'
class-attribute
instance-attribute
#
The dtype of the labels, as the file declares it. A tool checks the column of labels against it, as it checks a
value column against Parameter.dtype. It is str when the file lists the labels under one_of.
one_of = None
class-attribute
instance-attribute
#
The labels that the file lists under one_of:, sorted by name, or None when it lists none. With data attached,
a tool refuses any other label, in the table of the dimension and in every relation column over it.
ordered = False
class-attribute
instance-attribute
#
Whether the order of the labels is part of the spec. When it is, a tool keeps the labels in the order the data gives them.
DimensionComparison(name, op, value)
dataclass
#
DimensionPosition(name, op, position, partition=None)
dataclass
#
Compare where a row sits along a dimension against a position — position(snapshot) == 0.
Both sides are integers, negative counting from the end. With a
partition the position is counted within each group the relation makes
(Partition), whose joined columns' dimensions the frame carries.
Divide(numerator, divisor, *, dims)
dataclass
#
Dual(constraint, *, dims)
dataclass
#
A constraint's dual — the rate the optimal objective rises with the constraint's right side, read after the solve.
Of lhs <= rhs it is the rate in d of lhs <= rhs + d, for every
comparator and under either sense. Stands only under an
NamedExpression the math never reads: the loader refuses
dual() anywhere a solver ingests. One value per coordinate of the named
constraint's own dims frame: the leaf reshapes nothing, like a
parameter.
ExpressionComparison(left, op, right, dims)
dataclass
#
Compare two variable-free expressions, coordinate by coordinate — p_min <= 0.5 * p_max.
dims is every dim either side carries. A side whose value is absent at
a coordinate — a parameter row missing, a translation that vacated it —
makes the comparison false there, as a null does in every other
comparison; under a summing operator the absent term is one fewer.
ExpressionReference(name, body, *, dims)
dataclass
#
A use of an expressions: entry, standing where its name was written, with the entry's body under it.
Its value is its body's: a consumer building rows steps through it, as
children does. It is kept as a node rather than written in so the
typesetter can print the symbol where the name stood and define it once.
Every use of one entry holds the one node resolution built for it, whose
body is the NamedExpression.expression of that entry.
Footprint(quadratic, domains, sos_types, kinds)
dataclass
#
Which of the language's constructs one program uses.
A subset, never the whole: an empty field says this program does not use the construct.
| ATTRIBUTE | DESCRIPTION |
|---|---|
quadratic |
Each position a product of two variable-carrying operands stands in; empty is affine throughout.
TYPE:
|
domains |
Every domain declared.
TYPE:
|
sos_types |
The order of each special-ordered set declared. |
kinds |
Every expression node kind that appears.
TYPE:
|
Gate(variable, read, exists=None)
dataclass
#
The variable that switches a piecewise: entry's curves on, as each curve reads it.
variable is the variable the file named: a binary, or an integer under
method: incremental, which turns the curve off at 0 and, from 1 up,
moves it by its point at the first breakpoint once per unit above 1.
read is the gate at one curve: the variable on the entry's own frame,
or the variable read through a relation, as at(..., by=) reads it.
exists is where a curve reads the gate, and the curve is ungated
elsewhere, its weights summing to 1; it is None where every curve
reads it. A masked variable exists only on its mask, and a relation
declared missing: absent may have no row for a curve, so both leave
curves ungated; missing: 0 reads the variable as 0 off its mask
instead, which pins those curves off. Under the relation's
missing: refused every curve has a row.
Given(dims, description=None)
dataclass
#
A column or a row family this program reads and does not build.
The frame is the whole entry. A consumer looks the name up in the model this one is layered onto, checks the frame against what it finds, and refuses a name the host does not provide.
GivenParameter(dims, dtype='float', description=None)
dataclass
#
Data this program reads and another file declares: the frame the dim rules read, and the dtype a where compares against.
What a missing row means is the declaring file's missing:, so it is not here.
GivenTargets(parameters=Frozen({}), variables=Frozen({}), constraints=Frozen({}), expressions=Frozen({}), conditions=Frozen({}))
dataclass
#
What a program reads and does not build, by kind.
Every group is empty in a program built from one whole spec. Every group
is frozen at construction, like every group of Program.
conditions = Frozen({})
class-attribute
instance-attribute
#
The named conditions the host model defines, by name. In a condition, one is data: a ParameterDefined of a
boolean over its dimensions.
constraints = Frozen({})
class-attribute
instance-attribute
#
The constraints the host model builds, by name. A tool reads them back after the solver runs.
expressions = Frozen({})
class-attribute
instance-attribute
#
The named expressions the host model defines, by name.
parameters = Frozen({})
class-attribute
instance-attribute
#
The parameters another file declares, by name, with the dtype that a comparison in a condition tests.
variables = Frozen({})
class-attribute
instance-attribute
#
The variables the host model provides, by name.
Join(operand, columns, *, dims)
dataclass
#
Join operand to a relation on the columns columns joins on, one row per matching row of the relation.
The operand carries every dim joined on. The result has the operand's
dims, less the dims joined on, plus the dims grouped by, plus one axis
per column joined on and not grouped by (JoinColumns.axes), which
stands for the relation's column and runs over its dimension. So a column
dropped and a column added over one dimension stay two axes. A Sum over
those axes is the group-by that follows the join, which is how
sum(x, over=a, by=relation[b]) lowers. Where the grouped columns
hold the relation's whole key, they determine every other column, the
join opens no axis, and the bare Join is at(x,
by=relation[a]): one value per row of the result, fanned out where
several key tuples share the values joined on.
An axis is never a dim: a join that opens axes states the dims of the
sum that closes them, and that sum removes only the dims its plain axes
name.
JoinColumns(name, relation, joined, grouped)
dataclass
#
One relation as one call joins it: the columns joined on, and the columns grouped by.
The entry fixes no direction; the call does, and this is the one it
named. name is the relation's, as Program.relations keys it.
joined and grouped are roles, which are column names of
relation. relation maps every role to its dimension and names the
key. joined is
every column the join matches the operand on: the columns that leave the
frame, and every key column the call does not name. grouped is every
column of the relation the result keeps: the columns that arrive, and the
same unnamed key columns. Both are in the order of the relation's
columns: the key's first, each side sorted by role. A column in neither
is not read, so a relation may gain a value column without changing what a
call means.
A column both joined on and grouped by stays in the frame. One joined on
and not grouped by is dropped, and one grouped by and not joined
on is added, so the frame after the call is the operand's dims,
less the dims joined on, plus the dims grouped by.
added
property
#
The roles grouped by and not joined on: what the call brings into the frame.
added_dims
property
#
axes
property
#
The axis the join opens for each column it drops, empty where each group is one row.
Each runs over the column's dimension and stands for the column, so it never equals that dimension's own axis. A column the grouped columns determine opens none: in a lookup they hold the key, and the key determines every column.
dropped
property
#
The roles joined on and not grouped by: what the call sums away or looks up.
dropped_dims
property
#
grouped
instance-attribute
#
grouped_dims
property
#
joined
instance-attribute
#
joined_dims
property
#
kept
property
#
The roles both joined on and grouped by: the key columns the call did not name.
name
instance-attribute
#
one_row_per_group
property
#
Whether the grouped columns hold the relation's whole key, so each group is one row: a lookup, not a sum.
relation
instance-attribute
#
JoinedCondition(operand, columns, dims)
dataclass
#
operand read through a relation, written at(has_curve, by=converter_of[converter]): a lookup Join of a condition.
True at a coordinate where the relation has a row and operand holds at
the coordinate that row reads. False where the relation has no row, which
is what a missing row already means in a mask. The dims columns
joins on go and the dims it groups by arrive, as a lookup
Join's do.
Link(name, expression, values, dims, sign='==', by=None, walk=None, reads=False)
dataclass
#
One link of a piecewise: entry: an expression tied to the breakpoints a values parameter holds.
name is the link's key in the entry, and <entry>_<name> is the row
the expansion writes for it. sign is '==' where the link is pinned
to the curve, and one side of it where the link is bounded by the curve
instead. A link that walks a relation reads the curve's weights through
it, as at(..., by=) reads an array: by is the relation[column]
the file wrote, and walk the join it resolves to. dims is the row
the link builds: the entry's frame, or, for a walked link, that frame with
the dims the join drops replaced by the ones it adds. reads says
whether that row reads the entry's where through the relation, which
it does when the mask carries every dim the join drops or keeps.
by = None
class-attribute
instance-attribute
#
dims
instance-attribute
#
expression
instance-attribute
#
name
instance-attribute
#
reads = False
class-attribute
instance-attribute
#
sign = '=='
class-attribute
instance-attribute
#
values
instance-attribute
#
walk = None
class-attribute
instance-attribute
#
walks
property
#
Whether the link reads the curve's weights through a relation, rather than on the entry's own frame.
Mask(root)
dataclass
#
A resolved where and the questions the language answers about it.
root is the condition a consumer dispatches on with isinstance;
every question below is derived from it. Construction folds, so a boolean
literal stands at the root or nowhere in it, and refuses an unresolved
tree.
| ATTRIBUTE | DESCRIPTION |
|---|---|
root |
The resolved condition the mask restricts rows by, folded.
TYPE:
|
atoms
cached
property
#
The mask's leaves, connectives removed — the one walk the other questions read.
Held rather than re-walked: construction takes this walk anyway, to refuse an unresolved tree, and a mask cannot change afterwards.
conjuncts
property
#
The conditions the mask joins with AND — its AND spine flattened, stopping at an OR or a NOT.
dims
property
#
The dims the mask is read at, sorted by name — the union of what each leaf carries.
Empty for a mask over nothing but literals. Read off the leaves, which resolution stamped with their entries' dims, so a condition built from resolved pieces answers exactly as an entry's own does.
names_read
property
#
The parameters, relations and variables the mask names.
root
instance-attribute
#
Multiply(left, right, *, dims)
dataclass
#
Product of two operands.
Affine where at least one factor is variable-free; degree 2 where neither
is, which mathspec._degree admits in a QuadraticPosition alone.
NamedCondition(condition, dims, description=None)
dataclass
#
A named condition — a conditions: entry, read wherever a where, a when or a holds names it.
It builds nothing of its own: every use stands as a ConditionReference with
this condition under it, so a consumer reading a condition never looks the
name up here.
NamedExpression(expression, dims, in_math, description=None, adds_to=None)
dataclass
#
A named quantity — one the math reads, or one only read back after a solve.
in_math where the objective, a constraint or a term of a given
expression reads it, directly or through another entry or a macro, and
where it is such a term itself; its body then stands inside
Program.roots and is held to the degree rules where it is
read. Otherwise nothing a solver sees contains it: it is a reported
quantity, its body held to no degree, the one place a Dual may
stand. A bound and a where name no entry, so neither decides this.
adds_to = None
class-attribute
instance-attribute
#
The sum this entry adds to as a term, or None.
description = None
class-attribute
instance-attribute
#
dims
instance-attribute
#
The dimensions of the entry: the dims: that an entry with cases declares, or the dimensions of the body of a
plain entry.
expression
instance-attribute
#
in_math
instance-attribute
#
Negate(operand, *, dims)
dataclass
#
Objective(sense, expression, description=None)
dataclass
#
Parameter(dims, dtype='float', missing='refused', description=None, where=None)
dataclass
#
The shape of a column; data is attached at execution time by name.
dtype is what the entry claims the values are, and a consumer
attaching data refuses a column that is not it — so the entry is
what is read, rather than whatever the column happens to hold. It
refuses a null or NaN value too: a coordinate with no value has no row,
and inf is a value.
description = None
class-attribute
instance-attribute
#
dims
instance-attribute
#
dtype = 'float'
class-attribute
instance-attribute
#
missing = 'refused'
class-attribute
instance-attribute
#
What a missing row means where the parameter has coordinates, or the value it takes wherever an expression uses a
value. A bare numeric name in a where is still true only where the data has a row.
where = None
class-attribute
instance-attribute
#
The condition that limits the coordinates of the parameter, or None when it has every coordinate of its
dims. Where the condition is false, the parameter is absent and a tool drops its rows of data there. missing
applies where the condition is true.
ParameterComparison(name, op, value, dims)
dataclass
#
ParameterDefined(name, dims)
dataclass
#
ParameterReference(name, *, dims)
dataclass
#
Partition(name, relation, along, grouped, joined)
dataclass
#
One relation as a partition steps along it: the key column it steps along, the columns it partitions by, and the key columns it joins on.
name is the relation's, as Program.relations keys it.
along, grouped and joined are roles, which are column names
of relation. relation maps every role to its dimension and names
the key.
along is the one key column over the dimension stepped along, and
the frame keeps it. grouped is the value columns within= named,
read at the row's key: the partition's group. joined is the other key
columns, whose dimensions the frame carries. No column is dropped and
none is added: the frame does not change.
Piecewise(along, links, method, frame, where=None, activity=None, description=None)
dataclass
#
A piecewise: entry as the curve it states, which expand writes out as rows.
A program of a spec that still declares one carries it here, typed; a
program of the expanded spec carries the rows instead, under
Program.variables and Program.constraints, and what the
method assumes of the breakpoints under Program.assumptions. A
consumer building rows takes the expanded spec.
| ATTRIBUTE | DESCRIPTION |
|---|---|
along |
The dimension each curve runs along.
TYPE:
|
links |
The links, in the order the file wrote them, each with the row it builds. |
method |
How the weights are restricted.
TYPE:
|
frame |
The |
where |
Which coordinates of
TYPE:
|
activity |
The gate the weights sum to, or
TYPE:
|
description |
What the file wrote under
TYPE:
|
activity = None
class-attribute
instance-attribute
#
along
instance-attribute
#
curve
property
#
The two links as (x, y), the bounded one last. Two-link entries only.
description = None
class-attribute
instance-attribute
#
frame
instance-attribute
#
links
instance-attribute
#
method
instance-attribute
#
ragged
property
#
Whether where reads along, and so says how far each curve runs rather than only which exist.
where = None
class-attribute
instance-attribute
#
Power(base, exponent, *, dims)
dataclass
#
base ** exponent, both variable-free wherever the math reads it.
The language refuses a variable anywhere under it (mathspec._degree),
so in the program a solver sees it is degree 0 and folds to one number per
coordinate like any other parameter arithmetic.
Program(*, parameters, variables, constraints, objective, dimensions=Frozen({}), relations=Frozen({}), sos=Frozen({}), piecewise=Frozen({}), assumptions=Frozen({}), expressions=Frozen({}), conditions=Frozen({}), given=GivenTargets(), description=None)
dataclass
#
A complete declarative description of a mathematical program, with no data in it.
Every group of entries is keyed by the name the file wrote, sorted by
name, and is read-only: the mappings are wrapped at
construction, so a consumer cannot rewrite what another consumer reads.
Every dims and frame in it is sorted by name, whatever order the
file writes them in. A whole program is not hashable — the entries and expression nodes
inside it are, which is what dedup and memoisation ask for.
assumptions = Frozen({})
class-attribute
instance-attribute
#
What the data must satisfy, by the name that a tool quotes when it refuses data: each assumptions: entry of the
file, then what the method of each piecewise: entry assumes of its breakpoints. mathspec checks none of them at
load. A tool checks each when it attaches data, and refuses with assumption_message.
conditions = Frozen({})
class-attribute
instance-attribute
#
The conditions: that the file declares. Each use in the program is a ConditionReference, so a tool that
builds rows needs nothing from this mapping.
constraints
instance-attribute
#
description = None
class-attribute
instance-attribute
#
The description: of the file as a whole.
dimensions = Frozen({})
class-attribute
instance-attribute
#
expressions = Frozen({})
class-attribute
instance-attribute
#
The expressions: that the file declares, each with whether the math uses it. None of them has a row of its own,
and where the math uses one, it contains an ExpressionReference. mathspec compiles every named expression, so
it refuses at load a file whose named expression is outside the language, even one the math does not use.
footprint
cached
property
#
Which constructs this program uses — walked once, then held.
It answers for the rows this program holds. A curve still under
piecewise is not counted, so a sos2 curve adds no set order
here; ask the program of spec.expand('piecewise') for its rows.
given = GivenTargets()
class-attribute
instance-attribute
#
What this program uses and does not build (GivenTargets), for the host model to provide.
objective
instance-attribute
#
The objective, or None when the file declares none. A solver then only tests whether the constraints can be met.
parameters
instance-attribute
#
piecewise = Frozen({})
class-attribute
instance-attribute
#
Each piecewise: entry of the spec, as the curve it states. It is empty for the program of a spec whose curves
are expanded.
relations = Frozen({})
class-attribute
instance-attribute
#
roots
property
#
Every tree a row is built from — the objective and both sides of each constraint.
An expressions entry builds no row and is not among them. Nor is
a curve still under piecewise: it is not a row until
expand writes it out, and its rows are in
the program of the expansion.
separability
cached
property
#
Every axis, to what building it a window at a time asks and what it would break.
The locality :doc:the ceiling </about/ceiling> argues in — pointwise,
bounded halo, global — asked about the axes rather than about the
operators, so a driver may know before it cuts a horizon whether every
row it builds is complete inside some window.
A reduction means opposite things by position, which is the whole of the care: in a constraint a sum over the axis ties every window to every other, and in the objective it is additively separable, an objective being a sum already.
Every declared dimension has an entry, an axis nothing mentions being
trivially windowable. Walked once and held, like footprint and
for the same reason — a program cannot change after construction — and
answering for every axis costs what answering for one did, every
construct that ties an axis naming the axis it ties (#248).
It answers for the rows this program holds, as footprint does.
A curve still under piecewise ties nothing here, although its
rows sum over its breakpoint dimension; ask the program of
spec.expand('piecewise').
sos = Frozen({})
class-attribute
instance-attribute
#
variables
instance-attribute
#
relations_of(dimension)
#
The relations with a column over dimension, by name.
Reach(label, name, kind)
dataclass
#
One read along an axis whose distance only data can say.
| ATTRIBUTE | DESCRIPTION |
|---|---|
label |
The entry reading, as the lowering's messages label it.
TYPE:
|
name |
The parameter or relation that says how far.
TYPE:
|
kind |
An
TYPE:
|
Region(when, value)
dataclass
#
Relation(columns, key, missing='refused', description=None)
dataclass
#
One declared relation: a table over its columns, single-valued per key.
columns maps each role to its dimension, the key's roles first and
each side sorted by role; key is the roles a row is
identified by, and values the rest — every role is a key role for
a bare relation, which is one with no value columns. Every value is
checked when the data is attached to be a label of its column's dimension, and the table to
have one row per key tuple — which keeps a mistyped label from silently
dropping its terms in the join that places them, and is what lets at
read one value.
columns
instance-attribute
#
description = None
class-attribute
instance-attribute
#
dims
property
#
Every dimension a column is over, once, sorted by name.
key
instance-attribute
#
missing = 'refused'
class-attribute
instance-attribute
#
Whether every combination of the key needs a row: refused or absent.
roles
property
#
values
property
#
The roles the key determines.
dim(role)
#
RelationComparison(name, column, op, value, dims=())
dataclass
#
Compare one value column of a keyed relation against a literal — period_of == 2030.
column is the role read, and dims the dimensions of the key
columns: the leaf is read at them, one value per coordinate.
RelationDefined(name, dims=())
dataclass
#
RelationPairComparison(name, column, other, other_column, op, dims=())
dataclass
#
Compare a value column of one keyed relation with one of another — from_bus != to_bus — row by row on the key.
Both keys are over the same dims, and the two columns are over one
dimension, so a match is possible at all.
Separability(dimension, ahead, coupled, undecided, restarts, linking_rows, linking_columns)
dataclass
#
What building one dimension a window at a time asks of a driver, and what it would break.
A rolling-horizon or myopic driver cuts an axis into windows and builds each on its own, and a decomposition cuts the same axis and solves each piece on its own. What the program can say is whether every row it builds is then complete inside one window: how far a row reads ahead along the axis, which entries tie the axis together so that no window holds them, and — the same fact read as a set — which rows and columns are left over as the border every window shares. It cannot say whether the windowed answer is the one a whole-horizon solve would give — a store carried over one row windows cleanly, and a rolling solve of it is still a different answer — which is the driver's design and not the spec's.
What a row reads behind is not reported. A window starts where the driver puts it, and what its first rows meet there is the edge policy: the opening state a rolling horizon seeds, and the driver's to carry.
| ATTRIBUTE | DESCRIPTION |
|---|---|
dimension |
The axis asked about.
TYPE:
|
ahead |
Coordinates a window must see after its last row for every row
it builds to be complete — what a negative
TYPE:
|
coupled |
Each entry that ties the axis together, to what ties it and the one change to the spec that would not: a sum over the axis in a constraint, a grouping that sums it away, a wrapped translation, a set. No window satisfies these, and no rewrite here would keep the spec's meaning, so the remedy is named rather than applied. |
undecided |
Each read along the axis whose reach only data can say —
a named offset, a partition whose groups a window may cut, a read
through a relation at a coordinate the data chooses.
|
restarts |
Each entry counting a position along the axis, which a window restarts at its first row. Whether that is wanted — a seed once per window, or once per horizon — is for the spec's author to decide, so it is reported rather than refused. |
linking_rows |
|
linking_columns |
Each variable the axis does not index, in entry
order, whose column every window reads. A decomposition calls a
window a block, and with |
ahead
instance-attribute
#
coupled
instance-attribute
#
dimension
instance-attribute
#
linking_columns
instance-attribute
#
linking_rows
instance-attribute
#
restarts
instance-attribute
#
undecided
instance-attribute
#
windowable
property
#
resolved(least)
#
The same verdict with each named offset folded into ahead.
A driver holding the data reads the least value of each parameter an
undecided reach names and hands it here, so the rule that
turns a value into a reach — a negative offset reads ahead by that
much, a positive one reads behind and asks nothing — has one home.
| PARAMETER | DESCRIPTION |
|---|---|
least
|
Parameter name to the least of its values. A reach through a relation — a partition, a coordinate — cannot be folded this way and stays undecided, as does a parameter left out. |
| RAISES | DESCRIPTION |
|---|---|
KeyError
|
A name no undecided reach along this axis waits on. |
Source code in src/mathspec/program.py
Sos(variable, along, sos_type, description=None)
dataclass
#
One special-ordered set per coordinate of the variable's dims minus along.
The only entry that adds neither a column nor a row: it names
columns a consumer already has and says what may be nonzero among them. Which
dims those are is the variable's own dims and is read from it: a
copy here would be a second home for a fact
(Program.variables).
Sum(operand, over, *, dims)
dataclass
#
Sum operand over the axes in over, removing them from the result.
An axis in over is a dimension's own, or one a Join under it
opens (JoinColumns.axes): sum(x, over=a, by=relation[b])
lowers to a Sum over a Join, over the axis of each column the join
does not group by. This node is the group-by that follows the join.
Translate(operand, along, offset, wrap, fill=None, partition=None, *, dims)
dataclass
#
Re-index along one dimension: the result at t is operand at t - offset.
wrap is edge='wrap' in the file: periodic, and stated on every
node. fill is what an acyclic shift leaves behind: None leaves the
vacated positions absent, so the row drops; a number makes them present
and contribute it. Always None under wrap.
offset is an integer, or the name of an integer parameter that does
not depend on along and carries its sign in the values.
partition is a relation with a key column over along
(Partition), and the translation then happens inside each group
its within= columns make: the neighbour is the one before in the same
group, the edge is the group's, and a wrap closes each group onto itself.
A coordinate the relation sends nowhere reaches nothing.
TranslatedCondition(operand, along, offset, dims)
dataclass
#
operand read offset coordinates back along one dimension — shift(points, along=bp, offset=1).
A negative offset reads forward. Where the shift reads past the edge,
the condition is false, as a missing row is false in any condition, so the
file states no edge=.
Variable(dims, where=None, lower=None, upper=None, domain='continuous', missing='absent', description=None)
dataclass
#
description = None
class-attribute
instance-attribute
#
dims
instance-attribute
#
domain = 'continuous'
class-attribute
instance-attribute
#
lower = None
class-attribute
instance-attribute
#
A number, a parameter, or None when that side is open. An engine decides how an open side enters the model.
missing = 'absent'
class-attribute
instance-attribute
#
where = None
class-attribute
instance-attribute
#
VariableDefined(name, dims)
dataclass
#
VariableReference(name, *, dims)
dataclass
#
WindowSum(operand, along, width, wrap, partition=None, *, dims)
dataclass
#
Sum operand over a trailing window along one dimension.
The result at t is the sum of the operand at every position from t - width + 1 through t, so a width of 1 is the operand itself. The dimension survives: this replicates terms onto the positions that can see them rather than reducing anything away.
width is a whole number, or the name of an integer parameter when the
window differs per entity — a minimum up time, a rolling budget, a delivery
horizon. A named width may not depend on the dimension being summed over.
wrap says whether the window reaches around the start of the axis
instead of stopping short at it, and is stated on every node.
partition names a relation over that dimension, and the window then stops
at each group's edge. Positions are counted inside the group, so a
coordinate the relation places nowhere reaches nothing — not even itself.
assumption_message(name, assumption)
#
The sentence a consumer raises when the data attached to assumption, called name, fails it.
The language's own wording, so every consumer refuses in the same words;
a consumer appends the coordinates it saw. Where the file wrote a
description:, or a piecewise: method implied one, it trails the
sentence: the names say which columns are wrong, and the description says
why the rule is there.
Source code in src/mathspec/program.py
carries_variable(expression)
#
children(expression)
#
The sub-expressions of expression — what every walk recurses through.
Source code in src/mathspec/program.py
condition_children(condition)
#
The conditions under condition — a connective's operands, a named condition's body, and nothing under a leaf.
What every walk over a condition recurses through, as children is
for an expression. A leaf has nothing under it whether or not it is
resolved, so the grammar measures its own output with this too.
Source code in src/mathspec/program.py
is_quadratic(expression)
#
Whether expression contains a product of two variable-carrying operands.
A structural question over the program, and unrelated consumers ask it — what a solver must support, which entries to build last, whether this form can be represented at all — so it is answered once here beside the other walks rather than once per consumer in its own terms.
Whether a degree may be written is the language's verdict, and this is not a second opinion on it: by the time a program exists the question is which shape the expression has, and the program is what is in hand to answer it.
Source code in src/mathspec/program.py
parameters_of(*expressions)
#
Every parameter named anywhere under expressions.
variables_of(*expressions)
#
Every variable named anywhere under expressions.
walk(*expressions)
#
Every node under expressions, each expression itself included, parents first.
walk_regions with the regions dropped, for the questions that do
not ask where a node stands.
Source code in src/mathspec/program.py
walk_regions(*expressions)
#
Every node under expressions, each with the regions it stands inside, outermost first.
The traversal every question about a program is a filter of — which names
it mentions, whether a variable stands under it, which divisions it
contains, which rows a piece owes data at. One generator rather than that
five-line recursion once per question: how a program is traversed is one
fact, so a node kind children learns to descend into reaches every
caller at once rather than the callers that remembered.
The regions are the when of every Cases region the node's
value stands under, the outermost first, which is the order the masks
conjoin in. A node outside any cases: carries the empty tuple,
and a Cases node carries only the regions above it, not its own. The
tuple rather than one conjoined mask: what a consumer does with the
regions is its own, and the conjunction is one & away.
Source code in src/mathspec/program.py
See also
- reading a spec and its program, for how a tool reads a program
- Spec API, for the file as written