Skip to content

Dashboard Core — Circuit Diagram

Native circuit-diagram renderer — pure matplotlib, never a Qiskit QuantumCircuit. Replaces qiskit.circuit.draw(output='mpl') for exactly the gate set dense_evolution actually supports, so Composer never depends on Qiskit just to draw a picture.

circuit_diagram

Native circuit-diagram renderer -- pure matplotlib, never a Qiskit QuantumCircuit. Replaces qiskit's circuit.draw(output='mpl') for exactly the reason documented in dashboard_core/engine.py's module docstring: qiskit.circuit.QuantumCircuit.init itself segfaults (SIGSEGV) on macOS CI runners, on the simplest possible call (QuantumCircuit(3) alone, no QASM, no methods called on it) -- see tests/integration/test_interop.py:: TestQiskitInterop for the full reproduction story. There is no way to keep using Qiskit's own drawer without constructing that object, so this module draws directly from the same (name, *qubits[, param]) gate-tuple format every other dense_evolution entry point already uses.

Gate vocabulary mirrors dashboard_core/engine.py's dispatch tables exactly (_ONE_QUBIT_STATIC / _ONE_QUBIT_PARAM / _TWO_QUBIT_STATIC / _TWO_QUBIT_PARAM / _THREE_QUBIT_STATIC) -- the same gate set QASMParser can ever hand back, so nothing here can see a name it doesn't recognize.

draw_native_circuit_diagram

draw_native_circuit_diagram(
    ops, n_qubits: int, add_measure: bool = True
)

Draws a circuit diagram figure directly from dense_evolution gate tuples -- no Qiskit QuantumCircuit ever constructed. Qubit 0 is drawn at the top, increasing downward, matching Qiskit's own drawer convention so this is a drop-in replacement for dashboard_core.visuals.draw_circuit_figure's panel.

Source code in tools/dashboard_core/circuit_diagram.py
def draw_native_circuit_diagram(ops, n_qubits: int, add_measure: bool = True):
    """Draws a circuit diagram figure directly from dense_evolution gate
    tuples -- no Qiskit QuantumCircuit ever constructed. Qubit 0 is drawn
    at the top, increasing downward, matching Qiskit's own drawer
    convention so this is a drop-in replacement for
    dashboard_core.visuals.draw_circuit_figure's panel."""
    scheduled, n_columns = _schedule_columns(ops)
    n_display_columns = n_columns + (1 if add_measure else 0)

    fig_width = max(3.0, 1.1 * (n_display_columns + 1))
    fig_height = max(2.0, 0.9 * n_qubits + 0.5)
    fig, ax = plt.subplots(figsize=(fig_width, fig_height))

    def row_y(qubit):
        return n_qubits - 1 - qubit

    wire_x_end = n_display_columns + 0.7
    for q in range(n_qubits):
        y = row_y(q)
        ax.plot([0, wire_x_end], [y, y], color=_WIRE_COLOR, linewidth=1.2, zorder=1)
        ax.text(-0.35, y, f"q{q}", ha="right", va="center", fontsize=10)

    for op, col in scheduled:
        name = op[0]
        x = col + 1.0
        qubits = _op_qubits(op, name)
        rows = [row_y(q) for q in qubits]
        params = list(op[1 + len(qubits):])

        if name in _ONE_QUBIT_STATIC:
            _draw_one_qubit_box(ax, x, rows[0], _BOX_LABEL.get(name, name.upper()))
        elif name in _ONE_QUBIT_PARAM:
            _draw_one_qubit_box(ax, x, rows[0], _param_label(name, params))
        elif name == "swap":
            _draw_vertical_link(ax, x, rows[0], rows[1])
            _draw_swap_x(ax, x, rows[0])
            _draw_swap_x(ax, x, rows[1])
        elif name in _TWO_QUBIT_STATIC:
            # cx/cy/cz: qubits[0] is control, qubits[1] is target.
            _draw_vertical_link(ax, x, rows[0], rows[1])
            _draw_control_dot(ax, x, rows[0])
            if name == "cx":
                _draw_target_plus(ax, x, rows[1])
            else:
                _draw_one_qubit_box(ax, x, rows[1], _BOX_LABEL.get(name, name.upper()))
        elif name in _TWO_QUBIT_PARAM:
            _draw_vertical_link(ax, x, rows[0], rows[1])
            _draw_control_dot(ax, x, rows[0])
            _draw_one_qubit_box(ax, x, rows[1], _param_label(name, params))
        elif name in _THREE_QUBIT_STATIC:
            # ccx (Toffoli): qubits[0], qubits[1] control, qubits[2] target.
            _draw_vertical_link(ax, x, min(rows), max(rows))
            _draw_control_dot(ax, x, rows[0])
            _draw_control_dot(ax, x, rows[1])
            _draw_target_plus(ax, x, rows[2])
        else:
            raise ValueError(f"unsupported gate for native circuit diagram: {name!r}")

    if add_measure:
        x = n_columns + 1.0
        for q in range(n_qubits):
            y = row_y(q)
            box = Rectangle((x - 0.3, y - 0.3), 0.6, 0.6, facecolor=_MEASURE_FACE, edgecolor=_BOX_EDGE, zorder=3)
            ax.add_patch(box)
            ax.text(x, y, "M", ha="center", va="center", fontsize=9, zorder=4)

    ax.set_xlim(-1.2, wire_x_end + 0.3)
    ax.set_ylim(-0.7, n_qubits - 0.3)
    ax.set_aspect("equal")
    ax.axis("off")
    fig.tight_layout()
    return fig

See also: dashboard_core.visuals, which aggregates this with state_visuals.