Skip to content

Translation symmetry: TranslationGroup + orbit-representative Pauli-sum merging#180

Open
AlexSchuckert wants to merge 2 commits into
mainfrom
split/1-translation-symmetry
Open

Translation symmetry: TranslationGroup + orbit-representative Pauli-sum merging#180
AlexSchuckert wants to merge 2 commits into
mainfrom
split/1-translation-symmetry

Conversation

@AlexSchuckert

Copy link
Copy Markdown
Collaborator

PR 1 of 4 splitting #178 into reviewable chunks (this → CTPP core → symmetry-merged evolution → ledgers). Full development history: branch continuous-time-pauli-propagation.

Adds ppvm_pauli_sum::symmetry (pure Rust, no new dependencies):

  • TranslationGroup: finite abelian permutation groups — 1D chains, 2D/3D tori, multi-leg ladders, or arbitrary generator lists — with lex-min orbit canonicalization, shift counters, and momentum-sector characters χ_k(g).
  • k = 0 merging: canonicalize_pauli_sum (Vec-pair form used by the upcoming adaptive evolution) and symmetry_merge_pauli_sum (PauliSum form). Preserves all G-invariant expectation values for G-commuting dynamics (Theorem 1 of Teng, Chang, Rudolph & Holmes, arXiv:2512.12094).
  • k ≠ 0: canonicalize_pauli_sum_complex (character-weighted projection with 1/|G| normalization) and check_momentum_sector to validate inputs before projecting (silent projection discards physics).

Tests: canonicalization/orbit properties on chains, tori, ladders; merge correctness; momentum-eigenstate round trips; a Trotter end-to-end check that per-step merging matches merge-at-end in the dt → 0 limit.

🤖 Generated with Claude Code

…erging

TranslationGroup (finite abelian permutation groups: 1D chains, 2D/3D
tori, multi-leg ladders, arbitrary generators) with lex-min orbit
canonicalization, shift counters, and momentum-sector characters.
Pauli-sum merging in both sectors: canonicalize_pauli_sum /
symmetry_merge_pauli_sum (k=0, real coefficients) and
canonicalize_pauli_sum_complex (k≠0, character-weighted projection,
1/|G| normalization), plus check_momentum_sector to validate an input
before projecting. Following Teng, Chang, Rudolph & Holmes
(arXiv:2512.12094).

Split 1/4 of the CTPP work (see PR body for the stack); full
development history on branch continuous-time-pauli-propagation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 16, 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-180/

Built to branch gh-pages at 2026-07-16 12:35 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Rust-only symmetry layer to ppvm-pauli-sum for translation-invariant (and momentum-resolved) Pauli-sum canonicalization/merging, intended to support upcoming continuous-time Pauli propagation work (#178 split).

Changes:

  • Introduces ppvm_pauli_sum::symmetry with TranslationGroup plus orbit-canonicalization utilities.
  • Adds real (k=0) and complex/momentum-sector merging/canonicalization helpers, along with momentum-sector validation.
  • Exposes the new module from ppvm-pauli-sum’s public API and adds a comprehensive test suite for canonicalization/merge behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
crates/ppvm-pauli-sum/src/symmetry.rs New symmetry module: translation group model, orbit canonicalization, (momentum) merging, and tests.
crates/ppvm-pauli-sum/src/lib.rs Exposes the new symmetry module publicly.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +125 to +129
Self {
n_qubits,
perms,
orders,
}
Comment on lines +220 to +223
/// Total group order: `Π orders[g]`.
pub fn order(&self) -> usize {
self.orders.iter().map(|&o| o as usize).product()
}
Comment on lines +495 to +503
let inv_g: f64 = 1.0 / (group.order() as f64);
let mut merged: FxHashMap<PauliWord<A, S, R>, Complex<f64>> =
FxHashMap::with_capacity_and_hasher(basis.len(), Default::default());
for (w, &c) in basis.iter().zip(coeffs.iter()) {
let (rep, cnt) = group.canonicalize_with_shift(w);
let chi = group.character(k_modes, &cnt);
let contrib = inv_g * chi * c;
*merged.entry(rep).or_insert(Complex::new(0.0, 0.0)) += contrib;
}
Comment on lines +475 to +477
/// For the `k_modes = [0, 0, …]` (trivial) sector this reduces to plain
/// [`canonicalize_pauli_sum`] (real coefficients work, but on complex
/// input the result is complex with vanishing imaginary part).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 16, 2026 12:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 9 comments.

Comment on lines +130 to +133
pub fn chain_1d(n: usize) -> Self {
let perm: Vec<u32> = (0..n).map(|q| ((q + 1) % n) as u32).collect();
Self::from_generators(n, vec![perm], vec![n as u32])
}
Comment on lines +137 to +138
pub fn torus_2d(lx: usize, ly: usize) -> Self {
let n = lx * ly;
Comment on lines +156 to +157
pub fn torus_3d(lx: usize, ly: usize, lz: usize) -> Self {
let n = lx * ly * lz;
Comment on lines +194 to +195
pub fn ladder(l: usize, n_legs: usize) -> Self {
let n = l * n_legs;
Comment on lines +297 to +300
for &o in &self.orders {
counters.push((rem as u32) % o);
rem /= o as usize;
}
Comment on lines +347 to +350
for &o in &self.orders {
counter.push((rem as u32) % o);
rem /= o as usize;
}
Comment on lines +407 to +409
for (g, &o) in self.orders.iter().enumerate() {
let c = (rem as u32) % o;
rem /= o as usize;
Comment on lines +472 to +474
/// For the `k_modes = [0, 0, …]` (trivial) sector this reduces to plain
/// [`canonicalize_pauli_sum`] (real coefficients work, but on complex
/// input the result is complex with vanishing imaginary part).
Comment on lines +101 to +125
for (g, perm) in perms.iter().enumerate() {
assert_eq!(
perm.len(),
n_qubits,
"generator {g} permutation has length {} != n_qubits {n_qubits}",
perm.len()
);
let mut seen = vec![false; n_qubits];
for &p in perm {
assert!(
(p as usize) < n_qubits,
"generator {g} maps to out-of-range position {p}"
);
assert!(
!seen[p as usize],
"generator {g} is not a permutation (duplicate target {p})"
);
seen[p as usize] = true;
}
}
Self {
n_qubits,
perms,
orders,
}
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