paulistrings/truncation/
mod.rs

1//! [`TruncationPolicy<W>`] — composable per-term and per-layer term filters.
2//!
3//! The split between [`TruncationPolicy::keep_term`] (hot, per-output, must
4//! inline) and [`TruncationPolicy::finalize_layer`] (cold, once per layer,
5//! may be non-local) is performance-critical: `keep_term` runs millions of
6//! times per layer, `finalize_layer` runs once.
7//!
8//! Both methods have default no-op implementations, so a policy only needs
9//! to override the one it uses. Compose two policies with [`And`] (both must
10//! accept) or [`Or`] (either accepts).
11//!
12//! Built-ins: [`CoefficientThreshold`] drops terms below a magnitude;
13//! [`WeightCutoff`] drops terms above a Pauli weight; [`TopN`] keeps the `n`
14//! largest-magnitude terms via a layer-finalization partial sort.
15//!
16//! See design doc §7.
17//!
18//! # Examples
19//!
20//! ```
21//! use paulistrings::truncation::{And, CoefficientThreshold, WeightCutoff};
22//! use paulistrings::TruncationPolicy;
23//! use num_complex::Complex64;
24//!
25//! // Keep terms with |coeff| > 1e-9 AND weight ≤ 4.
26//! let policy = And(CoefficientThreshold(1e-9), WeightCutoff(4));
27//!
28//! // A weight-1 term with coeff 0.5: passes both.
29//! assert!(<_ as TruncationPolicy<1>>::keep_term(
30//!     &policy, &[1], &[0], Complex64::new(0.5, 0.0),
31//! ));
32//! ```
33
34#![allow(unused)]
35
36pub mod builtin;
37
38pub use builtin::{And, CoefficientThreshold, Or, TopN, WeightCutoff};
39
40use crate::pauli_sum::PauliSum;
41use num_complex::Complex64;
42
43/// A truncation strategy. Both methods have sensible defaults so users only
44/// implement the one they need.
45///
46/// # Implementing
47///
48/// Override [`keep_term`](Self::keep_term) for per-output decisions (runs
49/// inside the merge phase, must inline). Override
50/// [`finalize_layer`](Self::finalize_layer) for global decisions that
51/// depend on the whole layer's output (e.g. partial sort for selecting the
52/// top `n` terms).
53///
54/// ```
55/// use paulistrings::TruncationPolicy;
56/// use num_complex::Complex64;
57///
58/// /// Drop the imaginary half: keep only terms whose coefficient is real.
59/// struct RealOnly;
60/// impl<const W: usize> TruncationPolicy<W> for RealOnly {
61///     #[inline]
62///     fn keep_term(&self, _x: &[u64; W], _z: &[u64; W], c: Complex64) -> bool {
63///         c.im == 0.0
64///     }
65/// }
66/// ```
67pub trait TruncationPolicy<const W: usize>: Send + Sync {
68    /// Cheap per-term filter applied during the merge phase. Must inline.
69    ///
70    /// `c` is the **summed** coefficient at the key `(x, z)` — i.e.
71    /// `keep_term` runs after all scratch entries with this key have been
72    /// reduced, not on individual scratch entries.
73    #[inline]
74    fn keep_term(&self, _x: &[u64; W], _z: &[u64; W], _c: Complex64) -> bool {
75        true
76    }
77
78    /// Optional global pass after each circuit layer. May be non-local
79    /// (e.g. partial sort for [`TopN`]).
80    fn finalize_layer(&self, _sum: &mut PauliSum<W>) {}
81}