microsoft/qdk

Public

mirrored fromhttps://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/pip/tests/test_interpreter.py

493lines ยท modecode

1# Copyright (c) Microsoft Corporation.
2# Licensed under the MIT License.
3
4from textwrap import dedent
5from qsharp._native import (
6 Interpreter,
7 Result,
8 Pauli,
9 QSharpError,
10 TargetProfile,
11)
12import pytest
13
14
15# Tests for the native Q# interpreter class
16
17
18def test_output() -> None:
19 e = Interpreter(TargetProfile.Unrestricted)
20
21 def callback(output):
22 nonlocal called
23 called = True
24 assert output.__repr__() == "Hello, world!"
25
26 called = False
27 value = e.interpret('Message("Hello, world!")', callback)
28 assert called
29
30
31def test_dump_output() -> None:
32 e = Interpreter(TargetProfile.Unrestricted)
33
34 def callback(output):
35 nonlocal called
36 called = True
37 assert output.__repr__() == "STATE:\n|10โŸฉ: 1.0000+0.0000๐‘–"
38
39 called = False
40 value = e.interpret(
41 """
42 use q1 = Qubit();
43 use q2 = Qubit();
44 X(q1);
45 Microsoft.Quantum.Diagnostics.DumpMachine();
46 ResetAll([q1, q2]);
47 """,
48 callback,
49 )
50 assert called
51
52
53def test_quantum_seed() -> None:
54 e = Interpreter(TargetProfile.Unrestricted)
55 e.set_quantum_seed(42)
56 value1 = e.interpret(
57 "{ use qs = Qubit[16]; for q in qs { H(q); }; Microsoft.Quantum.Measurement.MResetEachZ(qs) }"
58 )
59 e = Interpreter(TargetProfile.Unrestricted)
60 e.set_quantum_seed(42)
61 value2 = e.interpret(
62 "{ use qs = Qubit[16]; for q in qs { H(q); }; Microsoft.Quantum.Measurement.MResetEachZ(qs) }"
63 )
64 assert value1 == value2
65
66
67def test_classical_seed() -> None:
68 e = Interpreter(TargetProfile.Unrestricted)
69 e.set_classical_seed(42)
70 value1 = e.interpret(
71 "{ mutable res = []; for _ in 0..15{ set res += [Microsoft.Quantum.Random.DrawRandomInt(0, 100)]; }; res }"
72 )
73 e = Interpreter(TargetProfile.Unrestricted)
74 e.set_classical_seed(42)
75 value2 = e.interpret(
76 "{ mutable res = []; for _ in 0..15{ set res += [Microsoft.Quantum.Random.DrawRandomInt(0, 100)]; }; res }"
77 )
78 assert value1 == value2
79
80
81def test_dump_machine() -> None:
82 e = Interpreter(TargetProfile.Unrestricted)
83
84 def callback(output):
85 assert output.__repr__() == "STATE:\n|10โŸฉ: 1.0000+0.0000๐‘–"
86
87 value = e.interpret(
88 """
89 use q1 = Qubit();
90 use q2 = Qubit();
91 X(q1);
92 Microsoft.Quantum.Diagnostics.DumpMachine();
93 """,
94 callback,
95 )
96 state_dump = e.dump_machine()
97 assert state_dump.qubit_count == 2
98 state_dump = state_dump.get_dict()
99 assert len(state_dump) == 1
100 assert state_dump[2].real == 1.0
101 assert state_dump[2].imag == 0.0
102
103
104def test_error() -> None:
105 e = Interpreter(TargetProfile.Unrestricted)
106
107 with pytest.raises(QSharpError) as excinfo:
108 e.interpret("a864")
109 assert str(excinfo.value).find("name error") != -1
110
111
112def test_multiple_errors() -> None:
113 e = Interpreter(TargetProfile.Unrestricted)
114
115 with pytest.raises(QSharpError) as excinfo:
116 e.interpret("operation Foo() : Unit { Bar(); Baz(); }")
117 assert str(excinfo.value).find("`Bar` not found") != -1
118 assert str(excinfo.value).find("`Baz` not found") != -1
119
120
121def test_multiple_statements() -> None:
122 e = Interpreter(TargetProfile.Unrestricted)
123 value = e.interpret("1; Zero")
124 assert value == Result.Zero
125
126
127def test_value_int() -> None:
128 e = Interpreter(TargetProfile.Unrestricted)
129 value = e.interpret("5")
130 assert value == 5
131
132
133def test_value_double() -> None:
134 e = Interpreter(TargetProfile.Unrestricted)
135 value = e.interpret("3.1")
136 assert value == 3.1
137
138
139def test_value_bool() -> None:
140 e = Interpreter(TargetProfile.Unrestricted)
141 value = e.interpret("true")
142 assert value == True
143
144
145def test_value_string() -> None:
146 e = Interpreter(TargetProfile.Unrestricted)
147 value = e.interpret('"hello"')
148 assert value == "hello"
149
150
151def test_value_result() -> None:
152 e = Interpreter(TargetProfile.Unrestricted)
153 value = e.interpret("One")
154 assert value == Result.One
155
156
157def test_value_pauli() -> None:
158 e = Interpreter(TargetProfile.Unrestricted)
159 value = e.interpret("PauliX")
160 assert value == Pauli.X
161
162
163def test_value_tuple() -> None:
164 e = Interpreter(TargetProfile.Unrestricted)
165 value = e.interpret('(1, "hello", One)')
166 assert value == (1, "hello", Result.One)
167
168
169def test_value_unit() -> None:
170 e = Interpreter(TargetProfile.Unrestricted)
171 value = e.interpret("()")
172 assert value is None
173
174
175def test_value_array() -> None:
176 e = Interpreter(TargetProfile.Unrestricted)
177 value = e.interpret("[1, 2, 3]")
178 assert value == [1, 2, 3]
179
180
181def test_target_error() -> None:
182 e = Interpreter(TargetProfile.Base)
183 with pytest.raises(QSharpError) as excinfo:
184 e.interpret(
185 "operation Program() : Result { use q = Qubit(); if M(q) == Zero { return Zero } else { return One } }"
186 )
187 assert str(excinfo.value).startswith("Qsc.CapabilitiesCk.UseOfDynamicBool")
188
189
190def test_qirgen_compile_error() -> None:
191 e = Interpreter(TargetProfile.Base)
192 e.interpret("operation Program() : Int { return 0 }")
193 with pytest.raises(QSharpError) as excinfo:
194 e.qir("Foo()")
195 assert str(excinfo.value).startswith("Qsc.Resolve.NotFound")
196
197
198def test_error_spans_from_multiple_lines() -> None:
199 e = Interpreter(TargetProfile.Unrestricted)
200
201 # Qsc.Resolve.Ambiguous is chosen as a test case
202 # because it contains multiple spans which can be from different lines
203 e.interpret("namespace Other { operation DumpMachine() : Unit { } }")
204 e.interpret("open Other;")
205 e.interpret("open Microsoft.Quantum.Diagnostics;")
206 with pytest.raises(QSharpError) as excinfo:
207 e.interpret("DumpMachine()")
208 assert str(excinfo.value).startswith("Qsc.Resolve.Ambiguous")
209
210
211def test_qirgen() -> None:
212 e = Interpreter(TargetProfile.Base)
213 e.interpret("operation Program() : Result { use q = Qubit(); return M(q) }")
214 qir = e.qir("Program()")
215 assert isinstance(qir, str)
216
217
218def test_run_with_shots() -> None:
219 e = Interpreter(TargetProfile.Unrestricted)
220
221 def callback(output):
222 nonlocal called
223 called += 1
224 assert output.__repr__() == "Hello, world!"
225
226 called = 0
227 e.interpret('operation Foo() : Unit { Message("Hello, world!"); }', callback)
228 assert called == 0
229
230 value = []
231 for _ in range(5):
232 value.append(e.run("Foo()", callback))
233 assert called == 5
234
235 assert value == [None, None, None, None, None]
236
237
238def test_dump_circuit() -> None:
239 e = Interpreter(TargetProfile.Unrestricted)
240 e.interpret(
241 """
242 use q1 = Qubit();
243 use q2 = Qubit();
244 X(q1);
245 """
246 )
247 circuit = e.dump_circuit()
248 assert str(circuit) == dedent(
249 """\
250 q_0 โ”€โ”€ X โ”€โ”€
251 q_1 โ”€โ”€โ”€โ”€โ”€โ”€โ”€
252 """
253 )
254
255 e.interpret("X(q2);")
256 circuit = e.dump_circuit()
257 assert str(circuit) == dedent(
258 """\
259 q_0 โ”€โ”€ X โ”€โ”€
260 q_1 โ”€โ”€ X โ”€โ”€
261 """
262 )
263
264
265def test_entry_expr_circuit() -> None:
266 e = Interpreter(TargetProfile.Unrestricted)
267 e.interpret("operation Foo() : Result { use q = Qubit(); H(q); return M(q) }")
268 circuit = e.circuit("Foo()")
269 assert str(circuit) == dedent(
270 """\
271 q_0 โ”€โ”€ H โ”€โ”€โ”€โ”€ M โ”€โ”€
272 โ•˜โ•โ•โ•
273 """
274 )
275
276
277def test_swap_label_circuit() -> None:
278 e = Interpreter(TargetProfile.Unrestricted)
279 e.interpret(
280 "operation Foo() : Unit { use q1 = Qubit(); use q2 = Qubit(); X(q1); Relabel([q1, q2], [q2, q1]); X(q2); }"
281 )
282 circuit = e.circuit("Foo()")
283 assert str(circuit) == dedent(
284 """\
285 q_0 โ”€โ”€ X โ”€โ”€โ”€โ”€ X โ”€โ”€
286 q_1 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
287 """
288 )
289
290
291def test_callables_failing_profile_validation_are_not_registered() -> None:
292 e = Interpreter(TargetProfile.Adaptive_RI)
293 with pytest.raises(Exception) as excinfo:
294 e.interpret(
295 "operation Foo() : Double { use q = Qubit(); mutable x = 1.0; if MResetZ(q) == One { set x = 2.0; } x }"
296 )
297 assert "Qsc.CapabilitiesCk.UseOfDynamicDouble" in str(excinfo)
298 # In this case, the callable Foo failed compilation late enough that the symbol is bound. This makes later
299 # use of `Foo` valid from a name resolution standpoint, but the callable cannot be invoked because it was found
300 # to be invalid for the current profile. To stay consistent with the behavior of other compilations that
301 # leave unbound symbols, the call will compile but fail to run.
302 with pytest.raises(Exception) as excinfo:
303 e.interpret("Foo()")
304 assert "Qsc.Eval.UnboundName" in str(excinfo)
305
306
307def test_once_callable_fails_profile_validation_it_fails_compile_to_QIR() -> None:
308 e = Interpreter(TargetProfile.Adaptive_RI)
309 with pytest.raises(Exception) as excinfo:
310 e.interpret(
311 "operation Foo() : Double { use q = Qubit(); mutable x = 1.0; if MResetZ(q) == One { set x = 2.0; } x }"
312 )
313 assert "Qsc.CapabilitiesCk.UseOfDynamicDouble" in str(excinfo)
314 with pytest.raises(Exception) as excinfo:
315 e.qir("{Foo();}")
316 assert "Qsc.PartialEval.EvaluationFailed" in str(excinfo)
317 assert "name is not bound" in str(excinfo)
318
319
320def test_once_rca_validation_fails_following_calls_do_not_fail() -> None:
321 e = Interpreter(TargetProfile.Adaptive_RI)
322 with pytest.raises(Exception) as excinfo:
323 e.interpret(
324 "operation Foo() : Double { use q = Qubit(); mutable x = 1.0; if MResetZ(q) == One { set x = 2.0; } x }"
325 )
326 assert "Qsc.CapabilitiesCk.UseOfDynamicDouble" in str(excinfo)
327 value = e.interpret("let x = 5; x")
328 assert value == 5
329
330
331def test_adaptive_errors_are_raised_when_interpreting() -> None:
332 e = Interpreter(TargetProfile.Adaptive_RI)
333 with pytest.raises(Exception) as excinfo:
334 e.interpret(
335 "operation Foo() : Double { use q = Qubit(); mutable x = 1.0; if MResetZ(q) == One { set x = 2.0; } x }"
336 )
337 assert "Qsc.CapabilitiesCk.UseOfDynamicDouble" in str(excinfo)
338
339
340def test_adaptive_errors_are_raised_from_entry_expr() -> None:
341 e = Interpreter(TargetProfile.Adaptive_RI)
342 e.interpret("use q = Qubit();")
343 with pytest.raises(Exception) as excinfo:
344 e.run("{mutable x = 1.0; if MResetZ(q) == One { set x = 2.0; }}")
345 assert "Qsc.CapabilitiesCk.UseOfDynamicDouble" in str(excinfo)
346
347
348def test_adaptive_ri_qir_can_be_generated() -> None:
349 adaptive_input = """
350 namespace Test {
351 import Std.Math.*;
352 open QIR.Intrinsic;
353 @EntryPoint()
354 operation Main() : Result {
355 use q = Qubit();
356 let pi_over_two = 4.0 / 2.0;
357 __quantum__qis__rz__body(pi_over_two, q);
358 mutable some_angle = ArcSin(0.0);
359 __quantum__qis__rz__body(some_angle, q);
360 set some_angle = ArcCos(-1.0) / PI();
361 __quantum__qis__rz__body(some_angle, q);
362 __quantum__qis__mresetz__body(q)
363 }
364 }
365 """
366 e = Interpreter(TargetProfile.Adaptive_RI)
367 e.interpret(adaptive_input)
368 qir = e.qir("Test.Main()")
369 assert qir == dedent(
370 """\
371 %Result = type opaque
372 %Qubit = type opaque
373
374 define void @ENTRYPOINT__main() #0 {
375 block_0:
376 call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
377 call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
378 call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
379 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
380 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
381 ret void
382 }
383
384 declare void @__quantum__qis__rz__body(double, %Qubit*)
385
386 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
387
388 declare void @__quantum__rt__result_record_output(%Result*, i8*)
389
390 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
391 attributes #1 = { "irreversible" }
392
393 ; module flags
394
395 !llvm.module.flags = !{!0, !1, !2, !3, !4}
396
397 !0 = !{i32 1, !"qir_major_version", i32 1}
398 !1 = !{i32 7, !"qir_minor_version", i32 0}
399 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
400 !3 = !{i32 1, !"dynamic_result_management", i1 false}
401 !4 = !{i32 1, !"int_computations", !"i64"}
402 """
403 )
404
405
406def test_base_qir_can_be_generated() -> None:
407 base_input = """
408 namespace Test {
409 import Std.Math.*;
410 open QIR.Intrinsic;
411 @EntryPoint()
412 operation Main() : Result {
413 use q = Qubit();
414 let pi_over_two = 4.0 / 2.0;
415 __quantum__qis__rz__body(pi_over_two, q);
416 mutable some_angle = ArcSin(0.0);
417 __quantum__qis__rz__body(some_angle, q);
418 set some_angle = ArcCos(-1.0) / PI();
419 __quantum__qis__rz__body(some_angle, q);
420 __quantum__qis__mresetz__body(q)
421 }
422 }
423 """
424 e = Interpreter(TargetProfile.Base)
425 e.interpret(base_input)
426 qir = e.qir("Test.Main()")
427 assert qir == dedent(
428 """\
429 %Result = type opaque
430 %Qubit = type opaque
431
432 define void @ENTRYPOINT__main() #0 {
433 block_0:
434 call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
435 call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
436 call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
437 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
438 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
439 ret void
440 }
441
442 declare void @__quantum__qis__rz__body(double, %Qubit*)
443
444 declare void @__quantum__rt__result_record_output(%Result*, i8*)
445
446 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
447
448 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="1" "required_num_results"="1" }
449 attributes #1 = { "irreversible" }
450
451 ; module flags
452
453 !llvm.module.flags = !{!0, !1, !2, !3}
454
455 !0 = !{i32 1, !"qir_major_version", i32 1}
456 !1 = !{i32 7, !"qir_minor_version", i32 0}
457 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
458 !3 = !{i32 1, !"dynamic_result_management", i1 false}
459 """
460 )
461
462
463def test_operation_circuit() -> None:
464 e = Interpreter(TargetProfile.Unrestricted)
465 e.interpret("operation Foo(q: Qubit) : Result { H(q); return M(q) }")
466 circuit = e.circuit(operation="Foo")
467 assert str(circuit) == dedent(
468 """\
469 q_0 โ”€โ”€ H โ”€โ”€โ”€โ”€ M โ”€โ”€
470 โ•˜โ•โ•โ•
471 """
472 )
473
474
475def test_unsupported_operation_circuit() -> None:
476 e = Interpreter(TargetProfile.Unrestricted)
477 e.interpret("operation Foo(n: Int) : Result { return One }")
478 with pytest.raises(QSharpError) as excinfo:
479 circuit = e.circuit(operation="Foo")
480 assert (
481 str(excinfo.value).find(
482 "expression does not evaluate to an operation that takes qubit parameters"
483 )
484 != -1
485 )
486
487
488def test_results_are_comparable() -> None:
489 e = Interpreter(TargetProfile.Unrestricted)
490 r = e.interpret("[One, Zero]")
491 assert r == [Result.One, Result.Zero]
492 r.sort()
493 assert r == [Result.Zero, Result.One]
494