microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/add-link-to-qsharp-application

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/pip/tests/test_adaptive_cpu_noise.py

412lines · modecode

1# Copyright (c) Microsoft Corporation.
2# Licensed under the MIT License.
3
4"""Noise tests for the adaptive CPU bytecode interpreter.
5
6Each test targets noise injection by supplying hand-written Adaptive Profile
7QIR that exercises noise channels and encodes the expected result into a
8measurement outcome.
9
10This is a CPU counterpart to ``test_adaptive_gpu_noise.py``.
11"""
12
13from collections import Counter
14from typing import Optional, List
15import pytest
16from qsharp._simulation import run_qir, NoiseConfig, Result
17import qsharp.openqasm
18from typing import Literal
19
20
21# ---------------------------------------------------------------------------
22# Helpers
23# ---------------------------------------------------------------------------
24
25SHOTS = 100
26SIM_TYPES = ["cpu", "clifford"]
27
28
29def map_result_list_to_str(results: List[Result]):
30 results_str = ""
31 for r in results:
32 match r:
33 case Result.Zero:
34 results_str += "0"
35 case Result.One:
36 results_str += "1"
37 case Result.Loss:
38 results_str += "L"
39 return results_str
40
41
42def get_histogram(
43 qir_fragment: str,
44 *,
45 extra_decls: str = "",
46 num_qubits: int = 1,
47 num_results: int = 1,
48 noise: Optional[NoiseConfig] = None,
49 record: Optional[List[int]] = None,
50 shots=SHOTS,
51 sim_type: Literal["clifford", "cpu"] = "cpu",
52):
53 qir = format_qir(
54 qir_fragment,
55 extra_decls=extra_decls,
56 num_qubits=num_qubits,
57 num_results=num_results,
58 record=record,
59 )
60 results = map(
61 map_result_list_to_str, run_qir(qir, shots, noise, seed=42, type=sim_type)
62 )
63 return Counter(results)
64
65
66def check_result(
67 qir_fragment: str,
68 expected: str,
69 *,
70 extra_decls: str = "",
71 num_qubits: int = 1,
72 num_results: int = 1,
73 noise: Optional[NoiseConfig] = None,
74 record: Optional[List[int]] = None,
75 sim_type: Literal["clifford", "cpu"] = "cpu",
76):
77 """Assert every shot produces *expected*."""
78 counts = get_histogram(
79 qir_fragment,
80 extra_decls=extra_decls,
81 num_qubits=num_qubits,
82 num_results=num_results,
83 noise=noise,
84 record=record,
85 sim_type=sim_type,
86 )
87
88 assert counts == {
89 expected: SHOTS
90 }, f"Expected all {SHOTS} shots to be '{expected}', got {counts}"
91
92
93_DECLS = """\
94declare void @__quantum__qis__x__body(%Qubit*)
95declare void @__quantum__qis__h__body(%Qubit*)
96declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*)
97declare void @__quantum__qis__mz__body(%Qubit*, %Result*) #1
98declare void @__quantum__qis__reset__body(%Qubit*)
99declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
100declare void @__quantum__qis__z__body(%Qubit*)
101declare void @__quantum__qis__s__body(%Qubit*)
102declare void @__quantum__qis__t__body(%Qubit*)
103declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*)
104declare void @__quantum__qis__rz__body(double, %Qubit*)
105declare i1 @__quantum__qis__read_result__body(%Result*)
106declare void @__quantum__rt__tuple_record_output(i64, i8*)
107declare void @__quantum__rt__result_record_output(%Result*, i8*)
108declare void @__quantum__rt__initialize(i8*)
109"""
110
111
112def format_qir(
113 body: str,
114 *,
115 extra_decls: str = "",
116 num_qubits: int = 1,
117 num_results: int = 1,
118 record=None,
119):
120 if record is None:
121 record = range(num_results)
122 output_recording = (
123 f" call void @__quantum__rt__tuple_record_output(i64 {len(record)}, i8* null)"
124 )
125 for result_id in record:
126 output_recording += f"\n call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 {result_id} to %Result*), i8* null)"
127
128 return f"""\
129%Result = type opaque
130%Qubit = type opaque
131
132define i64 @ENTRYPOINT__main() #0 {{
133{body}
134{output_recording}
135 ret i64 0
136}}
137
138{_DECLS}
139{extra_decls}
140attributes #0 = {{ "entry_point" "qir_profiles"="adaptive_profile" "required_num_qubits"="{num_qubits}" "required_num_results"="{num_results}" }}
141attributes #1 = {{ "irreversible" }}
142"""
143
144
145# The purpose of this test is to inject noise in an identity gate, and assert its behavior.
146# Since QIS does not specify an identity gate, we use CNOT and inject noise in the target qubit.
147I_QIR = """
148entry:
149 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
150 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
151"""
152
153H_I_H_QIR = """
154entry:
155 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
156 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
157 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
158 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
159"""
160
161
162@pytest.mark.parametrize("sim_type", SIM_TYPES)
163def test_no_noise_on_i_yields_0(sim_type):
164 check_result(I_QIR, "0", num_qubits=2, sim_type=sim_type)
165
166
167@pytest.mark.parametrize("sim_type", SIM_TYPES)
168def test_x_noise_on_i_yields_1(sim_type):
169 noise = NoiseConfig()
170 noise.cx.ix = 1.0
171 check_result(I_QIR, "1", num_qubits=2, noise=noise, sim_type=sim_type)
172
173
174@pytest.mark.parametrize("sim_type", SIM_TYPES)
175def test_y_noise_on_i_yields_1(sim_type):
176 noise = NoiseConfig()
177 noise.cx.iy = 1.0
178 check_result(I_QIR, "1", num_qubits=2, noise=noise, sim_type=sim_type)
179
180
181@pytest.mark.parametrize("sim_type", SIM_TYPES)
182def test_z_noise_on_i_yields_0(sim_type):
183 noise = NoiseConfig()
184 noise.cx.iz = 1.0
185 check_result(I_QIR, "0", num_qubits=2, noise=noise, sim_type=sim_type)
186
187
188@pytest.mark.parametrize("sim_type", SIM_TYPES)
189def test_x_noise_on_h_i_h_yields_0(sim_type):
190 noise = NoiseConfig()
191 noise.cx.ix = 1.0
192 check_result(H_I_H_QIR, "0", num_qubits=2, noise=noise, sim_type=sim_type)
193
194
195@pytest.mark.parametrize("sim_type", SIM_TYPES)
196def test_y_noise_on_h_i_h_yields_1(sim_type):
197 noise = NoiseConfig()
198 noise.cx.iy = 1.0
199 check_result(H_I_H_QIR, "1", num_qubits=2, noise=noise, sim_type=sim_type)
200
201
202@pytest.mark.parametrize("sim_type", SIM_TYPES)
203def test_z_noise_on_h_i_h_yields_1(sim_type):
204 noise = NoiseConfig()
205 noise.cx.iz = 1.0
206 check_result(H_I_H_QIR, "1", num_qubits=2, noise=noise, sim_type=sim_type)
207
208
209@pytest.mark.parametrize("sim_type", SIM_TYPES)
210def test_probabilistic_x_noise(sim_type):
211 noise = NoiseConfig()
212 noise.cx.ix = 0.5
213 counts = get_histogram(
214 I_QIR, shots=1000, num_qubits=2, noise=noise, sim_type=sim_type
215 )
216
217 assert counts["0"] > 400, f"Expected ~500 '0' results, got {counts['0']}"
218 assert counts["1"] > 400, f"Expected ~500 '1' results, got {counts['1']}"
219
220
221QASM_WITH_CORRELATED_NOISE = """
222OPENQASM 3.0;
223include "stdgates.inc";
224
225@qdk.qir.noise_intrinsic
226gate test_noise_intrinsic q0, q1, q2 {}
227
228qubit[3] qs;
229x qs[1];
230test_noise_intrinsic qs[0], qs[1], qs[2];
231bit[3] res = measure qs;
232"""
233
234QIR_WITH_CORRELATED_NOISE = qsharp.openqasm.compile(
235 QASM_WITH_CORRELATED_NOISE,
236 output_semantics=qsharp.openqasm.OutputSemantics.OpenQasm,
237 target_profile=qsharp.TargetProfile.Adaptive_RIF,
238)
239
240
241@pytest.mark.parametrize("sim_type", SIM_TYPES)
242def test_noise_intrinsics_noiseless(sim_type):
243 output = run_qir(QIR_WITH_CORRELATED_NOISE, shots=1, noise=None, type=sim_type)
244 assert output == [[Result.Zero, Result.One, Result.Zero]]
245
246
247@pytest.mark.parametrize("sim_type", SIM_TYPES)
248def test_noise_intrinsics_noisy(sim_type):
249 noise = NoiseConfig()
250 table = noise.intrinsic("test_noise_intrinsic", 3)
251 table.yyy = 1.0
252 output = run_qir(QIR_WITH_CORRELATED_NOISE, shots=1, noise=noise, type=sim_type)
253 assert output == [[Result.One, Result.Zero, Result.One]]
254
255
256@pytest.mark.parametrize("sim_type", SIM_TYPES)
257def test_noise_intrinsics_load_csv_dir(sim_type):
258 noise = NoiseConfig()
259 noise.load_csv_dir("./csv_dir_test")
260 output = run_qir(QIR_WITH_CORRELATED_NOISE, shots=1, noise=noise, type=sim_type)
261 assert output == [[Result.One, Result.Zero, Result.One]]
262
263
264NOISE_INTRINSICS_WITH_REGISTERS_QIR = r"""
265%Result = type opaque
266%Qubit = type opaque
267
268@0 = internal constant [4 x i8] c"0_a\00"
269@1 = internal constant [6 x i8] c"1_a0r\00"
270@2 = internal constant [6 x i8] c"2_a1r\00"
271@3 = internal constant [6 x i8] c"3_a2r\00"
272
273define i64 @ENTRYPOINT__main() #0 {
274block_0:
275 %q1 = inttoptr i64 0 to %Qubit*
276 %q2 = inttoptr i64 1 to %Qubit*
277 %q3 = inttoptr i64 2 to %Qubit*
278 call void @__quantum__rt__initialize(i8* null)
279 call void @__quantum__qis__x__body(%Qubit* %q2)
280 call void @test_noise_intrinsic(%Qubit* %q1, %Qubit* %q2, %Qubit* %q3)
281 call void @__quantum__qis__m__body(%Qubit* %q1, %Result* inttoptr (i64 0 to %Result*))
282 call void @__quantum__qis__m__body(%Qubit* %q2, %Result* inttoptr (i64 1 to %Result*))
283 call void @__quantum__qis__m__body(%Qubit* %q3, %Result* inttoptr (i64 2 to %Result*))
284 call void @__quantum__rt__array_record_output(i64 3, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
285 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
286 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
287 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 2 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @3, i64 0, i64 0))
288 ret i64 0
289}
290
291declare void @__quantum__rt__initialize(i8*)
292declare void @__quantum__qis__x__body(%Qubit*)
293declare void @test_noise_intrinsic(%Qubit*, %Qubit*, %Qubit*) #2
294declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
295declare void @__quantum__rt__array_record_output(i64, i8*)
296declare void @__quantum__rt__result_record_output(%Result*, i8*)
297
298attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="3" "required_num_results"="3" }
299attributes #1 = { "irreversible" }
300attributes #2 = { "qdk_noise" }
301
302!llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
303
304!0 = !{i32 1, !"qir_major_version", i32 1}
305!1 = !{i32 7, !"qir_minor_version", i32 0}
306!2 = !{i32 1, !"dynamic_qubit_management", i1 false}
307!3 = !{i32 1, !"dynamic_result_management", i1 false}
308!4 = !{i32 5, !"int_computations", !{!"i64"}}
309!5 = !{i32 5, !"float_computations", !{!"double"}}
310"""
311
312
313@pytest.mark.parametrize("sim_type", SIM_TYPES)
314def test_noise_intrinsics_with_registers_noisy(sim_type):
315 noise = NoiseConfig()
316 table = noise.intrinsic("test_noise_intrinsic", 3)
317 table.yyy = 1.0
318 output = run_qir(
319 NOISE_INTRINSICS_WITH_REGISTERS_QIR, shots=1, noise=noise, type=sim_type
320 )
321 assert output == [[Result.One, Result.Zero, Result.One]]
322
323
324# --- Tests for varied qubit counts (1, 2, 5) ---
325
326QASM_NOISE_1Q = """
327OPENQASM 3.0;
328include "stdgates.inc";
329
330@qdk.qir.noise_intrinsic
331gate noise_1q q0 {}
332
333qubit q;
334noise_1q q;
335bit res = measure q;
336"""
337
338QIR_NOISE_1Q = qsharp.openqasm.compile(
339 QASM_NOISE_1Q,
340 output_semantics=qsharp.openqasm.OutputSemantics.OpenQasm,
341 target_profile=qsharp.TargetProfile.Adaptive_RIF,
342)
343
344
345@pytest.mark.parametrize("sim_type", SIM_TYPES)
346def test_noise_intrinsic_1q_x_flip(sim_type):
347 noise = NoiseConfig()
348 table = noise.intrinsic("noise_1q", 1)
349 table.x = 1.0
350 output = run_qir(QIR_NOISE_1Q, shots=1, noise=noise, type=sim_type)
351 assert output == [Result.One]
352
353
354QASM_NOISE_2Q = """
355OPENQASM 3.0;
356include "stdgates.inc";
357
358@qdk.qir.noise_intrinsic
359gate noise_2q q0, q1 {}
360
361qubit[2] qs;
362x qs[0];
363noise_2q qs[0], qs[1];
364bit[2] res = measure qs;
365"""
366
367QIR_NOISE_2Q = qsharp.openqasm.compile(
368 QASM_NOISE_2Q,
369 output_semantics=qsharp.openqasm.OutputSemantics.OpenQasm,
370 target_profile=qsharp.TargetProfile.Adaptive_RIF,
371)
372
373
374@pytest.mark.parametrize("sim_type", SIM_TYPES)
375def test_noise_intrinsic_2q_xx_flip(sim_type):
376 noise = NoiseConfig()
377 table = noise.intrinsic("noise_2q", 2)
378 table.xx = 1.0
379 # qs[0] was |1>, qs[1] was |0> -> XX flips both -> qs[0]=|0>, qs[1]=|1>
380 output = run_qir(QIR_NOISE_2Q, shots=1, noise=noise, type=sim_type)
381 assert output == [[Result.Zero, Result.One]]
382
383
384QASM_NOISE_5Q = """
385OPENQASM 3.0;
386include "stdgates.inc";
387
388@qdk.qir.noise_intrinsic
389gate noise_5q q0, q1, q2, q3, q4 {}
390
391qubit[5] qs;
392x qs[1];
393x qs[3];
394noise_5q qs[0], qs[1], qs[2], qs[3], qs[4];
395bit[5] res = measure qs;
396"""
397
398QIR_NOISE_5Q = qsharp.openqasm.compile(
399 QASM_NOISE_5Q,
400 output_semantics=qsharp.openqasm.OutputSemantics.OpenQasm,
401 target_profile=qsharp.TargetProfile.Adaptive_RIF,
402)
403
404
405@pytest.mark.parametrize("sim_type", SIM_TYPES)
406def test_noise_intrinsic_5q_xxxxx_flip(sim_type):
407 noise = NoiseConfig()
408 table = noise.intrinsic("noise_5q", 5)
409 table.xxxxx = 1.0
410 # Initial: |01010> -> XXXXX flips all -> |10101>
411 output = run_qir(QIR_NOISE_5Q, shots=1, noise=noise, type=sim_type)
412 assert output == [[Result.One, Result.Zero, Result.One, Result.Zero, Result.One]]
413