Skip to content

docs(design): document traits 2 refactor - #184

Draft
Roger-luo wants to merge 10 commits into
mainfrom
codex/traits-2-design-refactor
Draft

docs(design): document traits 2 refactor#184
Roger-luo wants to merge 10 commits into
mainfrom
codex/traits-2-design-refactor

Conversation

@Roger-luo

Copy link
Copy Markdown
Collaborator

Summary

  • redesign the proposed ppvm-traits-2 configuration boundary around direct type composition
  • define the generalized Word and separate Indexable hashing capability
  • preserve existing names such as ACMap, PauliWord, GeneralizedTableauSum, and EntryStore where their concepts remain unchanged
  • document concrete Pauli, lossy, phased, and cached-hash representations
  • propose a contiguous, column-major tableau representation with temporary transposition

Motivation

The current Config bundle mixes coefficient, word representation, hashing, map, and truncation choices. These design notes separate algorithm policy and storage from quantum-data representations while retaining established terminology unless an abstraction materially changes.

Impact

This PR adds design documentation only. It does not modify the current implementation or public API. The documents are intended to guide a later ppvm-traits-2 prototype and further design iteration.

Validation

  • git diff --cached --check
  • repository pre-commit hooks, including trailing whitespace, EOF, merge-conflict, private-key, and Hawkeye license checks

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://QuEraComputing.github.io/ppvm/pr-preview/pr-184/

Built to branch gh-pages at 2026-07-31 01:22 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

david-pl and others added 9 commits July 23, 2026 12:35
Extend the traits-2 proposal with a batch-first map contract that frames
the branch-staging merge as a hash join with aggregation, specified for
memory prefetching, SIMD, multi-threading, and GPU offload.

- Reframe the merge as build/probe-with-group-by and pin its cost model
  to memory latency at out-of-cache sizes.
- Cite Chen, Ailamaki, Gibbons & Mowry, "Improving Hash Join Performance
  through Prefetching" (ICDE 2004; TODS 2007), as the origin of group and
  software-pipelined prefetching and the cost model the contract serves.
- Add a structure-of-arrays batch contract: KeyBatch/TermBatch split into
  key, precomputed-hash, and coefficient columns; ACMapBatch upsert/probe
  over columns; a KeyColumn capability behind a new Columnar trait that is
  kept separate from the minimal Indexable (no cache/column type leaks
  through the hashing contract).
- Separate term production from insertion so gates emit a TermBatch rather
  than mutating through a per-element closure, enabling prefetch, SIMD,
  radix partitioning, and device offload.
- Document the concrete packed-Pauli/LossyPauliWord plane layout,
  alignment/padding, hash_into agreement, and gather semantics in
  word-data-structures.md, per the docs' layout-lives-with-the-
  representation rule.
- Update the compatibility table, non-goals, and open questions (plane
  granularity and per-target alignment remain open).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ndle

Address the Step 2 (type-composition) design gaps surfaced in review:

- Split the Coefficient god-trait: cutoff moves to Policy (value type now
  exposes only magnitude()), sin_cos moves to a separate Angle<C> domain so
  the rotation angle is no longer welded to the coefficient. RotationOne
  gains a defaulted angle parameter (A = C).
- Give SumStorage associated Word/Coeff types, collapsing the engine to
  Sum<S, P> and removing the phantom word parameter.
- Rename OperatorSum -> Sum; reintroduce ergonomics as a defaulted
  type-alias family (PauliSum/LossyPauliSum/FermionSum) that is explicitly
  not load-bearing at the trait level, so it avoids Config's two sins.
- Drop the Policy: Copy bound (contradicted the proposal's own non-goal).
- Document the n_sites width invariant with a debug_assert.
- Update the LossChannel specialization to select on S::Word.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve the Step 3 (Word / Site) design gaps from review:

- Make Word a read-only inspection trait (n_sites, get, weight, iter);
  remove set. This stops Word from overclaiming as the propagation
  interface (the real kernels are sub-site) and lets ordered algebras
  implement it honestly, where in-place positional set is ill-defined.
- Relocate mutation to Pauli-specific traits, decomposed along the
  Clifford = Sp(2n,2) join phase structure:
  - PauliBits: mutable single-vector X/Z access; hosts rotation kernels.
  - SymplecticColumns (role-independent-semantics bit-plane column ops)
    and PhaseTrack (role-dependent Z4-word vs Z2+g-tableau phase),
    kept separate to mirror Sp and its extension.
  - Clifford: blanket impl over the two primitives — one audited copy of
    the symplectic sign logic, shared by PhasedPauliWord and Tableau.
  - StabilizerFrame: role-exclusive tableau ops (measure, row_multiply).
- PauliSum consumes the one-row action pointwise (Clifford re-keys the
  map); word-side Mul / key identity stay inherent.
- Update the compatibility table, trait admission rule, batch-contract
  note, and the word/tableau data-structure docs to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve the Step 4 (Indexable / hashing) design gaps from review:

- Replace `type BuildHasher` with `Indexable::key_hash() -> u64`, the one
  load-bearing value the whole design depends on. Scalar `Hash`, the map's
  hasher, and batch `hash_into` are all defined in terms of it, collapsing
  three implicit contracts into one explicit method with three equations.
- Drop the per-key `BuildHasher`: the direct-digest model leaves the map no
  hasher to choose, so the internal digest algorithm becomes a private
  representation parameter of the concrete key (the `H` in PauliWord<A, H>),
  on the same footing as the storage array `A`.
- Retain HashFinalize but relocate it to a private ppvm-pauli-word helper;
  the per-algorithm/per-width avalanche fold runs inside key_hash(), and the
  public contract is only "key_hash() is well distributed" (property-tested).
- Add the pass-through storage contract: provided SumStorage backends
  consume key_hash() directly via an IdentityBuildHasher, guaranteed rather
  than left to the user, so no second hash re-correlates the folded bits.
- Document that SumStorage's `&Word` (not `&mut Word`) closure signatures are
  the structural half of the key-mutation-invariant enforcement.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Recast the storage/ACMap layer from the algebraic structure (Step 5):

- Replace the flat ACMapBase/Iter/AddAssign/Insert/Retain/Consume split
  with layers graded by algebraic strength over the free module C[W]:
  L0 Support, L1 Accumulate+Reduce, L2 Scale, L3 Pair, L4 Multiply.
  The minimum is L0+L1 (a finitely-supported W->C you accumulate and
  reduce). Each layer names a distinct algebraic property and consumer.
- Add L4 Multiply (the group-algebra product / convolution), bounded on
  ComplexCoefficient since the Pauli product injects phase.
- Truncation leaves the algebra: it breaks module exactness, so it is a
  non-algebraic Retain capability consumed by Policy, not part of ACMap.
- Make reduce() first-class: exact-zero pruning is canonicalization to
  finite-support form, run only at finalize after all coefficients for a
  word are accumulated (never inline) — correctness by construction, and
  a bulk stream-compaction each backend specializes.
- Design columnar from day one: two backends (HashStore AoS, ColumnStore
  SoA) over the same graded traits, with four no-AoS-leak rules so the
  SIMD/GPU store is expressible immediately. Each layer maps to a bulk
  data-parallel primitive (scale=vector map, reduce=prefix-sum compaction,
  accumulate=hash-join build, pair=probe, multiply=outer product).
- Unify map_add/map_insert into one SumStorage::apply(producer): Clifford,
  rotation, and multiply are TermProducers feeding accumulate; the Vec
  staging leak is gone, and the Step 6 batch types become the columnar
  spelling of Accumulate/Pair.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve Step 7 (tableau) and unify the tableau side with Pauli propagation:

- Relax the graded algebra from C[Word] to C[Key] over any Indexable key.
  The Word bound leaked Pauli-specificity; C[K] is the free module over any
  index set. Pauli propagation re-adds Word/PauliBits on its own methods.
  This makes PauliSum and the stabilizer mixture the SAME Sum engine:
  PauliSum = Sum<HashStore<PauliWord, C>>, TableauMixture =
  Sum<HashStore<Tableau, C>> (replacing GeneralizedTableauSum). A gate is a
  TermProducer that re-keys each key via the key's own Clifford impl. This is
  the "smallest common factor" the prior iteration deferred.
- Add TermProducer/RekeyProducer with the zero-overhead guarantees the hot
  loop needs: generic (never dyn), #[inline] produce, pre-sized sink, so the
  gate body folds into the accumulate loop with no per-term allocation.
- Add KeyProduct (the L4 key-product capability) and Multiply's bound.
- Commit to the inverse tableau: measurement becomes a row read in the
  gate's column-major orientation, eliminating per-measurement transpose;
  the inverse is a private representation of Tableau (traits unchanged).
- Hoist the RNG out of Tableau: inject at measure(&mut, q, rng); clone is
  pure data, so mixture branches get independent streams (fixes the
  correlated-sampling bug); Tableau is trivially Send + Sync.
- StabilizerFrame holds frame primitives (pivot/row_multiply/canonicalize);
  Measure is the public algorithm built on them.
- State that bulk hash/compare requires canonical orientation, enforced by
  the transposition guard's &mut borrow; budget the non-square transpose's
  square-block padding + scratch rather than assuming it away.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Correct two over-designs surfaced while checking the generalized tableau:

- No SumStorage trait and no owned workspace. The reusable auxiliary map was
  an artifact of the old mutate-while-iterate model; the producer/TermBatch
  model reads the map through & and writes into a separate TermSink, so no aux
  map is needed. Bundling one into storage would double every sum's allocation
  and clone it on every mixture branch. `Sum<S: Accumulate, P>` now owns only
  storage + policy + n_sites; `apply` is a thin method producing into a
  transient batch, with reuse an opt-in driver optimization (PauliSum), never
  forced on the frequently-cloned tableau side.
- Backends are the containers themselves. The graded algebra traits are impl'd
  directly on Vec<(K,C)> (small support) and HashMap<K,C,IdentityBuildHasher>
  (large) — no VecStore/HashStore newtypes, only type aliases for readability.
  ColumnStore is the one backend that must be a new struct (SoA planes).
- Add the generalized tableau as the third instantiation: C[Bitstring]. Its
  current SparseVector trait is already a second hand-rolled copy of the
  algebra (add_or_insert = accumulate, retain = Retain, iter = Support); the
  amplitude vector is Sum<Vec<(Bitstring, Complex)>, CoefficientThreshold>,
  Clifford gates hit the frame, non-Clifford gates branch the amplitudes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve the key_hash/Indexable over-requirement surfaced by the Vec backend:

- The universal key bound (Support::Key, all containers) is now just Eq + Clone
  — what a linear-scan Vec needs to find and store a key. It never hashes.
- Indexable (the digest-bearing hash key) is unchanged but required only where
  hashing happens: the HashMap and ColumnStore Accumulate impls, and Columnar.
- Consequence: a Bitstring used only in a Vec-backed GeneralizedTableau is
  Eq + Clone and provides no key_hash() it would never use; PauliWord /
  LossyPauliWord / Tableau, used in hash backends, remain Indexable.
- Relax KeyProduct to Eq + Clone (the multiply's backend adds Indexable).

No new trait; Indexable keeps its meaning, the digest requirement just lands
where the cost is paid.

Co-Authored-By: Claude Opus 4.8 (1M context) <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