microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.27.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

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