Skip to content

chore(main): release 0.0.1-alpha.55 - #437

Merged
fluxopt-release[bot] merged 1 commit into
mainfrom
release-please--branches--main--components--lpspec
Jul 31, 2026
Merged

fluxopt-release[bot] merged 1 commit into
mainfrom
release-please--branches--main--components--lpspec

Conversation

@fluxopt-release

Copy link
Copy Markdown
Contributor

🤖 I have created a release beep boop

0.0.1-alpha.55 (2026-07-31)

Performance

  • engine: cols is positional, and the order is produced rather than repaired (#433) (22bb9c7)

This PR was generated with Release Please. See documentation.

@fluxopt-release fluxopt-release Bot added the autorelease: pending Release-please owns this one — a release PR awaiting merge label Jul 31, 2026
@fluxopt-release
fluxopt-release Bot enabled auto-merge (squash) July 31, 2026 18:10
@read-the-docs-community

Copy link
Copy Markdown

Documentation build overview

📚 lpspec | 🛠️ Build #33857475 | 📁 Comparing 50e56ca against latest (22bb9c7)

  🔍 Preview build  

1 file changed
± changelog/index.html

@fluxopt-release
fluxopt-release Bot merged commit 782f76b into main Jul 31, 2026
4 checks passed
@fluxopt-release

Copy link
Copy Markdown
Contributor Author

🤖 Created releases:

🌻

@fluxopt-release fluxopt-release Bot added autorelease: tagged Release-please owns this one — released, tag pushed and removed autorelease: pending Release-please owns this one — a release PR awaiting merge labels Jul 31, 2026
FBumann added a commit that referenced this pull request Jul 31, 2026
FBumann pushed a commit that referenced this pull request Sep 10, 2026
…ges keep the math they print

The pin was the stack head as it stood, which forked before math-spec's
alpha.85 — so regenerating every gallery page with that typesetter reverted
#446's GitHub math delimiters across 62 files, and rung 10's description to the
prose #442 had rewritten. Neither was this change's to make.

The stack's own #437 branch had already been rebased onto main; only #443's one
commit was left behind it. Rebased onto that head, and the pin follows it.

The rebase brings #444 with it: a grouped position carries the walk it counts
within rather than naming its pieces, so the predicate reads `partition`
straight and rebuilds no walk of its own — and the linopy lane now holds a
grouped position to the same one-dimension rule as every other walk.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SrEY2ZhFD1YXamRn4FzpMa
FBumann added a commit that referenced this pull request Sep 15, 2026
…h-spec 0.0.0-alpha.89 does (#1646)

> **Prompt:** Let's update lpspec to the latest mathspec version

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

The pin moves to `v0.0.0-alpha.89`, whose one feature
([#437](energy-models/mathspec#437)) replaced
`lookups:` with `relations:` and renamed two operator keywords. Both
lanes read the new plan; every model, example, rung and page is written
in it.

Either lane still builds the single-valued map the old lookup was — two
columns, one of them the key. A wider table is refused at `lowered`, so
`check`, `build` and an archive refuse the same file.

<details><summary>What the language change is</summary>

A relation is a table with one column per dimension it relates, and
`key:` names the columns a row is identified by. The declaration fixes
no direction: the operator says which column it consumes and which it
produces. `shift(over=, …)` became `shift(along=, …)`, and
`sum_back(over=, within=)` became `sum_back(along=, window=)`.

| before | after |
| --- | --- |
| `lookups: {gen_bus: {over: generator, into: bus}}` | `relations:
{gen_bus: {columns: [generator, bus], key: generator}}` |
| `shift(soc, over=snapshot, offset=1)` | `shift(soc, along=snapshot,
offset=1)` |
| `sum_back(started, over=hour, within=min_up)` | `sum_back(started,
along=hour, window=min_up)` |

</details>

<details><summary>What moved in the source</summary>

- **`relations.py`** is new: the one reading of a declared relation —
`refusal`, and the accessors that are well-defined once a wider table
has been turned away. Documented in
[ARCHITECTURE](docs/about/architecture.md)'s module table.
- **Both lanes read the plan's new shape**: `Walk` on `GroupSum`, `At`,
`Translate.partition`, `Window.partition` and
`DimensionPositionNode.partition`; `RelationComparisonNode`,
`RelationPairComparisonNode` and `RelationDefinedNode` in `where`.
- **`sources.py`** reads a relation's table under its declared *roles*
and hands on the `(key dim, relation)` frame both lanes already read, so
a self-map supplies cleanly and no reader downstream changed shape. The
checks are the ones it ran before: keys are labels, values are labels,
one row per key, no null.
- **The engine reads the plan directly**, not `lpspec.relations`: its
subpackage may import only `lpspec.errors` and `math_spec.program` (hard
rule 2), and `Walk` already carries everything it asked
`DimensionDeclaration.targets` for. `_Walk.of`, `_named_amount` and
`_grouped_into` take the walk instead of a partition name, which drops a
`compiler.program` lookup from `_grouped_into`.
- **The PyPSA parity runner** reads a relation's `key:` where it read
`lookup.over`, and the projection keeps the dimensions a relation's
`columns:` name. Second commit; it is why `differential/` is a separate
`chore`.

</details>

<details><summary>What I deliberately did not do</summary>

**The new reach is not implemented.** A bare relation, a key of several
columns, a walk with a column joined on, and `within=` on a partition
are all refused rather than built. Following a pin is not the PR that
grows two lanes a new join shape, and the refusal names the rewrite its
shape asks for:

```
relation 'gen_bus' declares no key, and this package builds the single-valued map: one row
per key, so a walk reaches one label rather than a set of them. Declare key: on it — one of
['generator', 'bus'], whichever holds each label once — or carry the membership as a bool
parameter over ['generator', 'bus'] and select on it with a where string.
```

```
relation 'zone_of' has 3 columns keyed by 2, and this package builds the single-valued map:
two columns, one of them the key, so a walk trades one dimension for one other and joins on
nothing. Split it into one relation per pair — 'zone_of' over ['generator', 'period', 'bus']
becomes a map per value column, each keyed by the same single column — or carry the wider
table as a parameter over its dimensions and select on it with a where string.
```

Worth its own issue, since the relational lane is close:
`_remap_fragment` already trades tuples of dims, and a joined column is
that join on more keys.

</details>

<details><summary>Coverage that moved</summary>

`test_a_hand_built_node_whose_tuples_disagree_is_refused` asserted a
guard the new IR makes unreachable — `GroupSum` derives `coordinate`,
`over` and `into` from `walks`, so the two tuples cannot disagree. The
`zip(strict=True)` it certified is still there and still reachable, by
the route that now exists: a walk onto two columns at once. It is
`test_a_hand_built_walk_onto_two_columns_is_refused` in the same file,
with the same purpose in its docstring.

`tests/test_sum_by_lookups.py` is `tests/test_sum_by_relations.py`.
`tests/test_assertions.py`'s ratchet falls 208 → 207.

</details>

<details><summary>The new guard, deleted and re-run</summary>

`relations.refusal`'s loop deleted, whole suite re-run:

```
FAILED tests/test_label_coords.py::test_a_relation_wider_than_a_map_is_refused_with_the_rewrite_named[a-bare-relation-is-a-set-per-label-and-not-a-map]
FAILED tests/test_label_coords.py::test_a_relation_wider_than_a_map_is_refused_with_the_rewrite_named[a-key-of-two-columns-joins-on-one-the-walk-does-not-trade]
2 failed, 3964 passed, 251 skipped, 1 xfailed
```

| guard | caught by |
| --- | --- |
| `relations.refusal`'s shape loop | the two cases above, and nothing
else |

Its complement,
`test_the_map_the_wider_shapes_are_rewritten_to_is_the_one_that_loads`,
keeps the pair from passing by refusing everything.

</details>

<details><summary>PyPSA parity</summary>

Every rung matches, and `references.json` and the written tables come
back **byte-identical** — so no objective, dual or structural comparison
moved. The rung projections changed in layout only, because the first
commit hand-edited files the harness generates; they and the ladder
pages that embed them are regenerated by the harness and by
`tools.ladder`.

```
rung_01_transport: MATCH · 45 rows, 16 columns · duals on 45 rows · 10 equal · 0 region
rung_02_storage: MATCH · 103 rows, 48 columns · duals on 103 rows, 2 negated · 27 equal · 0 region
rung_03_expansion: MATCH · 2 of 59 names differ · duals on 184 rows, 4 negated · 57 equal · 3 region
rung_04_ramps: MATCH · 64 rows, 20 columns · duals on 64 rows, 2 negated · 12 equal · 0 region
rung_05_global_constraints: MATCH · 2 of 24 names differ · duals on 102 rows, 2 negated · 23 equal · 2 region
rung_06_kvl: MATCH · 2 of 21 names differ · duals on 123 rows · 19 equal · 3 region
rung_07_commitment: MATCH · 116 rows, 44 columns · no duals — mixed-integer · 20 equal · 0 region · 3 recorded
rung_08_modular_big_m: MATCH · 191 rows, 80 columns · no duals — mixed-integer · 36 equal · 0 region
rung_09_multilink: MATCH · 92 rows, 32 columns · duals on 92 rows · 8 equal · 0 region
rung_10_quadratic_costs: MATCH · 60 rows, 24 columns · duals on 60 rows · 8 equal · 0 region
rung_11_ac_dc_meshed: MATCH · 2 of 19 names differ · duals on 468 rows · 17 equal · 1 region · 1 recorded
rung_12_linearized_uc: MATCH · 128 rows, 44 columns · duals on 128 rows, 1 negated, 2 names differ · 24 equal · 0 region · 3 recorded
rung_13_losses: MATCH · 2 of 23 names differ · duals on 150 rows · 22 equal · 0 region · 1 recorded
rung_14_stochastic: MATCH · 3 of 18 names differ · duals on 87 rows, 1 names differ · 17 equal · 0 region · 2 recorded
rung_15_multi_period: MATCH · 80 rows, 31 columns · duals on 80 rows · 14 equal · 0 region
rung_16_link_delay: MATCH · 52 rows, 20 columns · duals on 52 rows · 8 equal · 0 region
every rung matches PyPSA as deep as the engines allow, and says how deep that is
```

Run a second time on the regenerated tree with no further change, which
is what the job's `git diff --exit-code -- differential/pypsa` step
asks.

</details>

<details><summary>What was verified, and what was not</summary>

Every gate the required check runs, plus the docs build, the bench
harness and the parity runner, on `14de16e`:

```
ruff check .                              All checks passed!
ruff format --check .                     321 files already formatted
pyrefly check                             0 errors (both configs)
pytest tests -n 4                         3967 passed, 251 skipped, 1 xfailed
mkdocs build --strict                     Documentation built in 10.01 seconds
pytest bench/test_harness.py              145 passed, 10 skipped
differential/pypsa/parity.py <corpus>     every rung matches
```

Generated output was regenerated by its own generator and the diff read:
`tools.gallery_math` (47 pages), `tools.ladder` (the ladder page and 16
rungs), the parity runner (`references.json`, `rungs/`, `tables/`), `uv
lock`. The YAML fences the gallery pages carry byte for byte, and the
two PyPSA reference `build()` fences, were refreshed from their files.

**Not run, and why.** `pixi` is not installed in this environment, so
the gates ran in a `uv` venv on the pinned `ruff==0.16.1` and
`pyrefly==1.2.0` rather than through `pixi run check`. On an unpinned
`pyrefly 1.3.1` six errors appear, all in `api.py`, `frames.py` and
`assembly.py`, none of which this diff touches. No benchmark was taken:
nothing here claims a number, and the machine was not idle — CodSpeed
reports the PR does not alter performance.

</details>

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

https://claude.ai/code/session_01RuEnfVdQZKP2fxRWrgJRH1

---------

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

autorelease: tagged Release-please owns this one — released, tag pushed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants