microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
c1ca1a247cd0a9687a9fa0fbd08ff72ef5f451aa

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

1985lines · modecode

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