Skip to content

Commit

Permalink
The parameter idle_wires default flipped to False in all circuit …
Browse files Browse the repository at this point in the history
…drawers (#13865)

* The parameter ``idle_wires`` default flipped to False in all circuit drawers

* reno

* readjust tests

* test.python.visualization.test_circuit_text_drawer

* adjust visual tests

* bug in the tests

* adjust test

* black

* testing

* adjust tests

* revert test changes

* new reno

* fix layout-related plots

there's some bloch sphere plot failure which likely shouldn't be related to this, let's see if it persists

* top-level handling of "auto"

handle "auto" at the circuit_drawer level and subsequently use on booleans in internal logic

* Fix idle_wires None case

Co-authored-by: Jake Lishman <jake@binhbar.com>

---------

Co-authored-by: Julien Gacon <jules.gacon@googlemail.com>
Co-authored-by: Julien Gacon <gaconju@gmail.com>
Co-authored-by: Jake Lishman <jake@binhbar.com>
  • Loading branch information
4 people authored Mar 6, 2025
1 parent 519ce51 commit 2ec8f8f
Show file tree
Hide file tree
Showing 12 changed files with 166 additions and 23 deletions.
8 changes: 5 additions & 3 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -3421,7 +3421,7 @@ def draw(
reverse_bits: bool | None = None,
justify: str | None = None,
vertical_compression: str | None = "medium",
idle_wires: bool | None = None,
idle_wires: bool | str | None = None,
with_layout: bool = True,
fold: int | None = None,
# The type of ax is matplotlib.axes.Axes, but this is not a fixed dependency, so cannot be
Expand Down Expand Up @@ -3497,8 +3497,10 @@ def draw(
merges the lines generated by the `text` output so the drawing
will take less vertical room. Default is ``medium``. Only used by
the ``text`` output, will be silently ignored otherwise.
idle_wires: Include idle wires (wires with no circuit elements)
in output visualization. Default is ``True`` unless the
idle_wires: Include (or not) idle wires (wires with no circuit elements)
in output visualization. The string ``"auto"`` is also possible, in which
case idle wires are show except that the circuit has a layout attached.
Default is ``"auto"`` unless the
user config file (usually ``~/.qiskit/settings.conf``) has an
alternative value set. For example, ``circuit_idle_wires = False``.
with_layout: Include layout information, with labels on the
Expand Down
4 changes: 2 additions & 2 deletions qiskit/visualization/circuit/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,9 +450,9 @@ def _get_layered_instructions(
else:
nodes = _LayerSpooler(dag, qubits, clbits, justify, measure_map)

# Optionally remove all idle wires and instructions that are on them and
# on them only.
if not idle_wires:
# Optionally remove all idle wires and instructions that are on them and
# on them only.
for wire in dag.idle_wires(ignore=["barrier", "delay"]):
if wire in qubits:
qubits.remove(wire)
Expand Down
15 changes: 11 additions & 4 deletions qiskit/visualization/circuit/circuit_visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def circuit_drawer(
reverse_bits: bool | None = None,
justify: str | None = None,
vertical_compression: str | None = "medium",
idle_wires: bool | None = None,
idle_wires: bool | str | None = None,
with_layout: bool = True,
fold: int | None = None,
# The type of ax is matplotlib.axes.Axes, but this is not a fixed dependency, so cannot be
Expand Down Expand Up @@ -141,8 +141,10 @@ def circuit_drawer(
merges the lines generated by the `text` output so the drawing
will take less vertical room. Default is ``medium``. Only used by
the ``text`` output, will be silently ignored otherwise.
idle_wires: Include idle wires (wires with no circuit elements)
in output visualization. Default is ``True`` unless the
idle_wires: Include (or not) idle wires (wires with no circuit elements)
in output visualization. The string ``"auto"`` is also possible, in which
case idle wires are show except that the circuit has a layout attached.
Default is ``"auto"`` unless the
user config file (usually ``~/.qiskit/settings.conf``) has an
alternative value set. For example, ``circuit_idle_wires = False``.
with_layout: Include layout information, with labels on the
Expand Down Expand Up @@ -205,7 +207,7 @@ def circuit_drawer(
# Get default from config file else use text
default_output = "text"
default_reverse_bits = False
default_idle_wires = config.get("circuit_idle_wires", True)
default_idle_wires = config.get("circuit_idle_wires", "auto")
if config:
default_output = config.get("circuit_drawer", "text")
if default_output == "auto":
Expand All @@ -223,6 +225,11 @@ def circuit_drawer(

if idle_wires is None:
idle_wires = default_idle_wires
if isinstance(idle_wires, str):
if idle_wires == "auto":
idle_wires = hasattr(circuit, "_layout") and circuit._layout is None
else:
raise VisualizationError(f"Parameter idle_wires={idle_wires} unrecognized.")

if wire_order is not None and reverse_bits:
raise VisualizationError(
Expand Down
47 changes: 47 additions & 0 deletions releasenotes/notes/closes_12361-d3ea2c442a4a74a7.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
upgrade_visualization:
- |
The ``idle_wires`` parameter in all circuit drawers has been extended with a new option, ``"auto"``, which is now
the default behavior. If you still want to display wires without instructions, explicitly set ``idle_wires=True``.
When set to ``"auto"``, the behavior is as follows:
- If the circuit has a defined ``.layout`` attribute, ``idle_wires`` is automatically set to ``False`` (hiding idle wires).
- Otherwise, ``idle_wires`` remains ``True`` (showing all wires, as previous default).
Here an example. A circuit without a layout, using ``idle_wires="auto"``:
.. code-block:: text
qr_0: ────────
┌───┐┌─┐
qr_1: ┤ H ├┤M├
└───┘└╥┘
cr_0: ══════╬═
cr_1: ══════╩═
Once a layout is applied, ``idle_wires="auto"`` sets ``idle_wires`` to ``False``, hiding idle wires:
.. code-block:: text
┌───┐┌─┐
qr_1 -> 1 ┤ H ├┤M├
└───┘└╥┘
cr_1: ══════╩═
If you want to display all wires in a laid-out circuit, set ``idle_wires=True`` explicitly:
.. code-block:: text
qr_0 -> 0 ────────
┌───┐┌─┐
qr_1 -> 1 ┤ H ├┤M├
└───┘└╥┘
ancilla_0 -> 2 ──────╫─
cr_0: ══════╬═
cr_1: ══════╩═
As quantum computers scale to more qubits, even small circuits can produce large circuit representations after
transpilation. The ``"auto"`` setting helps improve readability by hiding unnecessary wires when possible.
2 changes: 1 addition & 1 deletion test/python/visualization/test_circuit_latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ def test_partial_layout(self):
seed_transpiler=0,
)

circuit_drawer(transpiled, filename=filename, output="latex_source")
circuit_drawer(transpiled, filename=filename, output="latex_source", idle_wires=True)

self.assertEqualToReference(filename)

Expand Down
109 changes: 97 additions & 12 deletions test/python/visualization/test_circuit_text_drawer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3237,7 +3237,7 @@ class TestTextWithLayout(QiskitTestCase):
"""The with_layout option"""

def test_with_no_layout(self):
"""A circuit without layout"""
"""A circuit without layout and idle_wires=auto"""
expected = "\n".join(
[
" ",
Expand All @@ -3252,10 +3252,13 @@ def test_with_no_layout(self):
qr = QuantumRegister(3, "q")
circuit = QuantumCircuit(qr)
circuit.h(qr[1])
self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected)
self.assertEqual(
str(circuit_drawer(circuit, output="text", initial_state=True, idle_wires="auto")),
expected,
)

def test_mixed_layout(self):
"""With a mixed layout."""
def test_mixed_layout_idle_wires_true(self):
"""With a mixed layout and idle_wires=True"""
expected = "\n".join(
[
" ┌───┐",
Expand All @@ -3279,11 +3282,45 @@ def test_mixed_layout(self):
circuit_with_layout = pass_(circuit, property_set={"layout": layout})

self.assertEqual(
str(circuit_drawer(circuit_with_layout, output="text", initial_state=True)), expected
str(
circuit_drawer(
circuit_with_layout, output="text", initial_state=True, idle_wires=True
)
),
expected,
)

def test_partial_layout(self):
"""With a partial layout.
def test_mixed_layout_idle_wires_auto(self):
"""With a mixed layout and idle_wires=False"""
expected = "\n".join(
[
" ┌───┐",
"v_0 -> 0 |0>┤ H ├",
" ├───┤",
"v_1 -> 3 |0>┤ H ├",
" └───┘",
]
)
qr = QuantumRegister(2, "v")
ancilla = QuantumRegister(2, "ancilla")
circuit = QuantumCircuit(qr, ancilla)
circuit.h(qr)

pass_ = ApplyLayout()
pass_.property_set["layout"] = Layout({qr[0]: 0, ancilla[1]: 1, ancilla[0]: 2, qr[1]: 3})
circuit_with_layout = pass_(circuit)

self.assertEqual(
str(
circuit_drawer(
circuit_with_layout, output="text", initial_state=True, idle_wires="auto"
)
),
expected,
)

def test_partial_layout_idle_true(self):
"""With a partial layout and idle_wires=True.
See: https://github.com/Qiskit/qiskit-terra/issues/4757"""
expected = "\n".join(
[
Expand All @@ -3309,7 +3346,38 @@ def test_partial_layout(self):
)
circuit._layout.initial_layout.add_register(qr)

self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected)
self.assertEqual(
str(circuit_drawer(circuit, output="text", initial_state=True, idle_wires=True)),
expected,
)

def test_partial_layout_idle_auto(self):
"""With a partial layout and idle_wires="auto"
See: https://github.com/Qiskit/qiskit-terra/issues/4757"""
expected = "\n".join(
[
" ┌───┐",
"v_0 -> 0 |0>┤ H ├",
" ├───┤",
"v_1 -> 3 |0>┤ H ├",
" └───┘",
]
)
qr = QuantumRegister(2, "v")
pqr = QuantumRegister(4, "physical")
circuit = QuantumCircuit(pqr)
circuit.h(0)
circuit.h(3)
circuit._layout = TranspileLayout(
Layout({0: qr[0], 1: None, 2: None, 3: qr[1]}),
{qubit: index for index, qubit in enumerate(circuit.qubits)},
)
circuit._layout.initial_layout.add_register(qr)

self.assertEqual(
str(circuit_drawer(circuit, output="text", initial_state=True, idle_wires="auto")),
expected,
)

def test_with_classical_regs(self):
"""Involving classical registers"""
Expand Down Expand Up @@ -3341,7 +3409,12 @@ def test_with_classical_regs(self):
circuit_with_layout = pass_(circuit, property_set={"layout": layout})

self.assertEqual(
str(circuit_drawer(circuit_with_layout, output="text", initial_state=True)), expected
str(
circuit_drawer(
circuit_with_layout, output="text", initial_state=True, idle_wires=True
)
),
expected,
)

def test_with_layout_but_disable(self):
Expand Down Expand Up @@ -3370,7 +3443,11 @@ def test_with_layout_but_disable(self):
circuit.measure(pqr[2], cr[0])
circuit.measure(pqr[3], cr[1])
self.assertEqual(
str(circuit_drawer(circuit, output="text", initial_state=True, with_layout=False)),
str(
circuit_drawer(
circuit, output="text", initial_state=True, with_layout=False, idle_wires=True
)
),
expected,
)

Expand Down Expand Up @@ -3448,7 +3525,10 @@ def test_after_transpile(self):
optimization_level=0,
seed_transpiler=0,
)
self.assertEqual(qc_result.draw(output="text", cregbundle=False).single_string(), expected)
self.assertEqual(
qc_result.draw(output="text", cregbundle=False, idle_wires=True).single_string(),
expected,
)


class TestTextInitialValue(QiskitTestCase):
Expand Down Expand Up @@ -4145,7 +4225,12 @@ def test_inner_wire_map_control_op(self):
self.assertEqual(
str(
circuit_drawer(
circuit, output="text", fold=78, initial_state=False, cregbundle=False
circuit,
output="text",
fold=78,
initial_state=False,
cregbundle=False,
idle_wires=True,
)
),
expected,
Expand Down
Binary file modified test/visual/mpl/circuit/references/idle_wires_barrier.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/visual/mpl/circuit/references/layout_control_flow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/visual/mpl/circuit/references/partial_layout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/visual/mpl/circuit/references/qreg_names_after_layout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1117,7 +1117,9 @@ def test_idle_wires_barrier(self):
circuit.barrier()

fname = "idle_wires_barrier.png"
self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname)
self.circuit_drawer(
circuit, output="mpl", cregbundle=False, filename=fname, idle_wires=False
)

ratio = VisualTestUtilities._save_diff(
self._image_path(fname),
Expand Down

0 comments on commit 2ec8f8f

Please sign in to comment.