Expand piecewise: blocks into plain variables and constraints.
A block becomes ordinary affine declarations before anything reads the model,
under names prefixed with the block's own; what each method emits is tabled in
docs/reference/language/piecewise.md. A link expression is judged before
expansion, so a refusal names the link the file wrote rather than an emitted
constraint.
declaration_of(expanded)
The facts of one expanded block, as a program carries them.
A curve has an x-axis only where two links tie it, so the increasing
condition — and the shape it is checked with — exist only there; lp
alone needs a segment to state a line for; a mask must be one run.
Source code in src/math_spec/piecewise.py
| def declaration_of(expanded: ExpandedPiecewise) -> PiecewiseDeclaration:
"""The facts of one expanded block, as a program carries them.
A curve has an x-axis only where two links tie it, so the increasing
condition — and the shape it is checked with — exist only there; ``lp``
alone needs a segment to state a line for; a mask must be one run.
"""
pw = expanded.block
checks: list[Check] = []
curvature = _curvature_required(pw)
if curvature is not None:
x, y = pw.curve
checks.append(Increasing(x.values, pw.over))
checks.append(Curved(x.values, y.values, pw.over, curvature))
if pw.method == 'lp':
checks.append(AtLeastTwo(pw.over, expanded.points))
if expanded.points is not None:
checks.append(Contiguous(expanded.points, _nominated(pw)))
return PiecewiseDeclaration(
over=pw.over,
method=pw.method,
breakpoints=tuple(link.values for link in pw.links),
checks=tuple(checks),
)
|
derivations_of(block, expanded)
How each parameter block's expansion emitted is filled, by name.
Everything emitted hangs off the mask, so a block masking nothing emits
nothing for the caller to be told about.
Source code in src/math_spec/piecewise.py
| def derivations_of(block: str, expanded: ExpandedPiecewise) -> dict[str, Derivation]:
"""How each parameter *block*'s expansion emitted is filled, by name.
Everything emitted hangs off the mask, so a block masking nothing emits
nothing for the caller to be told about.
"""
if (mask := expanded.points) is None:
return {}
derivations: dict[str, Derivation] = {}
if (values := _nominated(expanded.block)) is not None:
derivations[mask] = MaskOf(block, values)
if expanded.starts is not None:
derivations[expanded.starts] = FirstOf(block, mask)
if expanded.ends is not None:
derivations[expanded.ends] = LastOf(block, mask)
return derivations
|
expand_piecewise(schema)
Return schema as a :class:_ExpandedSpec — every piecewise: block expanded away.
Memoised on schema.
| RAISES |
DESCRIPTION |
PiecewiseExpansionError
|
A block naming something that does not exist,
or emitting a name the file already declares.
|
Source code in src/math_spec/piecewise.py
| def expand_piecewise(schema: Spec) -> _ExpandedSpec:
"""Return *schema* as a :class:`_ExpandedSpec` — every ``piecewise:`` block expanded away.
Memoised on *schema*.
Raises:
PiecewiseExpansionError: A block naming something that does not exist,
or emitting a name the file already declares.
"""
if isinstance(schema, _ExpandedSpec):
return schema
if schema._expansion is not None:
return schema._expansion
if not schema.piecewise:
schema._expansion = _ExpandedSpec.model_construct(**dict(schema))
return schema._expansion
raw = schema.model_dump()
raw.setdefault('variables', {})
raw.setdefault('constraints', {})
raw['expanded_piecewise'] = {name: _Block(schema, raw, name, pw).expand() for name, pw in schema.piecewise.items()}
raw['piecewise'].clear()
expanded = _ExpandedSpec.model_validate(raw)
schema._expansion = expanded
return expanded
|