microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
4fa10c30a716d7fc2f67a0a385f3da565daa1237

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/pip/src/qir_simulation/cpu_simulators.rs

269lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#[cfg(test)]
5mod tests;
6
7use crate::qir_simulation::{NoiseConfig, QirInstruction, QirInstructionId, unbind_noise_config};
8use pyo3::{IntoPyObjectExt, exceptions::PyValueError, prelude::*, types::PyList};
9use pyo3::{PyResult, pyfunction};
10use qdk_simulators::{
11 MeasurementResult, Simulator,
12 cpu_full_state_simulator::{NoiselessSimulator, NoisySimulator},
13 noise_config::{self, CumulativeNoiseConfig},
14 stabilizer_simulator::StabilizerSimulator,
15};
16use rand::{Rng, SeedableRng, rngs::StdRng};
17use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
18use std::{fmt::Write, sync::Arc};
19
20#[pyfunction]
21pub fn run_clifford<'py>(
22 py: Python<'py>,
23 input: &Bound<'py, PyList>,
24 num_qubits: u32,
25 num_results: u32,
26 shots: u32,
27 noise_config: Option<&Bound<'py, NoiseConfig>>,
28 seed: Option<u32>,
29) -> PyResult<Py<PyAny>> {
30 let make_simulator = |num_qubits, num_results, seed, noise| {
31 StabilizerSimulator::new(num_qubits as usize, num_results as usize, seed, noise)
32 };
33 py_run(
34 py,
35 input,
36 num_qubits,
37 num_results,
38 shots,
39 noise_config,
40 seed,
41 make_simulator,
42 )
43}
44
45#[pyfunction]
46pub fn run_cpu_full_state<'py>(
47 py: Python<'py>,
48 input: &Bound<'py, PyList>,
49 num_qubits: u32,
50 num_results: u32,
51 shots: u32,
52 noise_config: Option<&Bound<'py, NoiseConfig>>,
53 seed: Option<u32>,
54) -> PyResult<Py<PyAny>> {
55 use qdk_simulators::cpu_full_state_simulator::noise::Fault;
56 if noise_config.is_some() {
57 let make_simulator = |num_qubits, num_results, seed, noise| {
58 NoisySimulator::new(num_qubits as usize, num_results as usize, seed, noise)
59 };
60 py_run(
61 py,
62 input,
63 num_qubits,
64 num_results,
65 shots,
66 noise_config,
67 seed,
68 make_simulator,
69 )
70 } else {
71 let make_simulator =
72 |num_qubits, num_results, seed, _noise: Arc<CumulativeNoiseConfig<Fault>>| {
73 NoiselessSimulator::new(num_qubits as usize, num_results as usize, seed, ())
74 };
75 py_run(
76 py,
77 input,
78 num_qubits,
79 num_results,
80 shots,
81 noise_config,
82 seed,
83 make_simulator,
84 )
85 }
86}
87
88#[allow(clippy::too_many_arguments)]
89fn py_run<'py, SimulatorBuilder, Noise, S>(
90 py: Python<'py>,
91 input: &Bound<'py, PyList>,
92 num_qubits: u32,
93 num_results: u32,
94 shots: u32,
95 noise_config: Option<&Bound<'py, NoiseConfig>>,
96 seed: Option<u32>,
97 make_simulator: SimulatorBuilder,
98) -> PyResult<Py<PyAny>>
99where
100 SimulatorBuilder: Fn(u32, u32, u32, Arc<Noise>) -> S,
101 SimulatorBuilder: Send + Sync,
102 Noise: From<qdk_simulators::noise_config::NoiseConfig<f64, f64>> + Send + Sync,
103 S: Simulator,
104{
105 // Convert Python list to Vec<QirInstruction>.
106 let mut instructions: Vec<QirInstruction> = Vec::with_capacity(input.len());
107 for item in input.iter() {
108 let item: QirInstruction = item
109 .extract()
110 .map_err(|e| PyValueError::new_err(format!("expected QirInstruction: {e}")))?;
111 instructions.push(item);
112 }
113
114 // Convert NoiseConfig to a rust NoiseConfig.
115 let noise: qdk_simulators::noise_config::NoiseConfig<f64, f64> =
116 if let Some(noise_config) = noise_config {
117 unbind_noise_config(py, noise_config)
118 } else {
119 qdk_simulators::noise_config::NoiseConfig::NOISELESS
120 };
121
122 // Run the simulation.
123 let output = run(
124 &instructions,
125 num_qubits,
126 num_results,
127 shots,
128 seed,
129 noise,
130 make_simulator,
131 );
132
133 // Convert results back to Python.
134 let mut array = Vec::with_capacity(shots as usize);
135 for val in output {
136 array.push(
137 val.into_py_any(py).map_err(|e| {
138 PyValueError::new_err(format!("failed to create Python string: {e}"))
139 })?,
140 );
141 }
142
143 PyList::new(py, array)
144 .map_err(|e| PyValueError::new_err(format!("failed to create Python list: {e}")))?
145 .into_py_any(py)
146}
147
148fn run<SimulatorBuilder, Noise, S>(
149 instructions: &[QirInstruction],
150 num_qubits: u32,
151 num_results: u32,
152 shots: u32,
153 seed: Option<u32>,
154 mut noise: noise_config::NoiseConfig<f64, f64>,
155 make_simulator: SimulatorBuilder,
156) -> Vec<String>
157where
158 SimulatorBuilder: Fn(u32, u32, u32, Arc<Noise>) -> S,
159 SimulatorBuilder: Send + Sync,
160 Noise: From<noise_config::NoiseConfig<f64, f64>> + Send + Sync,
161 S: Simulator,
162{
163 if !noise.rz.is_noiseless() {
164 if noise.s.is_noiseless() {
165 noise.s = noise.rz.clone();
166 }
167 if noise.z.is_noiseless() {
168 noise.z = noise.rz.clone();
169 }
170 if noise.s_adj.is_noiseless() {
171 noise.s_adj = noise.rz.clone();
172 }
173 }
174
175 let noise: Noise = noise.into();
176 let noise = Arc::new(noise);
177
178 // Create a random number generator to generate the seed for each individual shot.
179 let mut rng = if let Some(seed) = seed {
180 StdRng::seed_from_u64(seed.into())
181 } else {
182 StdRng::from_entropy()
183 };
184
185 // run the shots
186 let output = (0..shots)
187 .map(|_| rng.r#gen())
188 .collect::<Vec<u32>>()
189 .par_iter()
190 .map(|shot_seed| {
191 let mut simulator = make_simulator(num_qubits, num_results, *shot_seed, noise.clone());
192 run_shot(instructions, &mut simulator);
193 simulator.take_measurements()
194 })
195 .collect::<Vec<_>>();
196
197 // Convert results to a list of strings.
198 let mut values = Vec::with_capacity(shots as usize);
199 for shot_result in output {
200 let mut buffer = String::with_capacity(shot_result.len());
201 for measurement in shot_result {
202 match measurement {
203 MeasurementResult::Zero => write!(&mut buffer, "0").expect("write should succeed"),
204 MeasurementResult::One => write!(&mut buffer, "1").expect("write should succeed"),
205 MeasurementResult::Loss => write!(&mut buffer, "L").expect("write should succeed"),
206 }
207 }
208 values.push(buffer);
209 }
210 values
211}
212
213fn run_shot<S: Simulator>(instructions: &[QirInstruction], sim: &mut S) {
214 for qir_inst in instructions {
215 match qir_inst {
216 QirInstruction::OneQubitGate(id, qubit) => match id {
217 QirInstructionId::I => {} // Identity gate is a no-op
218 QirInstructionId::H => sim.h(*qubit as usize),
219 QirInstructionId::X => sim.x(*qubit as usize),
220 QirInstructionId::Y => sim.y(*qubit as usize),
221 QirInstructionId::Z => sim.z(*qubit as usize),
222 QirInstructionId::S => sim.s(*qubit as usize),
223 QirInstructionId::SAdj => sim.s_adj(*qubit as usize),
224 QirInstructionId::SX => sim.sx(*qubit as usize),
225 QirInstructionId::SXAdj => sim.sx_adj(*qubit as usize),
226 QirInstructionId::T => sim.t(*qubit as usize),
227 QirInstructionId::TAdj => sim.t_adj(*qubit as usize),
228 QirInstructionId::Move => sim.mov(*qubit as usize),
229 QirInstructionId::RESET => sim.resetz(*qubit as usize),
230 _ => panic!("unsupported one-qubit gate: {id:?}"),
231 },
232 QirInstruction::TwoQubitGate(id, q1, q2) => match id {
233 QirInstructionId::CX => sim.cx(*q1 as usize, *q2 as usize),
234 QirInstructionId::CY => sim.cy(*q1 as usize, *q2 as usize),
235 QirInstructionId::CZ => sim.cz(*q1 as usize, *q2 as usize),
236 QirInstructionId::MZ | QirInstructionId::M => sim.mz(*q1 as usize, *q2 as usize),
237 QirInstructionId::MResetZ => sim.mresetz(*q1 as usize, *q2 as usize),
238 QirInstructionId::SWAP => sim.swap(*q1 as usize, *q2 as usize),
239 _ => panic!("unsupported two-qubits gate: {id:?}"),
240 },
241 QirInstruction::OneQubitRotationGate(id, angle, qubit) => match id {
242 QirInstructionId::RX => sim.rx(*angle, *qubit as usize),
243 QirInstructionId::RY => sim.ry(*angle, *qubit as usize),
244 QirInstructionId::RZ => sim.rz(*angle, *qubit as usize),
245 _ => {
246 panic!("unsupported one-qubit rotation gate: {id:?}");
247 }
248 },
249 QirInstruction::TwoQubitRotationGate(id, angle, qubit1, qubit2) => match id {
250 QirInstructionId::RXX => sim.rxx(*angle, *qubit1 as usize, *qubit2 as usize),
251 QirInstructionId::RYY => sim.ryy(*angle, *qubit1 as usize, *qubit2 as usize),
252 QirInstructionId::RZZ => sim.rzz(*angle, *qubit1 as usize, *qubit2 as usize),
253 _ => panic!("unsupported two-qubit rotation gate: {id:?}"),
254 },
255 QirInstruction::CorrelatedNoise(_id, intrinsic_id, qubits) => {
256 sim.correlated_noise_intrinsic(
257 *intrinsic_id,
258 &qubits.iter().map(|q| *q as usize).collect::<Vec<_>>(),
259 );
260 }
261 QirInstruction::OutputRecording(_id, _s, _tag) => {
262 // Ignore for now.
263 }
264 QirInstruction::ThreeQubitGate(..) => {
265 panic!("unsupported instruction: {qir_inst:?}")
266 }
267 }
268 }
269}
270