microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
billt/mac-intel-cryptography

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/compiler/qsc/src/interpret/circuit_tests.rs

2139lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#![allow(clippy::unicode_not_nfc)]
5
6use super::{CircuitEntryPoint, Debugger, Interpreter};
7use crate::{
8 interpret::{CircuitGenerationMethod, Error},
9 target::Profile,
10};
11use expect_test::expect;
12use miette::Diagnostic;
13use qsc_circuit::{Circuit, TracerConfig};
14use qsc_data_structures::{language_features::LanguageFeatures, source::SourceMap};
15use qsc_eval::output::GenericReceiver;
16use qsc_eval::val::Value;
17use qsc_passes::PackageType;
18
19fn interpreter(code: &str, package_type: PackageType, profile: Profile) -> Interpreter {
20 let sources = SourceMap::new([("test.qs".into(), code.into())], None);
21 let (std_id, store) = crate::compile::package_store_with_stdlib(profile.into());
22 Interpreter::new(
23 sources,
24 package_type,
25 profile.into(),
26 LanguageFeatures::default(),
27 store,
28 &[(std_id, None)],
29 )
30 .expect("interpreter creation should succeed")
31}
32
33fn interpreter_with_circuit_trace(code: &str, profile: Profile) -> Interpreter {
34 let sources = SourceMap::new([("test.qs".into(), code.into())], None);
35 let (std_id, store) = crate::compile::package_store_with_stdlib(profile.into());
36 Interpreter::with_circuit_trace(
37 sources,
38 PackageType::Exe,
39 profile.into(),
40 LanguageFeatures::default(),
41 store,
42 &[(std_id, None)],
43 default_test_tracer_config(),
44 )
45 .expect("interpreter creation should succeed")
46}
47
48fn circuit_without_groups(code: &str, entry: CircuitEntryPoint) -> String {
49 let eval_circ = circuit_with_options_success(
50 code,
51 Profile::Unrestricted,
52 entry.clone(),
53 CircuitGenerationMethod::ClassicalEval,
54 TracerConfig {
55 group_by_scope: false,
56 ..default_test_tracer_config()
57 },
58 );
59
60 let eval_circ_without_source_locations = circuit_with_options_success(
61 code,
62 Profile::Unrestricted,
63 entry.clone(),
64 CircuitGenerationMethod::ClassicalEval,
65 TracerConfig {
66 group_by_scope: false,
67 source_locations: false,
68 ..default_test_tracer_config()
69 },
70 );
71
72 let static_circ_without_source_locations = circuit_with_options_success(
73 code,
74 Profile::AdaptiveRIF,
75 entry,
76 CircuitGenerationMethod::Static,
77 TracerConfig {
78 group_by_scope: false,
79 source_locations: false,
80 ..default_test_tracer_config()
81 },
82 );
83
84 // Source locations for qubit allocation are not currently supported in static.
85 // For now, we'll just ignore this difference between the classicalEval and static methods.
86 assert_eq!(
87 eval_circ_without_source_locations.to_string(),
88 static_circ_without_source_locations.to_string()
89 );
90
91 eval_circ.to_string()
92}
93
94fn circuit_with_groups(code: &str, entry: CircuitEntryPoint) -> String {
95 let eval_circ = circuit_with_options_success(
96 code,
97 Profile::Unrestricted,
98 entry.clone(),
99 CircuitGenerationMethod::ClassicalEval,
100 default_test_tracer_config(),
101 );
102
103 let eval_circ_without_source_locations = circuit_with_options_success(
104 code,
105 Profile::Unrestricted,
106 entry.clone(),
107 CircuitGenerationMethod::ClassicalEval,
108 TracerConfig {
109 source_locations: false,
110 ..default_test_tracer_config()
111 },
112 );
113 let static_circ_without_source_locations = circuit_with_options_success(
114 code,
115 Profile::AdaptiveRIF,
116 entry,
117 CircuitGenerationMethod::Static,
118 TracerConfig {
119 source_locations: false,
120 ..default_test_tracer_config()
121 },
122 );
123
124 // Source locations for qubit allocation are not currently supported in static.
125 // For now, we'll just ignore this difference between the classicalEval and static methods.
126 assert_eq!(
127 eval_circ_without_source_locations
128 .display_with_groups()
129 .to_string(),
130 static_circ_without_source_locations
131 .display_with_groups()
132 .to_string()
133 );
134
135 eval_circ.display_with_groups().to_string()
136}
137
138/// Generates a grouped circuit with source locations disabled, asserts that
139/// classical evaluation and static generation produce the same grouped display,
140/// and returns the static rendering for snapshot comparison.
141fn circuit_with_groups_without_source_locations(code: &str, entry: CircuitEntryPoint) -> String {
142 let eval_circ = circuit_with_options_success(
143 code,
144 Profile::Unrestricted,
145 entry.clone(),
146 CircuitGenerationMethod::ClassicalEval,
147 TracerConfig {
148 source_locations: false,
149 ..default_test_tracer_config()
150 },
151 );
152
153 let static_circ = circuit_with_options_success(
154 code,
155 Profile::AdaptiveRIF,
156 entry,
157 CircuitGenerationMethod::Static,
158 TracerConfig {
159 source_locations: false,
160 ..default_test_tracer_config()
161 },
162 );
163
164 assert_eq!(
165 eval_circ.display_with_groups().to_string(),
166 static_circ.display_with_groups().to_string()
167 );
168
169 static_circ.display_with_groups().to_string()
170}
171
172fn circuit_static(code: &str) -> Circuit {
173 circuit_with_options_success(
174 code,
175 Profile::AdaptiveRIF,
176 CircuitEntryPoint::EntryPoint,
177 CircuitGenerationMethod::Static,
178 default_test_tracer_config(),
179 )
180}
181
182fn circuit_err(
183 code: &str,
184 entry: CircuitEntryPoint,
185 method: CircuitGenerationMethod,
186 tracer_config: TracerConfig,
187) -> Vec<Error> {
188 let profile = if method == CircuitGenerationMethod::Static {
189 Profile::AdaptiveRIF
190 } else {
191 Profile::Unrestricted
192 };
193
194 circuit_with_options(code, profile, entry, method, tracer_config)
195 .expect_err("circuit generation should fail")
196}
197
198fn circuit_with_profile_both_ways(
199 code: &str,
200 entry: CircuitEntryPoint,
201 profile: Profile,
202) -> String {
203 let eval_circ = circuit_with_options_success(
204 code,
205 profile,
206 entry.clone(),
207 CircuitGenerationMethod::ClassicalEval,
208 default_test_tracer_config(),
209 );
210
211 let static_circ = circuit_with_options_success(
212 code,
213 profile,
214 entry,
215 CircuitGenerationMethod::Static,
216 default_test_tracer_config(),
217 );
218
219 format!("Eval:\n{eval_circ}\nStatic:\n{static_circ}")
220}
221
222pub(crate) fn circuit_with_options_success(
223 code: &str,
224 profile: Profile,
225 entry: CircuitEntryPoint,
226 method: CircuitGenerationMethod,
227 config: TracerConfig,
228) -> Circuit {
229 circuit_with_options(code, profile, entry, method, config)
230 .expect("circuit generation should succeed")
231}
232
233fn circuit_with_options(
234 code: &str,
235 profile: Profile,
236 entry: CircuitEntryPoint,
237 method: CircuitGenerationMethod,
238 config: TracerConfig,
239) -> Result<Circuit, Vec<Error>> {
240 let mut interpreter = interpreter(code, PackageType::Exe, profile);
241 interpreter.set_quantum_seed(Some(2));
242 interpreter.circuit(entry, method, config)
243}
244
245pub(crate) fn default_test_tracer_config() -> TracerConfig {
246 TracerConfig {
247 max_operations: TracerConfig::DEFAULT_MAX_OPERATIONS,
248 source_locations: true,
249 group_by_scope: true,
250 prune_classical_qubits: false,
251 }
252}
253
254// Drives a SyntheticEntry-routed callable+args shape through the Static circuit
255// path and asserts that it matches the ClassicalEval circuit. A captureless
256// callable value (`AllH`) passed as an argument routes through
257// `prepare_codegen_fir_from_callable_args` -> `CallableArgsBackend::SyntheticEntry`,
258// exercising the `static_circuit_from_callable` SyntheticEntry arm. Parity is
259// asserted with `source_locations: false` because qubit-allocation source spans
260// are not currently represented in the Static method (matching the existing
261// `circuit_without_groups` harness constraint).
262#[test]
263fn static_circuit_from_callable_with_callable_arg_matches_classical_eval() {
264 let code = r#"
265 namespace Test {
266 operation InvokeWithQubits(nQubits : Int, f : Qubit[] => Unit) : Unit {
267 use qs = Qubit[nQubits];
268 f(qs);
269 }
270 operation AllH(qs : Qubit[]) : Unit {
271 for q in qs {
272 H(q);
273 }
274 }
275 }
276 "#;
277 let mut interp = interpreter(code, PackageType::Lib, Profile::AdaptiveRIF);
278 interp.set_quantum_seed(Some(2));
279
280 let globals = interp.source_globals();
281 let invoke = globals
282 .iter()
283 .find(|(_, n, _)| &**n == "InvokeWithQubits")
284 .expect("InvokeWithQubits should be a source global")
285 .2
286 .clone();
287 let all_h = globals
288 .iter()
289 .find(|(_, n, _)| &**n == "AllH")
290 .expect("AllH should be a source global")
291 .2
292 .clone();
293 let args = Value::Tuple(vec![Value::Int(3), all_h].into(), None);
294
295 let cfg = TracerConfig {
296 group_by_scope: false,
297 source_locations: false,
298 ..default_test_tracer_config()
299 };
300
301 let static_circ = interp
302 .circuit(
303 CircuitEntryPoint::Callable(invoke.clone(), args.clone()),
304 CircuitGenerationMethod::Static,
305 cfg,
306 )
307 .expect("static circuit generation should succeed");
308 let eval_circ = interp
309 .circuit(
310 CircuitEntryPoint::Callable(invoke, args),
311 CircuitGenerationMethod::ClassicalEval,
312 cfg,
313 )
314 .expect("classical-eval circuit generation should succeed");
315
316 // Regression lock: the Static path must consume the synthetic entry so
317 // its circuit matches the ClassicalEval circuit for SyntheticEntry-routed args.
318 assert_eq!(static_circ.to_string(), eval_circ.to_string());
319
320 expect![[r#"
321 q_0 ── H ──
322 q_1 ── H ──
323 q_2 ── H ──
324 "#]]
325 .assert_eq(&static_circ.to_string());
326}
327
328#[test]
329fn empty() {
330 let circ = circuit_without_groups(
331 r#"
332 namespace Test {
333 @EntryPoint()
334 operation Main() : Unit {
335 Message("hi");
336 }
337 }
338 "#,
339 CircuitEntryPoint::EntryPoint,
340 );
341
342 expect![""].assert_eq(&circ);
343}
344
345#[test]
346fn one_gate() {
347 let circ = circuit_without_groups(
348 r"
349 namespace Test {
350 @EntryPoint()
351 operation Main() : Unit {
352 use q = Qubit();
353 H(q);
354 }
355 }
356 ",
357 CircuitEntryPoint::EntryPoint,
358 );
359
360 expect![[r#"
361 q_0@test.qs:4:20 ─ H@test.qs:5:20 ──
362 "#]]
363 .assert_eq(&circ);
364}
365
366#[test]
367fn measure_same_qubit_twice() {
368 let circ = circuit_without_groups(
369 r"
370 namespace Test {
371 @EntryPoint()
372 operation Main() : Result[] {
373 use q = Qubit();
374 H(q);
375 let r1 = M(q);
376 let r2 = M(q);
377 [r1, r2]
378 }
379 }
380 ",
381 CircuitEntryPoint::EntryPoint,
382 );
383
384 expect![[r#"
385 q_0@test.qs:4:20 ─ H@test.qs:5:20 ─── M@test.qs:6:29 ─── M@test.qs:7:29 ──
386 ╘══════════════════╪═════════
387 ╘═════════
388 "#]]
389 .assert_eq(&circ);
390}
391
392#[test]
393fn toffoli() {
394 let circ = circuit_without_groups(
395 r"
396 namespace Test {
397 @EntryPoint()
398 operation Main() : Unit {
399 use q = Qubit[3];
400 CCNOT(q[0], q[1], q[2]);
401 }
402 }
403 ",
404 CircuitEntryPoint::EntryPoint,
405 );
406
407 expect![[r#"
408 q_0@test.qs:4:20 ──────── ● ────────
409 q_1@test.qs:4:20 ──────── ● ────────
410 q_2@test.qs:4:20 ─ X@test.qs:5:20 ──
411 "#]]
412 .assert_eq(&circ);
413}
414
415#[test]
416fn rotation_gate() {
417 let circ = circuit_without_groups(
418 r"
419 namespace Test {
420 @EntryPoint()
421 operation Main() : Unit {
422 use q = Qubit();
423 Rx(Microsoft.Quantum.Math.PI()/2.0, q);
424 }
425 }
426 ",
427 CircuitEntryPoint::EntryPoint,
428 );
429
430 expect![[r#"
431 q_0@test.qs:4:20 ─ Rx(1.5708)@test.qs:5:20 ─
432 "#]]
433 .assert_eq(&circ);
434}
435
436#[test]
437fn grouping_nested_callables() {
438 let circ = circuit_with_groups(
439 r"
440 namespace Test {
441 @EntryPoint()
442 operation Main() : Unit {
443 use q = Qubit();
444 Foo(q);
445 MResetZ(q);
446 }
447
448 operation Foo(q: Qubit) : Unit {
449 H(q);
450 }
451 }
452 ",
453 CircuitEntryPoint::EntryPoint,
454 );
455
456 expect![[r#"
457 q_0@test.qs:4:20 ─ Main[1] ─
458 ╘═════
459
460 [1] Main:
461 q_0@test.qs:4:20 ─ [ [Foo@test.qs:5:20] ─── H@test.qs:10:20 ─── ] ─── M@test.qs:6:20 ──── |0〉@test.qs:6:20 ───
462 ╘════════════════════════════════
463 "#]]
464 .assert_eq(&circ);
465}
466
467#[test]
468fn classical_for_loop_is_grouped() {
469 let circ = circuit_without_groups(
470 r"
471 namespace Test {
472 @EntryPoint()
473 operation Main() : Unit {
474 use q = Qubit();
475 for i in 0..2 {
476 Foo(q);
477 }
478 }
479
480 operation Foo(q: Qubit) : Unit {
481 X(q);
482 Y(q);
483 }
484 }
485 ",
486 CircuitEntryPoint::EntryPoint,
487 );
488
489 expect![[r#"
490 q_0@test.qs:4:20 ─ X@test.qs:11:20 ── Y@test.qs:12:20 ── X@test.qs:11:20 ── Y@test.qs:12:20 ── X@test.qs:11:20 ── Y@test.qs:12:20 ─
491 "#]]
492 .assert_eq(&circ);
493}
494
495#[test]
496fn dynamic_for_loop_is_grouped() {
497 let circ = circuit_with_options_success(
498 r"
499 operation Main() : Unit {
500 use qubit = Qubit();
501 repeat {
502 H(qubit);
503 } until M(qubit) == Zero
504 fixup {
505 Reset(qubit);
506 }
507 }
508 ",
509 Profile::Unrestricted,
510 CircuitEntryPoint::EntryPoint,
511 CircuitGenerationMethod::Simulate,
512 TracerConfig {
513 max_operations: 1000,
514 source_locations: true,
515 group_by_scope: true,
516 prune_classical_qubits: false,
517 },
518 );
519
520 let circ = circ.display_with_groups().to_string();
521
522 expect![[r#"
523 q_0@test.qs:2:15 ─ Main[1] ─
524 ╘═════
525 ╘═════
526 ╘═════
527 ╘═════
528
529 [1] Main:
530 q_0@test.qs:2:15 ─ loop: M(qubit) == Zero@test.qs:3:16[2] ──
531 ╘═════════════════════
532 ╘═════════════════════
533 ╘═════════════════════
534 ╘═════════════════════
535
536 [2] loop: M(qubit) == Zero:
537 q_0@test.qs:2:15 ─ (1)@test.qs:3:23[3] ── (2)@test.qs:3:23[4] ── (3)@test.qs:3:23[5] ── (4)@test.qs:3:23[6] ─
538 ╘══════════════════════┆══════════════════════┆══════════════════════┆═══════════
539 ╘══════════════════════┆══════════════════════┆═══════════
540 ╘══════════════════════┆═══════════
541 ╘═══════════
542
543 [3] (1):
544 q_0@test.qs:2:15 ─ H@test.qs:4:20 ─── M@test.qs:5:24 ──── |0〉@test.qs:7:20 ───
545 ╘════════════════════════════════
546
547
548
549
550 [4] (2):
551 q_0@test.qs:2:15 ─ H@test.qs:4:20 ─── M@test.qs:5:24 ──── |0〉@test.qs:7:20 ───
552
553 ╘════════════════════════════════
554
555
556
557 [5] (3):
558 q_0@test.qs:2:15 ─ H@test.qs:4:20 ─── M@test.qs:5:24 ──── |0〉@test.qs:7:20 ───
559
560
561 ╘════════════════════════════════
562
563
564 [6] (4):
565 q_0@test.qs:2:15 ─ H@test.qs:4:20 ─── M@test.qs:5:24 ──
566
567
568
569 ╘═════════
570 "#]]
571 .assert_eq(&circ);
572}
573
574#[test]
575fn repeat_until_loop_is_grouped() {
576 let circ = circuit_with_groups(
577 r"
578 namespace Test {
579 @EntryPoint()
580 operation Main() : Unit {
581 use q = Qubit();
582 mutable i = 0;
583 repeat {
584 Foo(q);
585 } until i == 2
586 fixup {
587 set i += 1;
588 }
589 }
590
591 operation Foo(q: Qubit) : Unit {
592 X(q);
593 Y(q);
594 }
595 }
596 ",
597 CircuitEntryPoint::EntryPoint,
598 );
599
600 expect![[r#"
601 q_0@test.qs:4:20 ─ [ [Main] ─── [ [loop: i == 2@test.qs:6:20] ── [ [(1)@test.qs:6:27] ─── [ [Foo@test.qs:7:24] ─── X@test.qs:15:20 ── Y@test.qs:16:20 ─── ] ──── ] ─── [ [(2)@test.qs:6:27] ─── [ [Foo@test.qs:7:24] ─── X@test.qs:15:20 ── Y@test.qs:16:20 ─── ] ──── ] ─── [ [(3)@test.qs:6:27] ─── [ [Foo@test.qs:7:24] ─── X@test.qs:15:20 ── Y@test.qs:16:20 ─── ] ──── ] ──── ] ──── ] ──
602 "#]]
603 .assert_eq(&circ);
604}
605
606#[test]
607fn while_loop_is_grouped() {
608 let circ = circuit_with_groups(
609 r"
610 namespace Test {
611 @EntryPoint()
612 operation Main() : Unit {
613 use q = Qubit();
614 mutable i = 0;
615 while (i < 2) {
616 Foo(q);
617 set i += 1;
618 }
619 }
620
621 operation Foo(q: Qubit) : Unit {
622 X(q);
623 Y(q);
624 }
625 }
626 ",
627 CircuitEntryPoint::EntryPoint,
628 );
629
630 expect![[r#"
631 q_0@test.qs:4:20 ─ [ [Main] ─── [ [loop: i < 2@test.qs:6:20] ─── [ [(1)@test.qs:6:34] ─── [ [Foo@test.qs:7:24] ─── X@test.qs:13:20 ── Y@test.qs:14:20 ─── ] ──── ] ─── [ [(2)@test.qs:6:34] ─── [ [Foo@test.qs:7:24] ─── X@test.qs:13:20 ── Y@test.qs:14:20 ─── ] ──── ] ──── ] ──── ] ──
632 "#]]
633 .assert_eq(&circ);
634}
635
636#[test]
637fn loop_single_iteration_is_not_grouped() {
638 let circ = circuit_with_groups(
639 r"
640 namespace Test {
641 @EntryPoint()
642 operation Main() : Unit {
643 use q = Qubit();
644 for i in 0..0 {
645 Foo(q);
646 }
647 }
648
649 operation Foo(q: Qubit) : Unit {
650 X(q);
651 Y(q);
652 }
653 }
654 ",
655 CircuitEntryPoint::EntryPoint,
656 );
657
658 expect![[r#"
659 q_0@test.qs:4:20 ─ [ [Main] ─── [ [Foo@test.qs:6:24] ─── X@test.qs:11:20 ── Y@test.qs:12:20 ─── ] ──── ] ──
660 "#]]
661 .assert_eq(&circ);
662}
663
664#[test]
665fn loop_vertical_is_not_grouped() {
666 let circ = circuit_with_groups(
667 r"
668 namespace Test {
669 @EntryPoint()
670 operation Main() : Unit {
671 use qs = Qubit[6];
672 for i in 0..5 {
673 Foo(qs[i]);
674 }
675 }
676
677 operation Foo(q: Qubit) : Unit {
678 X(q);
679 }
680 }
681 ",
682 CircuitEntryPoint::EntryPoint,
683 );
684
685 expect![[r#"
686 q_0@test.qs:4:20 ─ Main[1] ─
687
688 q_1@test.qs:4:20 ─ Main[1] ─
689
690 q_2@test.qs:4:20 ─ Main[1] ─
691
692 q_3@test.qs:4:20 ─ Main[1] ─
693
694 q_4@test.qs:4:20 ─ Main[1] ─
695
696 q_5@test.qs:4:20 ─ Main[1] ─
697
698 [1] Main:
699 q_0@test.qs:4:20 ─ [ [Foo@test.qs:6:24] ─── X@test.qs:11:20 ─── ] ──
700 q_1@test.qs:4:20 ─ [ [Foo@test.qs:6:24] ─── X@test.qs:11:20 ─── ] ──
701 q_2@test.qs:4:20 ─ [ [Foo@test.qs:6:24] ─── X@test.qs:11:20 ─── ] ──
702 q_3@test.qs:4:20 ─ [ [Foo@test.qs:6:24] ─── X@test.qs:11:20 ─── ] ──
703 q_4@test.qs:4:20 ─ [ [Foo@test.qs:6:24] ─── X@test.qs:11:20 ─── ] ──
704 q_5@test.qs:4:20 ─ [ [Foo@test.qs:6:24] ─── X@test.qs:11:20 ─── ] ──
705 "#]]
706 .assert_eq(&circ);
707}
708
709#[test]
710fn for_loop_nested() {
711 let circ = circuit_with_options(
712 r"
713 namespace Test {
714 @EntryPoint()
715 operation Main() : Unit {
716 use qs = Qubit[3];
717 for j in 0..2 {
718 for i in 0..2 {
719 Foo(qs[i]);
720 }
721 }
722 }
723
724 operation Foo(q: Qubit) : Unit {
725 X(q);
726 }
727 }
728 ",
729 Profile::Unrestricted,
730 CircuitEntryPoint::EntryPoint,
731 CircuitGenerationMethod::ClassicalEval,
732 TracerConfig {
733 max_operations: 1000,
734 source_locations: true,
735 group_by_scope: true,
736 prune_classical_qubits: false,
737 },
738 )
739 .expect("circuit generation should succeed");
740
741 let circ = circ.display_with_groups().to_string();
742
743 expect![[r#"
744 q_0@test.qs:4:20 ─ Main[1] ─
745
746 q_1@test.qs:4:20 ─ Main[1] ─
747
748 q_2@test.qs:4:20 ─ Main[1] ─
749
750 [1] Main:
751 q_0@test.qs:4:20 ─ loop: 0..2@test.qs:5:20[2] ──
752
753 q_1@test.qs:4:20 ─ loop: 0..2@test.qs:5:20[2] ──
754
755 q_2@test.qs:4:20 ─ loop: 0..2@test.qs:5:20[2] ──
756
757 [2] loop: 0..2:
758 q_0@test.qs:4:20 ─ (1)@test.qs:5:34[3] ── (2)@test.qs:5:34[4] ── (3)@test.qs:5:34[5] ─
759 ┆ ┆ ┆
760 q_1@test.qs:4:20 ─ (1)@test.qs:5:34[3] ── (2)@test.qs:5:34[4] ── (3)@test.qs:5:34[5] ─
761 ┆ ┆ ┆
762 q_2@test.qs:4:20 ─ (1)@test.qs:5:34[3] ── (2)@test.qs:5:34[4] ── (3)@test.qs:5:34[5] ─
763
764 [3] (1):
765 q_0@test.qs:4:20 ─ [ [Foo@test.qs:7:28] ─── X@test.qs:13:20 ─── ] ──
766 q_1@test.qs:4:20 ─ [ [Foo@test.qs:7:28] ─── X@test.qs:13:20 ─── ] ──
767 q_2@test.qs:4:20 ─ [ [Foo@test.qs:7:28] ─── X@test.qs:13:20 ─── ] ──
768
769 [4] (2):
770 q_0@test.qs:4:20 ─ [ [Foo@test.qs:7:28] ─── X@test.qs:13:20 ─── ] ──
771 q_1@test.qs:4:20 ─ [ [Foo@test.qs:7:28] ─── X@test.qs:13:20 ─── ] ──
772 q_2@test.qs:4:20 ─ [ [Foo@test.qs:7:28] ─── X@test.qs:13:20 ─── ] ──
773
774 [5] (3):
775 q_0@test.qs:4:20 ─ [ [Foo@test.qs:7:28] ─── X@test.qs:13:20 ─── ] ──
776 q_1@test.qs:4:20 ─ [ [Foo@test.qs:7:28] ─── X@test.qs:13:20 ─── ] ──
777 q_2@test.qs:4:20 ─ [ [Foo@test.qs:7:28] ─── X@test.qs:13:20 ─── ] ──
778 "#]]
779 .assert_eq(&circ);
780}
781
782#[test]
783fn m_base_profile() {
784 let circ = circuit_with_profile_both_ways(
785 r"
786 namespace Test {
787 import Std.Measurement.*;
788 @EntryPoint()
789 operation Main() : Result[] {
790 use q = Qubit();
791 H(q);
792 [M(q)]
793 }
794 }
795 ",
796 CircuitEntryPoint::EntryPoint,
797 Profile::Base,
798 );
799
800 expect![[r#"
801 Eval:
802 q_0@test.qs:5:20 ─ H@test.qs:6:20 ─── M@test.qs:7:21 ──
803 ╘═════════
804
805 Static:
806 q_0 ─ H@test.qs:6:20 ─── M@test.qs:7:21 ──
807 ╘═════════
808 "#]]
809 .assert_eq(&circ);
810}
811
812#[test]
813fn m_default_profile() {
814 let circ = circuit_without_groups(
815 r"
816 namespace Test {
817 import Std.Measurement.*;
818 @EntryPoint()
819 operation Main() : Result[] {
820 use q = Qubit();
821 H(q);
822 [M(q)]
823 }
824 }
825 ",
826 CircuitEntryPoint::EntryPoint,
827 );
828
829 expect![[r#"
830 q_0@test.qs:5:20 ─ H@test.qs:6:20 ─── M@test.qs:7:21 ──
831 ╘═════════
832 "#]]
833 .assert_eq(&circ);
834}
835
836#[test]
837fn mresetz_unrestricted_profile() {
838 let circ = circuit_without_groups(
839 r"
840 namespace Test {
841 import Std.Measurement.*;
842 @EntryPoint()
843 operation Main() : Result[] {
844 use q = Qubit();
845 H(q);
846 [MResetZ(q)]
847 }
848 }
849 ",
850 CircuitEntryPoint::EntryPoint,
851 );
852
853 expect![[r#"
854 q_0@test.qs:5:20 ─ H@test.qs:6:20 ─── M@test.qs:7:21 ──── |0〉@test.qs:7:21 ───
855 ╘════════════════════════════════
856 "#]]
857 .assert_eq(&circ);
858}
859
860#[test]
861fn mresetz_base_profile() {
862 let circ = circuit_with_profile_both_ways(
863 r"
864 namespace Test {
865 import Std.Measurement.*;
866 @EntryPoint()
867 operation Main() : Result[] {
868 use q = Qubit();
869 H(q);
870 [MResetZ(q)]
871 }
872 }
873 ",
874 CircuitEntryPoint::EntryPoint,
875 Profile::Base,
876 );
877
878 // code gen in Base turns the MResetZ into an M
879 expect![[r#"
880 Eval:
881 q_0@test.qs:5:20 ─ H@test.qs:6:20 ─── M@test.qs:7:21 ──── |0〉@test.qs:7:21 ───
882 ╘════════════════════════════════
883
884 Static:
885 q_0 ─ H@test.qs:6:20 ─── M@test.qs:7:21 ──
886 ╘═════════
887 "#]]
888 .assert_eq(&circ);
889}
890
891#[test]
892fn qubit_relabel() {
893 let circ = circuit_without_groups(
894 "
895 namespace Test {
896 operation Main() : Unit {
897 use (q1, q2) = (Qubit(), Qubit());
898 H(q1);
899 CNOT(q1, q2);
900 Relabel([q1, q2], [q2, q1]);
901 H(q1);
902 CNOT(q1, q2);
903 MResetZ(q1);
904 MResetZ(q2);
905 }
906 }
907 ",
908 CircuitEntryPoint::EntryPoint,
909 );
910
911 expect![[r#"
912 q_0@test.qs:3:32 ─ H@test.qs:4:16 ────────── ● ──────────────────────────── X@test.qs:8:16 ─── M@test.qs:10:16 ─── |0〉@test.qs:10:16 ──
913 │ │ ╘════════════════════════════════
914 q_1@test.qs:3:41 ──────────────────── X@test.qs:5:16 ─── H@test.qs:7:16 ────────── ● ───────── M@test.qs:9:16 ──── |0〉@test.qs:9:16 ───
915 ╘════════════════════════════════
916 "#]]
917 .assert_eq(&circ);
918}
919
920#[test]
921fn qubit_reuse() {
922 let circ = circuit_without_groups(
923 "
924 namespace Test {
925 operation Main() : Unit {
926 {
927 use q1 = Qubit();
928 X(q1);
929 MResetZ(q1);
930 }
931 {
932 use q2 = Qubit();
933 Y(q2);
934 MResetZ(q2);
935 }
936 }
937 }
938 ",
939 CircuitEntryPoint::EntryPoint,
940 );
941
942 expect![[r#"
943 q_0@test.qs:4:20, test.qs:9:20 ─ X@test.qs:5:20 ─── M@test.qs:6:20 ──── |0〉@test.qs:6:20 ──── Y@test.qs:10:20 ── M@test.qs:11:20 ─── |0〉@test.qs:11:20 ──
944 ╘════════════════════════════════════════════════════════════╪════════════════════════════════
945 ╘════════════════════════════════
946 "#]]
947 .assert_eq(&circ);
948}
949
950#[test]
951fn qubit_reuse_no_measurements() {
952 let circ = circuit_without_groups(
953 "
954 namespace Test {
955 operation Main() : Unit {
956 {
957 use q1 = Qubit();
958 X(q1);
959 Reset(q1);
960 }
961 {
962 use q2 = Qubit();
963 Y(q2);
964 Reset(q2);
965 }
966 }
967 }
968 ",
969 CircuitEntryPoint::EntryPoint,
970 );
971
972 expect![[r#"
973 q_0@test.qs:4:20, test.qs:9:20 ─ X@test.qs:5:20 ──── |0〉@test.qs:6:20 ──── Y@test.qs:10:20 ─── |0〉@test.qs:11:20 ──
974 "#]]
975 .assert_eq(&circ);
976}
977
978#[test]
979fn eval_method_result_comparison() {
980 let mut interpreter = interpreter_with_circuit_trace(
981 r"
982 namespace Test {
983 import Std.Measurement.*;
984 @EntryPoint()
985 operation Main() : Result[] {
986 use q1 = Qubit();
987 use q2 = Qubit();
988 H(q1);
989 H(q2);
990 let r1 = M(q1);
991 let r2 = M(q2);
992 if (r1 == r2) {
993 X(q1);
994 }
995 ResetAll([q1, q2]);
996 [r1, r2]
997 }
998 }
999 ",
1000 Profile::Unrestricted,
1001 );
1002
1003 interpreter.set_quantum_seed(Some(2));
1004
1005 let circuit_err = interpreter
1006 .circuit(
1007 CircuitEntryPoint::EntryPoint,
1008 CircuitGenerationMethod::ClassicalEval,
1009 default_test_tracer_config(),
1010 )
1011 .expect_err("circuit should return error")
1012 .pop()
1013 .expect("error should exist");
1014
1015 expect!["Qsc.Eval.ResultComparisonUnsupported"].assert_eq(
1016 &circuit_err
1017 .code()
1018 .expect("error code should exist")
1019 .to_string(),
1020 );
1021
1022 let circuit = interpreter.get_circuit();
1023 expect![""].assert_eq(&circuit.to_string());
1024
1025 let mut out = std::io::sink();
1026 let mut r = GenericReceiver::new(&mut out);
1027
1028 // Result comparisons are okay when tracing
1029 // circuit with the simulator.
1030 let circ = interpreter
1031 .circuit(
1032 CircuitEntryPoint::EntryPoint,
1033 CircuitGenerationMethod::Simulate,
1034 default_test_tracer_config(),
1035 )
1036 .expect("circuit generation should succeed");
1037
1038 expect![[r#"
1039 q_0@test.qs:5:20 ─ H@test.qs:7:20 ─── M@test.qs:9:29 ───── X@test.qs:12:24 ───── |0〉@test.qs:14:20 ──
1040 ╘═══════════════════════════════════════════════════════
1041 q_1@test.qs:6:20 ─ H@test.qs:8:20 ─── M@test.qs:10:29 ─── |0〉@test.qs:14:20 ─────────────────────────
1042 ╘═══════════════════════════════════════════════════════
1043 "#]]
1044 .assert_eq(&circ.to_string());
1045
1046 // Result comparisons are also okay if calling
1047 // get_circuit() after incremental evaluation,
1048 // because we're using the current simulator
1049 // state.
1050 interpreter
1051 .eval_fragments(&mut r, "Test.Main();")
1052 .expect("eval should succeed");
1053
1054 let circuit = interpreter.get_circuit();
1055 expect![[r#"
1056 q_0@test.qs:5:20 ─ H@test.qs:7:20 ─── M@test.qs:9:29 ───── X@test.qs:12:24 ───── |0〉@test.qs:14:20 ──
1057 ╘═══════════════════════════════════════════════════════
1058 q_1@test.qs:6:20 ─ H@test.qs:8:20 ─── M@test.qs:10:29 ─── |0〉@test.qs:14:20 ─────────────────────────
1059 ╘═══════════════════════════════════════════════════════
1060 "#]]
1061 .assert_eq(&circuit.to_string());
1062}
1063
1064#[test]
1065fn custom_intrinsic() {
1066 let circ = circuit_without_groups(
1067 r"
1068 namespace Test {
1069 operation foo(q: Qubit): Unit {
1070 body intrinsic;
1071 }
1072
1073 @EntryPoint()
1074 operation Main() : Unit {
1075 use q = Qubit();
1076 foo(q);
1077 }
1078 }",
1079 CircuitEntryPoint::EntryPoint,
1080 );
1081
1082 expect![[r#"
1083 q_0@test.qs:8:12 ─ foo@test.qs:9:12 ──
1084 "#]]
1085 .assert_eq(&circ);
1086}
1087
1088#[test]
1089fn custom_intrinsic_classical_arg() {
1090 let circ = circuit_without_groups(
1091 r"
1092 namespace Test {
1093 operation foo(n: Int): Unit {
1094 body intrinsic;
1095 }
1096
1097 @EntryPoint()
1098 operation Main() : Unit {
1099 use q = Qubit();
1100 X(q);
1101 foo(4);
1102 }
1103 }",
1104 CircuitEntryPoint::EntryPoint,
1105 );
1106
1107 // A custom intrinsic that doesn't take qubits just doesn't
1108 // show up on the circuit.
1109 expect![[r#"
1110 q_0@test.qs:8:12 ─ X@test.qs:9:12 ──
1111 "#]]
1112 .assert_eq(&circ);
1113}
1114
1115#[test]
1116fn custom_intrinsic_one_classical_arg() {
1117 let circ = circuit_without_groups(
1118 r"
1119 namespace Test {
1120 operation foo(n: Int, q: Qubit): Unit {
1121 body intrinsic;
1122 }
1123
1124 @EntryPoint()
1125 operation Main() : Unit {
1126 use q = Qubit();
1127 X(q);
1128 foo(4, q);
1129 }
1130 }",
1131 CircuitEntryPoint::EntryPoint,
1132 );
1133
1134 expect![[r#"
1135 q_0@test.qs:8:12 ─ X@test.qs:9:12 ─── foo(4)@test.qs:10:12 ──
1136 "#]]
1137 .assert_eq(&circ);
1138}
1139
1140#[test]
1141fn custom_intrinsic_no_qubit_args() {
1142 let circ = circuit_without_groups(
1143 r"
1144 namespace Test {
1145 operation foo(n: Int): Unit {
1146 body intrinsic;
1147 }
1148
1149 @EntryPoint()
1150 operation Main() : Unit {
1151 use q = Qubit();
1152 X(q);
1153 foo(4);
1154 }
1155 }",
1156 CircuitEntryPoint::EntryPoint,
1157 );
1158
1159 expect![[r#"
1160 q_0@test.qs:8:12 ─ X@test.qs:9:12 ──
1161 "#]]
1162 .assert_eq(&circ);
1163}
1164
1165#[test]
1166fn custom_intrinsic_mixed_args_classical_eval() {
1167 let circ = circuit_with_options_success(
1168 r"
1169 namespace Test {
1170 import Std.ResourceEstimation.*;
1171
1172 @EntryPoint()
1173 operation Main() : Unit {
1174 use qs = Qubit[10];
1175 AccountForEstimates(
1176 [
1177 AuxQubitCount(1),
1178 TCount(2),
1179 RotationCount(3),
1180 RotationDepth(4),
1181 CczCount(5),
1182 MeasurementCount(6),
1183 ],
1184 PSSPCLayout(),
1185 qs);
1186 }
1187 }",
1188 Profile::AdaptiveRIF,
1189 CircuitEntryPoint::EntryPoint,
1190 CircuitGenerationMethod::ClassicalEval,
1191 default_test_tracer_config(),
1192 );
1193
1194 expect![[r#"
1195 q_0@test.qs:6:12 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1)@test.qs:7:12 ─
1196
1197 q_1@test.qs:6:12 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1)@test.qs:7:12 ─
1198
1199 q_2@test.qs:6:12 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1)@test.qs:7:12 ─
1200
1201 q_3@test.qs:6:12 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1)@test.qs:7:12 ─
1202
1203 q_4@test.qs:6:12 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1)@test.qs:7:12 ─
1204
1205 q_5@test.qs:6:12 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1)@test.qs:7:12 ─
1206
1207 q_6@test.qs:6:12 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1)@test.qs:7:12 ─
1208
1209 q_7@test.qs:6:12 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1)@test.qs:7:12 ─
1210
1211 q_8@test.qs:6:12 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1)@test.qs:7:12 ─
1212
1213 q_9@test.qs:6:12 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1)@test.qs:7:12 ─
1214 "#]]
1215 .assert_eq(&circ.to_string());
1216}
1217
1218#[test]
1219fn custom_intrinsic_mixed_args_static() {
1220 let circ = circuit_static(
1221 r"
1222 namespace Test {
1223 import Std.ResourceEstimation.*;
1224
1225 @EntryPoint()
1226 operation Main() : Unit {
1227 use qs = Qubit[10];
1228 AccountForEstimates(
1229 [
1230 AuxQubitCount(1),
1231 TCount(2),
1232 RotationCount(3),
1233 RotationDepth(4),
1234 CczCount(5),
1235 MeasurementCount(6),
1236 ],
1237 PSSPCLayout(),
1238 qs);
1239 }
1240 }",
1241 );
1242
1243 // This intrinsic never gets codegenned, so it's missing from the
1244 // circuit too.
1245 expect![[r#"
1246 q_0
1247 q_1
1248 q_2
1249 q_3
1250 q_4
1251 q_5
1252 q_6
1253 q_7
1254 q_8
1255 q_9
1256 "#]]
1257 .assert_eq(&circ.to_string());
1258}
1259
1260#[test]
1261fn custom_intrinsic_apply_idle_noise_classical_eval() {
1262 let circ = circuit_with_options_success(
1263 r"
1264 namespace Test {
1265 import Std.Diagnostics.*;
1266 @EntryPoint()
1267 operation Main() : Unit {
1268 ConfigurePauliNoise(BitFlipNoise(1.0));
1269 use q = Qubit();
1270 ApplyIdleNoise(q);
1271 }
1272 }",
1273 Profile::AdaptiveRIF,
1274 CircuitEntryPoint::EntryPoint,
1275 CircuitGenerationMethod::ClassicalEval,
1276 default_test_tracer_config(),
1277 );
1278
1279 expect![[r#"
1280 q_0@test.qs:6:12 ─ ApplyIdleNoise@test.qs:7:12 ─
1281 "#]]
1282 .assert_eq(&circ.to_string());
1283}
1284
1285#[test]
1286fn custom_intrinsic_apply_idle_noise_static() {
1287 let circ = circuit_static(
1288 r"
1289 namespace Test {
1290 import Std.Diagnostics.*;
1291 @EntryPoint()
1292 operation Main() : Unit {
1293 ConfigurePauliNoise(BitFlipNoise(1.0));
1294 use q = Qubit();
1295 ApplyIdleNoise(q);
1296 }
1297 }",
1298 );
1299
1300 // These intrinsics never get codegenned, so they're missing from the
1301 // circuit too.
1302 expect![[r#"
1303 q_0
1304 "#]]
1305 .assert_eq(&circ.to_string());
1306}
1307
1308#[test]
1309fn operation_with_qubits() {
1310 let circ = circuit_without_groups(
1311 r"
1312 namespace Test {
1313 @EntryPoint()
1314 operation Main() : Result[] { [] }
1315
1316 operation Test(q1: Qubit, q2: Qubit) : Result[] {
1317 H(q1);
1318 CNOT(q1, q2);
1319 [M(q1), M(q2)]
1320 }
1321
1322 }",
1323 CircuitEntryPoint::Operation("Test.Test".into()),
1324 );
1325
1326 expect![[r#"
1327 q_0@test.qs:5:27 ─ H@test.qs:6:16 ────────── ● ───────── M@test.qs:8:17 ──
1328 │ ╘═════════
1329 q_1@test.qs:5:38 ──────────────────── X@test.qs:7:16 ─── M@test.qs:8:24 ──
1330 ╘═════════
1331 "#]]
1332 .assert_eq(&circ);
1333}
1334
1335#[test]
1336fn operation_with_qubit_arrays() {
1337 let circ = circuit_without_groups(
1338 r"
1339 namespace Test {
1340 @EntryPoint()
1341 operation Main() : Result[] { [] }
1342
1343 import Std.Measurement.*;
1344 operation Test(q1: Qubit[], q2: Qubit[][], q3: Qubit[][][], q: Qubit) : Result[] {
1345 for q in q1 {
1346 H(q);
1347 }
1348 for qs in q2 {
1349 for q in qs {
1350 X(q);
1351 }
1352 }
1353 for qss in q3 {
1354 for qs in qss {
1355 for q in qs {
1356 Y(q);
1357 }
1358 }
1359 }
1360 X(q);
1361 MeasureEachZ(q1)
1362 }
1363 }",
1364 CircuitEntryPoint::Operation("Test.Test".into()),
1365 );
1366
1367 expect![[r#"
1368 q_0@test.qs:6:27 ─ H@test.qs:8:20 ─── M@test.qs:23:16 ─
1369 ╘═════════
1370 q_1@test.qs:6:27 ─ H@test.qs:8:20 ─── M@test.qs:23:16 ─
1371 ╘═════════
1372 q_2@test.qs:6:40 ─ X@test.qs:12:24 ────────────────────
1373 q_3@test.qs:6:40 ─ X@test.qs:12:24 ────────────────────
1374 q_4@test.qs:6:40 ─ X@test.qs:12:24 ────────────────────
1375 q_5@test.qs:6:40 ─ X@test.qs:12:24 ────────────────────
1376 q_6@test.qs:6:55 ─ Y@test.qs:18:28 ────────────────────
1377 q_7@test.qs:6:55 ─ Y@test.qs:18:28 ────────────────────
1378 q_8@test.qs:6:55 ─ Y@test.qs:18:28 ────────────────────
1379 q_9@test.qs:6:55 ─ Y@test.qs:18:28 ────────────────────
1380 q_10@test.qs:6:55 ─ Y@test.qs:18:28 ────────────────────
1381 q_11@test.qs:6:55 ─ Y@test.qs:18:28 ────────────────────
1382 q_12@test.qs:6:55 ─ Y@test.qs:18:28 ────────────────────
1383 q_13@test.qs:6:55 ─ Y@test.qs:18:28 ────────────────────
1384 q_14@test.qs:6:72 ─ X@test.qs:22:16 ────────────────────
1385 "#]]
1386 .assert_eq(&circ);
1387}
1388
1389#[test]
1390fn adjoint_operation() {
1391 let circ = circuit_without_groups(
1392 r"
1393 namespace Test {
1394 @EntryPoint()
1395 operation Main() : Result[] { [] }
1396
1397 operation Foo (q : Qubit) : Unit
1398 is Adj + Ctl {
1399
1400 body (...) {
1401 X(q);
1402 }
1403
1404 adjoint (...) {
1405 Y(q);
1406 }
1407
1408 controlled (cs, ...) {
1409 }
1410 }
1411
1412 }",
1413 CircuitEntryPoint::Operation("Adjoint Test.Foo".into()),
1414 );
1415
1416 expect![[r#"
1417 q_0@test.qs:5:27 ─ Y@test.qs:13:20 ─
1418 "#]]
1419 .assert_eq(&circ);
1420}
1421
1422#[test]
1423fn lambda() {
1424 let circ = circuit_without_groups(
1425 r"
1426 namespace Test {
1427 @EntryPoint()
1428 operation Main() : Result[] { [] }
1429 }",
1430 CircuitEntryPoint::Operation("q => H(q)".into()),
1431 );
1432
1433 expect![[r#"
1434 q_0@line_0:0:0 ─ H@<entry>:2:18 ──
1435 "#]]
1436 .assert_eq(&circ);
1437}
1438
1439#[test]
1440fn controlled_operation() {
1441 let circ_err = circuit_err(
1442 r"
1443 namespace Test {
1444 @EntryPoint()
1445 operation Main() : Result[] { [] }
1446
1447 operation SWAP (q1 : Qubit, q2 : Qubit) : Unit
1448 is Adj + Ctl {
1449
1450 body (...) {
1451 CNOT(q1, q2);
1452 CNOT(q2, q1);
1453 CNOT(q1, q2);
1454 }
1455
1456 adjoint (...) {
1457 SWAP(q1, q2);
1458 }
1459
1460 controlled (cs, ...) {
1461 CNOT(q1, q2);
1462 Controlled CNOT(cs, (q2, q1));
1463 CNOT(q1, q2);
1464 }
1465 }
1466
1467 }",
1468 CircuitEntryPoint::Operation("Controlled Test.SWAP".into()),
1469 CircuitGenerationMethod::ClassicalEval,
1470 default_test_tracer_config(),
1471 );
1472
1473 // Controlled operations are not supported at the moment.
1474 // We don't generate an accurate call signature with the tuple arguments.
1475 expect![[r"
1476 [
1477 Circuit(
1478 ControlledUnsupported,
1479 ),
1480 ]
1481 "]]
1482 .assert_debug_eq(&circ_err);
1483}
1484
1485#[test]
1486fn internal_operation() {
1487 let circ = circuit_without_groups(
1488 r"
1489 namespace Test {
1490 @EntryPoint()
1491 operation Main() : Result[] { [] }
1492
1493 internal operation Test(q1: Qubit, q2: Qubit) : Result[] {
1494 H(q1);
1495 CNOT(q1, q2);
1496 [M(q1), M(q2)]
1497 }
1498 }",
1499 CircuitEntryPoint::Operation("Test.Test".into()),
1500 );
1501
1502 expect![[r#"
1503 q_0@test.qs:5:36 ─ H@test.qs:6:16 ────────── ● ───────── M@test.qs:8:17 ──
1504 │ ╘═════════
1505 q_1@test.qs:5:47 ──────────────────── X@test.qs:7:16 ─── M@test.qs:8:24 ──
1506 ╘═════════
1507 "#]]
1508 .assert_eq(&circ);
1509}
1510
1511#[test]
1512fn operation_with_non_qubit_args() {
1513 let circ_err = circuit_err(
1514 r"
1515 namespace Test {
1516 @EntryPoint()
1517 operation Main() : Result[] { [] }
1518
1519 operation Test(q1: Qubit, q2: Qubit, i: Int) : Unit {
1520 }
1521
1522 }",
1523 CircuitEntryPoint::Operation("Test.Test".into()),
1524 CircuitGenerationMethod::ClassicalEval,
1525 default_test_tracer_config(),
1526 );
1527
1528 expect![[r"
1529 [
1530 Circuit(
1531 NoQubitParameters,
1532 ),
1533 ]
1534 "]]
1535 .assert_debug_eq(&circ_err);
1536}
1537
1538#[test]
1539fn operation_with_long_gates_properly_aligned() {
1540 let circ = circuit_without_groups(
1541 r"
1542 namespace Test {
1543 import Std.Measurement.*;
1544
1545 @EntryPoint()
1546 operation Main() : Result[] {
1547 use q0 = Qubit();
1548 use q1 = Qubit();
1549
1550 H(q0);
1551 H(q1);
1552 X(q1);
1553 Ry(1.0, q1);
1554 CNOT(q0, q1);
1555 M(q0);
1556
1557 use q2 = Qubit();
1558
1559 H(q2);
1560 Rx(1.0, q2);
1561 H(q2);
1562 Rx(1.0, q2);
1563 H(q2);
1564 Rx(1.0, q2);
1565
1566 use q3 = Qubit();
1567
1568 Rxx(1.0, q1, q3);
1569
1570 CNOT(q0, q3);
1571
1572 [M(q1), M(q3)]
1573 }
1574 }
1575 ",
1576 CircuitEntryPoint::EntryPoint,
1577 );
1578
1579 expect![[r#"
1580 q_0@test.qs:6:20 ─ H@test.qs:9:20 ───────────────────────────────────────────────────────────────────────── ● ────────────── M@test.qs:14:20 ─────────────────────────────────────────────────────────────────── ● ───────────────────────────
1581 │ ╘════════════════════════════════════════════════════════════════════════════╪════════════════════════════
1582 q_1@test.qs:7:20 ─ H@test.qs:10:20 ─────── X@test.qs:11:20 ─────── Ry(1.0000)@test.qs:12:20 ──────── X@test.qs:13:20 ─────────────────────────────────────────────────────── Rxx(1.0000)@test.qs:27:20 ──────────┼────────── M@test.qs:31:21 ─
1583 ┆ │ ╘═════════
1584 q_2@test.qs:16:20 ─ H@test.qs:18:20 ── Rx(1.0000)@test.qs:19:20 ──────── H@test.qs:20:20 ─────── Rx(1.0000)@test.qs:21:20 ─── H@test.qs:22:20 ── Rx(1.0000)@test.qs:23:20 ────────────────┆───────────────────────┼────────────────────────────
1585 q_3@test.qs:25:20 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Rxx(1.0000)@test.qs:27:20 ── X@test.qs:29:20 ── M@test.qs:31:28 ─
1586 ╘═════════
1587 "#]]
1588 .assert_eq(&circ);
1589}
1590
1591#[test]
1592fn operation_with_subsequent_qubits_gets_horizontal_lines() {
1593 let circ = circuit_without_groups(
1594 r"
1595 namespace Test {
1596 import Std.Measurement.*;
1597
1598 @EntryPoint()
1599 operation Main() : Unit {
1600 use q0 = Qubit();
1601 use q1 = Qubit();
1602 Rxx(1.0, q0, q1);
1603
1604 use q2 = Qubit();
1605 use q3 = Qubit();
1606 Rxx(1.0, q2, q3);
1607 }
1608 }
1609 ",
1610 CircuitEntryPoint::EntryPoint,
1611 );
1612
1613 expect![[r#"
1614 q_0@test.qs:6:20 ─ Rxx(1.0000)@test.qs:8:20 ──
1615
1616 q_1@test.qs:7:20 ─ Rxx(1.0000)@test.qs:8:20 ──
1617 q_2@test.qs:10:20 ─ Rxx(1.0000)@test.qs:12:20 ─
1618
1619 q_3@test.qs:11:20 ─ Rxx(1.0000)@test.qs:12:20 ─
1620 "#]]
1621 .assert_eq(&circ);
1622}
1623
1624#[test]
1625fn operation_with_subsequent_qubits_no_double_rows() {
1626 let circ = circuit_static(
1627 r"
1628 namespace Test {
1629 import Std.Measurement.*;
1630
1631 @EntryPoint()
1632 operation Main() : Unit {
1633 use q0 = Qubit();
1634 use q1 = Qubit();
1635 Rxx(1.0, q0, q1);
1636 Rxx(1.0, q0, q1);
1637 }
1638 }
1639 ",
1640 );
1641
1642 expect![[r#"
1643 q_0 ─ Rxx(1.0000)@test.qs:8:20 ─── Rxx(1.0000)@test.qs:9:20 ──
1644 ┆ ┆
1645 q_1 ─ Rxx(1.0000)@test.qs:8:20 ─── Rxx(1.0000)@test.qs:9:20 ──
1646 "#]]
1647 .assert_eq(&circ.to_string());
1648}
1649
1650#[test]
1651fn operation_with_subsequent_qubits_no_added_rows() {
1652 let circ = circuit_static(
1653 r"
1654 namespace Test {
1655 import Std.Measurement.*;
1656
1657 @EntryPoint()
1658 operation Main() : Result[] {
1659 use q0 = Qubit();
1660 use q1 = Qubit();
1661 Rxx(1.0, q0, q1);
1662
1663 use q2 = Qubit();
1664 use q3 = Qubit();
1665 Rxx(1.0, q2, q3);
1666
1667 [M(q0), M(q2)]
1668 }
1669 }
1670 ",
1671 );
1672
1673 expect![[r#"
1674 q_0 ─ Rxx(1.0000)@test.qs:8:20 ─── M@test.qs:14:21 ─
1675 ┆ ╘═════════
1676 q_1 ─ Rxx(1.0000)@test.qs:8:20 ─────────────────────
1677 q_2 ─ Rxx(1.0000)@test.qs:12:20 ── M@test.qs:14:28 ─
1678 ┆ ╘═════════
1679 q_3 ─ Rxx(1.0000)@test.qs:12:20 ────────────────────
1680 "#]]
1681 .assert_eq(&circ.to_string());
1682}
1683
1684#[test]
1685fn operation_declared_in_eval() {
1686 let mut interpreter = interpreter("", PackageType::Lib, Profile::Unrestricted);
1687 let mut out = std::io::sink();
1688 let mut r = GenericReceiver::new(&mut out);
1689
1690 interpreter
1691 .eval_fragments(
1692 &mut r,
1693 "operation Foo() : Result { use q = Qubit(); H(q); return M(q) }",
1694 )
1695 .expect("eval should succeed");
1696
1697 let c = interpreter
1698 .circuit(
1699 CircuitEntryPoint::EntryExpr("Foo()".into()),
1700 CircuitGenerationMethod::ClassicalEval,
1701 TracerConfig {
1702 max_operations: usize::MAX,
1703 source_locations: false,
1704 group_by_scope: true,
1705 ..default_test_tracer_config()
1706 },
1707 )
1708 .expect("circuit generation should succeed");
1709
1710 expect![[r#"
1711 q_0 ─ Foo[1] ──
1712 ╘═════
1713
1714 [1] Foo:
1715 q_0 ── H ──── M ──
1716 ╘═══
1717 "#]]
1718 .assert_eq(&c.display_with_groups().to_string());
1719}
1720
1721#[test]
1722fn static_entrypoint_handles_callable_returned_from_function() {
1723 let circ = circuit_with_options_success(
1724 r#"
1725 namespace Test {
1726 operation ApplyOp(op : Qubit => Unit, q : Qubit) : Unit {
1727 op(q);
1728 }
1729
1730 function GetOp() : Qubit => Unit {
1731 H
1732 }
1733
1734 @EntryPoint()
1735 operation Main() : Unit {
1736 use q = Qubit();
1737 ApplyOp(GetOp(), q);
1738 }
1739 }
1740 "#,
1741 Profile::AdaptiveRIF,
1742 CircuitEntryPoint::EntryPoint,
1743 CircuitGenerationMethod::Static,
1744 TracerConfig {
1745 source_locations: false,
1746 ..default_test_tracer_config()
1747 },
1748 )
1749 .to_string();
1750
1751 expect![[r#"
1752 q_0 ── H ──
1753 "#]]
1754 .assert_eq(&circ);
1755}
1756
1757#[test]
1758fn grouped_scopes_use_source_name_for_specialized_direct_callables() {
1759 let circ = circuit_with_groups_without_source_locations(
1760 r#"
1761 namespace Test {
1762 operation ApplyOp(op : Qubit => Unit, q : Qubit) : Unit {
1763 op(q);
1764 }
1765
1766 @EntryPoint()
1767 operation Main() : Unit {
1768 use q = Qubit();
1769 ApplyOp(H, q);
1770 }
1771 }
1772 "#,
1773 CircuitEntryPoint::EntryPoint,
1774 );
1775
1776 expect![[r#"
1777 q_0 ─ [ [Main] ─── [ [ApplyOp] ─── H ──── ] ──── ] ──
1778 "#]]
1779 .assert_eq(&circ);
1780}
1781
1782#[test]
1783fn grouped_scopes_use_source_name_for_specialized_callable_arrays() {
1784 let circ = circuit_with_groups_without_source_locations(
1785 r#"
1786 namespace Test {
1787 operation ApplyOp(op : Qubit => Unit, q : Qubit) : Unit {
1788 op(q);
1789 }
1790
1791 @EntryPoint()
1792 operation Main() : Unit {
1793 use q = Qubit();
1794 let ops = [H, X];
1795 for op in ops {
1796 ApplyOp(op, q);
1797 }
1798 }
1799 }
1800 "#,
1801 CircuitEntryPoint::EntryPoint,
1802 );
1803
1804 expect![[r#"
1805 q_0 ─ [ [Main] ─── [ [loop: ops] ── [ [(1)] ── [ [ApplyOp] ─── H ──── ] ──── ] ─── [ [(2)] ── [ [ApplyOp] ─── X ──── ] ──── ] ──── ] ──── ] ──
1806 "#]]
1807 .assert_eq(&circ);
1808}
1809
1810#[test]
1811fn grouped_scopes_match_for_user_defined_adjoint_specialization() {
1812 let circ = circuit_with_groups_without_source_locations(
1813 r#"
1814 namespace Test {
1815 operation EncodeAsLogicalQubit(physicalQubit : Qubit, aux : Qubit[]) : Unit is Adj {
1816 ApplyToEachA(CNOT(physicalQubit, _), aux);
1817 }
1818
1819 @EntryPoint()
1820 operation Main() : Unit {
1821 use logicalQubit = Qubit[3];
1822 EncodeAsLogicalQubit(logicalQubit[0], logicalQubit[1...]);
1823 Adjoint EncodeAsLogicalQubit(logicalQubit[0], logicalQubit[1...]);
1824 }
1825 }
1826 "#,
1827 CircuitEntryPoint::EntryPoint,
1828 );
1829
1830 expect![[r#"
1831 q_0 ─ Main[1] ─
1832
1833 q_1 ─ Main[1] ─
1834
1835 q_2 ─ Main[1] ─
1836
1837 [1] Main:
1838 q_0 ─ EncodeAsLogicalQubit[2] ── EncodeAsLogicalQubit'[3] ──
1839 ┆ ┆
1840 q_1 ─ EncodeAsLogicalQubit[2] ── EncodeAsLogicalQubit'[3] ──
1841 ┆ ┆
1842 q_2 ─ EncodeAsLogicalQubit[2] ── EncodeAsLogicalQubit'[3] ──
1843
1844 [2] EncodeAsLogicalQubit:
1845 q_0 ─ <lambda>[4] ─
1846
1847 q_1 ─ <lambda>[4] ─
1848
1849 q_2 ─ <lambda>[4] ─
1850
1851 [3] EncodeAsLogicalQubit:
1852 q_0 ─ <lambda>'[5] ──
1853
1854 q_1 ─ <lambda>'[5] ──
1855
1856 q_2 ─ <lambda>'[5] ──
1857
1858 [4] <lambda>:
1859 q_0 ── ● ──── ● ──
1860 q_1 ── X ─────┼───
1861 q_2 ───────── X ──
1862
1863 [5] <lambda>:
1864 q_0 ── ● ──── ● ──
1865 q_1 ───┼───── X ──
1866 q_2 ── X ─────────
1867 "#]]
1868 .assert_eq(&circ);
1869}
1870
1871#[test]
1872fn grouped_scopes_match_for_apply_operation_power_ca_lambda() {
1873 let circ = circuit_with_groups_without_source_locations(
1874 r#"
1875 namespace Test {
1876 operation U(q : Qubit) : Unit is Ctl + Adj {
1877 Rz(Std.Math.PI() / 3.0, q);
1878 }
1879
1880 @EntryPoint()
1881 operation Main() : Unit {
1882 use state = Qubit();
1883 use phase = Qubit[2];
1884 let oracle = ApplyOperationPowerCA(_, qs => U(qs[0]), _);
1885 ApplyQPE(oracle, [state], phase);
1886 }
1887 }
1888 "#,
1889 CircuitEntryPoint::EntryPoint,
1890 );
1891
1892 expect![[r#"
1893 q_0 ─ Main[1] ─
1894
1895 q_1 ─ Main[1] ─
1896
1897 q_2 ─ Main[1] ─
1898
1899 [1] Main:
1900 q_0 ──────── U[2] ────────────────────────────────────────────────────────────────────
1901
1902 q_1 ── H ─── U[2] ──────── H ─────── Rz(-0.7854) ─── X ─── Rz(0.7854) ──── X ─────────
1903 ┆ │ │
1904 q_2 ── H ─── U[2] ─── Rz(-0.7854) ────────────────── ● ─────────────────── ● ──── H ──
1905
1906 [2] U:
1907 q_0 ─ Rz(0.5236) ──── X ─── Rz(-0.5236) ─── X ─── Rz(0.5236) ──── X ─── Rz(-0.5236) ─── X ─── Rz(0.5236) ──── X ─── Rz(-0.5236) ─── X ──
1908 q_1 ───────────────── ● ─────────────────── ● ─────────────────── ● ─────────────────── ● ────────────────────┼─────────────────────┼───
1909 q_2 ───────────────────────────────────────────────────────────────────────────────────────────────────────── ● ─────────────────── ● ──
1910 "#]]
1911 .assert_eq(&circ);
1912}
1913
1914#[test]
1915fn grouped_scopes_match_for_repeated_draw_random_bit_calls() {
1916 let circ = circuit_with_groups_without_source_locations(
1917 r#"
1918 namespace Test {
1919 operation DrawRandomBit() : Unit {
1920 use q = Qubit();
1921 H(q);
1922 MResetZ(q);
1923 }
1924
1925 @EntryPoint()
1926 operation Main() : Unit {
1927 DrawRandomBit();
1928 DrawRandomBit();
1929 }
1930 }
1931 "#,
1932 CircuitEntryPoint::EntryPoint,
1933 );
1934
1935 expect![[r#"
1936 q_0 ─ Main[1] ─
1937 ╘═════
1938 ╘═════
1939
1940 [1] Main:
1941 q_0 ─ DrawRandomBit[2] ─── DrawRandomBit[3] ──
1942 ╘════════════════════┆══════════
1943 ╘══════════
1944
1945 [2] DrawRandomBit:
1946 q_0 ── H ──── M ──── |0〉 ──
1947 ╘════════════
1948
1949
1950 [3] DrawRandomBit:
1951 q_0 ── H ──── M ──── |0〉 ──
1952
1953 ╘════════════
1954 "#]]
1955 .assert_eq(&circ);
1956}
1957
1958/// Tests that invoke circuit generation through the debugger.
1959mod debugger_stepping {
1960 use super::Debugger;
1961 use crate::target::Profile;
1962 use expect_test::expect;
1963 use qsc_data_structures::language_features::LanguageFeatures;
1964 use qsc_data_structures::line_column::Encoding;
1965 use qsc_data_structures::source::SourceMap;
1966 use qsc_eval::{StepAction, StepResult, output::GenericReceiver};
1967 use std::fmt::Write;
1968
1969 /// Steps through the code in the debugger and collects the
1970 /// circuit representation at each step.
1971 fn generate_circuit_steps(code: &str, profile: Profile) -> String {
1972 let sources = SourceMap::new([("test.qs".into(), code.into())], None);
1973 let (std_id, store) = crate::compile::package_store_with_stdlib(profile.into());
1974 let mut debugger = Debugger::new(
1975 sources,
1976 profile.into(),
1977 Encoding::Utf8,
1978 LanguageFeatures::default(),
1979 store,
1980 &[(std_id, None)],
1981 )
1982 .expect("debugger creation should succeed");
1983
1984 debugger.interpreter.set_quantum_seed(Some(2));
1985
1986 let mut out = std::io::sink();
1987 let mut r = GenericReceiver::new(&mut out);
1988
1989 let mut circs = String::new();
1990 let mut result = debugger
1991 .eval_step(&mut r, &[], StepAction::In)
1992 .expect("step should succeed");
1993
1994 write!(&mut circs, "step:\n{}", debugger.circuit()).expect("write should succeed");
1995 while !matches!(result, StepResult::Return(_)) {
1996 result = debugger
1997 .eval_step(&mut r, &[], StepAction::Next)
1998 .expect("step should succeed");
1999
2000 write!(&mut circs, "step:\n{}", debugger.circuit()).expect("write should succeed");
2001 }
2002 circs
2003 }
2004
2005 #[test]
2006 fn base_profile() {
2007 let circs = generate_circuit_steps(
2008 r"
2009 namespace Test {
2010 import Std.Measurement.*;
2011 @EntryPoint()
2012 operation Main() : Result[] {
2013 use q = Qubit();
2014 H(q);
2015 let r = M(q);
2016 Reset(q);
2017 [r]
2018 }
2019 }
2020 ",
2021 Profile::Base,
2022 );
2023
2024 expect![[r#"
2025 step:
2026 step:
2027 q_0@test.qs:5:24
2028 step:
2029 q_0@test.qs:5:24 ─ H@test.qs:6:24 ──
2030 step:
2031 q_0@test.qs:5:24 ─ H@test.qs:6:24 ─── M@test.qs:7:32 ──
2032 ╘═════════
2033 step:
2034 q_0@test.qs:5:24 ─ H@test.qs:6:24 ─── M@test.qs:7:32 ──── |0〉@test.qs:8:24 ───
2035 ╘════════════════════════════════
2036 step:
2037 q_0@test.qs:5:24 ─ H@test.qs:6:24 ─── M@test.qs:7:32 ──── |0〉@test.qs:8:24 ───
2038 ╘════════════════════════════════
2039 step:
2040 q_0@test.qs:5:24 ─ H@test.qs:6:24 ─── M@test.qs:7:32 ──── |0〉@test.qs:8:24 ───
2041 ╘════════════════════════════════
2042 "#]]
2043 .assert_eq(&circs);
2044 }
2045
2046 #[test]
2047 fn unrestricted_profile() {
2048 let circs = generate_circuit_steps(
2049 r"
2050 namespace Test {
2051 import Std.Measurement.*;
2052 @EntryPoint()
2053 operation Main() : Result[] {
2054 use q = Qubit();
2055 H(q);
2056 let r = M(q);
2057 Reset(q);
2058 [r]
2059 }
2060 }
2061 ",
2062 Profile::Unrestricted,
2063 );
2064
2065 expect![[r#"
2066 step:
2067 step:
2068 q_0@test.qs:5:24
2069 step:
2070 q_0@test.qs:5:24 ─ H@test.qs:6:24 ──
2071 step:
2072 q_0@test.qs:5:24 ─ H@test.qs:6:24 ─── M@test.qs:7:32 ──
2073 ╘═════════
2074 step:
2075 q_0@test.qs:5:24 ─ H@test.qs:6:24 ─── M@test.qs:7:32 ──── |0〉@test.qs:8:24 ───
2076 ╘════════════════════════════════
2077 step:
2078 q_0@test.qs:5:24 ─ H@test.qs:6:24 ─── M@test.qs:7:32 ──── |0〉@test.qs:8:24 ───
2079 ╘════════════════════════════════
2080 step:
2081 q_0@test.qs:5:24 ─ H@test.qs:6:24 ─── M@test.qs:7:32 ──── |0〉@test.qs:8:24 ───
2082 ╘════════════════════════════════
2083 "#]]
2084 .assert_eq(&circs);
2085 }
2086
2087 #[test]
2088 fn unrestricted_profile_result_comparison() {
2089 let circs = generate_circuit_steps(
2090 r"
2091 namespace Test {
2092 import Std.Measurement.*;
2093 @EntryPoint()
2094 operation Main() : Result[] {
2095 use q = Qubit();
2096 H(q);
2097 let r = M(q);
2098 if (r == One) {
2099 X(q);
2100 }
2101 [r]
2102 }
2103 }
2104 ",
2105 Profile::Unrestricted,
2106 );
2107
2108 // We set the random seed in the test to account for
2109 // the nondeterministic output. Since the debugger is running
2110 // the real simulator, the circuit is going to vary from run to run
2111 // depending on measurement outcomes.
2112 expect![[r#"
2113 step:
2114 step:
2115 q_0@test.qs:5:24
2116 step:
2117 q_0@test.qs:5:24 ─ H@test.qs:6:24 ──
2118 step:
2119 q_0@test.qs:5:24 ─ H@test.qs:6:24 ─── M@test.qs:7:32 ──
2120 ╘═════════
2121 step:
2122 q_0@test.qs:5:24 ─ H@test.qs:6:24 ─── M@test.qs:7:32 ──
2123 ╘═════════
2124 step:
2125 q_0@test.qs:5:24 ─ H@test.qs:6:24 ─── M@test.qs:7:32 ─── X@test.qs:9:28 ──
2126 ╘════════════════════════════
2127 step:
2128 q_0@test.qs:5:24 ─ H@test.qs:6:24 ─── M@test.qs:7:32 ─── X@test.qs:9:28 ──
2129 ╘════════════════════════════
2130 step:
2131 q_0@test.qs:5:24 ─ H@test.qs:6:24 ─── M@test.qs:7:32 ─── X@test.qs:9:28 ──
2132 ╘════════════════════════════
2133 step:
2134 q_0@test.qs:5:24 ─ H@test.qs:6:24 ─── M@test.qs:7:32 ─── X@test.qs:9:28 ──
2135 ╘════════════════════════════
2136 "#]]
2137 .assert_eq(&circs);
2138 }
2139}
2140