microsoft/qdk
Publicmirrored fromhttps://github.com/microsoft/qdkAvailable
source/pip/tests/test_clifford_simulator.py
247lines · modecode
| 1 | # Copyright (c) Microsoft Corporation. |
| 2 | # Licensed under the MIT License. |
| 3 | |
| 4 | from pathlib import Path |
| 5 | import pyqir |
| 6 | |
| 7 | import qsharp |
| 8 | from qsharp._simulation import run_qir_clifford, NoiseConfig |
| 9 | from qsharp._device._atom import NeutralAtomDevice |
| 10 | from qsharp._device._atom._decomp import DecomposeRzAnglesToCliffordGates |
| 11 | from qsharp._device._atom._validate import ValidateNoConditionalBranches |
| 12 | from qsharp import TargetProfile, Result |
| 13 | |
| 14 | current_file_path = Path(__file__) |
| 15 | # Get the directory of the current file |
| 16 | current_dir = current_file_path.parent |
| 17 | |
| 18 | # Tests for the Q# noisy simulator. |
| 19 | |
| 20 | |
| 21 | def transform_to_clifford(input) -> str: |
| 22 | native_qir = NeutralAtomDevice().compile(input) |
| 23 | module = pyqir.Module.from_ir(pyqir.Context(), str(native_qir)) |
| 24 | ValidateNoConditionalBranches().run(module) |
| 25 | DecomposeRzAnglesToCliffordGates().run(module) |
| 26 | return str(module) |
| 27 | |
| 28 | |
| 29 | def read_file(file_name: str) -> str: |
| 30 | return Path(file_name).read_text(encoding="utf-8") |
| 31 | |
| 32 | |
| 33 | def read_file_relative(file_name: str) -> str: |
| 34 | return Path(current_dir / file_name).read_text(encoding="utf-8") |
| 35 | |
| 36 | |
| 37 | def test_smoke(): |
| 38 | qsharp.init(target_profile=TargetProfile.Base) |
| 39 | qsharp.eval(read_file_relative("CliffordIsing.qs")) |
| 40 | |
| 41 | input = qsharp.compile( |
| 42 | "IsingModel2DEvolution(5, 5, PI() / 2.0, PI() / 2.0, 5.0, 5)" |
| 43 | ) |
| 44 | input = transform_to_clifford(input) |
| 45 | output = run_qir_clifford(input, 10, NoiseConfig()) |
| 46 | print(output) |
| 47 | |
| 48 | |
| 49 | def test_1224_clifford_ising(): |
| 50 | qsharp.init(target_profile=TargetProfile.Base) |
| 51 | qsharp.eval(read_file_relative("CliffordIsing.qs")) |
| 52 | |
| 53 | input = qsharp.compile( |
| 54 | "IsingModel2DEvolution(20, 50, PI() / 2.0, PI() / 2.0, 5.0, 5)" |
| 55 | ) |
| 56 | qir = transform_to_clifford(input) |
| 57 | |
| 58 | output = run_qir_clifford(qir, 1, NoiseConfig()) |
| 59 | |
| 60 | print(output) |
| 61 | |
| 62 | |
| 63 | def test_million(): |
| 64 | qsharp.init(target_profile=TargetProfile.Base) |
| 65 | qsharp.eval(read_file_relative("CliffordCalls.qs")) |
| 66 | |
| 67 | ir = qsharp.compile("Main()") |
| 68 | output = run_qir_clifford(str(ir), 1, NoiseConfig()) |
| 69 | print(output) |
| 70 | |
| 71 | |
| 72 | def test_s_noise_inherits_from_rz(): |
| 73 | qsharp.init(target_profile=TargetProfile.Base) |
| 74 | qsharp.eval("operation Main() : Result { use q = Qubit(); S(q); MResetZ(q) }") |
| 75 | ir = qsharp.compile("Main()") |
| 76 | noise = NoiseConfig() |
| 77 | noise.rz.x = 1.0 |
| 78 | output = run_qir_clifford(str(ir), 1, noise) |
| 79 | assert output == [Result.One] |
| 80 | |
| 81 | |
| 82 | def test_z_noise_inherits_from_rz(): |
| 83 | qsharp.init(target_profile=TargetProfile.Base) |
| 84 | qsharp.eval("operation Main() : Result { use q = Qubit(); Z(q); MResetZ(q) }") |
| 85 | ir = qsharp.compile("Main()") |
| 86 | noise = NoiseConfig() |
| 87 | noise.rz.x = 1.0 |
| 88 | output = run_qir_clifford(str(ir), 1, noise) |
| 89 | assert output == [Result.One] |
| 90 | |
| 91 | |
| 92 | def test_s_adj_noise_inherits_from_rz(): |
| 93 | qsharp.init(target_profile=TargetProfile.Base) |
| 94 | qsharp.eval( |
| 95 | "operation Main() : Result { use q = Qubit(); Adjoint S(q); MResetZ(q) }" |
| 96 | ) |
| 97 | ir = qsharp.compile("Main()") |
| 98 | noise = NoiseConfig() |
| 99 | noise.rz.x = 1.0 |
| 100 | output = run_qir_clifford(str(ir), 1, noise) |
| 101 | assert output == [Result.One] |
| 102 | |
| 103 | |
| 104 | def test_program_with_branching_fails(): |
| 105 | qsharp.init(target_profile=TargetProfile.Adaptive_RI) |
| 106 | qsharp.eval( |
| 107 | """ |
| 108 | operation Main() : Result { |
| 109 | use q = Qubit(); |
| 110 | H(q); |
| 111 | if (MResetZ(q) == One) { |
| 112 | X(q); |
| 113 | } |
| 114 | return MResetZ(q); |
| 115 | } |
| 116 | """ |
| 117 | ) |
| 118 | ir = qsharp.compile("Main()") |
| 119 | try: |
| 120 | run_qir_clifford(str(ir), 1, NoiseConfig()) |
| 121 | assert False, "Expected ValueError for branching control flow" |
| 122 | except ValueError as e: |
| 123 | assert ( |
| 124 | "simulation of programs with branching control flow is not supported" |
| 125 | in str(e) |
| 126 | ) |
| 127 | |
| 128 | |
| 129 | def test_program_with_unconditional_branching_succeeds(): |
| 130 | qir = """ |
| 131 | %Result = type opaque |
| 132 | %Qubit = type opaque |
| 133 | |
| 134 | @empty_tag = internal constant [1 x i8] c"\\00" |
| 135 | @0 = internal constant [6 x i8] c"0_a0r\\00" |
| 136 | @1 = internal constant [6 x i8] c"1_a1r\\00" |
| 137 | |
| 138 | define i64 @ENTRYPOINT__main() #0 { |
| 139 | block_0: |
| 140 | call void @__quantum__rt__initialize(i8* null) |
| 141 | br label %block_1 |
| 142 | block_1: |
| 143 | call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) |
| 144 | call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) |
| 145 | br label %block_2 |
| 146 | block_2: |
| 147 | call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) |
| 148 | call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) |
| 149 | br label %block_3 |
| 150 | block_3: |
| 151 | call void @__quantum__rt__array_record_output(i64 2, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @empty_tag, i64 0, i64 0)) |
| 152 | call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i64 0, i64 0)) |
| 153 | call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0)) |
| 154 | ret i64 0 |
| 155 | } |
| 156 | |
| 157 | declare void @__quantum__rt__initialize(i8*) |
| 158 | |
| 159 | declare void @__quantum__qis__h__body(%Qubit*) |
| 160 | |
| 161 | declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*) |
| 162 | |
| 163 | declare void @__quantum__rt__array_record_output(i64, i8*) |
| 164 | |
| 165 | declare void @__quantum__rt__result_record_output(%Result*, i8*) |
| 166 | |
| 167 | declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1 |
| 168 | |
| 169 | attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="2" } |
| 170 | attributes #1 = { "irreversible" } |
| 171 | |
| 172 | ; module flags |
| 173 | |
| 174 | !llvm.module.flags = !{!0, !1, !2, !3} |
| 175 | |
| 176 | !0 = !{i32 1, !"qir_major_version", i32 1} |
| 177 | !1 = !{i32 7, !"qir_minor_version", i32 0} |
| 178 | !2 = !{i32 1, !"dynamic_qubit_management", i1 false} |
| 179 | !3 = !{i32 1, !"dynamic_result_management", i1 false} |
| 180 | """ |
| 181 | |
| 182 | output = run_qir_clifford(qir, 1, NoiseConfig()) |
| 183 | assert output == [[Result.Zero, Result.Zero]] or output == [ |
| 184 | [Result.One, Result.One] |
| 185 | ] |
| 186 | |
| 187 | |
| 188 | def test_cy_direct_qir(): |
| 189 | qir = """ |
| 190 | %Result = type opaque |
| 191 | %Qubit = type opaque |
| 192 | |
| 193 | @empty_tag = internal constant [1 x i8] c"\\00" |
| 194 | @0 = internal constant [6 x i8] c"0_a0r\\00" |
| 195 | @1 = internal constant [6 x i8] c"1_a1r\\00" |
| 196 | |
| 197 | define i64 @ENTRYPOINT__main() #0 { |
| 198 | block_0: |
| 199 | call void @__quantum__rt__initialize(i8* null) |
| 200 | call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) |
| 201 | call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) |
| 202 | call void @__quantum__qis__s__body(%Qubit* inttoptr (i64 1 to %Qubit*)) |
| 203 | call void @__quantum__qis__cy__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) |
| 204 | call void @__quantum__qis__s__adj(%Qubit* inttoptr (i64 1 to %Qubit*)) |
| 205 | call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) |
| 206 | call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) |
| 207 | call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) |
| 208 | call void @__quantum__rt__array_record_output(i64 2, i8* getelementptr inbounds ([1 x i8], [1 x i8]* @empty_tag, i64 0, i64 0)) |
| 209 | call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i64 0, i64 0)) |
| 210 | call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0)) |
| 211 | ret i64 0 |
| 212 | } |
| 213 | |
| 214 | declare void @__quantum__rt__initialize(i8*) |
| 215 | |
| 216 | declare void @__quantum__qis__h__body(%Qubit*) |
| 217 | |
| 218 | declare void @__quantum__qis__s__body(%Qubit*) |
| 219 | |
| 220 | declare void @__quantum__qis__s__adj(%Qubit*) |
| 221 | |
| 222 | declare void @__quantum__qis__cy__body(%Qubit*, %Qubit*) |
| 223 | |
| 224 | declare void @__quantum__rt__array_record_output(i64, i8*) |
| 225 | |
| 226 | declare void @__quantum__rt__result_record_output(%Result*, i8*) |
| 227 | |
| 228 | declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1 |
| 229 | |
| 230 | attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="2" } |
| 231 | attributes #1 = { "irreversible" } |
| 232 | |
| 233 | ; module flags |
| 234 | |
| 235 | !llvm.module.flags = !{!0, !1, !2, !3} |
| 236 | |
| 237 | !0 = !{i32 1, !"qir_major_version", i32 1} |
| 238 | !1 = !{i32 7, !"qir_minor_version", i32 0} |
| 239 | !2 = !{i32 1, !"dynamic_qubit_management", i1 false} |
| 240 | !3 = !{i32 1, !"dynamic_result_management", i1 false} |
| 241 | """ |
| 242 | |
| 243 | # Do not go through Neutral Atom device compilation since we want to test CY. |
| 244 | output = run_qir_clifford(qir, 50, NoiseConfig()) |
| 245 | # This test should deterministically produce Zero. |
| 246 | # If CZ or CX is executed instead of CY, then some measurements will produce One. |
| 247 | assert all(shot[1] == Result.Zero for shot in output) |