Skip to main content

ppvm_traits/traits/
map.rs

1// SPDX-FileCopyrightText: 2026 The PPVM Authors
2// SPDX-License-Identifier: Apache-2.0
3
4use std::hash::BuildHasher;
5
6use crate::traits::{Coefficient, PauliStorage, PauliWordTrait};
7
8/// Minimal interface for any "associative coefficient map" backing a
9/// `PauliSum` — construction, length, and clear.
10///
11/// Implementations exist for `HashMap`, `IndexMap`, and `DashMap`; pick
12/// one via a [`Config`](crate::config::Config).
13pub trait ACMapBase {
14    /// Construct an empty map with at least `capacity` slots reserved.
15    fn with_capacity(capacity: usize) -> Self;
16    /// Number of stored `(key, value)` pairs.
17    fn len(&self) -> usize;
18    /// `true` if the map is empty.
19    fn is_empty(&self) -> bool {
20        self.len() == 0
21    }
22    /// Remove every entry, retaining allocated capacity.
23    fn clear(&mut self);
24}
25
26/// Borrowing iteration over an `ACMap`. Lives in its own trait so that
27/// implementations may pick their own item / iterator types.
28pub trait ACMapIter<'a> {
29    /// Yielded item type.
30    type Item;
31    /// Iterator type.
32    type Iter: Iterator<Item = Self::Item>;
33    /// Iterate over `(key, value)` pairs (or their representation).
34    fn iter(&'a self) -> Self::Iter;
35}
36
37/// `+=` semantics for an `ACMap`: insert a new entry or accumulate into
38/// the existing one with the same key.
39pub trait ACMapAddAssign<
40    S: PauliStorage,
41    V: Coefficient,
42    H: BuildHasher + Clone + Default,
43    W: PauliWordTrait,
44>
45{
46    /// Add `value` into the entry at `key`, creating the entry if absent.
47    fn add_assign(&mut self, key: W, value: V);
48    /// For every entry, compute `f(key, value)` and add the result into
49    /// `dest` with [`add_assign`](Self::add_assign).
50    fn map_add_assign<F>(&self, dest: &mut Self, f: F)
51    where
52        F: Fn(&W, &V) -> (W, V) + Sync + Send;
53}
54
55/// Scalar `*=` semantics for an `ACMap`: multiply every value by a
56/// constant.
57pub trait ACMapMulAssign<V: Coefficient, H: BuildHasher + Clone + Default> {
58    /// Scale every value in place.
59    fn mul_assign(&mut self, value: V);
60}
61
62/// In-place modify + insert pattern used to express branching gates
63/// (where one input entry can produce zero, one, or several output
64/// entries).
65pub trait ACMapInsert<
66    S: PauliStorage,
67    V: Coefficient,
68    H: BuildHasher + Clone + Default,
69    W: PauliWordTrait,
70>
71{
72    /// Modify each existing entry in place; if `f` returns `Some((k', v'))`,
73    /// append that new entry into `dest`, a plain `Vec` (a push per entry,
74    /// no hashing). This is the primary hot-path entry point: the caller
75    /// merges the buffer into the destination map afterwards, avoiding a
76    /// second hashmap probe per produced entry.
77    fn map_insert_vec<F>(&mut self, dest: &mut Vec<(W, V)>, f: F)
78    where
79        F: Fn(&W, &mut V) -> Option<(W, V)> + Sync + Send;
80
81    /// Like [`map_insert_vec`](Self::map_insert_vec) but `f` may return a
82    /// `Vec` of new entries per existing entry, inserted directly into the
83    /// destination map `dest`.
84    fn map_insert_multiple<F>(&mut self, dest: &mut Self, f: F)
85    where
86        F: Fn(&W, &mut V) -> Option<Vec<(W, V)>> + Sync + Send;
87}
88
89/// Membership queries — `(key, value)` exact match or with a custom
90/// predicate on the value.
91pub trait ACMapContains<
92    S: PauliStorage,
93    V: Coefficient,
94    H: BuildHasher + Clone + Default,
95    W: PauliWordTrait,
96>
97{
98    /// `true` if an entry with this exact `(key, value)` is present.
99    fn contains(&self, key: &W, value: &V) -> bool {
100        self.contains_with(key, |v| v == value)
101    }
102    /// `true` if an entry for `key` exists whose value satisfies `f`.
103    fn contains_with<F>(&self, key: &W, f: F) -> bool
104    where
105        F: Fn(&V) -> bool;
106}
107
108/// Merge two maps with accumulation: drain `dest` into `self`,
109/// summing values that share a key.
110pub trait ACMapConsume {
111    /// Drain `dest` into `self`, accumulating values on key collision.
112    fn consume(&mut self, dest: &mut Self);
113}
114
115/// In-place per-entry transformation of values.
116pub trait ACMapScale<
117    S: PauliStorage,
118    V: Coefficient,
119    H: BuildHasher + Clone + Default,
120    W: PauliWordTrait,
121>
122{
123    /// Apply `f(key, value)` to every entry; only the value is mutable.
124    fn scale<F>(&mut self, f: F)
125    where
126        F: Fn(&W, &mut V) + Sync + Send;
127}
128
129/// Drop entries that don't satisfy a predicate — used by truncation
130/// strategies.
131pub trait ACMapRetain<
132    S: PauliStorage,
133    V: Coefficient,
134    H: BuildHasher + Clone + Default,
135    W: PauliWordTrait,
136>
137{
138    /// Keep only entries for which `f(key, value)` returns `true`.
139    fn retain<F>(&mut self, f: F)
140    where
141        F: FnMut(&W, &V) -> bool;
142}
143
144/// Aggregate trait combining every operation a backing map must support
145/// to be usable as the storage for a `PauliSum`.
146///
147/// You don't normally implement `ACMap` directly: the blanket impl below
148/// covers any type that implements all the constituent traits.
149///
150/// # Map-backend implementor's guide
151///
152/// A new backing map (a GPU-resident map, a sharded map, anything beyond
153/// the existing `HashMap` / `IndexMap` / `AHashMap` / `DashMap` backends)
154/// needs these to satisfy the `ACMap` bound itself:
155///
156/// * `Clone`.
157/// * [`ACMapBase`] — construction, length, clear.
158/// * [`ACMapAddAssign`] — `+=` insert-or-accumulate.
159/// * [`ACMapMulAssign`] — scalar `*=`.
160/// * [`ACMapInsert`] — just `map_insert_vec` and `map_insert_multiple`
161///   (there is no third variant to implement).
162/// * [`ACMapContains`] — membership queries.
163/// * [`ACMapScale`] — in-place per-entry transform.
164/// * [`ACMapRetain`] — predicate-based entry removal.
165/// * [`ACMapConsume`] — drain-and-accumulate merge of two maps.
166///
167/// Satisfying `ACMap` is necessary but not sufficient: `ppvm-pauli-sum`
168/// bounds individual operations on further traits, so a backend that skips
169/// them only fails to compile at those call sites. In practice you also
170/// want:
171///
172/// * [`ACMapIter`] — borrowing iteration over `(key, value)`; needed by
173///   `PauliSum::iter`, `Display`, and the approximate-comparison helpers.
174/// * [`Trace`](crate::traits::Trace) — needed by `PauliSum::trace`.
175/// * `Extend<(Word, Coeff)>` — needed by the arithmetic operators.
176/// * `PartialEq` — needed by `PauliSum`'s approximate comparisons.
177///
178/// Of these, `map_insert_vec`, `map_insert_multiple`, `scale`,
179/// `mul_assign`, `retain`, and `consume` are whole-collection batch entry
180/// points: a concurrent or GPU backend can dispatch each of them as one
181/// parallel pass over every entry. `add_assign` is the exception — it
182/// operates on a single key at a time, so a thread-pool backend typically
183/// only parallelizes its batch sibling, `map_add_assign`. See the
184/// `DashMap` impl in `crates/ppvm-traits/src/map/dashmap.rs` for a working
185/// concurrent precedent: `map_add_assign`, `mul_assign`, `map_insert_vec`,
186/// `map_insert_multiple`, `scale`, `consume`, and `trace` all go through
187/// rayon `par_iter`. `retain` is the one that does not — its `FnMut`
188/// predicate is not shareable across threads, so `DashMap` forwards to the
189/// sequential `DashMap::retain`.
190pub trait ACMap<
191    S: PauliStorage,
192    V: Coefficient,
193    H: BuildHasher + Clone + Default,
194    W: PauliWordTrait,
195>:
196    Clone
197    + ACMapBase
198    + ACMapAddAssign<S, V, H, W>
199    + ACMapMulAssign<V, H>
200    + ACMapInsert<S, V, H, W>
201    + ACMapContains<S, V, H, W>
202    + ACMapScale<S, V, H, W>
203    + ACMapRetain<S, V, H, W>
204    + ACMapConsume
205{
206}
207
208impl<T, Storage, Coeff, Hasher, Word> ACMap<Storage, Coeff, Hasher, Word> for T
209where
210    Storage: PauliStorage,
211    Coeff: Coefficient,
212    Hasher: BuildHasher + Clone + Default,
213    Word: PauliWordTrait,
214    T: Clone
215        + ACMapBase
216        + ACMapAddAssign<Storage, Coeff, Hasher, Word>
217        + ACMapMulAssign<Coeff, Hasher>
218        + ACMapInsert<Storage, Coeff, Hasher, Word>
219        + ACMapScale<Storage, Coeff, Hasher, Word>
220        + ACMapContains<Storage, Coeff, Hasher, Word>
221        + ACMapRetain<Storage, Coeff, Hasher, Word>
222        + ACMapConsume,
223{
224}