Commit 65c9d96
Circuit diagrams: Group loops (#2827)
## Feature changes
Loops from user source are now grouped in circuit diagrams.
**BEFORE:**
<img width="980" height="397" alt="image"
src="https://github.com/user-attachments/assets/4051a229-0911-4f2f-b1d4-84a0eb214520"
/>
```plaintext
[email protected]:4:20 ─ [email protected]:6:24 ─── [email protected]:6:24 ─── [email protected]:6:24 ──
```
**AFTER:**
<img width="481" height="776" alt="image"
src="https://github.com/user-attachments/assets/fa839ef3-4385-439a-ace8-3bfe0d846adf"
/>
<img width="937" height="770" alt="image"
src="https://github.com/user-attachments/assets/5bc304de-fcea-43bc-ad32-6114c75d98ea"
/>
```plaintext
[email protected]:4:20 ─ [ [Main] ─── [ [loop: [email protected]:5:20] ── [ [(1)@test.qs:5:34] ─── [ [[email protected]:6:24] ─── [email protected]:11:20 ── [email protected]:12:20 ─── ] ──── ] ─── [ [(2)@test.qs:5:34] ─── [ [[email protected]:6:24] ─── [email protected]:11:20 ── [email protected]:12:20 ─── ] ──── ] ─── [ [(3)@test.qs:5:34] ─── [ [[email protected]:6:24] ─── [email protected]:11:20 ── [email protected]:12:20 ─── ] ──── ] ──── ] ──── ] ──
```
- All loops (`for`, `while`, `repeat`/`until`) are supported. The label
is the expression in the loop header, which can be, depending on the
loop type, an iterable, a "while" condition, or an "until" condition.
e.g.:
- `loop: 0..2`
- `loop: i < 2`
- `loop: i==2`
- Exception 1: If a loop only has a single iteration, we don't group.
- Exception 2: If a loop is "vertical", meaning each iteration of the
loop only interacts with a distinct set of qubits, then there is no
grouping, since grouping in this case tends to look more confusing than
helpful.
## Internals
### High level circuit diagram pipeline
To recap, this is how circuit diagrams get currently generated.
**Q#/OpenQASM** gets compiled to (via the compilation pipeline
--`qsc_frontend`, `qsc_lowererer` FIR lowerer etc)...
...**FIR** gets evaluated and traced (via `qsc_eval` Evaluator)...
...**Traces with raw stacks** get captured and transformed to (via
Circuit Tracer)...
...**Traces with logical stacks** get transformed to (via Circuit
Tracer)...
...**`Circuit` representation** gets serialized to (via `serde-json`)...
...**Circuit JSON object** gets rendered as (via `circuit-vis`)
...**SVG & HTML circuit diagram**
### Evaluator changes
- Tracing in the Evaluator: The `Tracer` now captures the stack of
`Scope`s in addition to the usual call stack (`Frame`s) in the tracing
calls. The scope stack includes the stack of current lexical scopes and
loops, including iteration count. This allows the circuit tracer to
build a comprehensive "logical" stack that includes both call frames and
lexical scopes in the source.
- The evaluator, when in DEBUG configuration, pushes LOOPs as a scope
into the `Scope` stack. Before this change, we did track each Block as a
scope, but not specifically loops. So now when we're in a loop, there is
an extra `Scope` in the stack:
```plaintext
> BEGIN LOOP SCOPE
> for i in 0..4
>
> BEGIN BLOCK SCOPE
> {
> H(qs[i]);
> }
> END BLOCK SCOPE
>
> END LOOP SCOPE
```
- The loop scope is also associated with an `iteration_count` which the
evaluator is instructed to increment every time it enters the body block
of the loop.
### FIR lowerer changes
- FIR: In the execution graph, debug-only nodes are consolidated into a
separate `ExecGraphDebugNode` enum, just for legibility.
- FIR: during lowering, we add some extra data to the execution graph
for loops: The a new `PushLoopScope` instruction to push loop scope,
with an attached `ExprId` (used later by the circuit tracer to look up
the loop source code and display the loop label) , and a `LoopIteration`
instruction that is used to indicate a new iteration of the loop has
started.
### Circuit tracer changes
- `builder.rs` / `CircuitTracer`: Introduces a `LogicalStack` which is a
blended stack of call frames and lexical scopes. This struct is produced
by transforming the stack traces passed down from the evaluator into a
more "friendly" shape, and it corresponds to the structure that will
ultimately be seen in the circuit diagram.
- `circuit.rs` / `Circuit` : The `SourceLocation::Unresolved` enum is a
now-unnecessary abstraction that is removed in this change.
- in `CircuitTracer`, `finish` now takes both FIR and HIR store to
resolve sources and scopes
### `circuit-vis` HTML renderer
- In the renderer, we now automatically expand any groups that only
contain a single operation, which saves the user having to manually
expand multiple levels of operations.
## Test coverage
- Native interpreter tests in
`compiler/qsc/src/interpret/circuit_tests.rs` test direct all the way
from Q# to the `Circuit` representation
- Snapshot tests in `npm/qsharp/test/circuits-cases` test all the way
from Q# to rendered SVG & HTML circuit diagram using the JS/WASM
component
- `compiler/qsc_circuit/src/builder/tests/logical_stack_trace.rs` - this
exercises the Q# -> traces pipeline, validating that the evaluator
returns expected "logical" stack traces
---------
Co-authored-by: Stefan J. Wernli <[email protected]>1 parent f7d15c5 commit 65c9d96
File tree
15 files changed
+10252
-1410
lines changed- source
- compiler
- qsc_circuit/src
- builder
- tests
- qsc_eval/src
- qsc_fir/src
- qsc_lowerer/src
- qsc/src
- interpret
- npm/qsharp/test/circuits-cases
15 files changed
+10252
-1410
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
848 | 848 | | |
849 | 849 | | |
850 | 850 | | |
851 | | - | |
| 851 | + | |
852 | 852 | | |
853 | 853 | | |
854 | 854 | | |
| |||
1022 | 1022 | | |
1023 | 1023 | | |
1024 | 1024 | | |
1025 | | - | |
| 1025 | + | |
1026 | 1026 | | |
1027 | 1027 | | |
1028 | 1028 | | |
| |||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
52 | | - | |
| 52 | + | |
53 | 53 | | |
54 | 54 | | |
55 | 55 | | |
| |||
99 | 99 | | |
100 | 100 | | |
101 | 101 | | |
102 | | - | |
| 102 | + | |
103 | 103 | | |
104 | 104 | | |
105 | 105 | | |
| |||
0 commit comments