Skip to content

feat(language): a piecewise gate reads its binary through a relation, as a link walks one - #784

Open
FBumann wants to merge 6 commits into
claude/adoring-galileo-mu42irfrom
claude/elegant-wozniak-m1axih
Open

FBumann wants to merge 6 commits into
claude/adoring-galileo-mu42irfrom
claude/elegant-wozniak-m1axih

Conversation

@FBumann

@FBumann FBumann commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Prompt: "Discuss hs request with me" … "Do all 3" … "782 merged" … "Should I start it now, stacked on #630, and watch #630's CI in the meantime?" — "Yes"

Note

The following content was generated by AI.

Stacked on #630. activity: may read its binary through a relation, so a unit-commitment status over its own dimension gates the curves it maps to. fluxopt no longer needs a re-indexed copy of running, or the constraint that ties the copy to it.

What this changes

activity: { variable: running, by: pw_status_of, over: status_entity, into: converter }

The expansion writes two convexity rows:

curve_convexity:
  where: at(running, by=pw_status_of, over=status_entity, into=converter)
  expression: sum(curve_lam, over=bp) == (at(running, by=pw_status_of, over=status_entity, into=converter))
curve_convexity_ungated:
  where: NOT (at(running, by=pw_status_of, over=status_entity, into=converter))
  expression: sum(curve_lam, over=bp) == 1
  • A converter with no row in the relation is ungated. A walked gate always writes the second row, since which coordinates the relation maps is data.
  • absence: zero pins a curve off where the relation has a row. The rows then read where: pw_status_of and NOT pw_status_of.
  • The walk is held to the rules of at. The gate lands on dims of dims:, and a gate landing elsewhere is refused on the block's line. A bare name stays a gate on dims:, as before.
  • The typesetter prints the read. The factor is running_{pw_status_of(c),t} where it exists, and 1 otherwise.

Why

This is the first ask from the fluxopt port, accepted on #630 as a follow-up. running[status_entity, time, period] is the one binary that startup costs and up/down times are written against. The curve is per converter, and #630 admits only a gate over dims:.

Where it lives, and what it reuses
Part Change
spec._Walk by, over, into, walks and the together-or-not-at-all check, moved off PiecewiseLink. PiecewiseLink and the new PiecewiseActivity both take it
spec.PiecewiseActivity the gate: a bare name, or a mapping with variable: and a walk. It serialises back to the form written, as a link does. The schema admits both forms
program.Gate variable, the typed read, and exists, where a curve reads the gate. It replaces PiecewiseDeclaration.activity: str
piecewise.gate_text one spelling of the read and its where, for the expansion rows and for lowering
piecewise.resolve_gate resolves the read as an expression at, so the walk meets the same refusals an expression does
piecewise._gate_fits a walked gate lands inside dims:
validation._piecewise_references the stray-dims check applies to a gate on dims: only, and its message now names the walk as a rewrite
typesetter _gate prints gate.read under gate.exists. The reserved names read gate.exists, not leaves_ungated of a variable

A gate on dims: prints and expands as before: the golden output and every generated page regenerate with no diff.

Tests, gates, and what was not run

Seven cases in tests/test_piecewise.py, on fluxopt's unit-commitment shape (COMMITTED):

  • the two convexity rows and their wheres;
  • the file and the expansion both load back as written;
  • absence: zero reads the relation alone;
  • the LaTeX line prints the read and the otherwise arm;
  • three refusals: a gate landing on period, which dims: lacks; a walk missing over and into; a by: that names no relation.

Each guard has a test that fails without it:

  • Without _gate_fits, the gate landing on period loads, and the test fails with DID NOT RAISE LanguageError.
  • Without the walked-gate skip in _piecewise_references, the use case is refused: activity 'running' carries ['status_entity'], which dims ['converter', 'snapshot'] does not.
Gate Result
pytest -q -n auto 2703 passed, 2 skipped
ruff check, ruff format --check (0.16.1) clean
pyrefly check (1.2.0) 0 errors
prettier --check on the reference page, typos clean
schema, golden output, generated pages schema regenerated; everything else regenerates with no diff
the reference page's YAML fence python -m mathspec check loads it
docs-build, compile-tex not run: the proxy refuses docs.python.org, and there is no tectonic

These ran in a uv venv (Python 3.12) with the pinned tool versions, since pixi cannot be installed here.

Deliberately not done

🤖 Generated with Claude Code

https://claude.ai/code/session_015VUkQfE5dT7mEGoTQXSxN3


Generated by Claude Code

… into=, as an expression does

The where grammar read one name or arithmetic after a kwarg's `=`, so
`at(p, by=r, over=a, into=[b, c])` failed to parse in a where string
while the expression grammar took the list. The bracketed name list is
now one grammar element both parsers use.

Closes #781.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VUkQfE5dT7mEGoTQXSxN3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VUkQfE5dT7mEGoTQXSxN3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VUkQfE5dT7mEGoTQXSxN3
… as a link walks one

`activity:` takes a mapping with variable, by, over and into, and the
weights sum to the binary read through the relation, as `at` reads it.
A curve whose coordinate has no row in the relation is ungated; under
`absence: zero` only a missing row ungates it. The walk is held to the
rules of `at`, and a gate landing outside `dims:` is refused.

The walk keys and their check move onto a base both the link and the
gate take. The program carries the gate as a `Gate`: its variable, the
read, and where a curve reads it, which the expansion, the typesetter
and the reserved names all read.

Reference page section: 8 sentences, median 16 words, none over 25.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VUkQfE5dT7mEGoTQXSxN3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VUkQfE5dT7mEGoTQXSxN3
@FBumann
FBumann added this pull request to stack #785 September 29, 2026 09:29
Takes #788 through #630.

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

This branch has not been deployed

No deployments
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