Repository navigation
feat(program)!: a program says where the model reads each refused parameter, so a tool refuses missing data before it builds - #885
Merged
Conversation
…rogram Work in progress, not for review. `Program.reads` maps each refused parameter to the mask over its dims where the model reads it. The reversed shift and the look-ahead window still use the `reverse` and `ahead` flags on `Translate` and `WindowSum`, which are to be replaced by one predicate node. No tests or docs yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AvtZGymCbY9swLnR8JwHng
…ing oracle Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AvtZGymCbY9swLnR8JwHng
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AvtZGymCbY9swLnR8JwHng
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AvtZGymCbY9swLnR8JwHng
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AvtZGymCbY9swLnR8JwHng
…ameter `Program.reads` maps each `refused` parameter to a mask over its dims that holds where the model reads it, so a tool evaluates it when it attaches the data and refuses a missing row before it builds anything. `TranslatedPredicate` is `WindowPredicate`: the operand holds at some position of a window along one dimension, back or ahead, with wrap and a partition. A file's predicate shift is a window of one position. docs: reading.md documents `program.reads` and the window predicate, and absence.md points to it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AvtZGymCbY9swLnR8JwHng
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AvtZGymCbY9swLnR8JwHng
Documentation build overview
61 files changed ·
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AvtZGymCbY9swLnR8JwHng
Contributor
Author
Note The following content was generated by AI. Closed in favour of #880. #880's branch was fast-forwarded to carry every commit of this PR, and it now has one description and one changelog line for both changes. Generated by Claude Code |
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.
Program.readsmaps eachrefusedparameter to a mask over its dims that holds exactly where the model reads it. A tool evaluates it at attach, like awhere, and refuses missing rows before building anything.TranslatedPredicatebecomesWindowPredicate.Stacked on #880, whose contract this states as data.
How the mask is built
mathspec/reads.pyruns three walks over the program.kbecomes a window ahead byk.sum_backbecomes a window ahead of the same width.at()becomes thesum(by=)that inverts it, andsum(by=)becomes the lookup. Both useJoinwith the columns swapped.boolflag in awherefollows feat(language)!: a missing row is refused only where the model reads it, a file names its value instead of neutral, and the program says where each parameter is read #880. It is read wherever the model evaluates thatwhere, which is wider than where it reads: a row'swhereis evaluated at every coordinate. So the top-down walk carries a second mask, evaluated, down to each case'swhen, and a walk over each mask passes it to the flags undercount,shiftandat.Every node in a mask is one a tool already evaluates, except one:
WindowPredicatereplacesTranslatedPredicate. It holds where its operand holds at some position of a window along a dimension, looking back or ahead, withwrapand a partition. A file's predicateshiftis a window of one position (offset=-1loads as "ahead by 1"). Onlyprogram.readswrites a wider window, or one that looks ahead by a named offset. A read through a relation is aJoinof an indicator (Casesof 1 and 0) compared> 0, so a missing map row reads as false.Alternative rejected: first draft put
reverseandaheadflags onTranslateandWindowSum. A default-off flag gives one node two meanings and gets around theassert_neverexhaustiveness every walker relies on. One window predicate in the closed predicate set makes each walker grow a branch.Verification
Differential oracle.
tests/test_reads.pyholds an oracle that shares no code withreads.py. It builds the rows of 26 small models on random data by the absence rules, and records each parameter coordinate that a built row, an existing variable's bound, the objective, a reported expression, or an evaluatedwherereads. Each mask is evaluated on the same data and must hold at exactly those coordinates. 26 models × 12 draws = 312 cases. The data is sparse, so most masks hold on a strict subset: for example 10 of 20 coordinates for a flag in a case, and 16 of 20 for a shifted flag.The oracle caught one real bug during the work: a read through
at()dropped the dimensions of the row beyond the node's frame, so the projection did not sum overt.Mutation table. Each guard was deleted by hand. The oracle suite was run, the file was restored through
git checkout --with__pycache__dropped on both sides, and the tree came back clean each time. Three deletions first survived; each got a model that catches it.wherea-masked-rowabsentparameter can be missings <= pbecames + y <= pboolname readsa-guard-reads-no-numberwhenis read where the case is evaluatedxwhereis read from the other sidewhereis evaluatedwhereis evaluatedOn the examples.
program.readscomputes for every spec inexamples/. The fullpypsa.yamlgets 188 masks in 0.33 s, one run, not measured on an idle machine.Gates.
pixi run lintandpixi run testpass (3178 passed, 1 skipped).tests/test_reading_page.pyruns the new example onreading.md.docs-buildstops only on the strict warning forpathlib.Path, because the container's proxy blocksdocs.python.org.compile-texwas not run.Not verified. The oracle does not cover a partition (
within=), aDualin a reported expression, or given parameters. The masks pass a partition through unchanged, so their correctness there depends onWindowPredicateandJoinmeaning in an engine what they mean on aTranslate.Not done
WindowPredicateyet. specsolve and its linopy backend need the rename, the wider window, and to evaluateprogram.readsat attach. That is their own PRs.piecewise:is not counted, as forfootprint. Ask the program ofspec.expand('piecewise'). specsolve already requires the expansion.assumptions:is not in a mask.∃.🤖 Generated with Claude Code
https://claude.ai/code/session_01AvtZGymCbY9swLnR8JwHng
Generated by Claude Code