Conversation
…ven expression, not as a variable The namespace files a given expression with the variables, because it is read as a column. A where that read one was refused as if it read a variable. The three refusals (the left name, the right name, a side of a comparison of expressions) now name the given expression and say why a mask may not read it: it may hold a variable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBmqeqBrqpQC6MVoSrguHK
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBmqeqBrqpQC6MVoSrguHK
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBmqeqBrqpQC6MVoSrguHK
The Upcoming section becomes 0.3.0, grouped into composition, language, typesetting and advice, and documentation. The notes name the two breaks against 0.2.0 (#788, #751). The #742 line is left out: #763 replaced `empty: true` before any release carried it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBmqeqBrqpQC6MVoSrguHK
…s working #788 and #751 change only documentation and a docstring. mathspec neither attaches data nor computes duals, so neither breaks a file or an import. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBmqeqBrqpQC6MVoSrguHK
…import Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBmqeqBrqpQC6MVoSrguHK
…luded Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBmqeqBrqpQC6MVoSrguHK
…e on its own Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBmqeqBrqpQC6MVoSrguHK
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBmqeqBrqpQC6MVoSrguHK
…ther file reads it under given: masks: A masks: entry names a where predicate. A bare mask name in a where:, a when: or a holds:, under NOT and inside count, shift and at, stands for its predicate. A use is a NamedMask node with the predicate under it, so .atoms, .names_read, .dims and the case-overlap proof read through it, and the typesetter prints the symbol and defines the mask once under Masks, with iff. given: masks: reads another file's mask. A where reads it as boolean data over its frame, a ParameterDefined, as a where may not read a given expression. merge folds the reading into the definer; the reader's dims bound the definer's frame. Refused at load: a cycle, a predicate that folds to a literal, a predicate wider than the declared dims, a mask in arithmetic or in a comparison, and a variable that asks through a mask whether it exists. Docs, named.md masks section (n 14, median 14, over25 2, both quoted messages); declarations.md given: masks (n 8, median 13, over25 0). Closes #803. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBmqeqBrqpQC6MVoSrguHK
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBmqeqBrqpQC6MVoSrguHK
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBmqeqBrqpQC6MVoSrguHK
FBumann
added this pull request to stack #823
October 1, 2026 15:19
FBumann
removed this pull request from stack #823
October 2, 2026 06:51
…s, and its topic files read them under given: masks: (#825) * docs(pypsa): the pypsa spec names its repeated row conditions as masks, and its topic files read them under given: masks: Sixteen masks name the conditions examples/pypsa.yaml wrote out at every row: per committable class C, C_committed (26 sites), C_com_ext (8), C_maint_ext (5) and C_ramps_from_previous (6); StorageUnit_fix and _ext (6 each); Line_lossy and Transformer_lossy (4 each). 165 where strings change. Each where, with every mask expanded, equals the one it replaces: 352 declarations and every cased expression were compared conjunct by conjunct. tools/pypsa_split.py places a mask in the topic its name names and writes given: masks: for every other topic that reads it. All 24 fragments load alone and merge to the one file's canonical form. The symbol table spells the masks as on^{qualifier} and prev, and the gallery's table cut includes masks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBmqeqBrqpQC6MVoSrguHK * docs: the changelog line links #825 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBmqeqBrqpQC6MVoSrguHK --------- Co-authored-by: Claude <noreply@anthropic.com>
FBumann
added this pull request to stack #831
October 2, 2026 06:52
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011a8N2JQdmmd1WhVCKN1a2A
FBumann
pushed a commit
that referenced
this pull request
Oct 2, 2026
Brings in origin/main through the base branch of #821. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011a8N2JQdmmd1WhVCKN1a2A
A masks: entry no longer takes dims:. Its frame is the dims its predicate reads, in the order dimensions: declares them, so a use can no longer print an index no quantifier binds. A given: masks: reader states exactly that frame, as a parameter reader states its introducer's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CsaSWMofYqJiKmnSiPycV9
This branch has not been deployed
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
masks:entry names awherepredicate once, and awhere:,when:orholds:reads it by name. Another file reads it undergiven: masks:. The PyPSA example names 16 masks (#825). Closes #803.A mask declares no
dims:. Its frame is the dims its predicate reads. A mask has nocases:, so its predicate always gives the frame, and a declared one could only repeat it or be wider. A wider one printed an index no quantifier binds.Why
#803 counts 51
where:strings that write "a generator stands in this period" out across four PyPSA files. Within one file, #795's workaround is a 1/0cases:expression read asactive == 1. Across files, awheremay not read a given expression at all, since one may hold a variable.Typesetting, for discussion
These are the choices this PR makes, with the alternatives I did not take.
:⟺("is defined as"), or=with a\{0,1\}reading.=would read a predicate as a number, which is the confusion the refusal names.=and⟺lines apart.inline_expressionsshiftRendered (Markdown), from the example above:
Design, refusals, guards, gate output, what is not done
Triage (against
limits.md, as #803 argued): a declaration section. It is not a macro, because a macro template is arithmetic with one owner. It is not a primitive, because it adds no operator. It is not a named expression: a predicate is not a number, and a bare parameter in awherealready means "has a row and is finite".limits.mddoes not move.Program.
NamedMask(name, body)is a newPredicatemember. It is a pass-through, asNamedis for expressions.where_children()steps into the body, so.atoms,.names_read,.dims, the frame checks, separability and the case-overlap proof read through it unchanged. The overlap proof needed one branch:_evaluatesteps into the body.Program.masksholds aMaskDeclaration(where, dims, description)per entry. Itsdimsare the dims the predicate reads, in the orderdimensions:declares them.GivenTargets.masksholds the given ones.ParameterDefined(name, dims), the boolean data it is to that file. This mirrors a given expression read asVariable.reading.mdsays so.Resolution.
Namespace.mask()resolves an entry once, on the same_loadingstack as named expressions, so a cycle across masks names its chain.Refused at load (
test_what_a_mask_may_not_be_is_refused_at_load, 11 cases):TrueorFalse;a == 1);Also refused: a variable that asks through a mask whether it exists, two fragments that define one mask, and
dims:on a mask. The closed schema's own error refuses the key:masks.young: unknown key 'dims' in a mask declaration. Valid keys: description, where.Merge. A given mask is boolean data to the file that reads it, and
mergefolds it into the definition.GIVEN_KINDSandREAD_KINDStakemasks._fitsreads the definer's frame off its program. No mask block writesdims:, so the frame is the dims the definer's predicate reads. A reader states exactly that frame, as a reader of a parameter states its introducer's. A wider or narrower one is refused at merge, and the message names both frames.Also changed.
checknotes a given mask, as it notes every other given kind.tools/notation.pygains a "Named masks" family; "Masks" is taken by the existingwhere:section of that page.examples/pypsa.yamlnames 16 masks in 165wherestrings, with the rows unchanged.tools/pypsa_split.pywritesgiven: masks:in the topic files, and the gallery is regenerated. docs(pypsa): the pypsa spec names its repeated row conditions as masks, and its topic files read them under given: masks: #825 has the masks table and the equivalence check.Guards. Each check was deleted and the suite run:
…refused_at_load[a-cycle],[a-mask-reading-itself][always-true],[always-false]dimsonMaskBlock(the old tree)test_a_mask_declares_no_frame: DID NOT RAISE[a-mask-in-arithmetic],[a-given-mask-in-arithmetic][a-mask-compared]test_a_variable_that_asks_through_a_mask_whether_it_exists_is_refused_fitsmask frame as<=(the old tree)test_a_reader_that_states_a_frame_wider_than_the_mask_s_is_refused: DID NOT RAISEmasksinREAD_KINDStest_a_reading_the_definer_does_not_answer_is_refused[a-mask-read-as-a-parameter]test_check_notes_the_mask_a_fragment_readsCoverage moved.
test_given_holds_four_kinds_and_refuses_a_fifthis now…five_kinds_and_refuses_a_sixth.test_declaration.pynames masks.tests/test_canonical.pylistsmasksin the canonical section order, afterexpressions(1bb0690). No example used a mask yet, so nothing failed. docs(pypsa): the pypsa spec names its repeated row conditions as masks, and its topic files read them under given: masks: #825 exposed it.Docs.
named.md: a## maskssection. The page title is now "Named expressions, masks and macros", in the nav too.declarations.md:### given: masks, and a pointer fromgiven: expressions.expressions.md: the bare-name row and the namespace.file.mdand the language index: thirteen keys.reading.md:NamedMask, and a given mask asParameterDefined.typeset.md: the Masks heading, andtypeset_declarationprints six kinds, masks and curves included.howto/compose.md: the fold row.Gates, on the head that removes
dims:from masks:pixi run lint: clean.pixi run test: 2697 passed.docs-build, strict: blocked in this session. The proxy refusesdocs.python.org/3/objects.inv, and the unresolvedpathlib.Pathautoref that follows is the only warning. Left to CI.compile-tex: not run. The tectonic bundle does not download here. Left to CI.Not done.
adds_to:for masks. A predicate has no+.shift(m, along=d)still needsdin the predicate's dims, as it does for a predicate written out.🤖 Generated with Claude Code
https://claude.ai/code/session_01PBmqeqBrqpQC6MVoSrguHK
Generated by Claude Code