paulistrings/engine/
mod.rs1pub(crate) mod backend;
4pub(crate) mod bucketed;
5mod coset;
6#[cfg(feature = "cuda")]
7mod cuda_context;
8pub(crate) mod driver;
9#[cfg(feature = "cuda")]
10pub mod gpu;
11pub(crate) mod knob;
12mod merge;
13pub(crate) mod partitioned;
14pub(crate) mod plan;
15#[cfg(feature = "phase-timing")]
16pub(crate) mod stats;
17pub(crate) mod timing;
18pub(crate) mod trace;
19pub(crate) mod transport;
20
21use crate::circuit::Circuit;
22use crate::pauli_sum::hash::PartitionRows;
23use crate::pauli_sum::storage::{DEFAULT_MIN_BUCKETS, DEFAULT_TARGET_BUCKET_LEN};
24use crate::pauli_sum::PauliSum;
25use crate::truncation::TruncationPolicy;
26use backend::HostPartition;
27use bucketed::LayerScratch;
28use driver::{run_layers, PartitionContext, PartitionWork};
29use trace::assemble;
30use transport::SoloTransport;
31
32pub(crate) const LOG_TARGET: &str = "paulistrings::propagate";
34
35pub(crate) fn progress_scope(
37 rank: Option<usize>,
38 partitions: usize,
39 placement: Option<&str>,
40) -> String {
41 let mut scope = match (rank, partitions) {
42 (Some(rank), _) => format!("partition {rank}/{partitions}"),
43 (None, 1) => "1 partition".to_string(),
44 (None, _) => format!("{partitions} partitions"),
45 };
46 if let Some(placement) = placement {
47 scope.push_str(&format!(" [{placement}]"));
48 }
49 scope
50}
51
52#[derive(Clone, Copy, Debug, PartialEq, Eq)]
54pub enum Direction {
55 Forward,
57 Heisenberg,
59}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq)]
63pub struct PropagateOptions {
64 pub target_bucket_len: usize,
69 pub min_buckets: usize,
73}
74
75const SMALLEST_MIN_BUCKETS: usize = 16;
77
78impl PropagateOptions {
79 pub fn validate(&self) -> Result<(), String> {
81 if self.target_bucket_len == 0 {
82 return Err("target_bucket_len must be at least 1, got 0".to_string());
83 }
84 if self.min_buckets < SMALLEST_MIN_BUCKETS {
85 return Err(format!(
86 "min_buckets must be at least {SMALLEST_MIN_BUCKETS}, got {}",
87 self.min_buckets
88 ));
89 }
90 Ok(())
91 }
92
93 pub(crate) fn assert_valid(&self) {
95 if let Err(message) = self.validate() {
96 panic!("{message}");
97 }
98 }
99}
100
101impl Default for PropagateOptions {
102 fn default() -> Self {
103 Self { target_bucket_len: DEFAULT_TARGET_BUCKET_LEN, min_buckets: DEFAULT_MIN_BUCKETS }
104 }
105}
106
107pub fn propagate<const W: usize, T>(
121 circuit: &Circuit<W>,
122 sum: PauliSum<W>,
123 policy: &T,
124 direction: Direction,
125) -> PauliSum<W>
126where
127 T: TruncationPolicy<W> + ?Sized,
128{
129 let mut scratch = LayerScratch::<W>::new();
130 propagate_with(circuit, sum, policy, direction, &mut scratch, PropagateOptions::default())
131}
132
133pub fn propagate_with<const W: usize, T>(
142 circuit: &Circuit<W>,
143 sum: PauliSum<W>,
144 policy: &T,
145 direction: Direction,
146 scratch: &mut LayerScratch<W>,
147 options: PropagateOptions,
148) -> PauliSum<W>
149where
150 T: TruncationPolicy<W> + ?Sized,
151{
152 options.assert_valid();
153 let num_channels = circuit.channels.len();
154 let terms_in = sum.len();
155 let started = std::time::Instant::now();
156 log::info!(
157 target: LOG_TARGET,
158 "propagate: {terms_in} terms through {num_channels} channels ({direction:?}), {}",
159 progress_scope(None, 1, None),
160 );
161
162 let tracing = scratch.trace.is_some();
164 let rows = PartitionRows::none(sum.num_qubits());
165 let mut work = PartitionWork {
166 local: HostPartition::with_layer_scratch(sum, std::mem::take(scratch)),
167 rows: Vec::with_capacity(if tracing { num_channels } else { 0 }),
168 };
169 let context = PartitionContext { rows: &rows, rank: 0, size: 1, tracing };
170 run_layers(circuit, policy, direction, options, context, &mut work, &SoloTransport);
171 let (sum, layer_scratch) = work.local.into_parts();
172 *scratch = layer_scratch;
173 if let Some(trace) = scratch.trace.as_mut() {
174 assemble(trace, vec![work.rows], 1);
175 }
176
177 log::info!(
178 target: LOG_TARGET,
179 "propagate: {num_channels} layers applied, {terms_in} -> {} terms, {:.3} s, {}",
180 sum.len(),
181 started.elapsed().as_secs_f64(),
182 progress_scope(None, 1, None),
183 );
184
185 sum
186}
187
188#[cfg(test)]
189mod tests;