Skip to content

chore(engine): a relation's table is read through one module, as the join and the grouping a walk names - #1660

Merged
FBumann merged 5 commits into
claude/mathspec-relations-issues-tsf1fafrom
chore/relation-seams
Sep 16, 2026
Merged

FBumann merged 5 commits into
claude/mathspec-relations-issues-tsf1fafrom
chore/relation-seams

Conversation

@FBumann

@FBumann FBumann commented Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

Prompt: "Please review everything about relations in lpspec. Do a stacked pr refactoring the code to make it more readable and have better seams and types" — and, on the first cut: "I fell like this lpspec pr adds a lot of new names which are things we already have conceptually", which took Ends and keyed back out; "Axis is a separability concept I think", which took the word out; and "Do Order", which named the type by the language's word.

Note

The following content was generated by AI.

Stacked on #1659. No consumer can tell the difference: the same queries, read through one module and two types instead of a dozen loose functions, in the language's own words — a walk's consumed, joined and produced dimensions, and the order a shift counts along.

What the review found. After #1659, the relational lane spelled the same three facts several times over:

  • a Walk's roles became columns in four places — compiler._walked, compiler.partitioned, predicates.join_relation and reindex._group_columns;
  • the walk join was written twice — _remap_fragment and _pulled_back_presences.pulled both keyed on consumed, joined and carried-produced dims and landed the rest under their names;
  • a partition's group key was recomputed in three places, and reindex._edge rebuilt the ranked table a second time per shift; reindex._Walk had come to hold a program.Walk as a field, under the same name.
What moved
  • relational/engines/polars/relations.py (new) holds the role-to-column translation once:
    • mapping and walk_join — the table a group or a pullback joins against, and the one join that trades the consumed dims for the produced ones (keyed on consumed, joined, and a produced dim the operand already carries) in a single select, which is what a self-map needs. _remap_fragment is two lines and the pullback's presence step one.
    • Grouping — the walked dimension ranked inside its groups, with key, keys, placed() and column_of(); replaces compiler.partitioned, reindex._grouped and reindex._group_columns.
    • joined_dims — the one property a node lacks until feat(program): a grouped sum and a pullback say which dimensions their walks join on energy-models/mathspec#488 releases; the pin bump deletes it. consumed_dims, produced_dims and landing are gone from the compiler.
    • GROUP_RANK, GROUP_SIZE, group_column and landing live here, out of fragments.py and compiler.py.
  • reindex.py: _Walk is _Order — one dimension's own order, or each group's own, as an operator counts along it: the positions and the two keyed sides of the remap — built once per operator and holding its Grouping; the acyclic edge is _Edge, one type with keys, coordinates(), filled() and vacated_of(), in place of _edge, _filled_edge, _vacated, edge_keys and offset_dims, and the named offset's table is read once. The prose says dimension where it said axis.
  • predicates.py reads a grouped position through Grouping; the relation read keeps its body.
  • compiler.py keeps the expression walk only.
Verified

ruff check · ruff format --check · pyrefly check · pytest -q -n 4 on the head: 3772 passed, the same 10 failures as the base (gurobi, xpress not installed here). No new guard, so no mutation table.

Departed from one default: the pass is not fewer lines. Six files, +417 −332 against the base, and the surplus is the docstrings on the two types; what it ends with is fewer concepts and one home per fact.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DT1r52zPp9BdnrzFdp1xoY

…ends, the grouping and the edge a walk names

The role-to-column translation of a Walk was spelled in four places, the
walk join in two, and a partition's group key in three. relations.py now
holds it once: Ends (the dims a node's walks consume, join on and produce),
mapping and walk_join (the one join a group or a pullback makes), Grouping
(the walked dimension ranked inside its groups), and keyed (a relation read
at its key by a where). reindex builds its axis once per operator and
holds the acyclic edge as a type of its own, in place of five functions
that each rebuilt the ranked table.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DT1r52zPp9BdnrzFdp1xoY
@codspeed

codspeed Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 24 untouched benchmarks
⏩ 58 skipped benchmarks1


Comparing chore/relation-seams (8c0e2cc) with claude/mathspec-relations-issues-tsf1fa (8c64e8c)

Open in CodSpeed

Footnotes

  1. 58 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

…alks

Ends duplicated what a node already says as over, into and joined, and
keyed named one caller's body; both go. What stays new is the join a group
or a pullback trades its dimensions through, and the grouping a partition
ranks inside.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DT1r52zPp9BdnrzFdp1xoY
@FBumann FBumann changed the title chore(engine): a relation's table is read through one module, as the ends, the grouping and the edge a walk names chore(engine): a relation's table is read through one module, as the join and the grouping a walk names Sep 16, 2026
… an axis

An axis is what separability asks about upstream; this is the dimension
table ranked, axis-wide or within each group, with the two keyed sides of
the remap.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DT1r52zPp9BdnrzFdp1xoY
…e's word

One dimension's own order, or each group's own, as an operator counts
along it: the positions, and the two keyed sides of the remap.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DT1r52zPp9BdnrzFdp1xoY
@FBumann
FBumann added this pull request to stack #1661 September 16, 2026 07:13
@FBumann
FBumann merged commit ef08322 into main Sep 16, 2026
12 of 13 checks passed
@FBumann
FBumann deleted the chore/relation-seams branch September 16, 2026 07:13
FBumann added a commit that referenced this pull request Sep 16, 2026
…lk joins on (#1663)

> **Prompt:** "488 merged and released. Bump mathspec version!"

> [!NOTE]
> The following content was generated by AI.

The pin follows math-spec 0.0.0-alpha.92, the release carrying
energy-models/mathspec#488: `GroupSum.joined` and `At.joined`. The two
copies of that answer here — `relations.joined_dims` on the relational
lane and `builder._joined_dims` on the eager one — are deleted, and both
lanes read it off the node. The follow-up #1659 and #1660 named.

<details><summary>What moved</summary>

- `pyproject.toml` and `uv.lock`: the pin, `v0.0.0-alpha.89` to
`v0.0.0-alpha.92`.
- `relations.py`: `landed` and `walk_join` take the node rather than its
walks, and read `node.joined`; `joined_dims` is gone.
- `compiler.py`: `_remap_fragment` takes the node; `_empty_groups` and
`_pulled_back_presences` read `g.joined` and `a.joined`.
- `linopy/builder.py`: the grouped sum passes `node.joined`;
`_joined_dims` is gone.

Five files, +26 −40.

</details>

<details><summary>Verified</summary>

`ruff check` · `ruff format --check` · `pyrefly check` · `pytest -q -n
4` on 309422e, with alpha.92 installed: 3772 passed, the same 10
failures as `main` (`gurobi`, `xpress` not installed here). Run in a
`uv` venv with the `linopy` extra rather than pixi.

**Not run:** `docs-build` and `test-floors`, no pixi environment here.
The parity job checks out the corpus at the pinned tag, so it runs
against alpha.92 in CI.

</details>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01DT1r52zPp9BdnrzFdp1xoY

---
_Generated by [Claude
Code](https://claude.ai/code/session_01DT1r52zPp9BdnrzFdp1xoY)_

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants