Skip to content

Commit 2347102

Browse files
Roger-luoclaude
andcommitted
bench(tableau): add fused+tgate combined circuit benchmark
Adds a `fused-tgate-circuit` benchmark group that combines batch Clifford operations (fusion) with variable T-gates and measurement. Also adds profile_combined example showing time breakdown. Key finding: measurement dominates at 85-90% of total circuit time when coefficient counts are large (>4K). Fusion reduces Clifford overhead to <1%. T-gate branching (rayon target) is ~10%. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 790d7ea commit 2347102

2 files changed

Lines changed: 128 additions & 0 deletions

File tree

‎crates/ppvm-tableau/benches/tableau.rs‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,56 @@ pub fn benchmark_suite_tableau(c: &mut Criterion, name: impl AsRef<str>) {
127127
}
128128

129129
large_group.finish();
130+
131+
// Combined benchmark: fused Clifford batches + variable T gates + measurement.
132+
// Shows how fusion and rayon compose in a realistic circuit.
133+
let mut combined_group = c.benchmark_group("fused-tgate-circuit");
134+
combined_group
135+
.warm_up_time(Duration::from_secs(1))
136+
.measurement_time(Duration::from_secs(5))
137+
.sample_size(20);
138+
139+
for n_tgates in [8, 12, 16] {
140+
// Circuit: batched Cliffords → H (ensure branching) → T gates → batched Cliffords → measure.
141+
// H is applied AFTER Cliffords to ensure T gates see non-stabilizer states and branch.
142+
let n_qubits: usize = 85;
143+
144+
// Pre-build the Clifford portion so the setup cost is excluded
145+
let mut setup = GeneralizedTableau::<Byte8F64<2>, u128>::new(n_qubits, 1e-10);
146+
let block1: Vec<usize> = (0..17).collect();
147+
let block2: Vec<usize> = (17..34).collect();
148+
setup.sqrt_y_batch(&block1);
149+
setup.sqrt_x_batch(&block2);
150+
setup.cz_block_pairs(0, 17, 17);
151+
// H on qubits that will get T gates — applied after Cliffords to guarantee branching
152+
for i in 0..n_tgates {
153+
setup.h(i);
154+
}
155+
156+
combined_group.bench_function(format!("fused-{n_tgates}t-{n_qubits}q"), |b| {
157+
b.iter_batched_ref(
158+
|| setup.fork(Some(0)),
159+
|tab| {
160+
// T gates (coefficient branching — rayon target)
161+
for i in 0..n_tgates {
162+
tab.t(i);
163+
}
164+
165+
// More batched Cliffords after T gates
166+
tab.sqrt_x_adj_batch(&block1);
167+
tab.sqrt_y_adj_batch(&block2);
168+
169+
// Measure all
170+
for i in 0..n_qubits {
171+
tab.measure(i);
172+
}
173+
},
174+
criterion::BatchSize::LargeInput,
175+
);
176+
});
177+
}
178+
179+
combined_group.finish();
130180
}
131181

132182
pub fn tableau_scaling_benchmarks(c: &mut Criterion) {
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//! Profile the time breakdown of a fused circuit with variable T gates.
2+
3+
use std::time::Instant;
4+
5+
use ppvm_runtime::config::fx64hash::Byte8F64;
6+
use ppvm_tableau::prelude::*;
7+
8+
type Tab = GeneralizedTableau<Byte8F64<2>, u128>;
9+
10+
fn main() {
11+
let n_qubits = 85;
12+
13+
println!(
14+
"{:>4} {:>10} {:>10} {:>10} {:>10} {:>6} {:>6} {:>6}",
15+
"T#", "clifford", "t-gates", "measure", "total", "%clif", "%t", "%meas"
16+
);
17+
18+
for n_tgates in [4, 8, 12, 14, 16] {
19+
let block1: Vec<usize> = (0..17).collect();
20+
let block2: Vec<usize> = (17..34).collect();
21+
22+
let n_runs = if n_tgates <= 12 { 5 } else { 2 };
23+
let mut clif_ns = 0u128;
24+
let mut tgate_ns = 0u128;
25+
let mut meas_ns = 0u128;
26+
27+
for _ in 0..n_runs {
28+
let mut tab: Tab = GeneralizedTableau::new(n_qubits, 1e-10);
29+
30+
// Clifford layer 1 (fused)
31+
let t0 = Instant::now();
32+
tab.sqrt_y_batch(&block1);
33+
tab.sqrt_x_batch(&block2);
34+
tab.cz_block_pairs(0, 17, 17);
35+
for i in 0..n_tgates {
36+
tab.h(i);
37+
}
38+
clif_ns += t0.elapsed().as_nanos();
39+
40+
// T gates
41+
let t1 = Instant::now();
42+
for i in 0..n_tgates {
43+
tab.t(i);
44+
}
45+
tgate_ns += t1.elapsed().as_nanos();
46+
47+
// Clifford layer 2 (fused)
48+
let t2 = Instant::now();
49+
tab.sqrt_x_adj_batch(&block1);
50+
tab.sqrt_y_adj_batch(&block2);
51+
clif_ns += t2.elapsed().as_nanos();
52+
53+
// Measure
54+
let t3 = Instant::now();
55+
for i in 0..n_qubits {
56+
tab.measure(i);
57+
}
58+
meas_ns += t3.elapsed().as_nanos();
59+
}
60+
61+
let d = n_runs as f64;
62+
let c = clif_ns as f64 / d / 1000.0;
63+
let t = tgate_ns as f64 / d / 1000.0;
64+
let m = meas_ns as f64 / d / 1000.0;
65+
let total = c + t + m;
66+
println!(
67+
"{:>4} {:>9.0}µ {:>9.0}µ {:>9.0}µ {:>9.0}µ {:>5.1}% {:>5.1}% {:>5.1}%",
68+
n_tgates,
69+
c,
70+
t,
71+
m,
72+
total,
73+
c / total * 100.0,
74+
t / total * 100.0,
75+
m / total * 100.0,
76+
);
77+
}
78+
}

0 commit comments

Comments
 (0)