Skip to content

[Circuit renderer] Compact rendering of classically controlled gate - #3637

Closed
Dima Fedoriaka (fedimser) wants to merge 11 commits into
mainfrom
fedimser/clas-control-2
Closed

[Circuit renderer] Compact rendering of classically controlled gate#3637
Dima Fedoriaka (fedimser) wants to merge 11 commits into
mainfrom
fedimser/clas-control-2

Conversation

@fedimser

@fedimser Dima Fedoriaka (fedimser) commented Aug 25, 2026

Copy link
Copy Markdown
Contributor
  • In JSON model (circuit.rs/circuit.ts) added classical_controls to represent classical controls of a gate.
  • In rust, if there is a then/else branch with condition "resullt==One" or "result==Zero" and all gates inside have single target and no controls, instead of generating a group, emit gates without grouping them, but with added classical control.
  • In typescript, add a code to render classically controlled gate.
  • Add circuit-to-Q# conversion for classically controlled gate.
  • Add circuit-to-string rendering for classically controlled gate.

Example 1 - teleportation

Before:
image

After:
image

Example 2

operation Tmp1() : Unit {
    use q = Qubit[4];

    H(q[0]);
    let m0 = M(q[0]);
    if (m0 == One) {
        X(q[1]);
    } else {
        X(q[2]);
        Z(q[2]);
    }
    let m1 = M(q[1]);
    if (m0 == Zero) {
        Rx(0.5, q[3]);
    } else {
        Ry(0.5, q[3]);
        if (m1==Zero) {
            X(q[1]);
        }
    }
}
image

Example 3 - negative cases (controlled gate, complex condition, 2-qubit gate).

operation Tmp2() : Unit {
    use q = Qubit[4];
    
    H(q[0]);
    H(q[1]);
    let m0 = M(q[0]);
    let m1 = M(q[1]);

    if (m0 == Zero) {
        CNOT(q[2], q[3]);
    }
    if (m0 == One and m1 == One) {
        X(q[1]);
    }
    if (m0 == Zero) {
        Rxx(0.5, q[2], q[3]);
    }
}

image

@fedimser Dima Fedoriaka (fedimser) changed the title [Circuit renderer] @InvisibleInCircuit annotation [Circuit renderer] Compact rendering of classically controlled gate Aug 25, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class support for “compact” classically controlled gates across the Rust circuit model, rendering pipeline, and circuit-to-Q# conversion, so simple if (result == One/Zero) branches can be visualized as gates annotated with classical controls (rather than expanded conditional groups).

Changes:

  • Extend the circuit JSON/schema and Rust circuit model with classicalControls for unitary operations.
  • Update circuit tracing/lowering and renderer logic to emit and draw compact classically controlled gates (including inverted/“anti” controls).
  • Add/adjust Rust, JS/TS, Python, and snapshot tests to cover compact classical control behavior and updated output sizes.

Reviewed changes

Copilot reviewed 26 out of 27 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
source/samples_test/src/tests/OpenQASM.rs Update expected circuit-length snapshot for teleportation due to compact rendering.
source/samples_test/src/tests/getting_started.rs Update expected circuit-length snapshot due to compact rendering.
source/samples_test/src/tests/algorithms.rs Update multiple expected circuit-length snapshots due to compact rendering.
source/qdk_package/tests/test_qsharp.py Update expected ASCII circuit strings for compact classical control rendering.
source/qdk_package/tests/test_qasm.py Update expected ASCII circuit strings for compact classical control rendering.
source/npm/qsharp/ux/qsharp-circuit.css Add styling for inverted (anti-) classical control dots.
source/npm/qsharp/ux/circuit-vis/utils.ts Include classicalControls registers in register collection for sizing/renumbering.
source/npm/qsharp/ux/circuit-vis/renderer/process.ts Map classicalControls into render data and ensure they’re included in classical-control register tracking.
source/npm/qsharp/ux/circuit-vis/renderer/gateRenderData.ts Add render-data shape for compact classical controls on a gate.
source/npm/qsharp/ux/circuit-vis/renderer/formatters/gateFormatter.ts Render compact classical control connectors/dots alongside gates.
source/npm/qsharp/ux/circuit-vis/actions/circuit-actions/classicalRefs.ts Treat classicalControls as classical consumers for cascade/move/delete logic.
source/npm/qsharp/test/circuits-cases/if-else.qs.snapshot.html Update SVG snapshot to reflect compact classical control rendering.
source/npm/qsharp/test/circuit-editor/utils.test.mjs Add coverage ensuring compact classical controls affect wire extent calculations.
source/npm/qsharp/test/circuit-editor/process.test.mjs Add coverage ensuring processOperations maps compact classical controls into render data.
source/npm/qsharp/test/circuit-editor/gateFormatter.test.mjs Add coverage ensuring compact classical controls render connectors and (anti-)dots.
source/npm/qsharp/test/circuit-editor/circuit-actions/measurementCascade.test.mjs Extend cascade tests to include compact classical control consumers.
source/npm/qsharp/src/data-structures/circuit.ts Add ClassicalControl type + runtime validation and wire into Unitary.
source/compiler/qsc/src/interpret/circuit_classical_ctl_tests.rs Update expected interpreter circuit output to reflect compact classical controls.
source/compiler/qsc_circuit/src/rir_to_circuit/tests/logical_stack_trace.rs Update/add logical trace expectations for compact classical control behavior (+ negative cases).
source/compiler/qsc_circuit/src/rir_to_circuit.rs Emit compact classical controls for simple if branches when safe (single-qubit, uncontrolled gates).
source/compiler/qsc_circuit/src/circuit/tests.rs Add circuit layout/string rendering test for direct classical control wire range.
source/compiler/qsc_circuit/src/circuit.rs Add classical_controls to the Rust circuit model + include in display/layout and serialization.
source/compiler/qsc_circuit/src/circuit_to_qsharp/tests.rs Add test coverage for circuit-to-Q# conversion of compact classical controls.
source/compiler/qsc_circuit/src/circuit_to_qsharp.rs Generate Q# if wrappers for unitaries with compact classical controls; disable Ctl+Adj when present.
source/compiler/qsc_circuit/src/builder/tests.rs Add test ensuring classical control result-ids are converted to classical registers.
source/compiler/qsc_circuit/src/builder.rs Thread classical control inputs through gate building into circuit operations.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread source/compiler/qsc_circuit/src/rir_to_circuit/tests/logical_stack_trace.rs Outdated
Comment on lines +1256 to +1259
let classical_controls = match &op {
Operation::Unitary(u) => &u.classical_controls,
Operation::Measurement(_) | Operation::Ket(_) => &vec![],
};
/** Control registers the gate acts on. */
controls?: Register[];
/** Classical controls that determine whether the gate is applied. */
classicalControls?: ClassicalControl[];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could have a more general approach and have all controls fall under the controls field. The type of that field would be modified to be (Register | ControlValue)[] with ControlValue being the rename for ClassicalControl. That would leave open the ability to specify inverted quantum controls also.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or better yet, for the type, it would be a Control[] with

export interface Control extends Register {
  inverted?: boolean;
}

/** The qubit registers the gate measures. */
qubits: Register[];
/** The classical registers the gate writes to. */
results: Register[];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a change to the data contract for circuits, we should consider bumping the circuit version number and making sure we have a plan to auto-update old circuit files for backwards compatibility.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants