microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
iadavis/pipeline-issue-debugging

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

2042lines · modecode

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