microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
minestarks/circuit-folding

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/pip/tests/test_interpreter.py

719lines · 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 CircuitConfig,
12)
13from qsharp._qsharp import qsharp_value_to_python_value
14import pytest
15
16# Test helpers
17
18
19def check_interpret(source: str, expect: str):
20 e = Interpreter(TargetProfile.Unrestricted)
21 value = qsharp_value_to_python_value(e.interpret(source))
22 assert str(value) == expect
23
24
25def check_invoke(source: str, callable: str, expect: str):
26 e = None
27 f = None
28
29 def _make_callable(callable, namespace, callable_name):
30 nonlocal f
31 f = callable
32
33 e = Interpreter(TargetProfile.Unrestricted, make_callable=_make_callable)
34 e.interpret(source)
35 e.interpret(callable)
36 value = qsharp_value_to_python_value(e.invoke(f))
37 assert str(value) == expect
38
39
40def check_run(entry_expr: str, expect: str):
41 e = Interpreter(TargetProfile.Unrestricted)
42 value = qsharp_value_to_python_value(e.run(entry_expr))
43 assert str(value) == expect
44
45
46def check_circuit(entry_expr: str, expect):
47 e = Interpreter(TargetProfile.Unrestricted)
48 value = e.circuit(CircuitConfig(), entry_expr)
49 assert str(value) == expect
50
51
52def check_qir(source: str, entry_expr, expect):
53 e = Interpreter(TargetProfile.Base)
54 e.interpret(source)
55 value = e.qir(entry_expr)
56 assert str(value) == expect
57
58
59def check_estimate(source: str):
60 e = Interpreter(TargetProfile.Unrestricted)
61 e.estimate("", source)
62
63
64def check_logical_counts(source: str):
65 e = Interpreter(TargetProfile.Unrestricted)
66 e.logical_counts(source)
67
68
69# Tests for the native Q# interpreter class
70
71
72def test_output() -> None:
73 e = Interpreter(TargetProfile.Unrestricted)
74
75 def callback(output):
76 nonlocal called
77 called = True
78 assert output.__repr__() == "Hello, world!"
79
80 called = False
81 value = e.interpret('Message("Hello, world!")', callback)
82 assert called
83
84
85def test_dump_output() -> None:
86 e = Interpreter(TargetProfile.Unrestricted)
87
88 def callback(output):
89 nonlocal called
90 called = True
91 assert output.__repr__() == "STATE:\n|10⟩: 1.0000+0.0000𝑖"
92
93 called = False
94 value = e.interpret(
95 """
96 use q1 = Qubit();
97 use q2 = Qubit();
98 X(q1);
99 Microsoft.Quantum.Diagnostics.DumpMachine();
100 ResetAll([q1, q2]);
101 """,
102 callback,
103 )
104 assert called
105
106
107def test_quantum_seed() -> None:
108 e = Interpreter(TargetProfile.Unrestricted)
109 e.set_quantum_seed(42)
110 value1 = e.interpret(
111 "{ use qs = Qubit[16]; for q in qs { H(q); }; Microsoft.Quantum.Measurement.MResetEachZ(qs) }"
112 )
113 e = Interpreter(TargetProfile.Unrestricted)
114 e.set_quantum_seed(42)
115 value2 = e.interpret(
116 "{ use qs = Qubit[16]; for q in qs { H(q); }; Microsoft.Quantum.Measurement.MResetEachZ(qs) }"
117 )
118 assert value1 == value2
119
120
121def test_classical_seed() -> None:
122 e = Interpreter(TargetProfile.Unrestricted)
123 e.set_classical_seed(42)
124 value1 = e.interpret(
125 "{ mutable res = []; for _ in 0..15{ set res += [Microsoft.Quantum.Random.DrawRandomInt(0, 100)]; }; res }"
126 )
127 e = Interpreter(TargetProfile.Unrestricted)
128 e.set_classical_seed(42)
129 value2 = e.interpret(
130 "{ mutable res = []; for _ in 0..15{ set res += [Microsoft.Quantum.Random.DrawRandomInt(0, 100)]; }; res }"
131 )
132 assert value1 == value2
133
134
135def test_dump_machine() -> None:
136 e = Interpreter(TargetProfile.Unrestricted)
137
138 def callback(output):
139 assert output.__repr__() == "STATE:\n|10⟩: 1.0000+0.0000𝑖"
140
141 value = e.interpret(
142 """
143 use q1 = Qubit();
144 use q2 = Qubit();
145 X(q1);
146 Microsoft.Quantum.Diagnostics.DumpMachine();
147 """,
148 callback,
149 )
150 state_dump = e.dump_machine()
151 assert state_dump.qubit_count == 2
152 state_dump = state_dump.get_dict()
153 assert len(state_dump) == 1
154 assert state_dump[2].real == 1.0
155 assert state_dump[2].imag == 0.0
156
157
158def test_error() -> None:
159 e = Interpreter(TargetProfile.Unrestricted)
160
161 with pytest.raises(QSharpError) as excinfo:
162 e.interpret("a864")
163 assert str(excinfo.value).find("name error") != -1
164
165
166def test_multiple_errors() -> None:
167 e = Interpreter(TargetProfile.Unrestricted)
168
169 with pytest.raises(QSharpError) as excinfo:
170 e.interpret("operation Foo() : Unit { Bar(); Baz(); }")
171 assert str(excinfo.value).find("`Bar` not found") != -1
172 assert str(excinfo.value).find("`Baz` not found") != -1
173
174
175def test_multiple_statements() -> None:
176 e = Interpreter(TargetProfile.Unrestricted)
177 value = e.interpret("1; Zero")
178 assert value == Result.Zero
179
180
181def test_value_int() -> None:
182 e = Interpreter(TargetProfile.Unrestricted)
183 value = e.interpret("5")
184 assert value == 5
185
186
187def test_value_double() -> None:
188 e = Interpreter(TargetProfile.Unrestricted)
189 value = e.interpret("3.1")
190 assert value == 3.1
191
192
193def test_value_complex() -> None:
194 e = Interpreter(TargetProfile.Unrestricted)
195 value = e.interpret("new Std.Math.Complex { Real = 2.0, Imag = 3.0 }")
196 assert value == 2 + 3j
197
198
199def test_value_bool() -> None:
200 e = Interpreter(TargetProfile.Unrestricted)
201 value = e.interpret("true")
202 assert value == True
203
204
205def test_value_string() -> None:
206 e = Interpreter(TargetProfile.Unrestricted)
207 value = e.interpret('"hello"')
208 assert value == "hello"
209
210
211def test_value_result() -> None:
212 e = Interpreter(TargetProfile.Unrestricted)
213 value = e.interpret("One")
214 assert value == Result.One
215
216
217def test_value_pauli() -> None:
218 e = Interpreter(TargetProfile.Unrestricted)
219 value = e.interpret("PauliX")
220 assert value == Pauli.X
221
222
223def test_value_tuple() -> None:
224 e = Interpreter(TargetProfile.Unrestricted)
225 value = e.interpret('(1, "hello", One)')
226 assert value == (1, "hello", Result.One)
227
228
229def test_value_unit() -> None:
230 e = Interpreter(TargetProfile.Unrestricted)
231 value = e.interpret("()")
232 assert value is None
233
234
235def test_value_array() -> None:
236 e = Interpreter(TargetProfile.Unrestricted)
237 value = e.interpret("[1, 2, 3]")
238 assert value == [1, 2, 3]
239
240
241def test_value_udt() -> None:
242 udt_def = "struct Data { a: Int, b: Int }"
243 new_udt = "new Data { a = 2, b = 3 }"
244 callable = f"function makeData() : Data {{ {new_udt} }}"
245 entry_expr = f"{{ {udt_def} {new_udt} }}"
246 output = "Data(a=2, b=3)"
247
248 check_interpret(entry_expr, output)
249 check_run(entry_expr, output)
250 check_invoke(udt_def, callable, output)
251 check_circuit(entry_expr, "")
252 check_estimate(entry_expr)
253 check_logical_counts(entry_expr)
254 with pytest.raises(QSharpError, match="Qsc.CapabilitiesCk.UseOfAdvancedOutput"):
255 check_qir(udt_def + callable, "makeData()", "")
256
257
258def test_value_nested_udts() -> None:
259 udt_def = """
260 struct Data { a: Int, b: MoreData }
261 struct MoreData { c: Int, d: Int }
262 """
263 new_udt = "new Data { a = 2, b = new MoreData { c = 3, d = 4 } }"
264 callable = f"function makeData() : Data {{ {new_udt} }}"
265 entry_expr = f"{{ {udt_def} {new_udt} }}"
266 output = "Data(a=2, b=MoreData(c=3, d=4))"
267
268 check_interpret(entry_expr, output)
269 check_run(entry_expr, output)
270 check_invoke(udt_def, callable, output)
271 check_circuit(entry_expr, "")
272 check_estimate(entry_expr)
273 check_logical_counts(entry_expr)
274 with pytest.raises(QSharpError, match="Qsc.CapabilitiesCk.UseOfAdvancedOutput"):
275 check_qir(udt_def + callable, "makeData()", "")
276
277
278def test_value_udts_with_complex_field() -> None:
279 udt_def = "struct Data { a: Std.Math.Complex }"
280 new_udt = "new Data { a = new Std.Math.Complex { Real = 2.0, Imag = 3.0 } }"
281 callable = f"function makeData() : Data {{ {new_udt} }}"
282 entry_expr = f"{{ {udt_def} {new_udt} }}"
283 output = "Data(a=(2+3j))"
284
285 check_interpret(entry_expr, output)
286 check_run(entry_expr, output)
287 check_invoke(udt_def, callable, output)
288 check_circuit(entry_expr, "")
289 check_estimate(entry_expr)
290 check_logical_counts(entry_expr)
291 with pytest.raises(QSharpError, match="Qsc.CapabilitiesCk.UseOfAdvancedOutput"):
292 check_qir(udt_def + callable, "makeData()", "")
293
294
295def test_value_udts_with_array_field() -> None:
296 udt_def = "struct Data { a: Int[] }"
297 new_udt = "new Data { a = [2, 3, 4] }"
298 callable = f"function makeData() : Data {{ {new_udt} }}"
299 entry_expr = f"{{ {udt_def} {new_udt} }}"
300 output = "Data(a=[2, 3, 4])"
301
302 check_interpret(entry_expr, output)
303 check_run(entry_expr, output)
304 check_invoke(udt_def, callable, output)
305 check_circuit(entry_expr, "")
306 check_estimate(entry_expr)
307 check_logical_counts(entry_expr)
308 with pytest.raises(QSharpError, match="Qsc.CapabilitiesCk.UseOfAdvancedOutput"):
309 check_qir(udt_def + callable, "makeData()", "")
310
311
312def test_value_udts_with_tuple_field() -> None:
313 udt_def = "struct Data { a: (Int, Int, Int) }"
314 new_udt = "new Data { a = (2, 3, 4) }"
315 callable = f"function makeData() : Data {{ {new_udt} }}"
316 entry_expr = f"{{ {udt_def} {new_udt} }}"
317 output = "Data(a=(2, 3, 4))"
318
319 check_interpret(entry_expr, output)
320 check_run(entry_expr, output)
321 check_invoke(udt_def, callable, output)
322 check_circuit(entry_expr, "")
323 check_estimate(entry_expr)
324 check_logical_counts(entry_expr)
325 with pytest.raises(QSharpError, match="Qsc.CapabilitiesCk.UseOfAdvancedOutput"):
326 check_qir(udt_def + callable, "makeData()", "")
327
328
329def test_value_array_of_udts() -> None:
330 udt_def = "struct Data { a: Int }"
331 new_udt = "[new Data { a = 2 }, new Data { a = 3 }]"
332 callable = f"function makeData() : Data[] {{ {new_udt} }}"
333 entry_expr = f"{{ {udt_def} {new_udt} }}"
334 output = "[Data(a=2), Data(a=3)]"
335
336 check_interpret(entry_expr, output)
337 check_run(entry_expr, output)
338 check_invoke(udt_def, callable, output)
339 check_circuit(entry_expr, "")
340 check_estimate(entry_expr)
341 check_logical_counts(entry_expr)
342 with pytest.raises(QSharpError, match="Qsc.CapabilitiesCk.UseOfAdvancedOutput"):
343 check_qir(udt_def + callable, "makeData()", "")
344
345
346def test_value_array_of_complex() -> None:
347 new_udt = "[new Std.Math.Complex { Real = 2.0, Imag = 3.0 }]"
348 callable = f"function makeData() : Std.Math.Complex[] {{ {new_udt} }}"
349 entry_expr = f"{{ {new_udt} }}"
350 output = "[(2+3j)]"
351
352 check_interpret(entry_expr, output)
353 check_run(entry_expr, output)
354 check_invoke("", callable, output)
355 check_circuit(entry_expr, "")
356 check_estimate(entry_expr)
357 check_logical_counts(entry_expr)
358 with pytest.raises(QSharpError, match="Qsc.CapabilitiesCk.UseOfAdvancedOutput"):
359 check_qir(callable, "makeData()", "")
360
361
362def test_value_tuple_of_udts() -> None:
363 udt_def = "struct Data { a: Int }"
364 new_udt = "(new Data { a = 2 }, new Data { a = 3 })"
365 callable = f"function makeData() : (Data, Data) {{ {new_udt} }}"
366 entry_expr = f"{{ {udt_def} {new_udt} }}"
367 output = "(Data(a=2), Data(a=3))"
368
369 check_interpret(entry_expr, output)
370 check_run(entry_expr, output)
371 check_invoke(udt_def, callable, output)
372 check_circuit(entry_expr, "")
373 check_estimate(entry_expr)
374 check_logical_counts(entry_expr)
375 with pytest.raises(QSharpError, match="Qsc.CapabilitiesCk.UseOfAdvancedOutput"):
376 check_qir(udt_def + callable, "makeData()", "")
377
378
379def test_value_tuple_of_complex() -> None:
380 new_udt = "(new Std.Math.Complex { Real = 2.0, Imag = 3.0 },)"
381 callable = f"function makeData() : (Std.Math.Complex,) {{ {new_udt} }}"
382 entry_expr = f"{{ {new_udt} }}"
383 output = "((2+3j),)"
384
385 check_interpret(entry_expr, output)
386 check_run(entry_expr, output)
387 check_invoke("", callable, output)
388 check_circuit(entry_expr, "")
389 check_estimate(entry_expr)
390 check_logical_counts(entry_expr)
391 with pytest.raises(QSharpError, match="Qsc.CapabilitiesCk.UseOfAdvancedOutput"):
392 check_qir(callable, "makeData()", "")
393
394
395def test_target_error() -> None:
396 e = Interpreter(TargetProfile.Base)
397 with pytest.raises(QSharpError) as excinfo:
398 e.interpret(
399 "operation Program() : Result { use q = Qubit(); if M(q) == Zero { return Zero } else { return One } }"
400 )
401 assert str(excinfo.value).startswith("Qsc.CapabilitiesCk.UseOfDynamicBool")
402
403
404def test_qirgen_compile_error() -> None:
405 e = Interpreter(TargetProfile.Base)
406 e.interpret("operation Program() : Int { return 0 }")
407 with pytest.raises(QSharpError) as excinfo:
408 e.qir("Foo()")
409 assert str(excinfo.value).startswith("Qsc.Resolve.NotFound")
410
411
412def test_error_spans_from_multiple_lines() -> None:
413 e = Interpreter(TargetProfile.Unrestricted)
414
415 # Qsc.Resolve.Ambiguous is chosen as a test case
416 # because it contains multiple spans which can be from different lines
417 e.interpret("namespace Other { operation DumpMachine() : Unit { } }")
418 e.interpret("open Other;")
419 e.interpret("open Microsoft.Quantum.Diagnostics;")
420 with pytest.raises(QSharpError) as excinfo:
421 e.interpret("DumpMachine()")
422 assert str(excinfo.value).startswith("Qsc.Resolve.Ambiguous")
423
424
425def test_qirgen() -> None:
426 e = Interpreter(TargetProfile.Base)
427 e.interpret("operation Program() : Result { use q = Qubit(); return M(q) }")
428 qir = e.qir("Program()")
429 assert isinstance(qir, str)
430
431
432def test_run_with_shots() -> None:
433 e = Interpreter(TargetProfile.Unrestricted)
434
435 def callback(output):
436 nonlocal called
437 called += 1
438 assert output.__repr__() == "Hello, world!"
439
440 called = 0
441 e.interpret('operation Foo() : Unit { Message("Hello, world!"); }', callback)
442 assert called == 0
443
444 value = []
445 for _ in range(5):
446 value.append(e.run("Foo()", callback))
447 assert called == 5
448
449 assert value == [None, None, None, None, None]
450
451
452def test_dump_circuit() -> None:
453 e = Interpreter(TargetProfile.Unrestricted, trace_circuit=True)
454 e.interpret(
455 """
456 use q1 = Qubit();
457 use q2 = Qubit();
458 X(q1);
459 """
460 )
461 circuit = e.dump_circuit()
462 assert str(circuit) == dedent(
463 """\
464 q_0 ── X ──
465 q_1 ───────
466 """
467 )
468
469 e.interpret("X(q2);")
470 circuit = e.dump_circuit()
471 assert str(circuit) == dedent(
472 """\
473 q_0 ── X ──
474 q_1 ── X ──
475 """
476 )
477
478
479def test_entry_expr_circuit() -> None:
480 e = Interpreter(TargetProfile.Unrestricted)
481 e.interpret("operation Foo() : Result { use q = Qubit(); H(q); return M(q) }")
482 circuit = e.circuit(CircuitConfig(), "Foo()")
483 assert str(circuit) == dedent(
484 """\
485 q_0 ── H ──── M ──
486 ╘═══
487 """
488 )
489
490
491def test_swap_label_circuit() -> None:
492 e = Interpreter(TargetProfile.Unrestricted)
493 e.interpret(
494 "operation Foo() : Unit { use q1 = Qubit(); use q2 = Qubit(); X(q1); Relabel([q1, q2], [q2, q1]); X(q2); }"
495 )
496 circuit = e.circuit(CircuitConfig(), "Foo()")
497 assert str(circuit) == dedent(
498 """\
499 q_0 ── X ──── X ──
500 q_1 ──────────────
501 """
502 )
503
504
505def test_callables_failing_profile_validation_are_not_registered() -> None:
506 e = Interpreter(TargetProfile.Adaptive_RI)
507 with pytest.raises(Exception) as excinfo:
508 e.interpret(
509 "operation Foo() : Double { use q = Qubit(); mutable x = 1.0; if MResetZ(q) == One { set x = 2.0; } x }"
510 )
511 assert "Qsc.CapabilitiesCk.UseOfDynamicDouble" in str(excinfo)
512 # In this case, the callable Foo failed compilation late enough that the symbol is bound. This makes later
513 # use of `Foo` valid from a name resolution standpoint, but the callable cannot be invoked because it was found
514 # to be invalid for the current profile. To stay consistent with the behavior of other compilations that
515 # leave unbound symbols, the call will compile but fail to run.
516 with pytest.raises(Exception) as excinfo:
517 e.interpret("Foo()")
518 assert "Qsc.Eval.UnboundName" in str(excinfo)
519
520
521def test_once_callable_fails_profile_validation_it_fails_compile_to_QIR() -> None:
522 e = Interpreter(TargetProfile.Adaptive_RI)
523 with pytest.raises(Exception) as excinfo:
524 e.interpret(
525 "operation Foo() : Double { use q = Qubit(); mutable x = 1.0; if MResetZ(q) == One { set x = 2.0; } x }"
526 )
527 assert "Qsc.CapabilitiesCk.UseOfDynamicDouble" in str(excinfo)
528 with pytest.raises(Exception) as excinfo:
529 e.qir("{Foo();}")
530 assert "Qsc.PartialEval.EvaluationFailed" in str(excinfo)
531 assert "name is not bound" in str(excinfo)
532
533
534def test_once_rca_validation_fails_following_calls_do_not_fail() -> None:
535 e = Interpreter(TargetProfile.Adaptive_RI)
536 with pytest.raises(Exception) as excinfo:
537 e.interpret(
538 "operation Foo() : Double { use q = Qubit(); mutable x = 1.0; if MResetZ(q) == One { set x = 2.0; } x }"
539 )
540 assert "Qsc.CapabilitiesCk.UseOfDynamicDouble" in str(excinfo)
541 value = e.interpret("let x = 5; x")
542 assert value == 5
543
544
545def test_adaptive_errors_are_raised_when_interpreting() -> None:
546 e = Interpreter(TargetProfile.Adaptive_RI)
547 with pytest.raises(Exception) as excinfo:
548 e.interpret(
549 "operation Foo() : Double { use q = Qubit(); mutable x = 1.0; if MResetZ(q) == One { set x = 2.0; } x }"
550 )
551 assert "Qsc.CapabilitiesCk.UseOfDynamicDouble" in str(excinfo)
552
553
554def test_adaptive_errors_are_raised_from_entry_expr() -> None:
555 e = Interpreter(TargetProfile.Adaptive_RI)
556 e.interpret("use q = Qubit();")
557 with pytest.raises(Exception) as excinfo:
558 e.run("{mutable x = 1.0; if MResetZ(q) == One { set x = 2.0; }}")
559 assert "Qsc.CapabilitiesCk.UseOfDynamicDouble" in str(excinfo)
560
561
562def test_adaptive_ri_qir_can_be_generated() -> None:
563 adaptive_input = """
564 namespace Test {
565 import Std.Math.*;
566 open QIR.Intrinsic;
567 @EntryPoint()
568 operation Main() : Result {
569 use q = Qubit();
570 let pi_over_two = 4.0 / 2.0;
571 __quantum__qis__rz__body(pi_over_two, q);
572 mutable some_angle = ArcSin(0.0);
573 __quantum__qis__rz__body(some_angle, q);
574 set some_angle = ArcCos(-1.0) / PI();
575 __quantum__qis__rz__body(some_angle, q);
576 __quantum__qis__mresetz__body(q)
577 }
578 }
579 """
580 e = Interpreter(TargetProfile.Adaptive_RI)
581 e.interpret(adaptive_input)
582 qir = e.qir("Test.Main()")
583 assert qir == dedent(
584 """\
585 %Result = type opaque
586 %Qubit = type opaque
587
588 @empty_tag = internal constant [1 x i8] c"\\00"
589 @0 = internal constant [4 x i8] c"0_r\\00"
590
591 define i64 @ENTRYPOINT__main() #0 {
592 block_0:
593 call void @__quantum__rt__initialize(i8* null)
594 call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
595 call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
596 call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
597 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
598 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
599 ret i64 0
600 }
601
602 declare void @__quantum__rt__initialize(i8*)
603
604 declare void @__quantum__qis__rz__body(double, %Qubit*)
605
606 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
607
608 declare void @__quantum__rt__result_record_output(%Result*, i8*)
609
610 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
611 attributes #1 = { "irreversible" }
612
613 ; module flags
614
615 !llvm.module.flags = !{!0, !1, !2, !3, !4}
616
617 !0 = !{i32 1, !"qir_major_version", i32 1}
618 !1 = !{i32 7, !"qir_minor_version", i32 0}
619 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
620 !3 = !{i32 1, !"dynamic_result_management", i1 false}
621 !4 = !{i32 5, !"int_computations", !{!"i64"}}
622 """
623 )
624
625
626def test_base_qir_can_be_generated() -> None:
627 base_input = """
628 namespace Test {
629 import Std.Math.*;
630 open QIR.Intrinsic;
631 @EntryPoint()
632 operation Main() : Result {
633 use q = Qubit();
634 let pi_over_two = 4.0 / 2.0;
635 __quantum__qis__rz__body(pi_over_two, q);
636 mutable some_angle = ArcSin(0.0);
637 __quantum__qis__rz__body(some_angle, q);
638 set some_angle = ArcCos(-1.0) / PI();
639 __quantum__qis__rz__body(some_angle, q);
640 __quantum__qis__mresetz__body(q)
641 }
642 }
643 """
644 e = Interpreter(TargetProfile.Base)
645 e.interpret(base_input)
646 qir = e.qir("Test.Main()")
647 assert qir == dedent(
648 """\
649 %Result = type opaque
650 %Qubit = type opaque
651
652 @empty_tag = internal constant [1 x i8] c"\\00"
653 @0 = internal constant [4 x i8] c"0_r\\00"
654
655 define i64 @ENTRYPOINT__main() #0 {
656 block_0:
657 call void @__quantum__rt__initialize(i8* null)
658 call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
659 call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
660 call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
661 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
662 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
663 ret i64 0
664 }
665
666 declare void @__quantum__rt__initialize(i8*)
667
668 declare void @__quantum__qis__rz__body(double, %Qubit*)
669
670 declare void @__quantum__rt__result_record_output(%Result*, i8*)
671
672 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
673
674 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="1" "required_num_results"="1" }
675 attributes #1 = { "irreversible" }
676
677 ; module flags
678
679 !llvm.module.flags = !{!0, !1, !2, !3}
680
681 !0 = !{i32 1, !"qir_major_version", i32 1}
682 !1 = !{i32 7, !"qir_minor_version", i32 0}
683 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
684 !3 = !{i32 1, !"dynamic_result_management", i1 false}
685 """
686 )
687
688
689def test_operation_circuit() -> None:
690 e = Interpreter(TargetProfile.Unrestricted)
691 e.interpret("operation Foo(q: Qubit) : Result { H(q); return M(q) }")
692 circuit = e.circuit(CircuitConfig(), operation="Foo")
693 assert str(circuit) == dedent(
694 """\
695 q_0 ── H ──── M ──
696 ╘═══
697 """
698 )
699
700
701def test_unsupported_operation_circuit() -> None:
702 e = Interpreter(TargetProfile.Unrestricted)
703 e.interpret("operation Foo(n: Int) : Result { return One }")
704 with pytest.raises(QSharpError) as excinfo:
705 circuit = e.circuit(CircuitConfig(), operation="Foo")
706 assert (
707 str(excinfo.value).find(
708 "expression does not evaluate to an operation that takes qubit parameters"
709 )
710 != -1
711 )
712
713
714def test_results_are_comparable() -> None:
715 e = Interpreter(TargetProfile.Unrestricted)
716 r = e.interpret("[One, Zero]")
717 assert r == [Result.One, Result.Zero]
718 r.sort()
719 assert r == [Result.Zero, Result.One]