microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.25.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

1932lines · modecode

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