microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
7421e7dd1015dcbd940bf843d33583470de580ea

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/pip/tests/test_interpreter.py

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