1use crate::circuit::QuantumCircuit;
4use crate::gate::Gate;
5use crate::state::QuantumState;
6use crate::types::*;
7use crate::error::Result;
8
9use rand::Rng;
10use std::collections::HashMap;
11use std::time::Instant;
12
13pub struct SimConfig {
15 pub seed: Option<u64>,
17 pub noise: Option<NoiseModel>,
19 pub shots: Option<u32>,
21}
22
23impl Default for SimConfig {
24 fn default() -> Self {
25 Self {
26 seed: None,
27 noise: None,
28 shots: None,
29 }
30 }
31}
32
33pub struct SimulationResult {
35 pub state: QuantumState,
36 pub measurements: Vec<MeasurementOutcome>,
37 pub metrics: SimulationMetrics,
38}
39
40pub struct ShotResult {
42 pub counts: HashMap<Vec<bool>, usize>,
43 pub metrics: SimulationMetrics,
44}
45
46pub struct Simulator;
48
49impl Simulator {
50 pub fn run(circuit: &QuantumCircuit) -> Result<SimulationResult> {
52 Self::run_with_config(circuit, &SimConfig::default())
53 }
54
55 pub fn run_with_config(
57 circuit: &QuantumCircuit,
58 config: &SimConfig,
59 ) -> Result<SimulationResult> {
60 let start = Instant::now();
61
62 let mut state = match config.seed {
63 Some(seed) => QuantumState::new_with_seed(circuit.num_qubits(), seed)?,
64 None => QuantumState::new(circuit.num_qubits())?,
65 };
66
67 let mut measurements = Vec::new();
68 let mut gate_count: usize = 0;
69
70 for gate in circuit.gates() {
71 let outcomes = state.apply_gate(gate)?;
72 measurements.extend(outcomes);
73 if !gate.is_non_unitary() {
74 gate_count += 1;
75 }
76 if let Some(ref noise) = config.noise {
78 apply_noise(&mut state, gate, noise);
79 }
80 }
81
82 let elapsed = start.elapsed();
83 let metrics = SimulationMetrics {
84 num_qubits: circuit.num_qubits(),
85 gate_count,
86 execution_time_ns: elapsed.as_nanos() as u64,
87 peak_memory_bytes: QuantumState::estimate_memory(circuit.num_qubits()),
88 gates_per_second: if elapsed.as_secs_f64() > 0.0 {
89 gate_count as f64 / elapsed.as_secs_f64()
90 } else {
91 0.0
92 },
93 gates_fused: 0,
94 };
95
96 Ok(SimulationResult {
97 state,
98 measurements,
99 metrics,
100 })
101 }
102
103 pub fn run_shots(
108 circuit: &QuantumCircuit,
109 shots: u32,
110 seed: Option<u64>,
111 ) -> Result<ShotResult> {
112 let start = Instant::now();
113 let mut counts: HashMap<Vec<bool>, usize> = HashMap::new();
114 let base_seed = seed.unwrap_or(42);
115 let mut total_gates: usize = 0;
116 let n_qubits = circuit.num_qubits();
117
118 let has_measurements = circuit
119 .gates()
120 .iter()
121 .any(|g| matches!(g, Gate::Measure(_)));
122
123 for shot in 0..shots {
124 let config = SimConfig {
125 seed: Some(base_seed.wrapping_add(shot as u64)),
126 noise: None,
127 shots: None,
128 };
129
130 let mut result = Self::run_with_config(circuit, &config)?;
131 total_gates += result.metrics.gate_count;
132
133 if !has_measurements {
135 let outcomes = result.state.measure_all()?;
136 result.measurements.extend(outcomes);
137 }
138
139 let mut bits = vec![false; n_qubits as usize];
141 for m in &result.measurements {
142 if (m.qubit as usize) < bits.len() {
143 bits[m.qubit as usize] = m.result;
144 }
145 }
146 *counts.entry(bits).or_insert(0) += 1;
147 }
148
149 let elapsed = start.elapsed();
150 let metrics = SimulationMetrics {
151 num_qubits: n_qubits,
152 gate_count: total_gates,
153 execution_time_ns: elapsed.as_nanos() as u64,
154 peak_memory_bytes: QuantumState::estimate_memory(n_qubits),
155 gates_per_second: if elapsed.as_secs_f64() > 0.0 {
156 total_gates as f64 / elapsed.as_secs_f64()
157 } else {
158 0.0
159 },
160 gates_fused: 0,
161 };
162
163 Ok(ShotResult { counts, metrics })
164 }
165}
166
167fn apply_noise(state: &mut QuantumState, gate: &Gate, noise: &NoiseModel) {
179 let qubits = gate.qubits();
180 if qubits.is_empty() {
181 return;
182 }
183
184 for &qubit in &qubits {
185 if noise.depolarizing_rate > 0.0 {
187 let r: f64 = state.rng_mut().gen();
188 if r < noise.depolarizing_rate {
189 let choice: f64 = state.rng_mut().gen();
190 let pauli = if choice < 1.0 / 3.0 {
191 Gate::X(qubit)
192 } else if choice < 2.0 / 3.0 {
193 Gate::Y(qubit)
194 } else {
195 Gate::Z(qubit)
196 };
197 if let Some(m) = pauli.matrix_1q() {
198 state.apply_single_qubit_gate(qubit, &m);
199 }
200 }
201 }
202
203 if noise.bit_flip_rate > 0.0 {
205 let r: f64 = state.rng_mut().gen();
206 if r < noise.bit_flip_rate {
207 let m = Gate::X(qubit).matrix_1q().unwrap();
208 state.apply_single_qubit_gate(qubit, &m);
209 }
210 }
211
212 if noise.phase_flip_rate > 0.0 {
214 let r: f64 = state.rng_mut().gen();
215 if r < noise.phase_flip_rate {
216 let m = Gate::Z(qubit).matrix_1q().unwrap();
217 state.apply_single_qubit_gate(qubit, &m);
218 }
219 }
220 }
221}