-
Notifications
You must be signed in to change notification settings - Fork 6
PPVM-traits-2 Patch #219
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
JonhasA
wants to merge
18
commits into
main
Choose a base branch
from
trait-2/ppvm-traits-2
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
PPVM-traits-2 Patch #219
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
2a7f074
initial commit of breaking up roger's PR on rebuilding the trait syst…
JonhasA fab45e2
added ppvm-traits-2 to workspace and forgot to factor back in fermion…
JonhasA ebb25e0
ran cargo fmt
JonhasA 5e2e779
adding missing header to fermion_factor
JonhasA 17d676e
condensed comments to maximum 3 lines or so
JonhasA 0c358a7
removed clippy expections, and uses toggle_x, toggle_z masks instead …
JonhasA 24dbeed
removed orphaned traits and structs that don't use it. Such traits an…
JonhasA 140ae62
modified exponent(self)-> u8 in algebra.rs to just return self as u8
JonhasA 26c2d12
merged CliffordExtension into Clifford
JonhasA a14a33a
Merge branch 'main' into trait-2/ppvm-traits-2
JonhasA c26c14b
Added AddAssign and MulAssign bounds to coefficient to avoid cloning
JonhasA 5e066c7
moved RotationOne and RotationTwo to match Clifford/CliffordBatch org…
JonhasA db46e67
removed BlanketClifford as we force end users to define the clifford …
JonhasA cf2cbc3
seperated KeyColumn into two seperate traits. KeyColumn for reading, …
JonhasA bb21999
made pattern matching explicit for k & 3 in algebra.rs
JonhasA 6341d2c
removed add_assign_ref as *self += rhs is more natural
JonhasA 2bb8f63
another round of responses
JonhasA fbb95dd
other round of cleanup given feedback
JonhasA File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # SPDX-FileCopyrightText: 2026 The PPVM Authors | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| [package] | ||
| name = "ppvm-traits-2" | ||
| version = "0.1.0" | ||
| edition = "2024" | ||
|
|
||
| [dependencies] | ||
| num = "0.4" | ||
| rand = "0.10" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| // SPDX-FileCopyrightText: 2026 The PPVM Authors | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| //! Algebra capabilities: key products, coefficient conjugation, and imaginary units. | ||
| //! [`Phase`] carries the residual fourth root of unity emitted by a key product. | ||
|
|
||
| use crate::arithmetic::Coefficient; | ||
|
|
||
| /// A fourth root of unity `iᵏ`, with `k` modulo four. | ||
| /// [`KeyProduct::key_mul`] returns this residual phase for the coefficient to absorb. | ||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
| pub enum Phase { | ||
| /// `i⁰ = +1`. | ||
| Pos1, | ||
| /// `i¹ = +i`. | ||
| PosI, | ||
| /// `i² = −1`. | ||
| Neg1, | ||
| /// `i³ = −i`. | ||
| NegI, | ||
| } | ||
|
|
||
| impl Phase { | ||
| /// The exponent `k ∈ {0, 1, 2, 3}` such that this phase equals `iᵏ`. | ||
| #[inline] | ||
| pub fn exponent(self) -> u8 { | ||
| self as u8 | ||
| } | ||
|
|
||
| /// The phase `iᵏ` for exponent `k` (taken mod 4). | ||
| #[inline] | ||
| pub fn from_exponent(k: u8) -> Self { | ||
| match k & 3 { | ||
| 0 => Phase::Pos1, | ||
| 1 => Phase::PosI, | ||
| 2 => Phase::Neg1, | ||
| 3 => Phase::NegI, | ||
| _ => unreachable!("masking with 3 produces an exponent in 0..=3"), | ||
| } | ||
| } | ||
|
|
||
| /// The identity phase: `p.compose(Self::one()) == p`. | ||
| #[inline] | ||
| pub fn one() -> Self { | ||
| Phase::Pos1 | ||
| } | ||
|
|
||
| /// Compose phases by adding exponents modulo four: `iᵃ · iᵇ = i^{a+b}`. | ||
| /// Composition is commutative and can accumulate key-product phases before application. | ||
| #[inline] | ||
| pub fn compose(self, other: Self) -> Self { | ||
| Phase::from_exponent(self.exponent() + other.exponent()) | ||
| } | ||
|
|
||
| /// The group inverse `i^{-k} = i^{4-k}`, i.e. the phase `q` with | ||
| /// `self.compose(q) == Phase::one()`. | ||
| #[inline] | ||
| pub fn inverse(self) -> Self { | ||
| // 4 - k is exact for k ∈ {0,1,2,3}; the mod-4 reduction in | ||
| // `from_exponent` sends k = 0 back to 0. | ||
| Phase::from_exponent(4 - self.exponent()) | ||
| } | ||
|
|
||
| /// Apply `iᵏ` to a coefficient through [`ImaginaryUnit::mul_i_pow`]. | ||
| /// Overrides can preserve symbolic representations; complex multiplication by `i` | ||
| /// uses component swaps to preserve signed zeros and avoid non-finite contamination. | ||
| #[inline] | ||
| pub fn apply<C: ImaginaryUnit>(self, c: &C) -> C { | ||
| c.mul_i_pow(self.exponent()) | ||
| } | ||
| } | ||
|
|
||
| /// `iᵃ · iᵇ = i^{a+b}` — [`compose`](Phase::compose) as the `*` operator, so a | ||
| /// residual-phase accumulator reads `acc *= phase` / `acc = a * b`. | ||
| impl core::ops::Mul for Phase { | ||
| type Output = Phase; | ||
|
|
||
| #[inline] | ||
| fn mul(self, rhs: Phase) -> Phase { | ||
| self.compose(rhs) | ||
| } | ||
| } | ||
|
|
||
| impl core::ops::MulAssign for Phase { | ||
| #[inline] | ||
| fn mul_assign(&mut self, rhs: Phase) { | ||
| *self = self.compose(rhs); | ||
| } | ||
| } | ||
|
|
||
| /// A key product returning a key and residual phase `i^{β(u,v)}`. | ||
| /// Key multiplication must be associative, and phases must satisfy the cocycle law: | ||
| /// `β(u,v) + β(u·v,w) == β(v,w) + β(u,v·w)` modulo four. | ||
| pub trait KeyProduct: Eq + Clone { | ||
| /// Product of two keys, with the phase it produces (folded onto the coeff). | ||
| fn key_mul(&self, other: &Self) -> (Self, Phase); | ||
| } | ||
|
|
||
| /// A commutative coefficient ring with an imaginary unit. | ||
| /// Implementations must satisfy `Self::imaginary_unit() * Self::imaginary_unit() | ||
| /// == -Self::one()`, hence `i⁴ = 1`. | ||
| pub trait ImaginaryUnit: Coefficient + num::One { | ||
| /// The imaginary unit `i`; impls must satisfy | ||
| /// `Self::imaginary_unit() * Self::imaginary_unit() == -Self::one()`. | ||
| fn imaginary_unit() -> Self; | ||
|
|
||
| /// Multiply by `i`; the default uses ring multiplication. | ||
| /// Complex values override this with `(re, im) ↦ (-im, re)` to preserve signed | ||
| /// zeros and avoid contaminating both components when one is non-finite. | ||
| #[inline] | ||
| fn mul_i(&self) -> Self { | ||
| self.clone() * Self::imaginary_unit() | ||
| } | ||
|
|
||
| /// Multiply by `iᵏ`, with `k` modulo four; used by [`Phase::apply`]. | ||
| /// Overrides may fold phases into symbolic data, including when `k == 0`. | ||
| /// The result must denote `iᵏ · self`, with `mul_i_pow(1) == mul_i()`. | ||
| #[inline] | ||
| fn mul_i_pow(&self, k: u8) -> Self { | ||
| match k & 3 { | ||
| 0 => self.clone(), | ||
| 1 => self.mul_i(), | ||
| 2 => -(self.clone()), | ||
| 3 => -(self.mul_i()), | ||
| _ => unreachable!("masking with 3 produces an exponent in 0..=3"), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// A commutative ring involution used by [`crate::containers::Pair::hermitian_overlap`]. | ||
| /// Requires `conj(conj(a)) == a`, preservation of addition and multiplication, | ||
| /// and `conj(i) == -i` when the ring also implements [`ImaginaryUnit`]. | ||
| pub trait Conjugate: Coefficient { | ||
| /// The ring involution applied to this value. | ||
| fn conj(&self) -> Self; | ||
| } | ||
|
|
||
| impl ImaginaryUnit for num::Complex<f64> { | ||
| #[inline] | ||
| fn imaginary_unit() -> Self { | ||
| num::Complex::new(0.0, 1.0) | ||
| } | ||
|
|
||
| /// The old `ComplexCoefficient::mul_phase(1)` component swap, verbatim | ||
| /// (`crates/ppvm-traits/src/traits/coefficient.rs`): total on non-finite | ||
| /// components and sign-of-zero exact, unlike the generic `self * i`. | ||
| #[inline] | ||
| fn mul_i(&self) -> Self { | ||
| num::Complex::new(-self.im, self.re) | ||
| } | ||
| } | ||
|
|
||
| impl Conjugate for num::Complex<f64> { | ||
| #[inline] | ||
| fn conj(&self) -> Self { | ||
| num::Complex::conj(self) | ||
| } | ||
| } | ||
|
|
||
| impl Conjugate for f64 { | ||
| /// Conjugation is the identity on a real ring. | ||
| #[inline] | ||
| fn conj(&self) -> Self { | ||
| *self | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| // SPDX-FileCopyrightText: 2026 The PPVM Authors | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| use crate::arithmetic::Coefficient; | ||
|
|
||
| // A rotation angle that yields `(sin, cos)` already in coefficient domain `C`. | ||
| pub trait Angle<C: Coefficient> { | ||
| /// Return `(sin θ, cos θ)` in the coefficient domain `C`. | ||
| fn sin_cos(&self) -> (C, C); | ||
| } | ||
|
|
||
| impl Angle<f64> for f64 { | ||
| #[inline] | ||
| fn sin_cos(&self) -> (f64, f64) { | ||
| num::traits::Float::sin_cos(*self) | ||
| } | ||
| } | ||
|
|
||
| /// The complex-coefficient angle domain, i.e. the defaulted `A = C` case of | ||
| /// [`crate::gates::RotationOne`] at `C = Complex<f64>`. | ||
| impl Angle<num::Complex<f64>> for num::Complex<f64> { | ||
| #[inline] | ||
| fn sin_cos(&self) -> (num::Complex<f64>, num::Complex<f64>) { | ||
| let (s, c) = num::traits::Float::sin_cos(self.re); | ||
| (num::Complex::new(s, 0.0), num::Complex::new(c, 0.0)) | ||
| } | ||
| } | ||
|
|
||
| // A **real** angle driving a complex-coefficient sum. | ||
| impl Angle<num::Complex<f64>> for f64 { | ||
| #[inline] | ||
| fn sin_cos(&self) -> (num::Complex<f64>, num::Complex<f64>) { | ||
| let (s, c) = num::traits::Float::sin_cos(*self); | ||
| (num::Complex::new(s, 0.0), num::Complex::new(c, 0.0)) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| // SPDX-FileCopyrightText: 2026 The PPVM Authors | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub}; | ||
|
|
||
| pub trait Coefficient: | ||
| PartialEq | ||
| + Clone | ||
| + num::Zero | ||
| + Neg<Output = Self> | ||
| + Add<Self, Output = Self> | ||
| + Sub<Self, Output = Self> | ||
| + Mul<Self, Output = Self> | ||
| + AddAssign<Self> | ||
| + for<'a> AddAssign<&'a Self> | ||
| + MulAssign<Self> | ||
| + for<'a> MulAssign<&'a Self> | ||
| + std::iter::Sum | ||
| + Send | ||
| + Sync | ||
| { | ||
| /// Multiply by `sign ∈ {-1, +1}` (encoded as `i8`). | ||
| fn mul_sign(&self, sign: i8) -> Self; | ||
|
|
||
| /// Multiply this coefficient in place by `sign ∈ {-1, +1}`. | ||
| #[inline] | ||
| fn mul_sign_assign(&mut self, sign: i8) { | ||
| *self = self.mul_sign(sign) | ||
| } | ||
|
|
||
| /// Add this coefficient to itself. Numeric implementations may use their | ||
| /// native multiply-by-two operation; exact rings retain the additive default. | ||
| #[inline(always)] | ||
| fn doubled(&self) -> Self { | ||
| let mut result = self.clone(); | ||
| result += self; | ||
| result | ||
| } | ||
|
|
||
| /// Nonnegative magnitude. Exposes a property of the value for a `Policy` to | ||
| /// threshold; it does not itself decide any cutoff. Replaces the old | ||
| /// `Coefficient::cutoff`. | ||
| fn magnitude(&self) -> f64; | ||
| } | ||
|
|
||
| impl Coefficient for f64 { | ||
| #[inline] | ||
| fn mul_sign(&self, sign: i8) -> Self { | ||
| (sign as f64) * (*self) | ||
| } | ||
|
|
||
| #[inline(always)] | ||
| fn doubled(&self) -> Self { | ||
| *self * 2.0 | ||
| } | ||
|
|
||
| #[inline] | ||
| fn magnitude(&self) -> f64 { | ||
| self.abs() | ||
| } | ||
| } | ||
|
|
||
| impl Coefficient for num::Complex<f64> { | ||
| #[inline] | ||
| fn mul_sign(&self, sign: i8) -> Self { | ||
| (sign as f64) * (*self) | ||
| } | ||
|
|
||
| #[inline(always)] | ||
| fn doubled(&self) -> Self { | ||
| *self * 2.0 | ||
| } | ||
|
|
||
| #[inline] | ||
| fn magnitude(&self) -> f64 { | ||
| self.norm() | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| // SPDX-FileCopyrightText: 2026 The PPVM Authors | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| use crate::arithmetic::Coefficient; | ||
|
|
||
| /// A coefficient ring in which halving (`0.5·x`) is total and exact: the | ||
| /// capability the projective computational-basis measurement kernel needs to | ||
| /// apply the `(I ± Z)/2` projectors. | ||
| pub trait Halvable: Coefficient { | ||
| /// Divide by two. Impls must be exact: `x.half() + x.half() == x` for every | ||
| /// finite `x` (`f64::NAN` and the infinities are exempt — they are not | ||
| /// ring elements). | ||
| fn half(&self) -> Self; | ||
| } | ||
|
|
||
| impl Halvable for f64 { | ||
| #[inline] | ||
| fn half(&self) -> Self { | ||
| *self / 2.0 | ||
| } | ||
| } | ||
|
|
||
| impl Halvable for num::Complex<f64> { | ||
| #[inline] | ||
| fn half(&self) -> Self { | ||
| *self / 2.0 | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| // SPDX-FileCopyrightText: 2026 The PPVM Authors | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| //! Scalar arithmetic and rotation-angle capabilities. | ||
|
|
||
| mod angle; | ||
| mod coefficient; | ||
| mod halvable; | ||
|
|
||
| pub use angle::Angle; | ||
| pub use coefficient::Coefficient; | ||
| pub use halvable::Halvable; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm surprised that this holds up in the exact
x.half() + x.half() == xcondition.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, there is an edge case here: this doesn't hold for
f64::NAN, which is a perfectly validf64, butx.half() == xifxisNAN.I'm not saying this bothers me overly much, but as I said I'd also be happy not to go the full formal ring way anyway.