microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/fix-2145

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc/src/interpret/circuit_tests.rs

1171lines · 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::target::Profile;
8use expect_test::expect;
9use miette::Diagnostic;
10use qsc_data_structures::language_features::LanguageFeatures;
11use qsc_eval::output::GenericReceiver;
12use qsc_frontend::compile::SourceMap;
13use qsc_passes::PackageType;
14
15fn interpreter(code: &str, profile: Profile) -> Interpreter {
16 let sources = SourceMap::new([("test.qs".into(), code.into())], None);
17 let (std_id, store) = crate::compile::package_store_with_stdlib(profile.into());
18 Interpreter::new(
19 sources,
20 PackageType::Exe,
21 profile.into(),
22 LanguageFeatures::default(),
23 store,
24 &[(std_id, None)],
25 )
26 .expect("interpreter creation should succeed")
27}
28
29#[test]
30fn empty() {
31 let mut interpreter = interpreter(
32 r#"
33 namespace Test {
34 @EntryPoint()
35 operation Main() : Unit {
36 Message("hi");
37 }
38 }
39 "#,
40 Profile::Unrestricted,
41 );
42
43 let circ = interpreter
44 .circuit(CircuitEntryPoint::EntryPoint, false)
45 .expect("circuit generation should succeed");
46
47 expect![].assert_eq(&circ.to_string());
48}
49
50#[test]
51fn one_gate() {
52 let mut interpreter = interpreter(
53 r"
54 namespace Test {
55 @EntryPoint()
56 operation Main() : Unit {
57 use q = Qubit();
58 H(q);
59 }
60 }
61 ",
62 Profile::Unrestricted,
63 );
64
65 let circ = interpreter
66 .circuit(CircuitEntryPoint::EntryPoint, false)
67 .expect("circuit generation should succeed");
68
69 expect![[r"
70 q_0 ── H ──
71 "]]
72 .assert_eq(&circ.to_string());
73}
74
75#[test]
76fn measure_same_qubit_twice() {
77 let mut interpreter = interpreter(
78 r"
79 namespace Test {
80 @EntryPoint()
81 operation Main() : Result[] {
82 use q = Qubit();
83 H(q);
84 let r1 = M(q);
85 let r2 = M(q);
86 [r1, r2]
87 }
88 }
89 ",
90 Profile::Unrestricted,
91 );
92
93 let circ = interpreter
94 .circuit(CircuitEntryPoint::EntryPoint, false)
95 .expect("circuit generation should succeed");
96
97 expect![["
98 q_0 ── H ──── M ──── M ──
99 ╘══════╪═══
100 ╘═══
101 "]]
102 .assert_eq(&circ.to_string());
103}
104
105#[test]
106fn toffoli() {
107 let mut interpreter = interpreter(
108 r"
109 namespace Test {
110 @EntryPoint()
111 operation Main() : Unit {
112 use q = Qubit[3];
113 CCNOT(q[0], q[1], q[2]);
114 }
115 }
116 ",
117 Profile::Unrestricted,
118 );
119
120 let circ = interpreter
121 .circuit(CircuitEntryPoint::EntryPoint, false)
122 .expect("circuit generation should succeed");
123
124 expect![[r"
125 q_0 ── ● ──
126 q_1 ── ● ──
127 q_2 ── X ──
128 "]]
129 .assert_eq(&circ.to_string());
130}
131
132#[test]
133fn rotation_gate() {
134 let mut interpreter = interpreter(
135 r"
136 namespace Test {
137 @EntryPoint()
138 operation Main() : Unit {
139 use q = Qubit();
140 Rx(Microsoft.Quantum.Math.PI()/2.0, q);
141 }
142 }
143 ",
144 Profile::Unrestricted,
145 );
146
147 let circ = interpreter
148 .circuit(CircuitEntryPoint::EntryPoint, false)
149 .expect("circuit generation should succeed");
150
151 expect![[r#"
152 q_0 ─ Rx(1.5708) ──
153 "#]]
154 .assert_eq(&circ.to_string());
155}
156
157#[test]
158fn classical_for_loop() {
159 let mut interpreter = interpreter(
160 r"
161 namespace Test {
162 @EntryPoint()
163 operation Main() : Unit {
164 use q = Qubit();
165 for i in 0..5 {
166 X(q);
167 }
168 }
169 }
170 ",
171 Profile::Unrestricted,
172 );
173
174 let circ = interpreter
175 .circuit(CircuitEntryPoint::EntryPoint, false)
176 .expect("circuit generation should succeed");
177
178 expect![[r"
179 q_0 ── X ──── X ──── X ──── X ──── X ──── X ──
180 "]]
181 .assert_eq(&circ.to_string());
182}
183
184#[test]
185fn m_base_profile() {
186 let mut interpreter = interpreter(
187 r"
188 namespace Test {
189 import Std.Measurement.*;
190 @EntryPoint()
191 operation Main() : Result[] {
192 use q = Qubit();
193 H(q);
194 [M(q)]
195 }
196 }
197 ",
198 Profile::Base,
199 );
200
201 let circ = interpreter
202 .circuit(CircuitEntryPoint::EntryPoint, false)
203 .expect("circuit generation should succeed");
204
205 expect![[r#"
206 q_0 ── H ──── M ──
207 ╘═══
208 "#]]
209 .assert_eq(&circ.to_string());
210}
211
212#[test]
213fn m_unrestricted_profile() {
214 let mut interpreter = interpreter(
215 r"
216 namespace Test {
217 import Std.Measurement.*;
218 @EntryPoint()
219 operation Main() : Result[] {
220 use q = Qubit();
221 H(q);
222 [M(q)]
223 }
224 }
225 ",
226 Profile::Unrestricted,
227 );
228
229 let circ = interpreter
230 .circuit(CircuitEntryPoint::EntryPoint, false)
231 .expect("circuit generation should succeed");
232
233 expect![[r"
234 q_0 ── H ──── M ──
235 ╘═══
236 "]]
237 .assert_eq(&circ.to_string());
238}
239
240#[test]
241fn mresetz_unrestricted_profile() {
242 let mut interpreter = interpreter(
243 r"
244 namespace Test {
245 import Std.Measurement.*;
246 @EntryPoint()
247 operation Main() : Result[] {
248 use q = Qubit();
249 H(q);
250 [MResetZ(q)]
251 }
252 }
253 ",
254 Profile::Unrestricted,
255 );
256
257 let circ = interpreter
258 .circuit(CircuitEntryPoint::EntryPoint, false)
259 .expect("circuit generation should succeed");
260
261 expect![[r"
262 q_0 ── H ──── M ──── |0〉 ──
263 ╘════════════
264 "]]
265 .assert_eq(&circ.to_string());
266}
267
268#[test]
269fn mresetz_base_profile() {
270 let mut interpreter = interpreter(
271 r"
272 namespace Test {
273 import Std.Measurement.*;
274 @EntryPoint()
275 operation Main() : Result[] {
276 use q = Qubit();
277 H(q);
278 [MResetZ(q)]
279 }
280 }
281 ",
282 Profile::Base,
283 );
284
285 let circ = interpreter
286 .circuit(CircuitEntryPoint::EntryPoint, false)
287 .expect("circuit generation should succeed");
288
289 expect![[r#"
290 q_0 ── H ──── M ──── |0〉 ──
291 ╘════════════
292 "#]]
293 .assert_eq(&circ.to_string());
294}
295
296#[test]
297fn unrestricted_profile_result_comparison() {
298 let mut interpreter = interpreter(
299 r"
300 namespace Test {
301 import Std.Measurement.*;
302 @EntryPoint()
303 operation Main() : Result[] {
304 use q1 = Qubit();
305 use q2 = Qubit();
306 H(q1);
307 H(q2);
308 let r1 = M(q1);
309 let r2 = M(q2);
310 if (r1 == r2) {
311 X(q1);
312 }
313 ResetAll([q1, q2]);
314 [r1, r2]
315 }
316 }
317 ",
318 Profile::Unrestricted,
319 );
320
321 interpreter.set_quantum_seed(Some(2));
322
323 let circuit_err = interpreter
324 .circuit(CircuitEntryPoint::EntryPoint, false)
325 .expect_err("circuit should return error")
326 .pop()
327 .expect("error should exist");
328
329 expect!["Qsc.Eval.ResultComparisonUnsupported"].assert_eq(
330 &circuit_err
331 .code()
332 .expect("error code should exist")
333 .to_string(),
334 );
335
336 let circuit = interpreter.get_circuit();
337 expect![""].assert_eq(&circuit.to_string());
338
339 let mut out = std::io::sink();
340 let mut r = GenericReceiver::new(&mut out);
341
342 // Result comparisons are okay when tracing
343 // circuit with the simulator.
344 let circ = interpreter
345 .circuit(CircuitEntryPoint::EntryPoint, true)
346 .expect("circuit generation should succeed");
347
348 expect![[r"
349 q_0 ── H ──── M ───── X ───── |0〉 ──
350 ╘═════════════════════
351 q_1 ── H ──── M ──── |0〉 ───────────
352 ╘═════════════════════
353 "]]
354 .assert_eq(&circ.to_string());
355
356 // Result comparisons are also okay if calling
357 // get_circuit() after incremental evaluation,
358 // because we're using the current simulator
359 // state.
360 interpreter
361 .eval_fragments(&mut r, "Test.Main();")
362 .expect("eval should succeed");
363
364 let circuit = interpreter.get_circuit();
365 expect![[r"
366 q_0 ── H ──── M ───── X ───── |0〉 ──
367 ╘═════════════════════
368 q_1 ── H ──── M ──── |0〉 ───────────
369 ╘═════════════════════
370 "]]
371 .assert_eq(&circuit.to_string());
372}
373
374#[test]
375fn custom_intrinsic() {
376 let mut interpreter = interpreter(
377 r"
378 namespace Test {
379 operation foo(q: Qubit): Unit {
380 body intrinsic;
381 }
382
383 @EntryPoint()
384 operation Main() : Unit {
385 use q = Qubit();
386 foo(q);
387 }
388 }",
389 Profile::Unrestricted,
390 );
391
392 let circ = interpreter
393 .circuit(CircuitEntryPoint::EntryPoint, false)
394 .expect("circuit generation should succeed");
395
396 expect![[r"
397 q_0 ─ foo ─
398 "]]
399 .assert_eq(&circ.to_string());
400}
401
402#[test]
403fn custom_intrinsic_classical_arg() {
404 let mut interpreter = interpreter(
405 r"
406 namespace Test {
407 operation foo(n: Int): Unit {
408 body intrinsic;
409 }
410
411 @EntryPoint()
412 operation Main() : Unit {
413 use q = Qubit();
414 X(q);
415 foo(4);
416 }
417 }",
418 Profile::Unrestricted,
419 );
420
421 let circ = interpreter
422 .circuit(CircuitEntryPoint::EntryPoint, false)
423 .expect("circuit generation should succeed");
424
425 // A custom intrinsic that doesn't take qubits just doesn't
426 // show up on the circuit.
427 expect![[r"
428 q_0 ── X ──
429 "]]
430 .assert_eq(&circ.to_string());
431}
432
433#[test]
434fn custom_intrinsic_one_classical_arg() {
435 let mut interpreter = interpreter(
436 r"
437 namespace Test {
438 operation foo(n: Int, q: Qubit): Unit {
439 body intrinsic;
440 }
441
442 @EntryPoint()
443 operation Main() : Unit {
444 use q = Qubit();
445 X(q);
446 foo(4, q);
447 }
448 }",
449 Profile::Unrestricted,
450 );
451
452 let circ = interpreter
453 .circuit(CircuitEntryPoint::EntryPoint, false)
454 .expect("circuit generation should succeed");
455
456 expect![[r"
457 q_0 ── X ─── foo(4) ──
458 "]]
459 .assert_eq(&circ.to_string());
460}
461
462#[test]
463fn custom_intrinsic_mixed_args() {
464 let mut interpreter = interpreter(
465 r"
466 namespace Test {
467 import Std.ResourceEstimation.*;
468
469 @EntryPoint()
470 operation Main() : Unit {
471 use qs = Qubit[10];
472 AccountForEstimates(
473 [
474 AuxQubitCount(1),
475 TCount(2),
476 RotationCount(3),
477 RotationDepth(4),
478 CczCount(5),
479 MeasurementCount(6),
480 ],
481 PSSPCLayout(),
482 qs);
483 }
484 }",
485 Profile::Unrestricted,
486 );
487
488 let circ = interpreter
489 .circuit(CircuitEntryPoint::EntryPoint, false)
490 .expect("circuit generation should succeed");
491
492 expect![[r"
493 q_0 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
494
495 q_1 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
496
497 q_2 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
498
499 q_3 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
500
501 q_4 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
502
503 q_5 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
504
505 q_6 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
506
507 q_7 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
508
509 q_8 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
510
511 q_9 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
512 "]]
513 .assert_eq(&circ.to_string());
514
515 assert_eq!(circ.component_grid.len(), 1);
516 assert_eq!(circ.component_grid[0].components.len(), 1);
517}
518
519#[test]
520fn custom_intrinsic_apply_idle_noise() {
521 let mut interpreter = interpreter(
522 r"
523 namespace Test {
524 import Std.Diagnostics.*;
525 @EntryPoint()
526 operation Main() : Unit {
527 ConfigurePauliNoise(BitFlipNoise(1.0));
528 use q = Qubit();
529 ApplyIdleNoise(q);
530 }
531 }",
532 Profile::Unrestricted,
533 );
534
535 let circ = interpreter
536 .circuit(CircuitEntryPoint::EntryPoint, false)
537 .expect("circuit generation should succeed");
538
539 // ConfigurePauliNoise has no qubit arguments so it shouldn't show up.
540 // ApplyIdleNoise is a quantum operation so it shows up.
541 expect![[r#"
542 q_0 ─ ApplyIdleNoise ──
543 "#]]
544 .assert_eq(&circ.to_string());
545}
546
547#[test]
548fn operation_with_qubits() {
549 let mut interpreter = interpreter(
550 r"
551 namespace Test {
552 @EntryPoint()
553 operation Main() : Result[] { [] }
554
555 operation Test(q1: Qubit, q2: Qubit) : Result[] {
556 H(q1);
557 CNOT(q1, q2);
558 [M(q1), M(q2)]
559 }
560
561 }",
562 Profile::Unrestricted,
563 );
564
565 let circ = interpreter
566 .circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
567 .expect("circuit generation should succeed");
568
569 expect![[r"
570 q_0 ── H ──── ● ──── M ──
571 │ ╘═══
572 q_1 ───────── X ──── M ──
573 ╘═══
574 "]]
575 .assert_eq(&circ.to_string());
576}
577
578#[test]
579fn operation_with_qubits_base_profile() {
580 let mut interpreter = interpreter(
581 r"
582 namespace Test {
583 @EntryPoint()
584 operation Main() : Result[] { [] }
585
586 operation Test(q1: Qubit, q2: Qubit) : Result[] {
587 H(q1);
588 CNOT(q1, q2);
589 [M(q1), M(q2)]
590 }
591
592 }",
593 Profile::Base,
594 );
595
596 let circ = interpreter
597 .circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
598 .expect("circuit generation should succeed");
599
600 expect![[r#"
601 q_0 ── H ──── ● ──── M ──
602 │ ╘═══
603 q_1 ───────── X ──── M ──
604 ╘═══
605 "#]]
606 .assert_eq(&circ.to_string());
607}
608
609#[test]
610fn operation_with_qubit_arrays() {
611 let mut interpreter = interpreter(
612 r"
613 namespace Test {
614 @EntryPoint()
615 operation Main() : Result[] { [] }
616
617 import Std.Measurement.*;
618 operation Test(q1: Qubit[], q2: Qubit[][], q3: Qubit[][][], q: Qubit) : Result[] {
619 for q in q1 {
620 H(q);
621 }
622 for qs in q2 {
623 for q in qs {
624 X(q);
625 }
626 }
627 for qss in q3 {
628 for qs in qss {
629 for q in qs {
630 Y(q);
631 }
632 }
633 }
634 X(q);
635 MeasureEachZ(q1)
636 }
637 }",
638 Profile::Unrestricted,
639 );
640
641 let circ = interpreter
642 .circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
643 .expect("circuit generation should succeed");
644
645 expect![[r"
646 q_0 ── H ──── M ──
647 ╘═══
648 q_1 ── H ──── M ──
649 ╘═══
650 q_2 ── X ─────────
651 q_3 ── X ─────────
652 q_4 ── X ─────────
653 q_5 ── X ─────────
654 q_6 ── Y ─────────
655 q_7 ── Y ─────────
656 q_8 ── Y ─────────
657 q_9 ── Y ─────────
658 q_10 ── Y ─────────
659 q_11 ── Y ─────────
660 q_12 ── Y ─────────
661 q_13 ── Y ─────────
662 q_14 ── X ─────────
663 "]]
664 .assert_eq(&circ.to_string());
665}
666
667#[test]
668fn adjoint_operation() {
669 let mut interpreter = interpreter(
670 r"
671 namespace Test {
672 @EntryPoint()
673 operation Main() : Result[] { [] }
674
675 operation Foo (q : Qubit) : Unit
676 is Adj + Ctl {
677
678 body (...) {
679 X(q);
680 }
681
682 adjoint (...) {
683 Y(q);
684 }
685
686 controlled (cs, ...) {
687 }
688 }
689
690 }",
691 Profile::Unrestricted,
692 );
693
694 let circ = interpreter
695 .circuit(
696 CircuitEntryPoint::Operation("Adjoint Test.Foo".into()),
697 false,
698 )
699 .expect("circuit generation should succeed");
700
701 expect![[r"
702 q_0 ── Y ──
703 "]]
704 .assert_eq(&circ.to_string());
705}
706
707#[test]
708fn lambda() {
709 let mut interpreter = interpreter(
710 r"
711 namespace Test {
712 @EntryPoint()
713 operation Main() : Result[] { [] }
714 }",
715 Profile::Unrestricted,
716 );
717
718 let circ = interpreter
719 .circuit(CircuitEntryPoint::Operation("q => H(q)".into()), false)
720 .expect("circuit generation should succeed");
721
722 expect![[r"
723 q_0 ── H ──
724 "]]
725 .assert_eq(&circ.to_string());
726}
727
728#[test]
729fn controlled_operation() {
730 let mut interpreter = interpreter(
731 r"
732 namespace Test {
733 @EntryPoint()
734 operation Main() : Result[] { [] }
735
736 operation SWAP (q1 : Qubit, q2 : Qubit) : Unit
737 is Adj + Ctl {
738
739 body (...) {
740 CNOT(q1, q2);
741 CNOT(q2, q1);
742 CNOT(q1, q2);
743 }
744
745 adjoint (...) {
746 SWAP(q1, q2);
747 }
748
749 controlled (cs, ...) {
750 CNOT(q1, q2);
751 Controlled CNOT(cs, (q2, q1));
752 CNOT(q1, q2);
753 }
754 }
755
756 }",
757 Profile::Unrestricted,
758 );
759
760 let circ_err = interpreter
761 .circuit(
762 CircuitEntryPoint::Operation("Controlled Test.SWAP".into()),
763 false,
764 )
765 .expect_err("circuit generation should fail");
766
767 // Controlled operations are not supported at the moment.
768 // We don't generate an accurate call signature with the tuple arguments.
769 expect![[r"
770 [
771 Circuit(
772 ControlledUnsupported,
773 ),
774 ]
775 "]]
776 .assert_debug_eq(&circ_err);
777}
778
779#[test]
780fn internal_operation() {
781 let mut interpreter = interpreter(
782 r"
783 namespace Test {
784 @EntryPoint()
785 operation Main() : Result[] { [] }
786
787 internal operation Test(q1: Qubit, q2: Qubit) : Result[] {
788 H(q1);
789 CNOT(q1, q2);
790 [M(q1), M(q2)]
791 }
792 }",
793 Profile::Unrestricted,
794 );
795
796 let circ = interpreter
797 .circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
798 .expect("circuit generation should not fail");
799
800 expect![[r#"
801 q_0 ── H ──── ● ──── M ──
802 │ ╘═══
803 q_1 ───────── X ──── M ──
804 ╘═══
805 "#]]
806 .assert_eq(&circ.to_string());
807}
808
809#[test]
810fn operation_with_non_qubit_args() {
811 let mut interpreter = interpreter(
812 r"
813 namespace Test {
814 @EntryPoint()
815 operation Main() : Result[] { [] }
816
817 operation Test(q1: Qubit, q2: Qubit, i: Int) : Unit {
818 }
819
820 }",
821 Profile::Unrestricted,
822 );
823
824 let circ_err = interpreter
825 .circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
826 .expect_err("circuit generation should fail");
827
828 expect![[r"
829 [
830 Circuit(
831 NoQubitParameters,
832 ),
833 ]
834 "]]
835 .assert_debug_eq(&circ_err);
836}
837
838#[test]
839fn operation_with_long_gates_properly_aligned() {
840 let mut interpreter = interpreter(
841 r"
842 namespace Test {
843 import Std.Measurement.*;
844
845 @EntryPoint()
846 operation Main() : Result[] {
847 use q0 = Qubit();
848 use q1 = Qubit();
849
850 H(q0);
851 H(q1);
852 X(q1);
853 Ry(1.0, q1);
854 CNOT(q0, q1);
855 M(q0);
856
857 use q2 = Qubit();
858
859 H(q2);
860 Rx(1.0, q2);
861 H(q2);
862 Rx(1.0, q2);
863 H(q2);
864 Rx(1.0, q2);
865
866 use q3 = Qubit();
867
868 Rxx(1.0, q1, q3);
869
870 CNOT(q0, q3);
871
872 [M(q1), M(q3)]
873 }
874 }
875 ",
876 Profile::Unrestricted,
877 );
878
879 let circ = interpreter
880 .circuit(CircuitEntryPoint::EntryPoint, false)
881 .expect("circuit generation should succeed");
882
883 expect![[r#"
884 q_0 ── H ────────────────────────────────────── ● ──────── M ────────────────────────────────── ● ─────────
885 │ ╘════════════════════════════════════╪══════════
886 q_1 ── H ──────── X ─────── Ry(1.0000) ──────── X ───────────────────────────── Rxx(1.0000) ────┼───── M ──
887 ┆ │ ╘═══
888 q_2 ── H ─── Rx(1.0000) ──────── H ─────── Rx(1.0000) ──── H ─── Rx(1.0000) ─────────┆──────────┼──────────
889 q_3 ─────────────────────────────────────────────────────────────────────────── Rxx(1.0000) ─── X ──── M ──
890 ╘═══
891 "#]]
892 .assert_eq(&circ.to_string());
893}
894
895#[test]
896fn operation_with_subsequent_qubits_gets_horizontal_lines() {
897 let mut interpreter = interpreter(
898 r"
899 namespace Test {
900 import Std.Measurement.*;
901
902 @EntryPoint()
903 operation Main() : Unit {
904 use q0 = Qubit();
905 use q1 = Qubit();
906 Rxx(1.0, q0, q1);
907
908 use q2 = Qubit();
909 use q3 = Qubit();
910 Rxx(1.0, q2, q3);
911 }
912 }
913 ",
914 Profile::Unrestricted,
915 );
916
917 let circ = interpreter
918 .circuit(CircuitEntryPoint::EntryPoint, false)
919 .expect("circuit generation should succeed");
920
921 expect![[r#"
922 q_0 ─ Rxx(1.0000) ─
923
924 q_1 ─ Rxx(1.0000) ─
925 q_2 ─ Rxx(1.0000) ─
926
927 q_3 ─ Rxx(1.0000) ─
928 "#]]
929 .assert_eq(&circ.to_string());
930}
931
932#[test]
933fn operation_with_subsequent_qubits_no_double_rows() {
934 let mut interpreter = interpreter(
935 r"
936 namespace Test {
937 import Std.Measurement.*;
938
939 @EntryPoint()
940 operation Main() : Unit {
941 use q0 = Qubit();
942 use q1 = Qubit();
943 Rxx(1.0, q0, q1);
944 Rxx(1.0, q0, q1);
945 }
946 }
947 ",
948 Profile::Unrestricted,
949 );
950
951 let circ = interpreter
952 .circuit(CircuitEntryPoint::EntryPoint, false)
953 .expect("circuit generation should succeed");
954
955 expect![[r#"
956 q_0 ─ Rxx(1.0000) ── Rxx(1.0000) ─
957 ┆ ┆
958 q_1 ─ Rxx(1.0000) ── Rxx(1.0000) ─
959 "#]]
960 .assert_eq(&circ.to_string());
961}
962
963#[test]
964fn operation_with_subsequent_qubits_no_added_rows() {
965 let mut interpreter = interpreter(
966 r"
967 namespace Test {
968 import Std.Measurement.*;
969
970 @EntryPoint()
971 operation Main() : Result[] {
972 use q0 = Qubit();
973 use q1 = Qubit();
974 Rxx(1.0, q0, q1);
975
976 use q2 = Qubit();
977 use q3 = Qubit();
978 Rxx(1.0, q2, q3);
979
980 [M(q0), M(q2)]
981 }
982 }
983 ",
984 Profile::Unrestricted,
985 );
986
987 let circ = interpreter
988 .circuit(CircuitEntryPoint::EntryPoint, false)
989 .expect("circuit generation should succeed");
990
991 expect![[r#"
992 q_0 ─ Rxx(1.0000) ─── M ──
993 ┆ ╘═══
994 q_1 ─ Rxx(1.0000) ────────
995 q_2 ─ Rxx(1.0000) ─── M ──
996 ┆ ╘═══
997 q_3 ─ Rxx(1.0000) ────────
998 "#]]
999 .assert_eq(&circ.to_string());
1000}
1001
1002/// Tests that invoke circuit generation throught the debugger.
1003mod debugger_stepping {
1004 use super::Debugger;
1005 use crate::target::Profile;
1006 use expect_test::expect;
1007 use qsc_data_structures::language_features::LanguageFeatures;
1008 use qsc_data_structures::line_column::Encoding;
1009 use qsc_eval::{output::GenericReceiver, StepAction, StepResult};
1010 use qsc_frontend::compile::SourceMap;
1011 use std::fmt::Write;
1012
1013 /// Steps through the code in the debugger and collects the
1014 /// circuit representation at each step.
1015 fn generate_circuit_steps(code: &str, profile: Profile) -> String {
1016 let sources = SourceMap::new([("test.qs".into(), code.into())], None);
1017 let (std_id, store) = crate::compile::package_store_with_stdlib(profile.into());
1018 let mut debugger = Debugger::new(
1019 sources,
1020 profile.into(),
1021 Encoding::Utf8,
1022 LanguageFeatures::default(),
1023 store,
1024 &[(std_id, None)],
1025 )
1026 .expect("debugger creation should succeed");
1027
1028 debugger.interpreter.set_quantum_seed(Some(2));
1029
1030 let mut out = std::io::sink();
1031 let mut r = GenericReceiver::new(&mut out);
1032
1033 let mut circs = String::new();
1034 let mut result = debugger
1035 .eval_step(&mut r, &[], StepAction::In)
1036 .expect("step should succeed");
1037
1038 write!(&mut circs, "step:\n{}", debugger.circuit()).expect("write should succeed");
1039 while !matches!(result, StepResult::Return(_)) {
1040 result = debugger
1041 .eval_step(&mut r, &[], StepAction::Next)
1042 .expect("step should succeed");
1043
1044 write!(&mut circs, "step:\n{}", debugger.circuit()).expect("write should succeed");
1045 }
1046 circs
1047 }
1048
1049 #[test]
1050 fn base_profile() {
1051 let circs = generate_circuit_steps(
1052 r"
1053 namespace Test {
1054 import Std.Measurement.*;
1055 @EntryPoint()
1056 operation Main() : Result[] {
1057 use q = Qubit();
1058 H(q);
1059 let r = M(q);
1060 Reset(q);
1061 [r]
1062 }
1063 }
1064 ",
1065 Profile::Base,
1066 );
1067
1068 expect![[r#"
1069 step:
1070 step:
1071 q_0
1072 step:
1073 q_0 ── H ──
1074 step:
1075 q_0 ── H ──── M ──
1076 ╘═══
1077 step:
1078 q_0 ── H ──── M ──── |0〉 ──
1079 ╘════════════
1080 step:
1081 q_0 ── H ──── M ──── |0〉 ──
1082 ╘════════════
1083 "#]]
1084 .assert_eq(&circs);
1085 }
1086
1087 #[test]
1088 fn unrestricted_profile() {
1089 let circs = generate_circuit_steps(
1090 r"
1091 namespace Test {
1092 import Std.Measurement.*;
1093 @EntryPoint()
1094 operation Main() : Result[] {
1095 use q = Qubit();
1096 H(q);
1097 let r = M(q);
1098 Reset(q);
1099 [r]
1100 }
1101 }
1102 ",
1103 Profile::Unrestricted,
1104 );
1105
1106 expect![[r"
1107 step:
1108 step:
1109 q_0
1110 step:
1111 q_0 ── H ──
1112 step:
1113 q_0 ── H ──── M ──
1114 ╘═══
1115 step:
1116 q_0 ── H ──── M ──── |0〉 ──
1117 ╘════════════
1118 step:
1119 q_0 ── H ──── M ──── |0〉 ──
1120 ╘════════════
1121 "]]
1122 .assert_eq(&circs);
1123 }
1124
1125 #[test]
1126 fn unrestricted_profile_result_comparison() {
1127 let circs = generate_circuit_steps(
1128 r"
1129 namespace Test {
1130 import Std.Measurement.*;
1131 @EntryPoint()
1132 operation Main() : Result[] {
1133 use q = Qubit();
1134 H(q);
1135 let r = M(q);
1136 if (r == One) {
1137 X(q);
1138 }
1139 [r]
1140 }
1141 }
1142 ",
1143 Profile::Unrestricted,
1144 );
1145
1146 // We set the random seed in the test to account for
1147 // the nondeterministic output. Since the debugger is running
1148 // the real simulator, the circuit is going to vary from run to run
1149 // depending on measurement outcomes.
1150 expect![[r"
1151 step:
1152 step:
1153 q_0
1154 step:
1155 q_0 ── H ──
1156 step:
1157 q_0 ── H ──── M ──
1158 ╘═══
1159 step:
1160 q_0 ── H ──── M ──
1161 ╘═══
1162 step:
1163 q_0 ── H ──── M ──── X ──
1164 ╘══════════
1165 step:
1166 q_0 ── H ──── M ──── X ──
1167 ╘══════════
1168 "]]
1169 .assert_eq(&circs);
1170 }
1171}
1172