microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.18.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/noisy_simulator/src/tests/noiseless_tests.rs

325lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use crate::{
5 instrument::Instrument,
6 operation::{operation, Operation},
7 NoisySimulator,
8};
9use num_complex::Complex;
10
11use super::assert_approx_eq;
12
13/// Returns an H gate.
14fn noiseless_h() -> Operation {
15 let f = 0.5_f64.sqrt();
16 operation!([f, f;
17 f, -f;])
18 .expect("operation should be valid")
19}
20
21/// Returns a CNOT gate.
22fn noiseless_cnot() -> Operation {
23 operation!([1., 0., 0., 0.;
24 0., 1., 0., 0.;
25 0., 0., 0., 1.;
26 0., 0., 1., 0.;])
27 .expect("operation should be valid")
28}
29
30/// Returns the 0-projection of an MZ measurement.
31fn noiseless_mz0() -> Operation {
32 operation!([1., 0.;
33 0., 0.;])
34 .expect("operation should be valid")
35}
36
37/// Returns the 1-projection of an MZ measurement.
38fn noiseless_mz1() -> Operation {
39 operation!([0., 0.;
40 0., 1.;])
41 .expect("operation should be valid")
42}
43
44/// Returns an MZ measurement.
45pub(super) fn noiseless_mz() -> Instrument {
46 Instrument::new(vec![noiseless_mz0(), noiseless_mz1()]).expect("instrument should be valid")
47}
48
49pub fn check_measuring_plus_state_yields_zero_with_50_percent_probability<NS: NoisySimulator>() {
50 let h = noiseless_h();
51 let mz = noiseless_mz();
52 let mut sim = NS::new(1);
53 sim.apply_operation(&h, &[0])
54 .expect("operation should succeed");
55
56 // Random samples less than 0.5 should yield a 0-measurement.
57 let measurement = sim
58 .sample_instrument_with_distribution(&mz, &[0], 0.49999)
59 .expect("measurement should succeed");
60 assert_eq!(measurement, 0);
61}
62
63pub fn check_measuring_plus_state_yields_one_with_50_percent_probability<NS: NoisySimulator>() {
64 let h = noiseless_h();
65 let mz = noiseless_mz();
66 let mut sim = NS::new(1);
67 sim.apply_operation(&h, &[0])
68 .expect("operation should succeed");
69
70 // Random samples greater than 0.5 should yield a 1-measurement.
71 let measurement = sim
72 .sample_instrument_with_distribution(&mz, &[0], 0.50001)
73 .expect("measurement should succeed");
74 assert_eq!(measurement, 1);
75}
76
77/// Check that both measurements in a Bell Pair yield the same result.
78pub fn check_bell_pair_sampling_yields_same_outcome_for_both_qubits<NS: NoisySimulator>(seed: u64) {
79 let (h, cnot, mz) = (noiseless_h(), noiseless_cnot(), noiseless_mz());
80 let mut sim = NS::new_with_seed(2, seed);
81
82 // Make a Bell Pair.
83 sim.apply_operation(&h, &[0])
84 .expect("operation should succeed");
85 sim.apply_operation(&cnot, &[1, 0])
86 .expect("operation should succeed");
87
88 // Measure both qubits.
89 let m1 = sim
90 .sample_instrument(&mz, &[0])
91 .expect("measurement should succeed");
92 let m2 = sim
93 .sample_instrument(&mz, &[1])
94 .expect("measurement should succeed");
95
96 // Check that both measurements yield the same result.
97 assert_eq!(m1, m2);
98}
99
100/// Project both qubits of a Bell Pair on the mz0 direction.
101/// The trace of the system (i.e. the probability of finding
102/// the quantum system in this state) should be 0.5.
103pub fn check_bell_pair_projection_on_mz0_yields_50_percent_probability_trace<NS: NoisySimulator>() {
104 let (h, cnot, mz0) = (noiseless_h(), noiseless_cnot(), noiseless_mz0());
105 let mut sim = NS::new(2);
106
107 // Make a Bell Pair.
108 sim.apply_operation(&h, &[0])
109 .expect("operation should succeed");
110 sim.apply_operation(&cnot, &[1, 0])
111 .expect("operation should succeed");
112
113 // Project both qubits on the mz0 direction.
114 sim.apply_operation(&mz0, &[0])
115 .expect("operation should succeed");
116 sim.apply_operation(&mz0, &[1])
117 .expect("operation should succeed");
118 assert_approx_eq(0.5, sim.trace_change().expect("state should be valid"));
119
120 // Repeating the projection twice should yield the same result.
121 sim.apply_operation(&mz0, &[0])
122 .expect("operation should succeed");
123 sim.apply_operation(&mz0, &[1])
124 .expect("operation should succeed");
125 assert_approx_eq(0.5, sim.trace_change().expect("state should be valid"));
126}
127
128/// Project both qubits of a Bell Pair on the mz1 direction.
129/// The trace of the system (i.e. the probability of finding
130/// the quantum system in this state) should be 0.5.
131pub fn check_bell_pair_projection_on_mz1_yields_50_percent_probability_trace<NS: NoisySimulator>() {
132 let (h, cnot, mz1) = (noiseless_h(), noiseless_cnot(), noiseless_mz1());
133 let mut sim = NS::new(2);
134
135 // Make a Bell Pair.
136 sim.apply_operation(&h, &[0])
137 .expect("operation should succeed");
138 sim.apply_operation(&cnot, &[1, 0])
139 .expect("operation should succeed");
140
141 // Project both qubits on the mz1 direction.
142 sim.apply_operation(&mz1, &[0])
143 .expect("operation should succeed");
144 sim.apply_operation(&mz1, &[1])
145 .expect("operation should succeed");
146 assert_approx_eq(0.5, sim.trace_change().expect("state should be valid"));
147
148 // Repeating the projection twice should yield the same result.
149 sim.apply_operation(&mz1, &[0])
150 .expect("operation should succeed");
151 sim.apply_operation(&mz1, &[1])
152 .expect("operation should succeed");
153 assert_approx_eq(0.5, sim.trace_change().expect("state should be valid"));
154}
155
156/// Project one qubit of a Bell Pair on the mz0 direction and the other on the mz1 direction.
157/// This should yield a 0-probability error.
158pub fn check_bell_pair_projection_on_oposite_directions_yields_an_error<NS: NoisySimulator>() {
159 let (h, cnot, mz0, mz1) = (
160 noiseless_h(),
161 noiseless_cnot(),
162 noiseless_mz0(),
163 noiseless_mz1(),
164 );
165 let mut sim = NS::new(2);
166
167 // Make a Bell Pair.
168 sim.apply_operation(&h, &[0])
169 .expect("operation should succeed");
170 sim.apply_operation(&cnot, &[1, 0])
171 .expect("operation should succeed");
172
173 // Project first qubit on the mz0 direction.
174 sim.apply_operation(&mz0, &[0])
175 .expect("operation should succeed");
176
177 // Project second qubit on the mz1 direction.
178 // This should yield a 0-probability error.
179 sim.apply_operation(&mz1, &[1])
180 .expect("operation should fail");
181}
182
183/// Check that projecting the target qubit in a CRX gate on the mz0 direction yields the right probabilities.
184pub fn check_crx_gate_projection_on_mz0_yields_right_probabilities<NS: NoisySimulator>() {
185 let (h, mz0, mz1) = (noiseless_h(), noiseless_mz0(), noiseless_mz1());
186 let probabilities: Vec<f64> = vec![0.05, 0.1, 0.3, 0.7, 0.8, 0.9, 0.99];
187
188 // A CRX gate (Controlled Rotation around X axis).
189 let crx = |t: f64| {
190 let c = t.cos();
191 let s = t.sin() * Complex::I;
192 operation!([1., 0., 0., 0.;
193 0., 1., 0., 0.;
194 0., 0., c, s;
195 0., 0., s, c;])
196 .expect("operation should be valid")
197 };
198
199 for p in &probabilities {
200 let t = p.sqrt().acos();
201 let mut sim = NS::new(2);
202
203 // Apply CRX gate
204 sim.apply_operation(&h, &[0])
205 .expect("operation should succeed");
206 sim.apply_operation(&crx(0.3 * t), &[1, 0])
207 .expect("operation should succeed");
208 sim.apply_operation(&crx(0.7 * t), &[1, 0])
209 .expect("operation should succeed");
210
211 sim.apply_operation(&mz1, &[0])
212 .expect("operation should succeed");
213 assert_approx_eq(0.5, sim.trace_change().expect("state should be valid"));
214
215 // Project target qubit on mz0 and check the trace
216 // (the probability of finding the system in that state).
217 sim.apply_operation(&mz0, &[1])
218 .expect("operation should succeed");
219 assert_approx_eq(0.5 * *p, sim.trace_change().expect("state should be valid"));
220
221 // Repeating a projection should yield the same result.
222 sim.apply_operation(&mz0, &[1])
223 .expect("operation should succeed");
224 assert_approx_eq(0.5 * *p, sim.trace_change().expect("state should be valid"));
225 }
226}
227
228/// Check that projecting the target qubit in a CRX gate on the mz1 direction yields the right probabilities.
229pub fn check_crx_gate_projection_on_mz1_yields_right_probabilities<NS: NoisySimulator>() {
230 let (h, mz1) = (noiseless_h(), noiseless_mz1());
231 let probabilities: Vec<f64> = vec![0.05, 0.1, 0.3, 0.7, 0.8, 0.9, 0.99];
232
233 // A CRX gate (Controlled Rotation around X axis).
234 let crx = |t: f64| {
235 let c = t.cos();
236 let s = t.sin() * Complex::I;
237 operation!([1., 0., 0., 0.;
238 0., 1., 0., 0.;
239 0., 0., c, s;
240 0., 0., s, c;])
241 .expect("operation should be valid")
242 };
243
244 for p in &probabilities {
245 let t = p.sqrt().acos();
246 let mut sim = NS::new(2);
247
248 // Apply CRX gate
249 sim.apply_operation(&h, &[0])
250 .expect("operation should succeed");
251 sim.apply_operation(&crx(0.3 * t), &[1, 0])
252 .expect("operation should succeed");
253 sim.apply_operation(&crx(0.7 * t), &[1, 0])
254 .expect("operation should succeed");
255
256 sim.apply_operation(&mz1, &[0])
257 .expect("operation should succeed");
258 assert_approx_eq(0.5, sim.trace_change().expect("state should be valid"));
259
260 // Project target qubit on mz1 and check the trace
261 // (the probability of finding the system in that state).
262 sim.apply_operation(&mz1, &[1])
263 .expect("operation should succeed");
264 assert_approx_eq(
265 0.5 * (1. - *p),
266 sim.trace_change().expect("state should be valid"),
267 );
268
269 // Repeating a projection should yield the same result.
270 sim.apply_operation(&mz1, &[1])
271 .expect("operation should succeed");
272 assert_approx_eq(
273 0.5 * (1. - *p),
274 sim.trace_change().expect("state should be valid"),
275 );
276 }
277}
278
279/// Check that two consecutive MZ on the same qubit yield the same outcome.
280pub fn check_two_consecutive_mz_yield_same_outcome<NS: NoisySimulator>(seed: u64) {
281 let h = noiseless_h();
282 let mz = noiseless_mz();
283 let mut sim = NS::new_with_seed(1, seed);
284
285 sim.apply_operation(&h, &[0])
286 .expect("operation should succeed");
287 let outcome_0 = sim
288 .sample_instrument(&mz, &[0])
289 .expect("measurement should succeed");
290 let outcome_1 = sim
291 .sample_instrument(&mz, &[0])
292 .expect("measurement should succeed");
293 assert_eq!(outcome_0, outcome_1);
294}
295
296pub fn check_alternating_mz_and_mx_yield_right_probabilities<NS: NoisySimulator>() {
297 let h = noiseless_h();
298 let mz = noiseless_mz();
299 let mx = Instrument::new(vec![
300 operation!([0.5, 0.5;
301 0.5, 0.5;])
302 .expect("operation should be valid"),
303 operation!([ 0.5, -0.5;
304 -0.5, 0.5;])
305 .expect("operation should be valid"),
306 ])
307 .expect("instrument should be valid");
308
309 let mut sim = NS::new(1);
310 sim.apply_operation(&h, &[0])
311 .expect("operation should succeed");
312 let mut prob = 1.0;
313
314 // Alternate MZ and MX 5 times.
315 for _ in 0..5 {
316 sim.sample_instrument(&mz, &[0])
317 .expect("measurement should succeed");
318 prob *= 0.5;
319 assert_approx_eq(prob, sim.trace_change().expect("state should be valid"));
320 sim.sample_instrument(&mx, &[0])
321 .expect("measurement should succeed");
322 prob *= 0.5;
323 assert_approx_eq(prob, sim.trace_change().expect("state should be valid"));
324 }
325}
326