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}