microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.27.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

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