diff --git a/examples/building-models-from-specs.ipynb b/examples/building-models-from-specs.ipynb index 0b6bdfcb..09495cd4 100644 --- a/examples/building-models-from-specs.ipynb +++ b/examples/building-models-from-specs.ipynb @@ -925,7 +925,7 @@ "cell_type": "markdown", "id": "49", "metadata": {}, - "source": "## Where the code lives, and an upstream note\n\nThe feature is a small package, `linopy/spec/`, imported only when you call\n`add_spec`/`from_spec` — `import linopy` never pulls in `math_spec`. Roughly:\n\n- `accessor.py` — `model.spec`, the `NamedExpression` views, `evaluate`, and\n typesetting: the whole model (`m.spec.to_latex` / `.to_markdown` /\n `.to_typst`) and any single declaration — a named expression, constraint or\n variable — via `m.spec.declaration(name)` and math-spec's\n `typeset_declaration`.\n- `attach.py` — the three attachment rules; data onto master coordinates.\n- `builder.py` — emits variables, constraints, objective; folds expressions.\n- `operators.py` — `sum`, `by=`, `shift`, `at`, `sum_back`.\n- `where.py` — `where:` predicates as boolean masks.\n- `coverage.py` / `terms.py` — the absence rule from section 6: a missing row\n is refused wherever it is used.\n- `curves.py` — the data side of `piecewise:` blocks.\n- `netcdf.py` — the factorize-based persistence from section 10.\n- `nodes.py` — walks over expression nodes. One workaround lives here:\n math-spec alpha.73's `program.children()` does not descend into a `Power`\n node, so parameters hidden under `**` would be missed; `nodes.py` walks into\n the base and exponent itself.\n\n### Summary\n\nA spec is the maths over labelled axes; the sources are the numbers. `linopy`\nattaches them into an ordinary model, hands each named expression back as three\nviews — its formula, its unsolved linopy expression and its solution — refuses a\nmissing parameter row wherever it is used (as a coefficient, bound, constant\nside or divisor alike, with `where:` and filling the data as the escape\nhatches), and round-trips the lot through netCDF by keeping the spec as text\nbeside factorized labels." + "source": "## Where the code lives\n\nThe feature is a small package, `linopy/spec/`, imported only when you call\n`add_spec`/`from_spec` — `import linopy` never pulls in `math_spec`. Roughly:\n\n- `accessor.py` — `model.spec`, the `NamedExpression` views, `evaluate`, and\n typesetting: the whole model (`m.spec.to_latex` / `.to_markdown` /\n `.to_typst`) and any single declaration — a named expression, constraint or\n variable — via `m.spec.declaration(name)` and math-spec's\n `typeset_declaration`.\n- `attach.py` — the three attachment rules; data onto master coordinates.\n- `builder.py` — emits variables, constraints, objective; folds expressions.\n- `operators.py` — `sum`, `by=`, `shift`, `at`, `sum_back`.\n- `where.py` — `where:` predicates as boolean masks.\n- `coverage.py` / `terms.py` — the absence rule from section 6: a missing row\n is refused wherever it is used.\n- `curves.py` — the data side of `piecewise:` blocks.\n- `netcdf.py` — the factorize-based persistence from section 10.\n- `nodes.py` — walks over expression nodes, and the dimensions a node\n spans before any data is bound.\n\n### Summary\n\nA spec is the maths over labelled axes; the sources are the numbers. `linopy`\nattaches them into an ordinary model, hands each named expression back as three\nviews — its formula, its unsolved linopy expression and its solution — refuses a\nmissing parameter row wherever it is used (as a coefficient, bound, constant\nside or divisor alike, with `where:` and filling the data as the escape\nhatches), and round-trips the lot through netCDF by keeping the spec as text\nbeside factorized labels." } ], "metadata": { diff --git a/linopy/spec/coverage.py b/linopy/spec/coverage.py index 401c6898..11da5e04 100644 --- a/linopy/spec/coverage.py +++ b/linopy/spec/coverage.py @@ -21,7 +21,7 @@ from linopy.spec import terms from linopy.spec.context import Context from linopy.spec.errors import SpecDataError -from linopy.spec.nodes import amounts_of, children, parameters_of +from linopy.spec.nodes import amounts_of, parameters_of from linopy.spec.where import evaluate_where Rows = xr.DataArray | None @@ -101,7 +101,7 @@ def _collect( narrowed = inside if rows is None else rows & inside _collect(region.value, ctx, narrowed, constant, into) return - for child in children(node): + for child in ms.children(node): _collect(child, ctx, rows, constant, into) diff --git a/linopy/spec/nodes.py b/linopy/spec/nodes.py index 7c158a4e..9c95c968 100644 --- a/linopy/spec/nodes.py +++ b/linopy/spec/nodes.py @@ -1,4 +1,4 @@ -"""Walks over expression nodes that descend into every operand, a ``Power``'s included.""" +"""Walks over a program's expression nodes, and the dimensions a node spans.""" from __future__ import annotations @@ -7,18 +7,11 @@ from math_spec import program as ms -def children(node: ms.ExpressionNode) -> tuple[ms.ExpressionNode, ...]: - """The operands of *node*: ``math_spec.program.children`` plus a power's base and exponent.""" - if isinstance(node, ms.Power): - return (node.base, node.exponent) - return ms.children(node) - - def walk(*nodes: ms.ExpressionNode) -> Iterator[ms.ExpressionNode]: """Every node under *nodes*, each of them included, parents first.""" for node in nodes: yield node - yield from walk(*children(node)) + yield from walk(*ms.children(node)) def amounts_of(node: ms.ExpressionNode) -> Iterator[str]: @@ -55,4 +48,4 @@ def _dims(node: ms.ExpressionNode, program: ms.Program) -> frozenset[str]: return (_dims(node.operand, program) - {node.over}) | set(node.into) if isinstance(node, ms.Cases): return frozenset().union(*(_dims(r.value, program) for r in node.regions)) - return frozenset().union(*(_dims(c, program) for c in children(node))) + return frozenset().union(*(_dims(c, program) for c in ms.children(node))) diff --git a/pyproject.toml b/pyproject.toml index 482334e6..f8b3ec14 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -123,7 +123,7 @@ gpu = [ # keeps the git pin out of the published wheel metadata, which PyPI rejects. # Install with `uv sync --group spec` or `uv pip install --group spec`. spec = [ - "math-spec @ git+https://github.com/energy-models/math-spec.git@67aeedb988ee95d196456b4cbe50821e3799edaa ; python_version >= '3.12'", + "math-spec @ git+https://github.com/energy-models/math-spec.git@v0.0.0-alpha.76 ; python_version >= '3.12'", "pyyaml ; python_version >= '3.12'", "pyarrow ; python_version >= '3.12'", ]