Skip to content

feat(program): the axis a join opens names the relation column it stands for - #665

Closed
FBumann wants to merge 9 commits into
claude/serene-goldberg-m8xwru-2from
claude/serene-goldberg-m8xwru-3
Closed

FBumann wants to merge 9 commits into
claude/serene-goldberg-m8xwru-2from
claude/serene-goldberg-m8xwru-3

Conversation

@FBumann

@FBumann FBumann commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Prompt: Let's do this as 2 stacked PRs. One introducing the new syntax, but keeping the dot notation internally, and then the one adding the axis class […] Can we simplify it with a stacked PR?

Note

The following content was generated by AI.

Axis(dimension, column) replaces the dotted axis names in Sum.over and JoinColumns.axes. A join's axis names its Column(relation, name). A dimension's own axis has no column. Frames stay sets of dimension names. This PR includes #799.

What this changes

  • Column(relation, name) and Axis(dimension, column=None) are public in mathspec.program. str() gives the user spelling: generator, or zone_of[generator] for a join's axis.
  • Sum.over is tuple[Axis, ...]. sum(p, over=g) stores (Axis('g'),). sum(p, over=g, by=R[c]) stores the join's axes. A dim that no column of the relation is over gets its own Sum of dimension axes around that.
  • JoinColumns.axes is tuple[Axis, ...]. Each axis runs over the dimension of the column the join drops, and stands for that column. A map into its own dimension gives the join axis Axis('snapshot', Column('rep_of', 'snapshot')), which is not equal to Axis('snapshot'). The type keeps them apart. Before, a naming trick did it.
  • Separability reads axis.column to tell a grouping from a plain sum. The dictionary that mapped an axis string back to its dimension is removed.
  • The dimension check is unchanged for callers. dims_of still returns a set of dimension names. It reads a sum over a join as one rule, so a join's axes never reach a frame (chore(program): a frame stays a set of dimension names, and only the sum over a join reads the axes it opens #799).

Why

Agreed in the discussion on #662. With the dotted name, a join axis was kept apart from a dimension by a character, and every consumer had to know that. With Axis, the type does it: pyrefly checks it, and a consumer reads axis.dimension or axis.column. It does not branch on the kind of axis and does not parse strings.

The axes are needed only between a join and the sum that closes it. The resolver is the only code that builds a sum over a join, and it always sums over exactly join.axes. So frames do not carry axes (#799). That removed about 40% of this PR's diff, and one class of silent error: a string tested for membership in a set of axes.

Method, guards, coverage moved, gates, not done

How this PR was made

The first version changed frames to frozenset[Axis] as well. #799, stacked on this PR, changed them back to dimension names and was squash-merged into this branch as 8aed330. 3dfafa6 changes the changelog line to this title. The branch is not rebased and not force-pushed.

src/ and tests/, against #664's head files lines
before #799 13 +183 / −107
now 9 +106 / −61

Guards and the tests that fail without them

Each guard was undone in turn, the suite was run, and the tree was restored. The first two were taken before #799, and #799 did not change their lines.

guard caught by
a join's axis is not the dimension's own axis 5 tests, e.g. test_dim_inference[a-map-into-its-own-dimension-keeps-the-frame]
separability reads a join axis as a grouping test_a_grouping_that_sums_the_axis_away_couples_it

#799 added two asserts in dimensions.py:

  • A sum over a join closes exactly join.columns.axes.
  • A join that opens axes is read only by the sum that closes them.

The resolver makes both true, so no test fails when either one is deleted. No probe builds such a node by hand. The three Axis(d) in frame guards of the first version are removed together with the lines they guarded.

Coverage that moved

Gates, on 3dfafa6

Pixi is not reachable here. The gates ran from a uv venv on Python 3.12, with the pinned ruff and pyrefly.

gate result
pytest -q -n auto 2557 passed, 53 skipped
ruff check, ruff format --check . clean
pyrefly check on src/ 4 errors, all about missing yaml stubs in the venv
generators, docs-build, compile-tex, the git hooks not run here. #799 changes no generated file, no docs and no printed output. CI runs docs-build and compile-tex.

Not done

🤖 Generated with Claude Code

https://claude.ai/code/session_01CPwrjirBZhVS4CcndN6f4H

…e relation column it stands for

Axis(dimension, column) is a position in a frame: the dimension whose
labels it runs over, and the Column(relation, name) a join opened it
for, or None for a dimension's own axis. Sum.over and JoinColumns.axes
hold axes, and dims_of returns a frame of them. A declaration keeps
its dims as strings, and frame_of is where the two meet.

The dotted name a join axis had is gone, and so is the side table
separability kept to map an axis back to its dimension.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PHNF149r8B4tGmGUKmAdUd
@FBumann
FBumann added this pull request to stack #652 September 23, 2026 22:47
@FBumann
FBumann removed this pull request from stack #652 September 24, 2026 08:35
@FBumann
FBumann added this pull request to stack #670 September 24, 2026 09:44
…664

One conflict: piecewise.py imports this branch's Axis beside
VariableBlock and VariableDeclaration, which main's leaves_ungated reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GpnFcQDuvt2bExGngb6MYk
@FBumann FBumann added the area: relations relations and dimensions: the relation design label Sep 24, 2026
…wru-3, through the updated #664

#633 retired fan_in, quotients and divisor_parameters. This branch's Axis
forms of their tests go with them, and the divisor tests assert through
parameters_of over an Axis.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012JFQDAjuZ3sCndQ4cD77Gc
… into sync/665

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8Y8AoPsJCyMsURHvgzm7h
…664

Main's given-term, declared-frame and composition checks compare frames of axes. Adds the changelog line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YUK4sePJqJfxJJZLhdvZnS
@read-the-docs-community

read-the-docs-community Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

…wru-3, with #662 and #664 as one changelog line

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MKjqJocyxdRYvx7xin6NCM
FBumann and others added 2 commits September 30, 2026 17:28
…sum over a join reads the axes it opens (#799)

The axes a join opens are closed by the sum over that join, which the
resolver always builds with exactly `join.axes`. So `dims_of` reads a sum
over a join as one rule, and no frame holds a join's axis. `Axis` and
`Column` stay in `Sum.over` and `JoinColumns.axes`, where consumers read
them. `frame_of`, `_names`, `_dimensions` and each `Axis(d) in frame`
membership test go.


Claude-Session: https://claude.ai/code/session_01CPwrjirBZhVS4CcndN6f4H

Co-authored-by: Claude <noreply@anthropic.com>
…s dimension names

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPwrjirBZhVS4CcndN6f4H
@FBumann FBumann changed the title feat(program): a frame holds axes, and the axis a join opens names the relation column it stands for feat(program): the axis a join opens names the relation column it stands for Sep 30, 2026
@FBumann FBumann mentioned this pull request Oct 1, 2026
FBumann pushed a commit that referenced this pull request Oct 2, 2026
…nds for (#665)

Axis(dimension, column) replaces the dotted axis names in Sum.over and
JoinColumns.axes. A join's axis names its Column(relation, name), a
dimension's own axis has no column, and frames stay sets of dimension
names. Folded into #664, so its changelog line is #664's.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVJQzL5G4boHcJDwrhgfag
@FBumann FBumann closed this Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: relations relations and dimensions: the relation design

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants