Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changelog

## Unreleased
## 0.7.0 (2026-05-01)

### Added

Expand All @@ -9,11 +9,16 @@
- **New demo `demos/larql_polysemantic_12/demo.py`** — runs 12 independent single-query Qiskit simulations at 1024 shots each and recovers the analytic polysemy scores (≈ 75% on in-feature concepts, ≈ 33.3% cross-talk floor on out-of-feature) within Monte-Carlo tolerance.
- **Resource estimation** — new `## resources` section declares which static cost numbers the compiler should report (`gate_count`, `depth`, `cx_count`, `t_count`, `logical_qubits`); `## invariants` accepts the same five identifiers as bound LHS so a budget overrun becomes a verifier error (`RESOURCE_BOUND_EXCEEDED`) before any hardware run. Two new compiler entry points — `q_orca.estimate_resources(machine)` and `q_orca.compile_with_resources(machine)` — share one transpile pass per machine via `id(machine)` memoization. Verifier stage 4c (`resource_bounds`) is gated on the presence of resource invariants and can be skipped via `--skip-resource-bounds` or `VerifyOptions(skip_resource_bounds=True)`. Examples `bell-entangler`, `qaoa-maxcut`, and `vqe-heisenberg` pin their gate budgets. See `docs/language/resources.md`.
- **Structured-overlap polysemantic example, demo, and Gram helper** — companion to `larql-polysemantic-12` that swaps the flat Hadamard dictionary for a *clustered* concept geometry on a compact 3-qubit register. `examples/larql-polysemantic-clusters.q.orca.md` encodes 12 concepts in 3 clusters of 4 (capitals, fruits, vehicles) via a multi-angle parametric `prepare_concept(a, b, c)` + `query_concept(a, b, c)` pair (intra-cluster overlap 0.72 uniform, inter-cluster < 0.10 — a block-structured Gram matrix). `demos/larql_polysemantic_clusters/demo.py` prints an ASCII Gram heatmap and recovers the three-tier polysemy column `1.0 / 0.72 / ≲ 0.09` empirically. New optional helper `q_orca.compute_concept_gram(machine, concept_action_label="query_concept")` returns the analytic N×N overlap matrix for any machine that follows the product-state preparation convention; raises `ConceptGramConfigurationError` when the convention is violated.
- **Hierarchical (MPS bond-2) polysemantic example, demo, and Gram helper** — rung-1 sibling of `larql-polysemantic-clusters` that lifts the 12-concept dictionary from product states to **bond-dimension-2 matrix product states** via a CNOT-staircase preparation `Ry(q0,α); CNOT(q0,q1); Ry(q1,β); CNOT(q1,q2); Ry(q2,γ)`. `examples/larql-polysemantic-hierarchical.q.orca.md` organizes 12 concepts as a two-level hierarchy (3 super-groups × 2 sub-clusters × 2 concepts) on the same 3-qubit register and produces a four-tier Gram matrix — self 1.000 / sub-cluster-mate 0.882 / super-group-sibling [0.47, 0.54] / cross-group [0.12, 0.25] — one tier richer than rung 0. `demos/larql_polysemantic_hierarchical/demo.py` prints a 4-tier ASCII Gram heatmap, recovers the polysemy column from 12 Qiskit circuits, and prints a side-by-side rung-0 vs rung-1 comparison. New optional helper `q_orca.compute_concept_gram_mps(machine, concept_action_label="query_concept", bond_dim=2)` enumerates the parametric call sites of a CNOT-staircase action, builds each concept statevector, and returns the analytic N×N overlap matrix; raises `MpsGramConfigurationError` (missing action, wrong signature, non-staircase effect, no call sites, or `bond_dim != 2`).
- **Hierarchical (MPS bond-2) polysemantic example, demo, and Gram helper** — rung-1 sibling of `larql-polysemantic-clusters` that lifts the 12-concept dictionary from product states to **bond-dimension-2 matrix product states** via a cross-coupled CNOT-staircase preparation `Ry(q0, a); CNOT(q0, q1); Ry(q1, a + b); CNOT(q1, q2); Ry(q2, b + c)` (linear-combination angles cross-couple adjacent qubits, producing a Gram that does not factorize as `|⟨φᵢ|φⱼ⟩|² = ∏ₖ |⟨φᵢᵏ|φⱼᵏ⟩|²`). `examples/larql-polysemantic-hierarchical.q.orca.md` organizes 12 concepts as a two-level hierarchy (3 super-groups × 2 sub-clusters × 2 concepts) on the same 3-qubit register and produces a four-tier Gram matrix — self 1.000 / sub-cluster-mate 0.882 / super-group-sibling {0.335, 0.593, 0.753} / cross-group [0.000, 0.178] — one tier richer than rung 0. `demos/larql_polysemantic_hierarchical/demo.py` prints a 4-tier ASCII Gram heatmap, recovers the polysemy column from 12 Qiskit circuits, and prints a side-by-side rung-0 vs rung-1 comparison. New optional helper `q_orca.compute_concept_gram_mps(machine, concept_action_label="query_concept", bond_dim=2)` enumerates the parametric call sites of a CNOT-staircase action (single-bound-param **or** linear-combination angles), builds each concept statevector, and returns the analytic N×N overlap matrix; raises `MpsGramConfigurationError` (missing action, wrong signature, non-staircase effect, unrecognized angle expression, no call sites, or `bond_dim != 2`).

### Changed

- Backwards-compatible: no behavior change for existing machines. Both halves of `extend-gate-set-and-parametric-actions` (multi-controlled gates, parametric actions) are additive.
- **Generalized angle parser** — `q_orca/angle.py::evaluate_angle` now accepts top-level linear combinations (`a + b`, `2*pi + gamma`, `-a - b`) in addition to the prior literal / single-identifier shapes. The Ry-segment matcher in `concept_gram_mps.py` parses the angle as a linear combination via `ast.parse` and substitutes call-site argument values per coefficient/parameter pair. The single-bound-param shape remains a degenerate one-term linear combination, so all archived examples parse unchanged.

### Fixed

- **MPS encoding factorization bug** (`fix-mps-encoding-non-factorizing`, [PR #48](../../pull/48)) — the original rung-1 staircase preparation `Ry(q0,α); CNOT(q0,q1); Ry(q1,β); CNOT(q1,q2); Ry(q2,γ)` was advertised as producing non-factorized concept overlap "by virtue of being entangled". This is mathematically false: while the staircase output has Schmidt rank > 1, its same-angle Gram matrix factorizes exactly as the product-state Gram (`|⟨φᵢ|φⱼ⟩|² = ∏ₖ cos²((θᵢᵏ − θⱼᵏ)/2)`), giving only 3 visible tiers, not 4. Replaced with the cross-coupled-by-sum encoding above, where the Ry on each non-leading qubit takes a linear combination of *two* bound parameters; this breaks the per-qubit factorization and yields the four-tier hierarchy the example claims. Adds new error kind `MpsGramConfigurationError("unrecognized_angle_expression")` for non-linear angle expressions in the Ry-segment matcher; appends post-mortem entry to `add-mps-concept-encoding/design.md`; cross-links new tech-debt §5.7 (verifier blind spot — Gram factorization vs. encoding entanglement) for a future verifier rule that would catch this class of bug at verify time.

---

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1087,6 +1087,7 @@ Each of these entered main with a full OpenSpec proposal → implementation →
- ✅ **Completeness-check hardening** — measurement-bearing transitions are detected by action effect, not just event name *(`harden-completeness-detection`)*
- ✅ **Bell-pair compiler fixture** — per-backend regression tests against the canonical two-qubit circuit *(`bell-pair-example`)*
- ✅ **LARQL polysemantic clusters** — block-structured 12-concept polysemy on a 3-qubit register, matching the empirical signature reported by sparse-autoencoder studies of real transformer FFNs *(`add-polysemantic-clusters`)*
- ✅ **MPS concept encoding (rung 1)** — hierarchical 12-concept polysemy on a bond-2 matrix product state via a cross-coupled CNOT-staircase preparation `Ry(q0,a); CNOT(q0,q1); Ry(q1,a+b); CNOT(q1,q2); Ry(q2,b+c)`; produces a four-tier Gram matrix (self / sub-cluster-mate / super-group-sibling / cross-group). *(`add-mps-concept-encoding` + `fix-mps-encoding-non-factorizing`, [PR #46](../../pull/46) / [PR #48](../../pull/48))*

See each folder under [`openspec/changes/archive/`](openspec/changes/archive/) for the proposal, design, and scenarios that shipped.

Expand All @@ -1096,7 +1097,6 @@ Proposals with an OpenSpec folder and agreed scope, awaiting code.

- 🧾 **Cross-machine composition** — `[invoke: Child(args) shots=N]` state-level delegation with static arg/return type-checking and a `## returns` section. Mermaid renders composed machines; QASM/Qiskit refuse until a runtime lands. *([`add-parameterized-invoke`](openspec/changes/add-parameterized-invoke/))*
- 🧾 **Per-state runtime assertions** — `[assert: classical(qs[0]); entangled(qs[0], qs[1])]` annotations backed by statistical sampling on Stage 4b, with a `## assertion policy` section for shots/confidence/on-failure. *([`add-runtime-state-assertions`](openspec/changes/add-runtime-state-assertions/))*
- 🧾 **MPS concept encoding** — hierarchical polysemantic concept registers encoded as matrix-product states, extending the LARQL polysemantic family beyond the dense-statevector regime. *([`add-mps-concept-encoding`](openspec/changes/add-mps-concept-encoding/))*

### Longer-term — research / not yet specced

Expand Down
10 changes: 7 additions & 3 deletions docs/research/polysemantic-encoding-beyond-product-states.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,9 +371,13 @@ confirmed in KB — see `mcp__q-orca-kb__batch_index`):

- **`add-polysemantic-clusters`** (shipped, PR #31): the rung-0
example this note builds on. Status: merged to main.
- **`add-mps-concept-encoding`** (drafted alongside this note): the
rung-1 proposal. Adds example + demo + `compute_concept_gram_mps`
helper. No grammar changes.
- **`add-mps-concept-encoding`** (shipped, [PR #46](../../pull/46)) +
**`fix-mps-encoding-non-factorizing`** (shipped, [PR #48](../../pull/48),
merged 2026-05-01): the rung-1 proposal and its same-cycle correction
to the cross-coupled-by-sum encoding (per the caveat box above and
the post-mortem in `add-mps-concept-encoding/design.md`). Adds
example + demo + `compute_concept_gram_mps` helper supporting
linear-combination angles. No grammar changes.
- **`concept-encoding-efficiency.md`** (companion research note):
language-extension questions A/B/C. Question A becomes
load-bearing for rung 2; this note does not require it.
Expand Down
127 changes: 127 additions & 0 deletions openspec/changes/tech-debt-backlog/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,133 @@ remaining findings.
immediate fix.
(Source: 2026-05-01 `fix-mps-encoding-non-factorizing` post-mortem.)

The §5.8–§5.15 entries below come from a post-merge self-review of
PR #48 (`fix-mps-encoding-non-factorizing` implementation). They are
not example-library QA findings but the numbering continues §5 to
keep the 2026-05-01 cluster contiguous.

- [ ] 5.8 **`evaluate_angle` regression on scientific-notation
literals.** Severity: HIGH. `q_orca/angle.py:127-164`. The new
top-level `+`/`-` splitter (`_split_linear_combination`) walks
character-by-character and treats any non-operator preceding char
as a license to split. The `e` in `1e-5` is non-operator, so the
splitter splits `1e-5` into `[1e, -5]` and `evaluate_angle("1e-5")`
raises `Unrecognized angle expression '1e'` where it returned
`1e-5` before this commit. Reproducer:
`evaluate_angle("1e-5*a", {"a": 0.1})` and
`evaluate_angle("1e-5 + a", ...)` both fail. Fix: in the look-back
at `angle.py:154`, also exclude `+`/`-` that follow `e`/`E`
preceded by a digit (treat as exponent sign). No example in the
repo uses scientific notation today, so this is latent but real;
add a regression test alongside the fix.
(Source: 2026-05-01 PR #48 self-review.)

- [ ] 5.9 **Inverse-form `Ry(qs[k], -(a + b))` does not parse end-
to-end.** Severity: HIGH. The language spec at
`openspec/changes/fix-mps-encoding-non-factorizing/specs/language/spec.md:64-66`
explicitly says `Ry(qs[k], -(a + b))` is "equivalently" valid
alongside `-a - b`. Two parser-side defects block it:
1. `_ROTATION_GATE_ANGLE_RE` in
`q_orca/parser/markdown_parser.py:1170-1175` uses `[^)]+` to
capture the angle, which truncates `-(a+b)` to `-(a+b` at
the inner `)`.
2. Even if captured, `_split_linear_combination` does not recurse
into parens, so `evaluate_angle("-(a + b)", ctx)` raises.
Reproducer: parsing an effect with `Ry(qs[2], -(b + c))` gives
parser error `unrecognized angle expression '-(b + c'`. The
helper's own AST walk handles `-(a+b)` fine, so the asymmetry is
purely on the parser. Fix: balance parens in the angle regex, and
have the splitter return `None` (delegate to a paren-stripping
retry) when the whole text is a parenthesized expression. The
shipped example sidesteps this by writing `-a - b`/`-b - c` — but
the spec promises both forms.
(Source: 2026-05-01 PR #48 self-review.)

- [ ] 5.10 **Test gap — bare-literal sad path uncovered.**
Severity: MEDIUM. The compiler spec scenario at
`openspec/changes/fix-mps-encoding-non-factorizing/specs/compiler/spec.md:90-101`
enumerates four trigger forms for `unrecognized_angle_expression`:
`a * b`, `sin(a)`, `a^2`, and **`Ry(qs[1], 2.5)`** (bare numeric
literal with no parameter reference). `tests/test_compiler.py`
(`TestComputeConceptGramMps`) covers `a * b` and `sin(a)` only —
bare-literal and `a^2` have no test. Manually verified that
`Ry(qs[1], 2.5)` does in fact raise with kind
`unrecognized_angle_expression`, but this is an untested behavior
the spec explicitly calls out. Fix: add
`test_unrecognized_angle_expression_bare_literal_raises` and
`test_unrecognized_angle_expression_power_raises` to
`TestComputeConceptGramMps`.
(Source: 2026-05-01 PR #48 self-review.)

- [ ] 5.11 **Test gap — inverse-form linear combination uncovered
by focused unit test.** Severity: MEDIUM. All happy-path tests
for cross-coupled angles in `tests/test_compiler.py` use the prep
form (`prepare_concept`, `Ry(qs[0], a); ...; Ry(qs[2], b + c)`).
No unit test exercises the inverse form (`Ry(qs[2], -b - c); ...;
Ry(qs[0], -a)`) with a linear-combination angle. The helper's
`is_inverse=True` branch with cross-coupled angles is only
covered transitively via
`test_examples.py::test_larql_polysemantic_hierarchical_pipeline`.
Fix: add a focused unit test in `TestComputeConceptGramMps` that
constructs an inverse-form effect with `-a - b` and asserts the
Gram matches the inverse of a prep-form Gram on the same angles.
(Source: 2026-05-01 PR #48 self-review.)

- [ ] 5.12 **`_parse_linear_combination` rejects `2*-a`.**
Severity: LOW. `q_orca/compiler/concept_gram_mps.py:147-165` only
matches `Constant * Name` and `Name * Constant` for `Mult`; the
AST shape `Constant * UnaryOp(USub, Name)` (i.e., `2*-a`) is
rejected as "product of two non-constant terms". This is a
syntactically valid linear combination. Unlikely in practice —
users would write `-2*a` — but the matching is incomplete. Fix:
when one operand is `Constant` and the other is a `UnaryOp` over
a `Name`, multiply the constant into the sign and recurse, or
permit `walk(other_operand, sign * float(const))`.
(Source: 2026-05-01 PR #48 self-review.)

- [ ] 5.13 **Misleading `_parse_linear_combination` error message
for all-constant products.** Severity: LOW. Same site as §5.12:
`2*3` is rejected with `"product of two non-constant terms"` even
though both terms *are* constants. The intent is to reject
expressions with no parameter reference, and a separate branch
already handles bare `Constant`. Fix: tighten the `Mult` fall-
through message to say "no parameter reference" when both sides
are constants, or short-circuit the case earlier with a clearer
diagnostic.
(Source: 2026-05-01 PR #48 self-review.)

- [ ] 5.14 **Polysemy column tabulates `0.000` for entries that
compute as ~`1e-4`.** Severity: LOW.
`examples/larql-polysemantic-hierarchical.q.orca.md:138-139`
claims `mango (6) → 0.000` and `papaya (7) → 0.000`. Actual
computed values are `~0.00012` (rounding to `0.000` at 3-decimal
display; ASCII heatmap correctly shows blank). Not numerically
wrong, but a reader running `compute_concept_gram_mps` and seeing
`9.5e-5` may read the rounded display as misleadingly "exactly
zero". Fix optional — add a "≈" prefix on near-zero rows or
document the rounding convention in the surrounding paragraph.
(Source: 2026-05-01 PR #48 self-review.)

- [ ] 5.15 **Documentation/contract polish on
`MpsGramConfigurationError` and the compiler spec example.**
Severity: NIT. Two minor doc-vs-code mismatches:
- `q_orca/compiler/concept_gram_mps.py:66-78`: the
`MpsGramConfigurationError` class docstring lists only
`unrecognized_angle_expression` as a possible `kind`. All
other configuration errors in the module raise without a
`kind` (correct), but the docstring could note that
explicitly so callers know to fall through to message
inspection for non-angle errors.
- `openspec/changes/fix-mps-encoding-non-factorizing/specs/compiler/spec.md:107-109`
says "the second and third `qc.ry(` calls receiving the
*evaluated* linear combination (e.g., `qc.ry(a_value +
b_value, 1)` rather than a single bound parameter)". The
actual Qiskit compiler emits a *fully-evaluated* float
(e.g., `qc.ry(-1.594, 1)`), not the symbolic
`a_value + b_value` form. Both satisfy the contract, but
the spec example is misleading.
(Source: 2026-05-01 PR #48 self-review.)

## 6. How to use this file

- [x] 6.1 **Meta**: when an item is fixed, leave the task checked
Expand Down
Loading
Loading