Conversation
…component library is files rather than a script `merge(fragments)` takes templates that each own part of the math and hands back one mapping to validate, resolve and lower exactly once. A fragment may name what a sibling declares, because merging happens before validation, so a template writes `at(flow, by=gen_port)` against a coupling surface another file owns. Peers obey the opposite law to patches, which is why this is a second verb rather than a mode on `override`: - `dimensions` and `relations` are the coordinate space, shared on purpose, and two fragments saying different things about one is the error; - everything else is owned, so a name two fragments declare is a collision that names both and the rewrite it usually is — two rows of a dimension; - objectives are summed, each term parenthesised, and senses must agree. The shell front's `compose` verb now takes several models, merged as peers, before it lays any patches over them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CA5v9XJvgYViUHP7hSiKPU
…y file loads and prints on its own `given_variables:` names a column this file reads and another file introduces. A component template was a load error on its own before it, which meant the unit a library ships was the one unit nobody could review as math. Now it loads, and it prints in all three formats, under a `Given` heading that says which symbols the file does not introduce. The section carries a frame and a domain, and no `bounds:` or `where:` — the file that introduces the column owns those, and a second spelling would be a second home for one fact. `merge` folds each given declaration into the declaration that introduces it, after checking that what the reader states agrees with it, so a composed model carries none. `to_program` refuses a file that still reads a column nothing in it introduces, because a program builds every column it carries, and the message names the composition that fixes it. So no consumer downstream ever sees a given declaration, and the engine needs no change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CA5v9XJvgYViUHP7hSiKPU
Documentation build overview
11 files changed ·
|
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CA5v9XJvgYViUHP7hSiKPU
|
Thanks for the initiative Felix, I think this is very nice. Though I still have to figure out whether the incremental addition of optimization definitions as I do it in linopy is the right way. It feels glumpsy to me and I needed to think about many edge cases. most of them are resolved now and the implementation is highly tested. But I have the fealing that this feature might spoil out packages and we should do it in the clean way. While I am happy to spoil linopy a bit, I would like to keep math-spec as lean as possible. let's discuss that in a second |
|
thanks for the initiative @FBumann , let's close this, I don't think partial definitions is what we want in linopy/pypsa either. |
|
ah this is a stack. should we close all then? |
|
@FabianHofmann This really grew on me to be honest... Inthink with a quick call i can convince you it might be a good addition! |
|
I would be curious! |
Note
The following content was generated by AI.
What this changes
A component file was a load error on its own, so the unit a library ships was the one unit nobody could review as math.
given_variables:ends that: the file names the column it reads, and then it loads and prints.mergecomposes the fragments as peers. It folds each given declaration into the declaration that introduces it, so a composed model carries none of them and no consumer downstream ever sees one. The engine needs no change.The three doors, and which is shut
given_variables:to_spectypesetGivenheading in the legendto_programThat last row is the whole safety story. A program builds every column it carries, and this file says the opposite about one of its own:
Because the refusal is at lowering, a given declaration never reaches an engine. That is what makes this cheaper than #452, which carried given declarations into the program and had to invent bounds for them.
⚠ This asks for a decision on the limits
Read this before the rest.
given_variables:is a declaration section, which is none of the three kindslimits.mdadmits — not a macro, not a primitive, not a formulation. #452 raised this and closed without it being settled.This PR widens the taxonomy with a fourth kind and states the test it has to pass: a section enters where it says something no section already says, where a file decides it without data, and where the typesetter prints it.
given_variables:passes all three — nothing else states that a column belongs to another file, which is exactly what lets a component file stand alone.The ten declaration keys become eleven. That is the cost, and it is the thing to accept or refuse before reviewing the diff.
What the section carries, and what it refuses
dims(required),domain,description. Nobounds:and nowhere:— the file that introduces the column owns both, and a second spelling here would be a second home for one fact. Both are refused by the closed schema, which names the valid keys itself.Folding checks agreement: what the reader states must match the introducer, or say less.
descriptionis prose rather than a claim, so it is excluded from every comparison inmerge— otherwise two fragments describing one shared axis in their own words would be a disagreement.`merge`, ported forward from #244
Peers, at the current spellings —
relations:anddims:, which #244 predates:dimensionsandrelationsare the coordinate space, shared on purpose, and two fragments saying different things about one is the error;mergeandoverrideare two verbs rather than one with a flag, because peers and layers obey opposite laws. They compose:override(merge({…}), {…})builds the model and then configures the run.every_variablewas proposed and dropped.spec.variablesandspec.given_variablesstay separate, and a pass that wants both writes{**spec.variables, **spec.given_variables}where a reader sees it. A property hides which of the two a pass meant, which is how #452'spiecewise.pycame to read the wrong one.Verified
No pixi here, so the gates ran against python 3.13 with the runtime and docs dependencies installed by pip, not the pinned solve.
pytest -q— 1330 passed, 6 skipped on9c7532e(1302 on feat(language): a patch file extends a model rather than a copy of it #505's head, so 28 new).pyrefly1.2.0 — nothing in the changed modules.ruff0.15.8 (repo pins 0.16.1) clean.typos1.50.2 clean.python -m tools.schemaandpython -m tests.typesetting.goldenregenerated: the schema gainsGivenVariableBlock, and the golden output does not move, since the golden model declares no given column.mkdocs build --strictbuilds, after droppingdocs.python.org/objects.invlocally — the proxy here 403s it. The dropped line is not in the commit.coverageinstalled) and passes, so the new legend group is not an unreached line.Not run:
reuse,zizmor,taplo,compile-tex.Mutation table
Each guard deleted in turn, whole suite run, tree restored between them. Taken before the base branch dropped its shell verb, so the totals are that head's.
No survivors this time.
Deliberately not done
given_constraints:.dual()over a row family built elsewhere is the basket case (PyPSA/linopy#954, and @FabianHofmann's comment on #302). It is a separate decision with a separate engine story, since a program would then have to carry the declaration rather than refuse it.piecewise:curve or sit in ansos:. Both readschema.variables, which is correct — this file introduces neither. The refusal message will read oddly for a file that did declare the name undergiven_variables:.examples/entry. A fragment is a model now, so one could go there; the worked library lives on the how-to page and intests/test_given.pyuntil someone wants it rendered in the gallery.Spec, andgiven_variables:is how — and leaves the rest of the issue where it is.Stack: on #505, which is the base branch.
🤖 Generated with Claude Code
https://claude.ai/code/session_01CA5v9XJvgYViUHP7hSiKPU
Generated by Claude Code