microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
iadavis/qiskit2-explore

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/compiler/qsc/src/codegen/tests.rs

1800lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use expect_test::expect;
5use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
6use qsc_frontend::compile::SourceMap;
7
8use crate::codegen::qir::get_qir;
9
10fn compile_source_to_qir(source: &str, capabilities: TargetCapabilityFlags) -> String {
11 let sources = SourceMap::new([("test.qs".into(), source.into())], None);
12 let language_features = LanguageFeatures::default();
13
14 let (std_id, store) = crate::compile::package_store_with_stdlib(capabilities);
15 get_qir(
16 sources,
17 language_features,
18 capabilities,
19 store,
20 &[(std_id, None)],
21 )
22 .expect("Failed to generate QIR")
23}
24
25#[test]
26fn code_with_errors_returns_errors() {
27 let source = "namespace Test {
28 @EntryPoint()
29 operation Main() : Unit {
30 use q = Qubit()
31 let pi_over_two = 4.0 / 2.0;
32 }
33 }";
34 let sources = SourceMap::new([("test.qs".into(), source.into())], None);
35 let language_features = LanguageFeatures::default();
36 let capabilities = TargetCapabilityFlags::empty();
37 let (std_id, store) = crate::compile::package_store_with_stdlib(capabilities);
38
39 expect![[r#"
40 Err(
41 [
42 Compile(
43 WithSource {
44 sources: [
45 Source {
46 name: "test.qs",
47 contents: "namespace Test {\n @EntryPoint()\n operation Main() : Unit {\n use q = Qubit()\n let pi_over_two = 4.0 / 2.0;\n }\n }",
48 offset: 0,
49 },
50 ],
51 error: Frontend(
52 Error(
53 Parse(
54 Error(
55 Token(
56 Semi,
57 Keyword(
58 Let,
59 ),
60 Span {
61 lo: 129,
62 hi: 132,
63 },
64 ),
65 ),
66 ),
67 ),
68 ),
69 },
70 ),
71 ],
72 )
73 "#]]
74 .assert_debug_eq(&get_qir(sources, language_features, capabilities, store, &[(std_id, None)]));
75}
76
77#[test]
78fn code_returning_struct_from_entry_point_generates_errors() {
79 let source = "namespace Test {
80 @EntryPoint()
81 operation Main() : Std.Math.Complex {
82 new Std.Math.Complex { Real = 0.0, Imag = 0.0 }
83 }
84 }";
85 let sources = SourceMap::new([("test.qs".into(), source.into())], None);
86 let language_features = LanguageFeatures::default();
87 let capabilities = TargetCapabilityFlags::empty();
88 let (std_id, store) = crate::compile::package_store_with_stdlib(capabilities);
89
90 expect![[r#"
91 Err(
92 [
93 Pass(
94 WithSource {
95 sources: [
96 Source {
97 name: "test.qs",
98 contents: "namespace Test {\n @EntryPoint()\n operation Main() : Std.Math.Complex {\n new Std.Math.Complex { Real = 0.0, Imag = 0.0 }\n }\n }",
99 offset: 0,
100 },
101 ],
102 error: CapabilitiesCk(
103 UseOfAdvancedOutput(
104 Span {
105 lo: 65,
106 hi: 69,
107 },
108 ),
109 ),
110 },
111 ),
112 ],
113 )
114 "#]]
115 .assert_debug_eq(&get_qir(sources, language_features, capabilities, store, &[(std_id, None)]));
116}
117
118#[test]
119fn code_returning_struct_from_entry_expr_generates_errors() {
120 let source = "";
121 let entry = "new Std.Math.Complex { Real = 0.0, Imag = 0.0 }";
122 let sources = SourceMap::new([("test.qs".into(), source.into())], Some(entry.into()));
123 let language_features = LanguageFeatures::default();
124 let capabilities = TargetCapabilityFlags::empty();
125 let (std_id, store) = crate::compile::package_store_with_stdlib(capabilities);
126
127 expect![[r#"
128 Err(
129 [
130 Pass(
131 WithSource {
132 sources: [
133 Source {
134 name: "<entry>",
135 contents: "new Std.Math.Complex { Real = 0.0, Imag = 0.0 }",
136 offset: 0,
137 },
138 ],
139 error: CapabilitiesCk(
140 UseOfAdvancedOutput(
141 Span {
142 lo: 0,
143 hi: 47,
144 },
145 ),
146 ),
147 },
148 ),
149 ],
150 )
151 "#]]
152 .assert_debug_eq(&get_qir(
153 sources,
154 language_features,
155 capabilities,
156 store,
157 &[(std_id, None)],
158 ));
159}
160
161#[test]
162fn code_returning_struct_from_block_entry_expr_generates_errors() {
163 let source = "";
164 let entry = "{ new Std.Math.Complex { Real = 0.0, Imag = 0.0 } }";
165 let sources = SourceMap::new([("test.qs".into(), source.into())], Some(entry.into()));
166 let language_features = LanguageFeatures::default();
167 let capabilities = TargetCapabilityFlags::empty();
168 let (std_id, store) = crate::compile::package_store_with_stdlib(capabilities);
169
170 expect![[r#"
171 Err(
172 [
173 Pass(
174 WithSource {
175 sources: [
176 Source {
177 name: "<entry>",
178 contents: "{ new Std.Math.Complex { Real = 0.0, Imag = 0.0 } }",
179 offset: 0,
180 },
181 ],
182 error: CapabilitiesCk(
183 UseOfAdvancedOutput(
184 Span {
185 lo: 0,
186 hi: 51,
187 },
188 ),
189 ),
190 },
191 ),
192 ],
193 )
194 "#]]
195 .assert_debug_eq(&get_qir(
196 sources,
197 language_features,
198 capabilities,
199 store,
200 &[(std_id, None)],
201 ));
202}
203
204#[test]
205fn code_returning_struct_from_if_entry_expr_generates_errors() {
206 let source = "";
207 let entry = "if (true) { new Std.Math.Complex { Real = 0.0, Imag = 0.0 } } else { fail \"shouldn't get here\" }";
208 let sources = SourceMap::new([("test.qs".into(), source.into())], Some(entry.into()));
209 let language_features = LanguageFeatures::default();
210 let capabilities = TargetCapabilityFlags::empty();
211 let (std_id, store) = crate::compile::package_store_with_stdlib(capabilities);
212
213 expect![[r#"
214 Err(
215 [
216 Pass(
217 WithSource {
218 sources: [
219 Source {
220 name: "<entry>",
221 contents: "if (true) { new Std.Math.Complex { Real = 0.0, Imag = 0.0 } } else { fail \"shouldn't get here\" }",
222 offset: 0,
223 },
224 ],
225 error: CapabilitiesCk(
226 UseOfAdvancedOutput(
227 Span {
228 lo: 0,
229 hi: 96,
230 },
231 ),
232 ),
233 },
234 ),
235 ],
236 )
237 "#]]
238 .assert_debug_eq(&get_qir(
239 sources,
240 language_features,
241 capabilities,
242 store,
243 &[(std_id, None)],
244 ));
245}
246
247mod base_profile {
248 use expect_test::expect;
249 use qsc_data_structures::target::TargetCapabilityFlags;
250
251 use super::compile_source_to_qir;
252 static CAPABILITIES: std::sync::LazyLock<TargetCapabilityFlags> =
253 std::sync::LazyLock::new(TargetCapabilityFlags::empty);
254
255 #[test]
256 fn simple() {
257 let source = "namespace Test {
258 import Std.Math.*;
259 open QIR.Intrinsic;
260 @EntryPoint()
261 operation Main() : Result {
262 use q = Qubit();
263 let pi_over_two = 4.0 / 2.0;
264 __quantum__qis__rz__body(pi_over_two, q);
265 mutable some_angle = ArcSin(0.0);
266 __quantum__qis__rz__body(some_angle, q);
267 set some_angle = ArcCos(-1.0) / PI();
268 __quantum__qis__rz__body(some_angle, q);
269 __quantum__qis__mresetz__body(q)
270 }
271 }";
272
273 let qir = compile_source_to_qir(source, *CAPABILITIES);
274 expect![[r#"
275 %Result = type opaque
276 %Qubit = type opaque
277
278 define void @ENTRYPOINT__main() #0 {
279 block_0:
280 call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
281 call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
282 call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
283 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
284 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
285 ret void
286 }
287
288 declare void @__quantum__qis__rz__body(double, %Qubit*)
289
290 declare void @__quantum__rt__result_record_output(%Result*, i8*)
291
292 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
293
294 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="1" "required_num_results"="1" }
295 attributes #1 = { "irreversible" }
296
297 ; module flags
298
299 !llvm.module.flags = !{!0, !1, !2, !3}
300
301 !0 = !{i32 1, !"qir_major_version", i32 1}
302 !1 = !{i32 7, !"qir_minor_version", i32 0}
303 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
304 !3 = !{i32 1, !"dynamic_result_management", i1 false}
305 "#]]
306 .assert_eq(&qir);
307 }
308
309 #[test]
310 fn qubit_reuse_triggers_reindexing() {
311 let source = "namespace Test {
312 @EntryPoint()
313 operation Main() : (Result, Result) {
314 use q = Qubit();
315 (MResetZ(q), MResetZ(q))
316 }
317 }";
318 let qir = compile_source_to_qir(source, *CAPABILITIES);
319 expect![[r#"
320 %Result = type opaque
321 %Qubit = type opaque
322
323 define void @ENTRYPOINT__main() #0 {
324 block_0:
325 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
326 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
327 call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
328 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
329 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
330 ret void
331 }
332
333 declare void @__quantum__rt__tuple_record_output(i64, i8*)
334
335 declare void @__quantum__rt__result_record_output(%Result*, i8*)
336
337 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
338
339 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="2" }
340 attributes #1 = { "irreversible" }
341
342 ; module flags
343
344 !llvm.module.flags = !{!0, !1, !2, !3}
345
346 !0 = !{i32 1, !"qir_major_version", i32 1}
347 !1 = !{i32 7, !"qir_minor_version", i32 0}
348 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
349 !3 = !{i32 1, !"dynamic_result_management", i1 false}
350 "#]].assert_eq(&qir);
351 }
352
353 #[test]
354 fn qubit_measurements_get_deferred() {
355 let source = "namespace Test {
356 @EntryPoint()
357 operation Main() : Result[] {
358 use (q0, q1) = (Qubit(), Qubit());
359 X(q0);
360 let r0 = MResetZ(q0);
361 X(q1);
362 let r1 = MResetZ(q1);
363 [r0, r1]
364 }
365 }";
366 let qir = compile_source_to_qir(source, *CAPABILITIES);
367 expect![[r#"
368 %Result = type opaque
369 %Qubit = type opaque
370
371 define void @ENTRYPOINT__main() #0 {
372 block_0:
373 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
374 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
375 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
376 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
377 call void @__quantum__rt__array_record_output(i64 2, i8* null)
378 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
379 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
380 ret void
381 }
382
383 declare void @__quantum__qis__x__body(%Qubit*)
384
385 declare void @__quantum__rt__array_record_output(i64, i8*)
386
387 declare void @__quantum__rt__result_record_output(%Result*, i8*)
388
389 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
390
391 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="2" }
392 attributes #1 = { "irreversible" }
393
394 ; module flags
395
396 !llvm.module.flags = !{!0, !1, !2, !3}
397
398 !0 = !{i32 1, !"qir_major_version", i32 1}
399 !1 = !{i32 7, !"qir_minor_version", i32 0}
400 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
401 !3 = !{i32 1, !"dynamic_result_management", i1 false}
402 "#]].assert_eq(&qir);
403 }
404
405 #[test]
406 fn qubit_id_swap_results_in_different_id_usage() {
407 let source = "namespace Test {
408 @EntryPoint()
409 operation Main() : (Result, Result) {
410 use (q0, q1) = (Qubit(), Qubit());
411 X(q0);
412 Relabel([q0, q1], [q1, q0]);
413 X(q1);
414 (MResetZ(q0), MResetZ(q1))
415 }
416 }";
417 let qir = compile_source_to_qir(source, *CAPABILITIES);
418 expect![[r#"
419 %Result = type opaque
420 %Qubit = type opaque
421
422 define void @ENTRYPOINT__main() #0 {
423 block_0:
424 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
425 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
426 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
427 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
428 call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
429 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
430 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
431 ret void
432 }
433
434 declare void @__quantum__qis__x__body(%Qubit*)
435
436 declare void @__quantum__rt__tuple_record_output(i64, i8*)
437
438 declare void @__quantum__rt__result_record_output(%Result*, i8*)
439
440 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
441
442 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="2" }
443 attributes #1 = { "irreversible" }
444
445 ; module flags
446
447 !llvm.module.flags = !{!0, !1, !2, !3}
448
449 !0 = !{i32 1, !"qir_major_version", i32 1}
450 !1 = !{i32 7, !"qir_minor_version", i32 0}
451 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
452 !3 = !{i32 1, !"dynamic_result_management", i1 false}
453 "#]].assert_eq(&qir);
454 }
455
456 #[test]
457 fn qubit_id_swap_across_reset_uses_updated_ids() {
458 let source = "namespace Test {
459 @EntryPoint()
460 operation Main() : (Result, Result) {
461 {
462 use (q0, q1) = (Qubit(), Qubit());
463 X(q0);
464 Relabel([q0, q1], [q1, q0]);
465 X(q1);
466 Reset(q0);
467 Reset(q1);
468 }
469 use (q0, q1) = (Qubit(), Qubit());
470 (MResetZ(q0), MResetZ(q1))
471 }
472 }";
473 let qir = compile_source_to_qir(source, *CAPABILITIES);
474 expect![[r#"
475 %Result = type opaque
476 %Qubit = type opaque
477
478 define void @ENTRYPOINT__main() #0 {
479 block_0:
480 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
481 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
482 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
483 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
484 call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
485 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
486 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
487 ret void
488 }
489
490 declare void @__quantum__qis__x__body(%Qubit*)
491
492 declare void @__quantum__rt__tuple_record_output(i64, i8*)
493
494 declare void @__quantum__rt__result_record_output(%Result*, i8*)
495
496 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
497
498 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="4" "required_num_results"="2" }
499 attributes #1 = { "irreversible" }
500
501 ; module flags
502
503 !llvm.module.flags = !{!0, !1, !2, !3}
504
505 !0 = !{i32 1, !"qir_major_version", i32 1}
506 !1 = !{i32 7, !"qir_minor_version", i32 0}
507 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
508 !3 = !{i32 1, !"dynamic_result_management", i1 false}
509 "#]].assert_eq(&qir);
510 }
511}
512
513mod adaptive_profile {
514 use super::compile_source_to_qir;
515 use expect_test::expect;
516 use qsc_data_structures::target::TargetCapabilityFlags;
517 static CAPABILITIES: std::sync::LazyLock<TargetCapabilityFlags> =
518 std::sync::LazyLock::new(|| TargetCapabilityFlags::Adaptive);
519
520 #[test]
521 fn simple() {
522 let source = "namespace Test {
523 import Std.Math.*;
524 open QIR.Intrinsic;
525 @EntryPoint()
526 operation Main() : Result {
527 use q = Qubit();
528 let pi_over_two = 4.0 / 2.0;
529 __quantum__qis__rz__body(pi_over_two, q);
530 mutable some_angle = ArcSin(0.0);
531 __quantum__qis__rz__body(some_angle, q);
532 set some_angle = ArcCos(-1.0) / PI();
533 __quantum__qis__rz__body(some_angle, q);
534 __quantum__qis__mresetz__body(q)
535 }
536 }";
537 let qir = compile_source_to_qir(source, *CAPABILITIES);
538 expect![[r#"
539 %Result = type opaque
540 %Qubit = type opaque
541
542 define void @ENTRYPOINT__main() #0 {
543 block_0:
544 call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
545 call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
546 call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
547 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
548 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
549 ret void
550 }
551
552 declare void @__quantum__qis__rz__body(double, %Qubit*)
553
554 declare void @__quantum__rt__result_record_output(%Result*, i8*)
555
556 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
557
558 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
559 attributes #1 = { "irreversible" }
560
561 ; module flags
562
563 !llvm.module.flags = !{!0, !1, !2, !3}
564
565 !0 = !{i32 1, !"qir_major_version", i32 1}
566 !1 = !{i32 7, !"qir_minor_version", i32 0}
567 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
568 !3 = !{i32 1, !"dynamic_result_management", i1 false}
569 "#]]
570 .assert_eq(&qir);
571 }
572
573 #[test]
574 fn qubit_reuse_triggers_reindexing() {
575 let source = "namespace Test {
576 @EntryPoint()
577 operation Main() : (Result, Result) {
578 use q = Qubit();
579 (MResetZ(q), MResetZ(q))
580 }
581 }";
582 let qir = compile_source_to_qir(source, *CAPABILITIES);
583 expect![[r#"
584 %Result = type opaque
585 %Qubit = type opaque
586
587 define void @ENTRYPOINT__main() #0 {
588 block_0:
589 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
590 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
591 call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
592 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
593 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
594 ret void
595 }
596
597 declare void @__quantum__rt__tuple_record_output(i64, i8*)
598
599 declare void @__quantum__rt__result_record_output(%Result*, i8*)
600
601 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
602
603 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
604 attributes #1 = { "irreversible" }
605
606 ; module flags
607
608 !llvm.module.flags = !{!0, !1, !2, !3}
609
610 !0 = !{i32 1, !"qir_major_version", i32 1}
611 !1 = !{i32 7, !"qir_minor_version", i32 0}
612 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
613 !3 = !{i32 1, !"dynamic_result_management", i1 false}
614 "#]].assert_eq(&qir);
615 }
616
617 #[test]
618 fn custom_measurement_generates_correct_qir() {
619 let source = "namespace Test {
620 operation Main() : Result {
621 use q = Qubit();
622 H(q);
623 __quantum__qis__mx__body(q)
624 }
625
626 @Measurement()
627 operation __quantum__qis__mx__body(target: Qubit) : Result {
628 body intrinsic;
629 }
630 }";
631 let qir = compile_source_to_qir(source, *CAPABILITIES);
632 expect![[r#"
633 %Result = type opaque
634 %Qubit = type opaque
635
636 define void @ENTRYPOINT__main() #0 {
637 block_0:
638 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
639 call void @__quantum__qis__mx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
640 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
641 ret void
642 }
643
644 declare void @__quantum__qis__h__body(%Qubit*)
645
646 declare void @__quantum__qis__mx__body(%Qubit*, %Result*) #1
647
648 declare void @__quantum__rt__result_record_output(%Result*, i8*)
649
650 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
651 attributes #1 = { "irreversible" }
652
653 ; module flags
654
655 !llvm.module.flags = !{!0, !1, !2, !3}
656
657 !0 = !{i32 1, !"qir_major_version", i32 1}
658 !1 = !{i32 7, !"qir_minor_version", i32 0}
659 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
660 !3 = !{i32 1, !"dynamic_result_management", i1 false}
661 "#]].assert_eq(&qir);
662 }
663
664 #[test]
665 fn custom_joint_measurement_generates_correct_qir() {
666 let source = "namespace Test {
667 operation Main() : (Result, Result) {
668 use q1 = Qubit();
669 use q2 = Qubit();
670 H(q1);
671 H(q2);
672 __quantum__qis__mzz__body(q1, q2)
673 }
674
675 @Measurement()
676 operation __quantum__qis__mzz__body(q1: Qubit, q2: Qubit) : (Result, Result) {
677 body intrinsic;
678 }
679 }";
680 let qir = compile_source_to_qir(source, *CAPABILITIES);
681 expect![[r#"
682 %Result = type opaque
683 %Qubit = type opaque
684
685 define void @ENTRYPOINT__main() #0 {
686 block_0:
687 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
688 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
689 call void @__quantum__qis__mzz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*), %Result* inttoptr (i64 1 to %Result*))
690 call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
691 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
692 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
693 ret void
694 }
695
696 declare void @__quantum__qis__h__body(%Qubit*)
697
698 declare void @__quantum__qis__mzz__body(%Qubit*, %Qubit*, %Result*, %Result*) #1
699
700 declare void @__quantum__rt__tuple_record_output(i64, i8*)
701
702 declare void @__quantum__rt__result_record_output(%Result*, i8*)
703
704 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
705 attributes #1 = { "irreversible" }
706
707 ; module flags
708
709 !llvm.module.flags = !{!0, !1, !2, !3}
710
711 !0 = !{i32 1, !"qir_major_version", i32 1}
712 !1 = !{i32 7, !"qir_minor_version", i32 0}
713 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
714 !3 = !{i32 1, !"dynamic_result_management", i1 false}
715 "#]].assert_eq(&qir);
716 }
717
718 #[test]
719 fn qubit_measurements_not_deferred() {
720 let source = "namespace Test {
721 @EntryPoint()
722 operation Main() : Result[] {
723 use (q0, q1) = (Qubit(), Qubit());
724 X(q0);
725 let r0 = MResetZ(q0);
726 X(q1);
727 let r1 = MResetZ(q1);
728 [r0, r1]
729 }
730 }";
731 let qir = compile_source_to_qir(source, *CAPABILITIES);
732 expect![[r#"
733 %Result = type opaque
734 %Qubit = type opaque
735
736 define void @ENTRYPOINT__main() #0 {
737 block_0:
738 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
739 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
740 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
741 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
742 call void @__quantum__rt__array_record_output(i64 2, i8* null)
743 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
744 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
745 ret void
746 }
747
748 declare void @__quantum__qis__x__body(%Qubit*)
749
750 declare void @__quantum__rt__array_record_output(i64, i8*)
751
752 declare void @__quantum__rt__result_record_output(%Result*, i8*)
753
754 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
755
756 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
757 attributes #1 = { "irreversible" }
758
759 ; module flags
760
761 !llvm.module.flags = !{!0, !1, !2, !3}
762
763 !0 = !{i32 1, !"qir_major_version", i32 1}
764 !1 = !{i32 7, !"qir_minor_version", i32 0}
765 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
766 !3 = !{i32 1, !"dynamic_result_management", i1 false}
767 "#]].assert_eq(&qir);
768 }
769}
770
771mod adaptive_ri_profile {
772
773 use expect_test::expect;
774 use qsc_data_structures::target::TargetCapabilityFlags;
775
776 use super::compile_source_to_qir;
777 static CAPABILITIES: std::sync::LazyLock<TargetCapabilityFlags> =
778 std::sync::LazyLock::new(|| {
779 TargetCapabilityFlags::Adaptive
780 | TargetCapabilityFlags::QubitReset
781 | TargetCapabilityFlags::IntegerComputations
782 });
783
784 #[test]
785 fn simple() {
786 let source = "namespace Test {
787 import Std.Math.*;
788 open QIR.Intrinsic;
789 @EntryPoint()
790 operation Main() : Result {
791 use q = Qubit();
792 let pi_over_two = 4.0 / 2.0;
793 __quantum__qis__rz__body(pi_over_two, q);
794 mutable some_angle = ArcSin(0.0);
795 __quantum__qis__rz__body(some_angle, q);
796 set some_angle = ArcCos(-1.0) / PI();
797 __quantum__qis__rz__body(some_angle, q);
798 __quantum__qis__mresetz__body(q)
799 }
800 }";
801 let qir = compile_source_to_qir(source, *CAPABILITIES);
802 expect![[r#"
803 %Result = type opaque
804 %Qubit = type opaque
805
806 define void @ENTRYPOINT__main() #0 {
807 block_0:
808 call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
809 call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
810 call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
811 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
812 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
813 ret void
814 }
815
816 declare void @__quantum__qis__rz__body(double, %Qubit*)
817
818 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
819
820 declare void @__quantum__rt__result_record_output(%Result*, i8*)
821
822 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
823 attributes #1 = { "irreversible" }
824
825 ; module flags
826
827 !llvm.module.flags = !{!0, !1, !2, !3, !4}
828
829 !0 = !{i32 1, !"qir_major_version", i32 1}
830 !1 = !{i32 7, !"qir_minor_version", i32 0}
831 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
832 !3 = !{i32 1, !"dynamic_result_management", i1 false}
833 !4 = !{i32 1, !"int_computations", !"i64"}
834 "#]]
835 .assert_eq(&qir);
836 }
837
838 #[test]
839 fn qubit_reuse_allowed() {
840 let source = "namespace Test {
841 @EntryPoint()
842 operation Main() : (Result, Result) {
843 use q = Qubit();
844 (MResetZ(q), MResetZ(q))
845 }
846 }";
847 let qir = compile_source_to_qir(source, *CAPABILITIES);
848 expect![[r#"
849 %Result = type opaque
850 %Qubit = type opaque
851
852 define void @ENTRYPOINT__main() #0 {
853 block_0:
854 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
855 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
856 call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
857 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
858 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
859 ret void
860 }
861
862 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
863
864 declare void @__quantum__rt__tuple_record_output(i64, i8*)
865
866 declare void @__quantum__rt__result_record_output(%Result*, i8*)
867
868 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="2" }
869 attributes #1 = { "irreversible" }
870
871 ; module flags
872
873 !llvm.module.flags = !{!0, !1, !2, !3, !4}
874
875 !0 = !{i32 1, !"qir_major_version", i32 1}
876 !1 = !{i32 7, !"qir_minor_version", i32 0}
877 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
878 !3 = !{i32 1, !"dynamic_result_management", i1 false}
879 !4 = !{i32 1, !"int_computations", !"i64"}
880 "#]].assert_eq(&qir);
881 }
882
883 #[test]
884 fn qubit_measurements_not_deferred() {
885 let source = "namespace Test {
886 @EntryPoint()
887 operation Main() : Result[] {
888 use (q0, q1) = (Qubit(), Qubit());
889 X(q0);
890 let r0 = MResetZ(q0);
891 X(q1);
892 let r1 = MResetZ(q1);
893 [r0, r1]
894 }
895 }";
896 let qir = compile_source_to_qir(source, *CAPABILITIES);
897 expect![[r#"
898 %Result = type opaque
899 %Qubit = type opaque
900
901 define void @ENTRYPOINT__main() #0 {
902 block_0:
903 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
904 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
905 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
906 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
907 call void @__quantum__rt__array_record_output(i64 2, i8* null)
908 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
909 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
910 ret void
911 }
912
913 declare void @__quantum__qis__x__body(%Qubit*)
914
915 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
916
917 declare void @__quantum__rt__array_record_output(i64, i8*)
918
919 declare void @__quantum__rt__result_record_output(%Result*, i8*)
920
921 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
922 attributes #1 = { "irreversible" }
923
924 ; module flags
925
926 !llvm.module.flags = !{!0, !1, !2, !3, !4}
927
928 !0 = !{i32 1, !"qir_major_version", i32 1}
929 !1 = !{i32 7, !"qir_minor_version", i32 0}
930 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
931 !3 = !{i32 1, !"dynamic_result_management", i1 false}
932 !4 = !{i32 1, !"int_computations", !"i64"}
933 "#]].assert_eq(&qir);
934 }
935
936 #[test]
937 fn qubit_id_swap_results_in_different_id_usage() {
938 let source = "namespace Test {
939 @EntryPoint()
940 operation Main() : (Result, Result) {
941 use (q0, q1) = (Qubit(), Qubit());
942 X(q0);
943 Relabel([q0, q1], [q1, q0]);
944 X(q1);
945 (MResetZ(q0), MResetZ(q1))
946 }
947 }";
948 let qir = compile_source_to_qir(source, *CAPABILITIES);
949 expect![[r#"
950 %Result = type opaque
951 %Qubit = type opaque
952
953 define void @ENTRYPOINT__main() #0 {
954 block_0:
955 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
956 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
957 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
958 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
959 call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
960 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
961 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
962 ret void
963 }
964
965 declare void @__quantum__qis__x__body(%Qubit*)
966
967 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
968
969 declare void @__quantum__rt__tuple_record_output(i64, i8*)
970
971 declare void @__quantum__rt__result_record_output(%Result*, i8*)
972
973 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
974 attributes #1 = { "irreversible" }
975
976 ; module flags
977
978 !llvm.module.flags = !{!0, !1, !2, !3, !4}
979
980 !0 = !{i32 1, !"qir_major_version", i32 1}
981 !1 = !{i32 7, !"qir_minor_version", i32 0}
982 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
983 !3 = !{i32 1, !"dynamic_result_management", i1 false}
984 !4 = !{i32 1, !"int_computations", !"i64"}
985 "#]].assert_eq(&qir);
986 }
987
988 #[test]
989 fn qubit_id_swap_across_reset_uses_updated_ids() {
990 let source = "namespace Test {
991 @EntryPoint()
992 operation Main() : (Result, Result) {
993 {
994 use (q0, q1) = (Qubit(), Qubit());
995 X(q0);
996 Relabel([q0, q1], [q1, q0]);
997 X(q1);
998 Reset(q0);
999 Reset(q1);
1000 }
1001 use (q0, q1) = (Qubit(), Qubit());
1002 (MResetZ(q0), MResetZ(q1))
1003 }
1004 }";
1005 let qir = compile_source_to_qir(source, *CAPABILITIES);
1006 expect![[r#"
1007 %Result = type opaque
1008 %Qubit = type opaque
1009
1010 define void @ENTRYPOINT__main() #0 {
1011 block_0:
1012 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1013 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1014 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 1 to %Qubit*))
1015 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1016 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1017 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
1018 call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
1019 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1020 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
1021 ret void
1022 }
1023
1024 declare void @__quantum__qis__x__body(%Qubit*)
1025
1026 declare void @__quantum__qis__reset__body(%Qubit*) #1
1027
1028 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1029
1030 declare void @__quantum__rt__tuple_record_output(i64, i8*)
1031
1032 declare void @__quantum__rt__result_record_output(%Result*, i8*)
1033
1034 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
1035 attributes #1 = { "irreversible" }
1036
1037 ; module flags
1038
1039 !llvm.module.flags = !{!0, !1, !2, !3, !4}
1040
1041 !0 = !{i32 1, !"qir_major_version", i32 1}
1042 !1 = !{i32 7, !"qir_minor_version", i32 0}
1043 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1044 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1045 !4 = !{i32 1, !"int_computations", !"i64"}
1046 "#]].assert_eq(&qir);
1047 }
1048
1049 #[test]
1050 fn qubit_id_swap_with_out_of_order_release_uses_correct_ids() {
1051 let source = "namespace Test {
1052 @EntryPoint()
1053 operation Main() : (Result, Result) {
1054 let q0 = QIR.Runtime.__quantum__rt__qubit_allocate();
1055 let q1 = QIR.Runtime.__quantum__rt__qubit_allocate();
1056 let q2 = QIR.Runtime.__quantum__rt__qubit_allocate();
1057 X(q0);
1058 X(q1);
1059 X(q2);
1060 Relabel([q0, q1], [q1, q0]);
1061 QIR.Runtime.__quantum__rt__qubit_release(q0);
1062 let q3 = QIR.Runtime.__quantum__rt__qubit_allocate();
1063 X(q3);
1064 (MResetZ(q3), MResetZ(q1))
1065 }
1066 }";
1067 let qir = compile_source_to_qir(source, *CAPABILITIES);
1068 expect![[r#"
1069 %Result = type opaque
1070 %Qubit = type opaque
1071
1072 define void @ENTRYPOINT__main() #0 {
1073 block_0:
1074 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1075 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
1076 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
1077 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
1078 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1079 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
1080 call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
1081 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1082 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
1083 ret void
1084 }
1085
1086 declare void @__quantum__qis__x__body(%Qubit*)
1087
1088 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1089
1090 declare void @__quantum__rt__tuple_record_output(i64, i8*)
1091
1092 declare void @__quantum__rt__result_record_output(%Result*, i8*)
1093
1094 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="3" "required_num_results"="2" }
1095 attributes #1 = { "irreversible" }
1096
1097 ; module flags
1098
1099 !llvm.module.flags = !{!0, !1, !2, !3, !4}
1100
1101 !0 = !{i32 1, !"qir_major_version", i32 1}
1102 !1 = !{i32 7, !"qir_minor_version", i32 0}
1103 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1104 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1105 !4 = !{i32 1, !"int_computations", !"i64"}
1106 "#]].assert_eq(&qir);
1107 }
1108
1109 #[test]
1110 fn dynamic_integer_with_branch_and_phi_supported() {
1111 let source = "namespace Test {
1112 @EntryPoint()
1113 operation Main() : Int {
1114 use q = Qubit();
1115 H(q);
1116 MResetZ(q) == Zero ? 0 | 1
1117 }
1118 }";
1119 let qir = compile_source_to_qir(source, *CAPABILITIES);
1120 expect![[r#"
1121 %Result = type opaque
1122 %Qubit = type opaque
1123
1124 define void @ENTRYPOINT__main() #0 {
1125 block_0:
1126 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1127 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1128 %var_0 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 0 to %Result*))
1129 %var_1 = icmp eq i1 %var_0, false
1130 br i1 %var_1, label %block_1, label %block_2
1131 block_1:
1132 br label %block_3
1133 block_2:
1134 br label %block_3
1135 block_3:
1136 %var_4 = phi i64 [0, %block_1], [1, %block_2]
1137 call void @__quantum__rt__int_record_output(i64 %var_4, i8* null)
1138 ret void
1139 }
1140
1141 declare void @__quantum__qis__h__body(%Qubit*)
1142
1143 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1144
1145 declare i1 @__quantum__qis__read_result__body(%Result*)
1146
1147 declare void @__quantum__rt__int_record_output(i64, i8*)
1148
1149 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
1150 attributes #1 = { "irreversible" }
1151
1152 ; module flags
1153
1154 !llvm.module.flags = !{!0, !1, !2, !3, !4}
1155
1156 !0 = !{i32 1, !"qir_major_version", i32 1}
1157 !1 = !{i32 7, !"qir_minor_version", i32 0}
1158 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1159 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1160 !4 = !{i32 1, !"int_computations", !"i64"}
1161 "#]].assert_eq(&qir);
1162 }
1163
1164 #[test]
1165 fn custom_reset_generates_correct_qir() {
1166 let source = "namespace Test {
1167 operation Main() : Result {
1168 use q = Qubit();
1169 __quantum__qis__custom_reset__body(q);
1170 M(q)
1171 }
1172
1173 @Reset()
1174 operation __quantum__qis__custom_reset__body(target: Qubit) : Unit {
1175 body intrinsic;
1176 }
1177 }";
1178 let qir = compile_source_to_qir(source, *CAPABILITIES);
1179 expect![[r#"
1180 %Result = type opaque
1181 %Qubit = type opaque
1182
1183 define void @ENTRYPOINT__main() #0 {
1184 block_0:
1185 call void @__quantum__qis__custom_reset__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1186 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1187 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1188 ret void
1189 }
1190
1191 declare void @__quantum__qis__custom_reset__body(%Qubit*) #1
1192
1193 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
1194
1195 declare void @__quantum__rt__result_record_output(%Result*, i8*)
1196
1197 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
1198 attributes #1 = { "irreversible" }
1199
1200 ; module flags
1201
1202 !llvm.module.flags = !{!0, !1, !2, !3, !4}
1203
1204 !0 = !{i32 1, !"qir_major_version", i32 1}
1205 !1 = !{i32 7, !"qir_minor_version", i32 0}
1206 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1207 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1208 !4 = !{i32 1, !"int_computations", !"i64"}
1209 "#]]
1210 .assert_eq(&qir);
1211 }
1212}
1213
1214mod adaptive_rif_profile {
1215 use super::compile_source_to_qir;
1216 use expect_test::expect;
1217 use qsc_data_structures::target::TargetCapabilityFlags;
1218 static CAPABILITIES: std::sync::LazyLock<TargetCapabilityFlags> =
1219 std::sync::LazyLock::new(|| {
1220 TargetCapabilityFlags::Adaptive
1221 | TargetCapabilityFlags::QubitReset
1222 | TargetCapabilityFlags::IntegerComputations
1223 | TargetCapabilityFlags::FloatingPointComputations
1224 });
1225
1226 #[test]
1227 fn simple() {
1228 let source = "namespace Test {
1229 import Std.Math.*;
1230 open QIR.Intrinsic;
1231 @EntryPoint()
1232 operation Main() : Result {
1233 use q = Qubit();
1234 let pi_over_two = 4.0 / 2.0;
1235 __quantum__qis__rz__body(pi_over_two, q);
1236 mutable some_angle = ArcSin(0.0);
1237 __quantum__qis__rz__body(some_angle, q);
1238 set some_angle = ArcCos(-1.0) / PI();
1239 __quantum__qis__rz__body(some_angle, q);
1240 __quantum__qis__mresetz__body(q)
1241 }
1242 }";
1243 let qir = compile_source_to_qir(source, *CAPABILITIES);
1244 expect![[r#"
1245 %Result = type opaque
1246 %Qubit = type opaque
1247
1248 define void @ENTRYPOINT__main() #0 {
1249 block_0:
1250 call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
1251 call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
1252 call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
1253 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1254 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1255 ret void
1256 }
1257
1258 declare void @__quantum__qis__rz__body(double, %Qubit*)
1259
1260 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1261
1262 declare void @__quantum__rt__result_record_output(%Result*, i8*)
1263
1264 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
1265 attributes #1 = { "irreversible" }
1266
1267 ; module flags
1268
1269 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
1270
1271 !0 = !{i32 1, !"qir_major_version", i32 1}
1272 !1 = !{i32 7, !"qir_minor_version", i32 0}
1273 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1274 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1275 !4 = !{i32 1, !"int_computations", !"i64"}
1276 !5 = !{i32 1, !"float_computations", !"f64"}
1277 "#]]
1278 .assert_eq(&qir);
1279 }
1280
1281 #[test]
1282 fn qubit_reuse_allowed() {
1283 let source = "namespace Test {
1284 @EntryPoint()
1285 operation Main() : (Result, Result) {
1286 use q = Qubit();
1287 (MResetZ(q), MResetZ(q))
1288 }
1289 }";
1290 let qir = compile_source_to_qir(source, *CAPABILITIES);
1291 expect![[r#"
1292 %Result = type opaque
1293 %Qubit = type opaque
1294
1295 define void @ENTRYPOINT__main() #0 {
1296 block_0:
1297 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1298 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
1299 call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
1300 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1301 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
1302 ret void
1303 }
1304
1305 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1306
1307 declare void @__quantum__rt__tuple_record_output(i64, i8*)
1308
1309 declare void @__quantum__rt__result_record_output(%Result*, i8*)
1310
1311 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="2" }
1312 attributes #1 = { "irreversible" }
1313
1314 ; module flags
1315
1316 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
1317
1318 !0 = !{i32 1, !"qir_major_version", i32 1}
1319 !1 = !{i32 7, !"qir_minor_version", i32 0}
1320 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1321 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1322 !4 = !{i32 1, !"int_computations", !"i64"}
1323 !5 = !{i32 1, !"float_computations", !"f64"}
1324 "#]].assert_eq(&qir);
1325 }
1326
1327 #[test]
1328 fn qubit_measurements_not_deferred() {
1329 let source = "namespace Test {
1330 @EntryPoint()
1331 operation Main() : Result[] {
1332 use (q0, q1) = (Qubit(), Qubit());
1333 X(q0);
1334 let r0 = MResetZ(q0);
1335 X(q1);
1336 let r1 = MResetZ(q1);
1337 [r0, r1]
1338 }
1339 }";
1340 let qir = compile_source_to_qir(source, *CAPABILITIES);
1341 expect![[r#"
1342 %Result = type opaque
1343 %Qubit = type opaque
1344
1345 define void @ENTRYPOINT__main() #0 {
1346 block_0:
1347 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1348 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1349 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
1350 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
1351 call void @__quantum__rt__array_record_output(i64 2, i8* null)
1352 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1353 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
1354 ret void
1355 }
1356
1357 declare void @__quantum__qis__x__body(%Qubit*)
1358
1359 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1360
1361 declare void @__quantum__rt__array_record_output(i64, i8*)
1362
1363 declare void @__quantum__rt__result_record_output(%Result*, i8*)
1364
1365 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
1366 attributes #1 = { "irreversible" }
1367
1368 ; module flags
1369
1370 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
1371
1372 !0 = !{i32 1, !"qir_major_version", i32 1}
1373 !1 = !{i32 7, !"qir_minor_version", i32 0}
1374 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1375 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1376 !4 = !{i32 1, !"int_computations", !"i64"}
1377 !5 = !{i32 1, !"float_computations", !"f64"}
1378 "#]].assert_eq(&qir);
1379 }
1380
1381 #[test]
1382 fn qubit_id_swap_results_in_different_id_usage() {
1383 let source = "namespace Test {
1384 @EntryPoint()
1385 operation Main() : (Result, Result) {
1386 use (q0, q1) = (Qubit(), Qubit());
1387 X(q0);
1388 Relabel([q0, q1], [q1, q0]);
1389 X(q1);
1390 (MResetZ(q0), MResetZ(q1))
1391 }
1392 }";
1393 let qir = compile_source_to_qir(source, *CAPABILITIES);
1394 expect![[r#"
1395 %Result = type opaque
1396 %Qubit = type opaque
1397
1398 define void @ENTRYPOINT__main() #0 {
1399 block_0:
1400 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1401 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1402 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1403 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
1404 call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
1405 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1406 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
1407 ret void
1408 }
1409
1410 declare void @__quantum__qis__x__body(%Qubit*)
1411
1412 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1413
1414 declare void @__quantum__rt__tuple_record_output(i64, i8*)
1415
1416 declare void @__quantum__rt__result_record_output(%Result*, i8*)
1417
1418 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
1419 attributes #1 = { "irreversible" }
1420
1421 ; module flags
1422
1423 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
1424
1425 !0 = !{i32 1, !"qir_major_version", i32 1}
1426 !1 = !{i32 7, !"qir_minor_version", i32 0}
1427 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1428 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1429 !4 = !{i32 1, !"int_computations", !"i64"}
1430 !5 = !{i32 1, !"float_computations", !"f64"}
1431 "#]].assert_eq(&qir);
1432 }
1433
1434 #[test]
1435 fn qubit_id_swap_across_reset_uses_updated_ids() {
1436 let source = "namespace Test {
1437 @EntryPoint()
1438 operation Main() : (Result, Result) {
1439 {
1440 use (q0, q1) = (Qubit(), Qubit());
1441 X(q0);
1442 Relabel([q0, q1], [q1, q0]);
1443 X(q1);
1444 Reset(q0);
1445 Reset(q1);
1446 }
1447 use (q0, q1) = (Qubit(), Qubit());
1448 (MResetZ(q0), MResetZ(q1))
1449 }
1450 }";
1451 let qir = compile_source_to_qir(source, *CAPABILITIES);
1452 expect![[r#"
1453 %Result = type opaque
1454 %Qubit = type opaque
1455
1456 define void @ENTRYPOINT__main() #0 {
1457 block_0:
1458 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1459 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1460 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 1 to %Qubit*))
1461 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1462 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1463 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
1464 call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
1465 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1466 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
1467 ret void
1468 }
1469
1470 declare void @__quantum__qis__x__body(%Qubit*)
1471
1472 declare void @__quantum__qis__reset__body(%Qubit*) #1
1473
1474 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1475
1476 declare void @__quantum__rt__tuple_record_output(i64, i8*)
1477
1478 declare void @__quantum__rt__result_record_output(%Result*, i8*)
1479
1480 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
1481 attributes #1 = { "irreversible" }
1482
1483 ; module flags
1484
1485 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
1486
1487 !0 = !{i32 1, !"qir_major_version", i32 1}
1488 !1 = !{i32 7, !"qir_minor_version", i32 0}
1489 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1490 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1491 !4 = !{i32 1, !"int_computations", !"i64"}
1492 !5 = !{i32 1, !"float_computations", !"f64"}
1493 "#]].assert_eq(&qir);
1494 }
1495
1496 #[test]
1497 fn qubit_id_swap_with_out_of_order_release_uses_correct_ids() {
1498 let source = "namespace Test {
1499 @EntryPoint()
1500 operation Main() : (Result, Result) {
1501 let q0 = QIR.Runtime.__quantum__rt__qubit_allocate();
1502 let q1 = QIR.Runtime.__quantum__rt__qubit_allocate();
1503 let q2 = QIR.Runtime.__quantum__rt__qubit_allocate();
1504 X(q0);
1505 X(q1);
1506 X(q2);
1507 Relabel([q0, q1], [q1, q0]);
1508 QIR.Runtime.__quantum__rt__qubit_release(q0);
1509 let q3 = QIR.Runtime.__quantum__rt__qubit_allocate();
1510 X(q3);
1511 (MResetZ(q3), MResetZ(q1))
1512 }
1513 }";
1514 let qir = compile_source_to_qir(source, *CAPABILITIES);
1515 expect![[r#"
1516 %Result = type opaque
1517 %Qubit = type opaque
1518
1519 define void @ENTRYPOINT__main() #0 {
1520 block_0:
1521 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1522 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
1523 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
1524 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
1525 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1526 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
1527 call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
1528 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1529 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
1530 ret void
1531 }
1532
1533 declare void @__quantum__qis__x__body(%Qubit*)
1534
1535 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1536
1537 declare void @__quantum__rt__tuple_record_output(i64, i8*)
1538
1539 declare void @__quantum__rt__result_record_output(%Result*, i8*)
1540
1541 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="3" "required_num_results"="2" }
1542 attributes #1 = { "irreversible" }
1543
1544 ; module flags
1545
1546 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
1547
1548 !0 = !{i32 1, !"qir_major_version", i32 1}
1549 !1 = !{i32 7, !"qir_minor_version", i32 0}
1550 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1551 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1552 !4 = !{i32 1, !"int_computations", !"i64"}
1553 !5 = !{i32 1, !"float_computations", !"f64"}
1554 "#]].assert_eq(&qir);
1555 }
1556
1557 #[test]
1558 fn dynamic_integer_with_branch_and_phi_supported() {
1559 let source = "namespace Test {
1560 @EntryPoint()
1561 operation Main() : Int {
1562 use q = Qubit();
1563 H(q);
1564 MResetZ(q) == Zero ? 0 | 1
1565 }
1566 }";
1567 let qir = compile_source_to_qir(source, *CAPABILITIES);
1568 expect![[r#"
1569 %Result = type opaque
1570 %Qubit = type opaque
1571
1572 define void @ENTRYPOINT__main() #0 {
1573 block_0:
1574 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1575 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1576 %var_0 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 0 to %Result*))
1577 %var_1 = icmp eq i1 %var_0, false
1578 br i1 %var_1, label %block_1, label %block_2
1579 block_1:
1580 br label %block_3
1581 block_2:
1582 br label %block_3
1583 block_3:
1584 %var_4 = phi i64 [0, %block_1], [1, %block_2]
1585 call void @__quantum__rt__int_record_output(i64 %var_4, i8* null)
1586 ret void
1587 }
1588
1589 declare void @__quantum__qis__h__body(%Qubit*)
1590
1591 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1592
1593 declare i1 @__quantum__qis__read_result__body(%Result*)
1594
1595 declare void @__quantum__rt__int_record_output(i64, i8*)
1596
1597 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
1598 attributes #1 = { "irreversible" }
1599
1600 ; module flags
1601
1602 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
1603
1604 !0 = !{i32 1, !"qir_major_version", i32 1}
1605 !1 = !{i32 7, !"qir_minor_version", i32 0}
1606 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1607 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1608 !4 = !{i32 1, !"int_computations", !"i64"}
1609 !5 = !{i32 1, !"float_computations", !"f64"}
1610 "#]].assert_eq(&qir);
1611 }
1612
1613 #[test]
1614 fn dynamic_double_with_branch_and_phi_supported() {
1615 let source = "namespace Test {
1616 @EntryPoint()
1617 operation Main() : Double {
1618 use q = Qubit();
1619 H(q);
1620 MResetZ(q) == Zero ? 0.0 | 1.0
1621 }
1622 }";
1623 let qir = compile_source_to_qir(source, *CAPABILITIES);
1624 expect![[r#"
1625 %Result = type opaque
1626 %Qubit = type opaque
1627
1628 define void @ENTRYPOINT__main() #0 {
1629 block_0:
1630 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1631 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1632 %var_0 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 0 to %Result*))
1633 %var_1 = icmp eq i1 %var_0, false
1634 br i1 %var_1, label %block_1, label %block_2
1635 block_1:
1636 br label %block_3
1637 block_2:
1638 br label %block_3
1639 block_3:
1640 %var_4 = phi double [0.0, %block_1], [1.0, %block_2]
1641 call void @__quantum__rt__double_record_output(double %var_4, i8* null)
1642 ret void
1643 }
1644
1645 declare void @__quantum__qis__h__body(%Qubit*)
1646
1647 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1648
1649 declare i1 @__quantum__qis__read_result__body(%Result*)
1650
1651 declare void @__quantum__rt__double_record_output(double, i8*)
1652
1653 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
1654 attributes #1 = { "irreversible" }
1655
1656 ; module flags
1657
1658 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
1659
1660 !0 = !{i32 1, !"qir_major_version", i32 1}
1661 !1 = !{i32 7, !"qir_minor_version", i32 0}
1662 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1663 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1664 !4 = !{i32 1, !"int_computations", !"i64"}
1665 !5 = !{i32 1, !"float_computations", !"f64"}
1666 "#]].assert_eq(&qir);
1667 }
1668
1669 #[test]
1670 fn custom_reset_generates_correct_qir() {
1671 let source = "namespace Test {
1672 operation Main() : Result {
1673 use q = Qubit();
1674 __quantum__qis__custom_reset__body(q);
1675 M(q)
1676 }
1677
1678 @Reset()
1679 operation __quantum__qis__custom_reset__body(target: Qubit) : Unit {
1680 body intrinsic;
1681 }
1682 }";
1683 let qir = compile_source_to_qir(source, *CAPABILITIES);
1684 expect![[r#"
1685 %Result = type opaque
1686 %Qubit = type opaque
1687
1688 define void @ENTRYPOINT__main() #0 {
1689 block_0:
1690 call void @__quantum__qis__custom_reset__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1691 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1692 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
1693 ret void
1694 }
1695
1696 declare void @__quantum__qis__custom_reset__body(%Qubit*) #1
1697
1698 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
1699
1700 declare void @__quantum__rt__result_record_output(%Result*, i8*)
1701
1702 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
1703 attributes #1 = { "irreversible" }
1704
1705 ; module flags
1706
1707 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
1708
1709 !0 = !{i32 1, !"qir_major_version", i32 1}
1710 !1 = !{i32 7, !"qir_minor_version", i32 0}
1711 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1712 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1713 !4 = !{i32 1, !"int_computations", !"i64"}
1714 !5 = !{i32 1, !"float_computations", !"f64"}
1715 "#]]
1716 .assert_eq(&qir);
1717 }
1718
1719 #[test]
1720 fn dynamic_double_intrinsic() {
1721 let source = "namespace Test {
1722 operation OpA(theta: Double, q : Qubit) : Unit { body intrinsic; }
1723 @EntryPoint()
1724 operation Main() : Double {
1725 use q = Qubit();
1726 H(q);
1727 let theta = MResetZ(q) == Zero ? 0.0 | 1.0;
1728 OpA(1.0 + theta, q);
1729 Rx(2.0 * theta, q);
1730 Ry(theta / 3.0, q);
1731 Rz(theta - 4.0, q);
1732 OpA(theta, q);
1733 Rx(theta, q);
1734 theta
1735 }
1736 }";
1737 let qir = compile_source_to_qir(source, *CAPABILITIES);
1738 expect![[r#"
1739 %Result = type opaque
1740 %Qubit = type opaque
1741
1742 define void @ENTRYPOINT__main() #0 {
1743 block_0:
1744 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1745 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1746 %var_0 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 0 to %Result*))
1747 %var_1 = icmp eq i1 %var_0, false
1748 br i1 %var_1, label %block_1, label %block_2
1749 block_1:
1750 br label %block_3
1751 block_2:
1752 br label %block_3
1753 block_3:
1754 %var_9 = phi double [0.0, %block_1], [1.0, %block_2]
1755 %var_4 = fadd double 1.0, %var_9
1756 call void @OpA(double %var_4, %Qubit* inttoptr (i64 0 to %Qubit*))
1757 %var_5 = fmul double 2.0, %var_9
1758 call void @__quantum__qis__rx__body(double %var_5, %Qubit* inttoptr (i64 0 to %Qubit*))
1759 %var_6 = fdiv double %var_9, 3.0
1760 call void @__quantum__qis__ry__body(double %var_6, %Qubit* inttoptr (i64 0 to %Qubit*))
1761 %var_7 = fsub double %var_9, 4.0
1762 call void @__quantum__qis__rz__body(double %var_7, %Qubit* inttoptr (i64 0 to %Qubit*))
1763 call void @OpA(double %var_9, %Qubit* inttoptr (i64 0 to %Qubit*))
1764 call void @__quantum__qis__rx__body(double %var_9, %Qubit* inttoptr (i64 0 to %Qubit*))
1765 call void @__quantum__rt__double_record_output(double %var_9, i8* null)
1766 ret void
1767 }
1768
1769 declare void @__quantum__qis__h__body(%Qubit*)
1770
1771 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1772
1773 declare i1 @__quantum__qis__read_result__body(%Result*)
1774
1775 declare void @OpA(double, %Qubit*)
1776
1777 declare void @__quantum__qis__rx__body(double, %Qubit*)
1778
1779 declare void @__quantum__qis__ry__body(double, %Qubit*)
1780
1781 declare void @__quantum__qis__rz__body(double, %Qubit*)
1782
1783 declare void @__quantum__rt__double_record_output(double, i8*)
1784
1785 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
1786 attributes #1 = { "irreversible" }
1787
1788 ; module flags
1789
1790 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
1791
1792 !0 = !{i32 1, !"qir_major_version", i32 1}
1793 !1 = !{i32 7, !"qir_minor_version", i32 0}
1794 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1795 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1796 !4 = !{i32 1, !"int_computations", !"i64"}
1797 !5 = !{i32 1, !"float_computations", !"f64"}
1798 "#]].assert_eq(&qir);
1799 }
1800}
1801