Skip to main content

ppvm_traits/traits/
coefficient.rs

1// SPDX-FileCopyrightText: 2026 The PPVM Authors
2// SPDX-License-Identifier: Apache-2.0
3
4use num::traits::Float;
5
6/// Numeric coefficient type usable inside a
7/// `PauliSum`.
8///
9/// `Coefficient` bundles every arithmetic operation a Pauli-propagation
10/// step needs — addition, multiplication, signed multiplication, half,
11/// sin/cos, and a `cutoff` predicate used by truncation strategies. The
12/// built-in `f64` impl covers the common case; `Complex<f64>` is
13/// available when phase tracking is required.
14pub trait Coefficient:
15    PartialEq
16    + Clone
17    + num::Zero
18    + From<f64>
19    + std::ops::Neg<Output = Self>
20    + std::ops::Add<f64, Output = Self>
21    + std::ops::Add<Self, Output = Self>
22    + std::ops::Sub<f64, Output = Self>
23    + std::ops::Sub<Self, Output = Self>
24    + std::ops::Mul<f64, Output = Self>
25    + std::ops::Mul<Self, Output = Self>
26    + std::ops::AddAssign<f64>
27    + std::ops::AddAssign<Self>
28    + std::ops::MulAssign<f64>
29    + std::ops::MulAssign<Self>
30    + std::iter::Sum
31    + Sync
32    + Send
33{
34    /// Multiply by `sign ∈ {-1, +1}` (encoded as `i8`).
35    fn mul_sign(&self, sign: i8) -> Self;
36    /// Divide by two.
37    fn half(&self) -> Self;
38    /// Return `(sin θ, cos θ)`.
39    fn sin_cos(&self) -> (Self, Self);
40
41    /// Determine whether this coefficient should be cutoff
42    /// Returns `true`, if the coefficient should be cut, and `false` else.
43    fn cutoff(&self, threshold: f64) -> bool;
44}
45
46impl Coefficient for f64 {
47    fn mul_sign(&self, sign: i8) -> Self {
48        (sign as f64) * (*self)
49    }
50
51    fn half(&self) -> Self {
52        *self / 2.0
53    }
54
55    fn sin_cos(&self) -> (Self, Self) {
56        Float::sin_cos(*self)
57    }
58
59    #[inline]
60    fn cutoff(&self, threshold: f64) -> bool {
61        self.abs() < threshold
62    }
63}
64
65/// A [`Coefficient`] extended with multiplication by a fourth-root-of-unity
66/// phase. Used by `PhasedPauliWord`.
67pub trait ComplexCoefficient: Coefficient {
68    /// multiply by phase encoded as:
69    ///
70    /// |  | sign | imag |
71    /// |--|------|------|
72    /// |+1|    0 |    0 |
73    /// |+i|    0 |    1 |
74    /// |-1|    1 |    0 |
75    /// |-i|    1 |    1 |
76    fn mul_phase(&self, phase: u8) -> Self;
77}
78
79impl Coefficient for num::complex::Complex<f64> {
80    #[inline]
81    fn cutoff(&self, threshold: f64) -> bool {
82        self.norm() < threshold
83    }
84
85    fn half(&self) -> Self {
86        *self / 2.0
87    }
88
89    fn mul_sign(&self, sign: i8) -> Self {
90        (sign as f64) * (*self)
91    }
92
93    fn sin_cos(&self) -> (Self, Self) {
94        let (s, c) = Float::sin_cos(self.re);
95        (
96            num::complex::Complex::new(s, 0.0),
97            num::complex::Complex::new(c, 0.0),
98        )
99    }
100}
101
102impl ComplexCoefficient for num::complex::Complex<f64> {
103    fn mul_phase(&self, phase: u8) -> Self {
104        match phase % 4 {
105            0 => *self,
106            1 => num::complex::Complex::new(-self.im, self.re),
107            2 => -*self,
108            3 => num::complex::Complex::new(self.im, -self.re),
109            _ => unreachable!(),
110        }
111    }
112}