microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
billti/sim

Branches

Tags

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

Clone

HTTPS

Download ZIP

compiler/qsc/src/interpret/circuit_tests.rs

1070lines · 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 ──── Z ────────────────
207 q_1 ── H ──── ● ──── H ──── M ──
208 ╘═══
209 "]]
210 .assert_eq(&circ.to_string());
211}
212
213#[test]
214fn m_unrestricted_profile() {
215 let mut interpreter = interpreter(
216 r"
217 namespace Test {
218 import Std.Measurement.*;
219 @EntryPoint()
220 operation Main() : Result[] {
221 use q = Qubit();
222 H(q);
223 [M(q)]
224 }
225 }
226 ",
227 Profile::Unrestricted,
228 );
229
230 let circ = interpreter
231 .circuit(CircuitEntryPoint::EntryPoint, false)
232 .expect("circuit generation should succeed");
233
234 expect![[r"
235 q_0 ── H ──── M ──
236 ╘═══
237 "]]
238 .assert_eq(&circ.to_string());
239}
240
241#[test]
242fn mresetz_unrestricted_profile() {
243 let mut interpreter = interpreter(
244 r"
245 namespace Test {
246 import Std.Measurement.*;
247 @EntryPoint()
248 operation Main() : Result[] {
249 use q = Qubit();
250 H(q);
251 [MResetZ(q)]
252 }
253 }
254 ",
255 Profile::Unrestricted,
256 );
257
258 let circ = interpreter
259 .circuit(CircuitEntryPoint::EntryPoint, false)
260 .expect("circuit generation should succeed");
261
262 expect![[r"
263 q_0 ── H ──── M ──── |0〉 ──
264 ╘════════════
265 "]]
266 .assert_eq(&circ.to_string());
267}
268
269#[test]
270fn mresetz_base_profile() {
271 let mut interpreter = interpreter(
272 r"
273 namespace Test {
274 import Std.Measurement.*;
275 @EntryPoint()
276 operation Main() : Result[] {
277 use q = Qubit();
278 H(q);
279 [MResetZ(q)]
280 }
281 }
282 ",
283 Profile::Base,
284 );
285
286 let circ = interpreter
287 .circuit(CircuitEntryPoint::EntryPoint, false)
288 .expect("circuit generation should succeed");
289
290 expect![[r"
291 q_0 ── H ──── M ──
292 ╘═══
293 "]]
294 .assert_eq(&circ.to_string());
295}
296
297#[test]
298fn unrestricted_profile_result_comparison() {
299 let mut interpreter = interpreter(
300 r"
301 namespace Test {
302 import Std.Measurement.*;
303 @EntryPoint()
304 operation Main() : Result[] {
305 use q1 = Qubit();
306 use q2 = Qubit();
307 H(q1);
308 H(q2);
309 let r1 = M(q1);
310 let r2 = M(q2);
311 if (r1 == r2) {
312 X(q1);
313 }
314 ResetAll([q1, q2]);
315 [r1, r2]
316 }
317 }
318 ",
319 Profile::Unrestricted,
320 );
321
322 interpreter.set_quantum_seed(Some(2));
323
324 let circuit_err = interpreter
325 .circuit(CircuitEntryPoint::EntryPoint, false)
326 .expect_err("circuit should return error")
327 .pop()
328 .expect("error should exist");
329
330 expect!["Qsc.Eval.ResultComparisonUnsupported"].assert_eq(
331 &circuit_err
332 .code()
333 .expect("error code should exist")
334 .to_string(),
335 );
336
337 let circuit = interpreter.get_circuit();
338 expect![""].assert_eq(&circuit.to_string());
339
340 let mut out = std::io::sink();
341 let mut r = GenericReceiver::new(&mut out);
342
343 // Result comparisons are okay when tracing
344 // circuit with the simulator.
345 let circ = interpreter
346 .circuit(CircuitEntryPoint::EntryPoint, true)
347 .expect("circuit generation should succeed");
348
349 expect![[r"
350 q_0 ── H ──── M ───── X ───── |0〉 ──
351 ╘═════════════════════
352 q_1 ── H ──── M ──── |0〉 ───────────
353 ╘═════════════════════
354 "]]
355 .assert_eq(&circ.to_string());
356
357 // Result comparisons are also okay if calling
358 // get_circuit() after incremental evaluation,
359 // because we're using the current simulator
360 // state.
361 interpreter
362 .eval_fragments(&mut r, "Test.Main();")
363 .expect("eval should succeed");
364
365 let circuit = interpreter.get_circuit();
366 expect![[r"
367 q_0 ── H ──── M ───── X ───── |0〉 ──
368 ╘═════════════════════
369 q_1 ── H ──── M ──── |0〉 ───────────
370 ╘═════════════════════
371 "]]
372 .assert_eq(&circuit.to_string());
373}
374
375#[test]
376fn custom_intrinsic() {
377 let mut interpreter = interpreter(
378 r"
379 namespace Test {
380 operation foo(q: Qubit): Unit {
381 body intrinsic;
382 }
383
384 @EntryPoint()
385 operation Main() : Unit {
386 use q = Qubit();
387 foo(q);
388 }
389 }",
390 Profile::Unrestricted,
391 );
392
393 let circ = interpreter
394 .circuit(CircuitEntryPoint::EntryPoint, false)
395 .expect("circuit generation should succeed");
396
397 expect![[r"
398 q_0 ─ foo ─
399 "]]
400 .assert_eq(&circ.to_string());
401}
402
403#[test]
404fn custom_intrinsic_classical_arg() {
405 let mut interpreter = interpreter(
406 r"
407 namespace Test {
408 operation foo(n: Int): Unit {
409 body intrinsic;
410 }
411
412 @EntryPoint()
413 operation Main() : Unit {
414 use q = Qubit();
415 X(q);
416 foo(4);
417 }
418 }",
419 Profile::Unrestricted,
420 );
421
422 let circ = interpreter
423 .circuit(CircuitEntryPoint::EntryPoint, false)
424 .expect("circuit generation should succeed");
425
426 // A custom intrinsic that doesn't take qubits just doesn't
427 // show up on the circuit.
428 expect![[r"
429 q_0 ── X ──
430 "]]
431 .assert_eq(&circ.to_string());
432}
433
434#[test]
435fn custom_intrinsic_one_classical_arg() {
436 let mut interpreter = interpreter(
437 r"
438 namespace Test {
439 operation foo(n: Int, q: Qubit): Unit {
440 body intrinsic;
441 }
442
443 @EntryPoint()
444 operation Main() : Unit {
445 use q = Qubit();
446 X(q);
447 foo(4, q);
448 }
449 }",
450 Profile::Unrestricted,
451 );
452
453 let circ = interpreter
454 .circuit(CircuitEntryPoint::EntryPoint, false)
455 .expect("circuit generation should succeed");
456
457 expect![[r"
458 q_0 ── X ─── foo(4) ──
459 "]]
460 .assert_eq(&circ.to_string());
461}
462
463#[test]
464fn custom_intrinsic_mixed_args() {
465 let mut interpreter = interpreter(
466 r"
467 namespace Test {
468 import Std.ResourceEstimation.*;
469
470 @EntryPoint()
471 operation Main() : Unit {
472 use qs = Qubit[10];
473 AccountForEstimates(
474 [
475 AuxQubitCount(1),
476 TCount(2),
477 RotationCount(3),
478 RotationDepth(4),
479 CczCount(5),
480 MeasurementCount(6),
481 ],
482 PSSPCLayout(),
483 qs);
484 }
485 }",
486 Profile::Unrestricted,
487 );
488
489 let circ = interpreter
490 .circuit(CircuitEntryPoint::EntryPoint, false)
491 .expect("circuit generation should succeed");
492
493 // This is one gate that spans ten target wires, even though the
494 // text visualization doesn't convey that clearly.
495 expect![[r"
496 q_0 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
497 q_1 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
498 q_2 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
499 q_3 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
500 q_4 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
501 q_5 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
502 q_6 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
503 q_7 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
504 q_8 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
505 q_9 ─ AccountForEstimatesInternal([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)], 1) ──
506 "]]
507 .assert_eq(&circ.to_string());
508
509 assert_eq!(circ.operations.len(), 1);
510}
511
512#[test]
513fn custom_intrinsic_apply_idle_noise() {
514 let mut interpreter = interpreter(
515 r"
516 namespace Test {
517 import Std.Diagnostics.*;
518 @EntryPoint()
519 operation Main() : Unit {
520 ConfigurePauliNoise(BitFlipNoise(1.0));
521 use q = Qubit();
522 ApplyIdleNoise(q);
523 }
524 }",
525 Profile::Unrestricted,
526 );
527
528 let circ = interpreter
529 .circuit(CircuitEntryPoint::EntryPoint, false)
530 .expect("circuit generation should succeed");
531
532 // ConfigurePauliNoise has no qubit arguments so it shouldn't show up.
533 // ApplyIdleNoise is a quantum operation so it shows up.
534 expect![[r#"
535 q_0 ─ ApplyIdleNoise ──
536 "#]]
537 .assert_eq(&circ.to_string());
538}
539
540#[test]
541fn operation_with_qubits() {
542 let mut interpreter = interpreter(
543 r"
544 namespace Test {
545 @EntryPoint()
546 operation Main() : Result[] { [] }
547
548 operation Test(q1: Qubit, q2: Qubit) : Result[] {
549 H(q1);
550 CNOT(q1, q2);
551 [M(q1), M(q2)]
552 }
553
554 }",
555 Profile::Unrestricted,
556 );
557
558 let circ = interpreter
559 .circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
560 .expect("circuit generation should succeed");
561
562 expect![[r"
563 q_0 ── H ──── ● ──── M ──
564 │ ╘═══
565 q_1 ───────── X ──── M ──
566 ╘═══
567 "]]
568 .assert_eq(&circ.to_string());
569}
570
571#[test]
572fn operation_with_qubits_base_profile() {
573 let mut interpreter = interpreter(
574 r"
575 namespace Test {
576 @EntryPoint()
577 operation Main() : Result[] { [] }
578
579 operation Test(q1: Qubit, q2: Qubit) : Result[] {
580 H(q1);
581 CNOT(q1, q2);
582 [M(q1), M(q2)]
583 }
584
585 }",
586 Profile::Base,
587 );
588
589 let circ = interpreter
590 .circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
591 .expect("circuit generation should succeed");
592
593 expect![[r"
594 q_0 ── H ──── ● ──── Z ──────────────────────────────
595 q_1 ───────── X ─────┼──────────── Z ────────────────
596 q_2 ── H ─────────── ● ──── H ─────┼───── M ─────────
597 │ ╘══════════
598 q_3 ── H ───────────────────────── ● ──── H ──── M ──
599 ╘═══
600 "]]
601 .assert_eq(&circ.to_string());
602}
603
604#[test]
605fn operation_with_qubit_arrays() {
606 let mut interpreter = interpreter(
607 r"
608 namespace Test {
609 @EntryPoint()
610 operation Main() : Result[] { [] }
611
612 import Std.Measurement.*;
613 operation Test(q1: Qubit[], q2: Qubit[][], q3: Qubit[][][], q: Qubit) : Result[] {
614 for q in q1 {
615 H(q);
616 }
617 for qs in q2 {
618 for q in qs {
619 X(q);
620 }
621 }
622 for qss in q3 {
623 for qs in qss {
624 for q in qs {
625 Y(q);
626 }
627 }
628 }
629 X(q);
630 MeasureEachZ(q1)
631 }
632 }",
633 Profile::Unrestricted,
634 );
635
636 let circ = interpreter
637 .circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
638 .expect("circuit generation should succeed");
639
640 expect![[r"
641 q_0 ── H ──── M ──
642 ╘═══
643 q_1 ── H ──── M ──
644 ╘═══
645 q_2 ── X ─────────
646 q_3 ── X ─────────
647 q_4 ── X ─────────
648 q_5 ── X ─────────
649 q_6 ── Y ─────────
650 q_7 ── Y ─────────
651 q_8 ── Y ─────────
652 q_9 ── Y ─────────
653 q_10 ── Y ─────────
654 q_11 ── Y ─────────
655 q_12 ── Y ─────────
656 q_13 ── Y ─────────
657 q_14 ── X ─────────
658 "]]
659 .assert_eq(&circ.to_string());
660}
661
662#[test]
663fn adjoint_operation() {
664 let mut interpreter = interpreter(
665 r"
666 namespace Test {
667 @EntryPoint()
668 operation Main() : Result[] { [] }
669
670 operation Foo (q : Qubit) : Unit
671 is Adj + Ctl {
672
673 body (...) {
674 X(q);
675 }
676
677 adjoint (...) {
678 Y(q);
679 }
680
681 controlled (cs, ...) {
682 }
683 }
684
685 }",
686 Profile::Unrestricted,
687 );
688
689 let circ = interpreter
690 .circuit(
691 CircuitEntryPoint::Operation("Adjoint Test.Foo".into()),
692 false,
693 )
694 .expect("circuit generation should succeed");
695
696 expect![[r"
697 q_0 ── Y ──
698 "]]
699 .assert_eq(&circ.to_string());
700}
701
702#[test]
703fn lambda() {
704 let mut interpreter = interpreter(
705 r"
706 namespace Test {
707 @EntryPoint()
708 operation Main() : Result[] { [] }
709 }",
710 Profile::Unrestricted,
711 );
712
713 let circ = interpreter
714 .circuit(CircuitEntryPoint::Operation("q => H(q)".into()), false)
715 .expect("circuit generation should succeed");
716
717 expect![[r"
718 q_0 ── H ──
719 "]]
720 .assert_eq(&circ.to_string());
721}
722
723#[test]
724fn controlled_operation() {
725 let mut interpreter = interpreter(
726 r"
727 namespace Test {
728 @EntryPoint()
729 operation Main() : Result[] { [] }
730
731 operation SWAP (q1 : Qubit, q2 : Qubit) : Unit
732 is Adj + Ctl {
733
734 body (...) {
735 CNOT(q1, q2);
736 CNOT(q2, q1);
737 CNOT(q1, q2);
738 }
739
740 adjoint (...) {
741 SWAP(q1, q2);
742 }
743
744 controlled (cs, ...) {
745 CNOT(q1, q2);
746 Controlled CNOT(cs, (q2, q1));
747 CNOT(q1, q2);
748 }
749 }
750
751 }",
752 Profile::Unrestricted,
753 );
754
755 let circ_err = interpreter
756 .circuit(
757 CircuitEntryPoint::Operation("Controlled Test.SWAP".into()),
758 false,
759 )
760 .expect_err("circuit generation should fail");
761
762 // Controlled operations are not supported at the moment.
763 // We don't generate an accurate call signature with the tuple arguments.
764 expect![[r"
765 [
766 Circuit(
767 ControlledUnsupported,
768 ),
769 ]
770 "]]
771 .assert_debug_eq(&circ_err);
772}
773
774#[test]
775fn internal_operation() {
776 let mut interpreter = interpreter(
777 r"
778 namespace Test {
779 @EntryPoint()
780 operation Main() : Result[] { [] }
781
782 internal operation Test(q1: Qubit, q2: Qubit) : Result[] {
783 H(q1);
784 CNOT(q1, q2);
785 [M(q1), M(q2)]
786 }
787 }",
788 Profile::Unrestricted,
789 );
790
791 let circ = interpreter
792 .circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
793 .expect("circuit generation should not fail");
794
795 expect![[r#"
796 q_0 ── H ──── ● ──── M ──
797 │ ╘═══
798 q_1 ───────── X ──── M ──
799 ╘═══
800 "#]]
801 .assert_eq(&circ.to_string());
802}
803
804#[test]
805fn operation_with_non_qubit_args() {
806 let mut interpreter = interpreter(
807 r"
808 namespace Test {
809 @EntryPoint()
810 operation Main() : Result[] { [] }
811
812 operation Test(q1: Qubit, q2: Qubit, i: Int) : Unit {
813 }
814
815 }",
816 Profile::Unrestricted,
817 );
818
819 let circ_err = interpreter
820 .circuit(CircuitEntryPoint::Operation("Test.Test".into()), false)
821 .expect_err("circuit generation should fail");
822
823 expect![[r"
824 [
825 Circuit(
826 NoQubitParameters,
827 ),
828 ]
829 "]]
830 .assert_debug_eq(&circ_err);
831}
832
833#[test]
834fn operation_with_long_gates_properly_aligned() {
835 let mut interpreter = interpreter(
836 r"
837 namespace Test {
838 import Std.Measurement.*;
839
840 @EntryPoint()
841 operation Main() : Result[] {
842 use q0 = Qubit();
843 use q1 = Qubit();
844
845 H(q0);
846 H(q1);
847 X(q1);
848 Ry(1.0, q1);
849 CNOT(q0, q1);
850 M(q0);
851
852 use q2 = Qubit();
853
854 H(q2);
855 Rx(1.0, q2);
856 H(q2);
857 Rx(1.0, q2);
858 H(q2);
859 Rx(1.0, q2);
860
861 use q3 = Qubit();
862
863 Rxx(1.0, q1, q3);
864
865 CNOT(q0, q3);
866
867 [M(q1), M(q3)]
868 }
869 }
870 ",
871 Profile::Unrestricted,
872 );
873
874 let circ = interpreter
875 .circuit(CircuitEntryPoint::EntryPoint, false)
876 .expect("circuit generation should succeed");
877
878 expect![[r#"
879 q_0 ── H ────────────────────────────────────── ● ──────── M ────────────────────────────────── ● ─────────
880 │ ╘════════════════════════════════════╪══════════
881 q_1 ── H ──────── X ─────── ry(1.0000) ──────── X ───────────────────────────── rxx(1.0000) ────┼───── M ──
882 ┆ │ ╘═══
883 q_2 ── H ─── rx(1.0000) ──────── H ─────── rx(1.0000) ──── H ─── rx(1.0000) ─────────┆──────────┼──────────
884 q_3 ─────────────────────────────────────────────────────────────────────────── rxx(1.0000) ─── X ──── M ──
885 ╘═══
886 "#]]
887 .assert_eq(&circ.to_string());
888}
889
890/// Tests that invoke circuit generation throught the debugger.
891mod debugger_stepping {
892 use super::Debugger;
893 use crate::target::Profile;
894 use expect_test::expect;
895 use qsc_data_structures::language_features::LanguageFeatures;
896 use qsc_data_structures::line_column::Encoding;
897 use qsc_eval::{output::GenericReceiver, StepAction, StepResult};
898 use qsc_frontend::compile::SourceMap;
899 use std::fmt::Write;
900
901 /// Steps through the code in the debugger and collects the
902 /// circuit representation at each step.
903 fn generate_circuit_steps(code: &str, profile: Profile) -> String {
904 let sources = SourceMap::new([("test.qs".into(), code.into())], None);
905 let (std_id, store) = crate::compile::package_store_with_stdlib(profile.into());
906 let mut debugger = Debugger::new(
907 sources,
908 profile.into(),
909 Encoding::Utf8,
910 LanguageFeatures::default(),
911 store,
912 &[(std_id, None)],
913 )
914 .expect("debugger creation should succeed");
915
916 debugger.interpreter.set_quantum_seed(Some(2));
917
918 let mut out = std::io::sink();
919 let mut r = GenericReceiver::new(&mut out);
920
921 let mut circs = String::new();
922 let mut result = debugger
923 .eval_step(&mut r, &[], StepAction::In)
924 .expect("step should succeed");
925
926 write!(&mut circs, "step:\n{}", debugger.circuit()).expect("write should succeed");
927 while !matches!(result, StepResult::Return(_)) {
928 result = debugger
929 .eval_step(&mut r, &[], StepAction::Next)
930 .expect("step should succeed");
931
932 write!(&mut circs, "step:\n{}", debugger.circuit()).expect("write should succeed");
933 }
934 circs
935 }
936
937 #[test]
938 fn base_profile() {
939 let circs = generate_circuit_steps(
940 r"
941 namespace Test {
942 import Std.Measurement.*;
943 @EntryPoint()
944 operation Main() : Result[] {
945 use q = Qubit();
946 H(q);
947 let r = M(q);
948 Reset(q);
949 [r]
950 }
951 }
952 ",
953 Profile::Base,
954 );
955
956 // Surprising but expected: Reset gates would *not* normally
957 // be generated in Base Profile, but they are here, since
958 // when running in tandem with the simulator, the resulting
959 // circuit is intended to match the calls into the simulator.
960 //
961 // Note the circuit still looks different than what would be
962 // generated in Unrestricted Profile for the same code,
963 // due to conditional compilation in the standard library.
964 expect![["
965 step:
966 step:
967 q_0
968 step:
969 q_0 ── H ──
970 step:
971 q_0 ── H ──── Z ─────────────────────────
972 q_1 ── H ──── ● ──── H ──── M ──── |0〉 ──
973 ╘════════════
974 step:
975 q_0 ── H ──── Z ──── |0〉 ──────────────────
976 q_1 ── H ──── ● ───── H ───── M ──── |0〉 ──
977 ╘════════════
978 step:
979 q_0 ── H ──── Z ──── |0〉 ──────────────────
980 q_1 ── H ──── ● ───── H ───── M ──── |0〉 ──
981 ╘════════════
982 "]]
983 .assert_eq(&circs);
984 }
985
986 #[test]
987 fn unrestricted_profile() {
988 let circs = generate_circuit_steps(
989 r"
990 namespace Test {
991 import Std.Measurement.*;
992 @EntryPoint()
993 operation Main() : Result[] {
994 use q = Qubit();
995 H(q);
996 let r = M(q);
997 Reset(q);
998 [r]
999 }
1000 }
1001 ",
1002 Profile::Unrestricted,
1003 );
1004
1005 expect![[r"
1006 step:
1007 step:
1008 q_0
1009 step:
1010 q_0 ── H ──
1011 step:
1012 q_0 ── H ──── M ──
1013 ╘═══
1014 step:
1015 q_0 ── H ──── M ──── |0〉 ──
1016 ╘════════════
1017 step:
1018 q_0 ── H ──── M ──── |0〉 ──
1019 ╘════════════
1020 "]]
1021 .assert_eq(&circs);
1022 }
1023
1024 #[test]
1025 fn unrestricted_profile_result_comparison() {
1026 let circs = generate_circuit_steps(
1027 r"
1028 namespace Test {
1029 import Std.Measurement.*;
1030 @EntryPoint()
1031 operation Main() : Result[] {
1032 use q = Qubit();
1033 H(q);
1034 let r = M(q);
1035 if (r == One) {
1036 X(q);
1037 }
1038 [r]
1039 }
1040 }
1041 ",
1042 Profile::Unrestricted,
1043 );
1044
1045 // We set the random seed in the test to account for
1046 // the nondeterministic output. Since the debugger is running
1047 // the real simulator, the circuit is going to vary from run to run
1048 // depending on measurement outcomes.
1049 expect![[r"
1050 step:
1051 step:
1052 q_0
1053 step:
1054 q_0 ── H ──
1055 step:
1056 q_0 ── H ──── M ──
1057 ╘═══
1058 step:
1059 q_0 ── H ──── M ──
1060 ╘═══
1061 step:
1062 q_0 ── H ──── M ──── X ──
1063 ╘══════════
1064 step:
1065 q_0 ── H ──── M ──── X ──
1066 ╘══════════
1067 "]]
1068 .assert_eq(&circs);
1069 }
1070}
1071