Conversation
… declare A `given:` block names the variables and constraints a file reads and does not introduce, so a layer of math can be written against a model that already exists. The declarations stay complete, so every load-time pass treats a given one as it treats any other and a `Spec` is still total. A given variable carries `foreach`, `where`, `domain` and `absence`, and no `bounds:` — the owner of the column owns those. A given constraint carries `foreach` and `sense`, and no `expression:` — the owner of the row owns that. `sense` is required because it fixes the sign of the dual, which is the one thing `dual()` can read off a row family the file does not build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUHW3akHVBCsuSjCXxf4Sf
Documentation build overview
16 files changed ·
|
`test_the_golden_model_reaches_every_line_of_the_walk` asks the fixture to render every line of the walk, and nothing in it declared a `given:` block, so the section printed nowhere. The fixture now reads a column and a row family it does not introduce, and the coverage script asks for each given declaration's own line as it already asks for a constraint's. The section prints last, beside the variable domains: both say what a symbol is rather than what the model asks of it, and `tools/notation.py` reads the objective as the first equation of the document. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PUHW3akHVBCsuSjCXxf4Sf
Contributor
Author
|
@FabianHofmann I tried to come up with a solution for the partial stuff that doesnt produce a ton of problems. Maybe take a look of you like this. |
This was referenced Sep 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
The following content was generated by AI.
What this changes
A
given:block names the variables and constraints a file reads and does not introduce, so a layer of math can be written against a model that already exists — PyPSA/linopy#954's case, and #302'sgiven: truecomment.There is no
PartialSpec. That was the worry on #302, and the block avoids it: a given declaration is complete, so every load-time pass treats it as it treats any other andto_programgains no branch. Two closed block types, each total in its own fields, rather than one type with holes.A given variable carries
foreach,where,domainandabsence, and nobounds:. Nothing at load reads a bound, and the owner of the column owns them; a second spelling here would be a second home for one fact. A given constraint carriesforeachandsense, and noexpression:.senseis required because it fixes the sign of the dual, which is the one thingdual()can read off a row family this file does not build.Where each fact goes, and why
The line is the one both repos already run on — decided at load stays in the file, decided by whoever holds the data stays with them.
foreachsum(by=)domainwhere/absenceabsence: zeroalready needs awhereboundssense(constraints)expression(constraints)This departs from what #954's description says it checks (dimensions, bounds and domain). Dropping bounds deletes a whole class of bind-time comparison, and an array-valued bound usually cannot be restated in YAML anyway — that the column already exists is the premise. The cost is honest:
boundedness.advicereadslower/upper, so a given variable is exempt from the runs-to-infinity note.How it prints
A
Givensection, last, beside the variable domains: both say what a symbol is rather than what the model asks of it. A given variable prints its domain like any other; a given row family prints as the one thing the file may do with it.Printing it first, as a preamble, was the first shape tried. It is the better read, and it breaks
tools/notation.py, which reads the objective as the first equation of the document in two places. Making that scanner position-independent is more fragility than the reading is worth here.What the language does not answer
Whether the bound column has the declared dimensions, coordinates and domain, and whether the bound row family has the declared sense, is a question about the model rather than about the file. That is a consumer's check, and this PR deliberately states none of it. The two traps worth naming for whoever writes the binder:
dual(balance)after a layer is added isbalance's shadow price in the model including the layer, not in the base model that was solved earlier. Correct, and surprising; it is stated once on the reference page.Why
#302 asks for a composition verb and has two incompatible drafts. This is not that. #244 and #250 both produce one mapping validated once, and neither can express a layer over a model that is not a spec at all.
The variable half arguably needs no language change — #954 binds a fully declared variable through its
sourcesentry today. What forces this upstream is the constraint half:dual(balance)cannot name a row family the file does not declare, because resolution has nothing to type andfan_inhas no frame to shape against. So the smallest thing that works is a block, and reading duals off a base model is what it buys.given: trueas a flag onvariables:/constraints:was the other shape considered. It was rejected because it makesexpression:conditionally optional, which is the first place the language would admit an incomplete declaration.⚠ This asks for a decision on the limits
Please read this before the rest.
given:is none of the three kindslimits.mdadmits — it is not a macro, a primitive or a formulation, and that page says a request that is none of the three is refused.sos:is in the same position and entered anyway, but the taxonomy was never widened to describe it.This PR widens it, adding a declaration block as a fourth kind. That is a decision, not a paragraph edit, and it is the one thing here that should be settled before anything else in the diff is reviewed.
The adjacent row in the refusals table is "A Python API for building models — the model is the file you review and diff." The argument that
given:is not that: a file with agiven:block still says all of its own math, in YAML, and still prints; what it does not say is which model supplies the declarations it reads, which is the same category of fact as the numbers behind a parameter.limits.mdnow makes that argument in two sentences rather than leaving it implicit.Verified
pytest -q -n 4: 1237 passed, 5 skipped (1205 on the base — 32 new).ruff format --check .andruff check .clean.prettier --checkclean overdocs/**/*.md.python -m tools.schema,python -m tools.notationandpython -m tests.typesetting.goldenregenerate to no diff.mkdocs build --strictbuilds.Not run:
pyrefly,reuse,typos,taplo,zizmor, and thetectonichalf ofcompile-tex. This environment has nopixi, so the suite ran in a plain 3.12 venv against the same runtime dependencies rather than throughpixi run ci.Two caveats on gates that could not run at the pinned version.
mkdocs build --strictneededdocs.python.org/objects.invdropped frommkdocs.yml, which this environment's proxy refuses with a 403; that affects cross-links into CPython's docs and nothing in this diff. Andprettier --checkfails onmkdocs.ymlon a pristine tree with the version available here (3.8.1), so the one added nav line is all this PR changes in that file — it is not reformatted.The CI failure this PR earned
The first push went red on
test_the_golden_model_reaches_every_line_of_the_walk, which asks the golden fixture to render every line of the walk. Nothing in the fixture declared agiven:block, so seven lines of the new section printed nowhere.The claim was the problem, not the code. The first run reported "1234 passed, 6 skipped" and I did not read what the six were: that guard is
pytest.importorskip('coverage'), and the venv had nocoverage. It skipped silently and I reported a green suite. The fixture now reads a column and a row family it does not introduce, and the coverage script asks for each given declaration's own line as it already asks for a constraint's. The suite above runs withcoverageinstalled; the five remaining skips aretypst, a dev dependency absent here.Mutation table
Each guard deleted in turn on a clean tree,
__pycache__dropped on both sides, restored throughgit checkout --, tree verified clean at the end. Re-run in full after the fix above.Every guard, and what caught it
dual()resolves againstwhereis never resolvedwheredims are not checked against its frameOne guard survived the first pass — the one that keeps a dimension only a given constraint indexes out of the never-an-axis advice. The layer fixture reached those dimensions through its given variable too, so nothing tested the constraint path.
test_a_dimension_only_a_given_row_family_indexes_is_in_useis the purpose-built probe, and the row above is with it in place.Deliberately not done
piecewise:block.piecewise.pystill readsschema.variables, soactivity:must name a column this file introduces. The refusal is correct; the message will read oddly for a file that did declare the name undergiven:.where:on a given constraint. Whether a layer should be able to narrow which rows of a given family it reads the dual of is open;foreachandsenseare whatdual()needs today.merge/override. shared math has no composition verb, so a component library ships as generated YAML or a script #302's verb question is untouched, and neither draft is blocked by this.Departures from Part 2
The advice pass gained a third kind (
given-never-read), soADVICE_KINDSgrew andtest_advice.py's two assertions on the closed set moved with it — that coverage did not go away, it now pins three kinds instead of two.test_nothing_the_model_is_given_prints_italicreadsevery_variablerather thanvariables, for the same reasonSymbolsdoes: a given column is one the solver chooses, so it prints italic.🤖 Generated with Claude Code
https://claude.ai/code/session_01PUHW3akHVBCsuSjCXxf4Sf