1use scirs2_core::parallel_ops::{
7 IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator,
8};
9use scirs2_core::Complex64;
10use std::sync::Arc;
11
12use quantrs2_circuit::builder::{Circuit, Simulator};
13use quantrs2_core::{
14 error::{QuantRS2Error, QuantRS2Result},
15 gate::{multi, single, GateOp},
16 qubit::QubitId,
17 register::Register,
18};
19
20use crate::specialized_gates::{specialize_gate, SpecializedGate};
21use crate::statevector::StateVectorSimulator;
22use crate::utils::flip_bit;
23
24#[derive(Debug, Clone)]
26pub struct SpecializedSimulatorConfig {
27 pub parallel: bool,
29 pub enable_fusion: bool,
31 pub enable_reordering: bool,
33 pub cache_conversions: bool,
35 pub parallel_threshold: usize,
37}
38
39impl Default for SpecializedSimulatorConfig {
40 fn default() -> Self {
41 Self {
42 parallel: true,
43 enable_fusion: true,
44 enable_reordering: true,
45 cache_conversions: true,
46 parallel_threshold: 10,
47 }
48 }
49}
50
51#[derive(Debug, Clone, Default)]
53pub struct SpecializationStats {
54 pub total_gates: usize,
56 pub specialized_gates: usize,
58 pub generic_gates: usize,
60 pub fused_gates: usize,
62 pub time_saved_ms: f64,
64}
65
66pub struct SpecializedStateVectorSimulator {
68 config: SpecializedSimulatorConfig,
70 base_simulator: StateVectorSimulator,
72 stats: SpecializationStats,
74 conversion_cache: Option<Arc<dashmap::DashMap<String, bool>>>,
76 work_buffer: Vec<Complex64>,
78}
79
80impl SpecializedStateVectorSimulator {
81 #[must_use]
83 pub fn new(config: SpecializedSimulatorConfig) -> Self {
84 let base_simulator = if config.parallel {
85 StateVectorSimulator::new()
86 } else {
87 StateVectorSimulator::sequential()
88 };
89
90 let conversion_cache = if config.cache_conversions {
91 Some(Arc::new(dashmap::DashMap::new()))
92 } else {
93 None
94 };
95
96 Self {
97 config,
98 base_simulator,
99 stats: SpecializationStats::default(),
100 conversion_cache,
101 work_buffer: Vec::new(),
102 }
103 }
104
105 pub const fn get_stats(&self) -> &SpecializationStats {
107 &self.stats
108 }
109
110 pub fn reset_stats(&mut self) {
112 self.stats = SpecializationStats::default();
113 }
114
115 pub fn run<const N: usize>(&mut self, circuit: &Circuit<N>) -> QuantRS2Result<Vec<Complex64>> {
117 let n_qubits = N;
118 let mut state = self.initialize_state(n_qubits);
119
120 let gates = if self.config.enable_reordering {
122 self.reorder_gates(circuit.gates())?
123 } else {
124 circuit.gates().to_vec()
125 };
126
127 if self.config.enable_fusion {
129 self.apply_gates_with_fusion(&mut state, &gates, n_qubits)?;
130 } else {
131 for gate in gates {
132 self.apply_gate(&mut state, &gate, n_qubits)?;
133 }
134 }
135
136 Ok(state)
137 }
138
139 fn initialize_state(&self, n_qubits: usize) -> Vec<Complex64> {
141 let size = 1 << n_qubits;
142 let mut state = vec![Complex64::new(0.0, 0.0); size];
143 state[0] = Complex64::new(1.0, 0.0);
144 state
145 }
146
147 fn apply_gate(
149 &mut self,
150 state: &mut [Complex64],
151 gate: &Arc<dyn GateOp + Send + Sync>,
152 n_qubits: usize,
153 ) -> QuantRS2Result<()> {
154 self.stats.total_gates += 1;
155
156 if let Some(specialized) = self.get_specialized_gate(gate.as_ref()) {
158 self.stats.specialized_gates += 1;
159 self.stats.time_saved_ms += self.estimate_time_saved(gate.as_ref());
160
161 let parallel = self.config.parallel && n_qubits >= self.config.parallel_threshold;
162 specialized.apply_specialized(state, n_qubits, parallel)
163 } else {
164 self.stats.generic_gates += 1;
165
166 match gate.num_qubits() {
168 1 => {
169 let qubits = gate.qubits();
170 let matrix = gate.matrix()?;
171 self.apply_single_qubit_generic(state, &matrix, qubits[0], n_qubits)
172 }
173 2 => {
174 let qubits = gate.qubits();
175 let matrix = gate.matrix()?;
176 self.apply_two_qubit_generic(state, &matrix, qubits[0], qubits[1], n_qubits)
177 }
178 _ => {
179 self.apply_multi_qubit_generic(state, gate.as_ref(), n_qubits)
181 }
182 }
183 }
184 }
185
186 fn get_specialized_gate(&self, gate: &dyn GateOp) -> Option<Box<dyn SpecializedGate>> {
188 specialize_gate(gate)
190 }
191
192 fn apply_gates_with_fusion(
194 &mut self,
195 state: &mut [Complex64],
196 gates: &[Arc<dyn GateOp + Send + Sync>],
197 n_qubits: usize,
198 ) -> QuantRS2Result<()> {
199 let mut i = 0;
200
201 while i < gates.len() {
202 if i + 1 < gates.len() {
204 if let (Some(gate1), Some(gate2)) = (
205 self.get_specialized_gate(gates[i].as_ref()),
206 self.get_specialized_gate(gates[i + 1].as_ref()),
207 ) {
208 if gate1.can_fuse_with(gate2.as_ref()) {
209 if let Some(fused) = gate1.fuse_with(gate2.as_ref()) {
210 self.stats.fused_gates += 2;
211 self.stats.total_gates += 1;
212
213 let parallel =
214 self.config.parallel && n_qubits >= self.config.parallel_threshold;
215 fused.apply_specialized(state, n_qubits, parallel)?;
216
217 i += 2;
218 continue;
219 }
220 }
221 }
222 }
223
224 self.apply_gate(state, &gates[i], n_qubits)?;
226 i += 1;
227 }
228
229 Ok(())
230 }
231
232 fn reorder_gates(
255 &self,
256 gates: &[Arc<dyn GateOp + Send + Sync>],
257 ) -> QuantRS2Result<Vec<Arc<dyn GateOp + Send + Sync>>> {
258 let mut reordered: Vec<Arc<dyn GateOp + Send + Sync>> = gates.to_vec();
259 let key =
260 |gate: &Arc<dyn GateOp + Send + Sync>| gate.qubits().first().map_or(0, QubitId::id);
261
262 for i in 0..reordered.len() {
263 let mut best_j = i;
264 let mut best_key = key(&reordered[i]);
265
266 for j in (i + 1)..reordered.len() {
267 if !Self::commutes_with_all(reordered[j].as_ref(), &reordered[i..j]) {
272 break;
273 }
274 let candidate_key = key(&reordered[j]);
275 if candidate_key < best_key {
276 best_key = candidate_key;
277 best_j = j;
278 }
279 }
280
281 if best_j != i {
282 let gate = reordered.remove(best_j);
283 reordered.insert(i, gate);
284 }
285 }
286
287 Ok(reordered)
288 }
289
290 fn commutes_with_all(candidate: &dyn GateOp, others: &[Arc<dyn GateOp + Send + Sync>]) -> bool {
294 others
295 .iter()
296 .all(|other| Self::gates_commute(candidate, other.as_ref()))
297 }
298
299 fn gates_commute(a: &dyn GateOp, b: &dyn GateOp) -> bool {
309 let qubits_a = a.qubits();
310 let qubits_b = b.qubits();
311 let disjoint = qubits_a.iter().all(|q| !qubits_b.contains(q));
312 if disjoint {
313 return true;
314 }
315 Self::is_diagonal_gate(a) && Self::is_diagonal_gate(b)
316 }
317
318 fn is_diagonal_gate(gate: &dyn GateOp) -> bool {
324 matches!(
325 gate.name(),
326 "Z" | "S" | "S†" | "T" | "T†" | "RZ" | "P" | "I" | "CZ" | "CRZ" | "CS" | "GlobalPhase"
327 )
328 }
329
330 fn estimate_time_saved(&self, gate: &dyn GateOp) -> f64 {
332 match gate.name() {
334 "H" | "X" | "Y" | "Z" => 0.001, "RX" | "RY" | "RZ" => 0.002, "CNOT" | "CZ" => 0.005, "Toffoli" => 0.010, _ => 0.0,
339 }
340 }
341
342 fn apply_single_qubit_generic(
344 &mut self,
345 state: &mut [Complex64],
346 matrix: &[Complex64],
347 target: QubitId,
348 n_qubits: usize,
349 ) -> QuantRS2Result<()> {
350 let target_idx = target.id() as usize;
351
352 if self.config.parallel && n_qubits >= self.config.parallel_threshold {
353 if self.work_buffer.len() < state.len() {
355 self.work_buffer
356 .resize(state.len(), Complex64::new(0.0, 0.0));
357 }
358 self.work_buffer[..state.len()].copy_from_slice(state);
359 let state_copy = &self.work_buffer[..state.len()];
360
361 state.par_iter_mut().enumerate().for_each(|(idx, amp)| {
362 let bit_val = (idx >> target_idx) & 1;
363 let paired_idx = idx ^ (1 << target_idx);
364
365 let idx0 = if bit_val == 0 { idx } else { paired_idx };
366 let idx1 = if bit_val == 0 { paired_idx } else { idx };
367
368 *amp = matrix[2 * bit_val] * state_copy[idx0]
369 + matrix[2 * bit_val + 1] * state_copy[idx1];
370 });
371 } else {
372 for i in 0..(1 << n_qubits) {
374 if (i >> target_idx) & 1 == 0 {
375 let j = i | (1 << target_idx);
376 let temp0 = state[i];
377 let temp1 = state[j];
378 state[i] = matrix[0] * temp0 + matrix[1] * temp1;
379 state[j] = matrix[2] * temp0 + matrix[3] * temp1;
380 }
381 }
382 }
383
384 Ok(())
385 }
386
387 fn apply_two_qubit_generic(
389 &mut self,
390 state: &mut [Complex64],
391 matrix: &[Complex64],
392 control: QubitId,
393 target: QubitId,
394 n_qubits: usize,
395 ) -> QuantRS2Result<()> {
396 let control_idx = control.id() as usize;
397 let target_idx = target.id() as usize;
398
399 if control_idx == target_idx {
400 return Err(QuantRS2Error::CircuitValidationFailed(
401 "Control and target must be different".into(),
402 ));
403 }
404
405 if self.work_buffer.len() < state.len() {
407 self.work_buffer
408 .resize(state.len(), Complex64::new(0.0, 0.0));
409 }
410
411 if self.config.parallel && n_qubits >= self.config.parallel_threshold {
412 self.work_buffer[..state.len()].copy_from_slice(state);
414 let state_copy = &self.work_buffer[..state.len()];
415
416 state.par_iter_mut().enumerate().for_each(|(idx, amp)| {
417 let ctrl_bit = (idx >> control_idx) & 1;
418 let tgt_bit = (idx >> target_idx) & 1;
419 let basis_idx = (ctrl_bit << 1) | tgt_bit;
420
421 let idx00 = idx & !(1 << control_idx) & !(1 << target_idx);
422 let idx01 = idx00 | (1 << target_idx);
423 let idx10 = idx00 | (1 << control_idx);
424 let idx11 = idx00 | (1 << control_idx) | (1 << target_idx);
425
426 *amp = matrix[4 * basis_idx] * state_copy[idx00]
427 + matrix[4 * basis_idx + 1] * state_copy[idx01]
428 + matrix[4 * basis_idx + 2] * state_copy[idx10]
429 + matrix[4 * basis_idx + 3] * state_copy[idx11];
430 });
431 } else {
432 for i in 0..state.len() {
434 let ctrl_bit = (i >> control_idx) & 1;
435 let tgt_bit = (i >> target_idx) & 1;
436 let basis_idx = (ctrl_bit << 1) | tgt_bit;
437
438 let i00 = i & !(1 << control_idx) & !(1 << target_idx);
439 let i01 = i00 | (1 << target_idx);
440 let i10 = i00 | (1 << control_idx);
441 let i11 = i10 | (1 << target_idx);
442
443 self.work_buffer[i] = matrix[4 * basis_idx] * state[i00]
444 + matrix[4 * basis_idx + 1] * state[i01]
445 + matrix[4 * basis_idx + 2] * state[i10]
446 + matrix[4 * basis_idx + 3] * state[i11];
447 }
448
449 state.copy_from_slice(&self.work_buffer[..state.len()]);
450 }
451
452 Ok(())
453 }
454
455 fn apply_multi_qubit_generic(
457 &mut self,
458 state: &mut [Complex64],
459 gate: &dyn GateOp,
460 _n_qubits: usize,
461 ) -> QuantRS2Result<()> {
462 let matrix = gate.matrix()?;
465 let qubits = gate.qubits();
466 let gate_qubits = qubits.len();
467 let gate_dim = 1 << gate_qubits;
468
469 if matrix.len() != gate_dim * gate_dim {
470 return Err(QuantRS2Error::InvalidInput(format!(
471 "Invalid matrix size for {gate_qubits}-qubit gate"
472 )));
473 }
474
475 if self.work_buffer.len() < state.len() {
477 self.work_buffer
478 .resize(state.len(), Complex64::new(0.0, 0.0));
479 }
480
481 for idx in 0..state.len() {
483 let mut basis_idx = 0;
484 for (i, &qubit) in qubits.iter().enumerate() {
485 if (idx >> qubit.id()) & 1 == 1 {
486 basis_idx |= 1 << i;
487 }
488 }
489
490 let mut new_amp = Complex64::new(0.0, 0.0);
491 for j in 0..gate_dim {
492 let mut target_idx = idx;
493 for (i, &qubit) in qubits.iter().enumerate() {
494 if (j >> i) & 1 != (idx >> qubit.id()) & 1 {
495 target_idx ^= 1 << qubit.id();
496 }
497 }
498
499 new_amp += matrix[basis_idx * gate_dim + j] * state[target_idx];
500 }
501
502 self.work_buffer[idx] = new_amp;
503 }
504
505 state.copy_from_slice(&self.work_buffer[..state.len()]);
506 Ok(())
507 }
508}
509
510#[must_use]
512pub fn benchmark_specialization(
513 n_qubits: usize,
514 n_gates: usize,
515) -> (f64, f64, SpecializationStats) {
516 use quantrs2_circuit::builder::Circuit;
517 use scirs2_core::random::prelude::*;
518 use std::time::Instant;
519
520 let mut rng = thread_rng();
521
522 assert!(
525 (n_qubits == 8),
526 "Benchmark currently only supports 8 qubits"
527 );
528
529 let mut circuit = Circuit::<8>::new();
530
531 for _ in 0..n_gates {
532 let gate_type = rng.random_range(0..5);
533 let qubit = QubitId(rng.random_range(0..n_qubits as u32));
534
535 match gate_type {
536 0 => {
537 let _ = circuit.h(qubit);
538 }
539 1 => {
540 let _ = circuit.x(qubit);
541 }
542 2 => {
543 let _ = circuit.ry(qubit, rng.random_range(0.0..std::f64::consts::TAU));
544 }
545 3 => {
546 if n_qubits > 1 {
547 let qubit2 = QubitId(rng.random_range(0..n_qubits as u32));
548 if qubit != qubit2 {
549 let _ = circuit.cnot(qubit, qubit2);
550 }
551 }
552 }
553 _ => {
554 let _ = circuit.z(qubit);
555 }
556 }
557 }
558
559 let mut specialized_sim = SpecializedStateVectorSimulator::new(Default::default());
561 let start = Instant::now();
562 let _ = specialized_sim
563 .run(&circuit)
564 .expect("Specialized simulator benchmark failed");
565 let specialized_time = start.elapsed().as_secs_f64();
566
567 let mut base_sim = StateVectorSimulator::new();
569 let start = Instant::now();
570 let _ = base_sim
571 .run(&circuit)
572 .expect("Base simulator benchmark failed");
573 let base_time = start.elapsed().as_secs_f64();
574
575 (specialized_time, base_time, specialized_sim.stats.clone())
576}
577
578#[cfg(test)]
579mod tests {
580 use super::*;
581 use quantrs2_circuit::builder::Circuit;
582 use quantrs2_core::gate::single::{Hadamard, PauliX};
583
584 #[test]
585 fn test_specialized_simulator() {
586 let mut circuit = Circuit::<2>::new();
587 let _ = circuit.h(QubitId(0));
588 let _ = circuit.cnot(QubitId(0), QubitId(1));
589
590 let mut sim = SpecializedStateVectorSimulator::new(Default::default());
591 let state = sim
592 .run(&circuit)
593 .expect("Failed to run specialized simulator test circuit");
594
595 let expected_amp = 1.0 / std::f64::consts::SQRT_2;
597 assert!((state[0].norm() - expected_amp).abs() < 1e-10);
598 assert!(state[1].norm() < 1e-10);
599 assert!(state[2].norm() < 1e-10);
600 assert!((state[3].norm() - expected_amp).abs() < 1e-10);
601
602 assert_eq!(sim.get_stats().total_gates, 2);
604 assert_eq!(sim.get_stats().specialized_gates, 2);
605 assert_eq!(sim.get_stats().generic_gates, 0);
606 }
607
608 #[test]
617 fn test_reorder_gates_preserves_semantics_for_noncommuting_gates() {
618 let mut circuit = Circuit::<2>::new();
619 let _ = circuit.x(QubitId(1));
620 let _ = circuit.cnot(QubitId(0), QubitId(1));
621
622 let reordering_config = SpecializedSimulatorConfig {
623 enable_reordering: true,
624 ..Default::default()
625 };
626 let mut sim_reordered = SpecializedStateVectorSimulator::new(reordering_config);
627 let state_reordered = sim_reordered.run(&circuit).expect("reordered run failed");
628
629 let no_reorder_config = SpecializedSimulatorConfig {
630 enable_reordering: false,
631 ..Default::default()
632 };
633 let mut sim_baseline = SpecializedStateVectorSimulator::new(no_reorder_config);
634 let state_baseline = sim_baseline
635 .run(&circuit)
636 .expect("baseline (unreordered) run failed");
637
638 for (i, (reordered_amp, baseline_amp)) in state_reordered
639 .iter()
640 .zip(state_baseline.iter())
641 .enumerate()
642 {
643 assert!(
644 (reordered_amp - baseline_amp).norm() < 1e-10,
645 "reordering changed circuit semantics at index {i}: {reordered_amp:?} vs {baseline_amp:?}"
646 );
647 }
648 }
649
650 #[test]
655 fn test_gates_commute_structural_checks() {
656 use quantrs2_core::gate::single::RotationZ;
657
658 let x0 = PauliX { target: QubitId(0) };
659 let x0_again = PauliX { target: QubitId(0) };
660 let rz0 = RotationZ {
661 target: QubitId(0),
662 theta: 0.5,
663 };
664 let rz0_b = RotationZ {
665 target: QubitId(0),
666 theta: 1.5,
667 };
668 let x1 = PauliX { target: QubitId(1) };
669
670 assert!(!SpecializedStateVectorSimulator::gates_commute(
675 &x0, &x0_again
676 ));
677 assert!(SpecializedStateVectorSimulator::gates_commute(&rz0, &rz0_b));
679 assert!(SpecializedStateVectorSimulator::gates_commute(&x0, &x1));
681 }
682
683 #[test]
684 fn test_benchmark() {
685 let (spec_time, base_time, stats) = benchmark_specialization(8, 20);
686
687 println!(
688 "Specialized: {:.3}ms, Base: {:.3}ms",
689 spec_time * 1000.0,
690 base_time * 1000.0
691 );
692 println!("Stats: {stats:?}");
693
694 assert!(spec_time <= base_time * 1.1); }
697}