microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
iadavis/pipeline-issue-debugging

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/pip/src/qir_simulation/cpu_simulators.rs

266lines · modecode

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