diff --git a/qiskit/visualization/circuit/circuit_visualization.py b/qiskit/visualization/circuit/circuit_visualization.py index ebdffca002a2..be5d2c423d7b 100644 --- a/qiskit/visualization/circuit/circuit_visualization.py +++ b/qiskit/visualization/circuit/circuit_visualization.py @@ -101,7 +101,7 @@ def circuit_drawer( information on the contents. output (str): select the output method to use for drawing the circuit. Valid choices are ``text``, ``mpl``, ``latex``, ``latex_source``. - By default the `text` drawer is used unless the user config file + By default, the `text` drawer is used unless the user config file (usually ``~/.qiskit/settings.conf``) has an alternative backend set as the default. For example, ``circuit_drawer = latex``. If the output kwarg is set, that backend will always be used over the default in @@ -205,27 +205,38 @@ def circuit_drawer( raise VisualizationError( "The wire_order option cannot be set when the reverse_bits option is True." ) - if wire_order is not None and len(wire_order) != circuit.num_qubits + circuit.num_clbits: - raise VisualizationError( - "The wire_order list must be the same " - "length as the sum of the number of qubits and clbits in the circuit." - ) - if wire_order is not None and set(wire_order) != set( - range(circuit.num_qubits + circuit.num_clbits) - ): - raise VisualizationError( - "There must be one and only one entry in the " - "wire_order list for the index of each qubit and each clbit in the circuit." - ) - if circuit.clbits and (reverse_bits or wire_order is not None): + complete_wire_order = wire_order + if wire_order is not None: + wire_order_len = len(wire_order) + total_wire_len = circuit.num_qubits + circuit.num_clbits + if wire_order_len not in [circuit.num_qubits, total_wire_len]: + raise VisualizationError( + f"The wire_order list (length {wire_order_len}) should as long as " + f"the number of qubits ({circuit.num_qubits}) or the " + f"total numbers of qubits and classical bits {total_wire_len}." + ) + + if len(set(wire_order)) != len(wire_order): + raise VisualizationError("The wire_order list should not have repeated elements.") + + if wire_order_len == circuit.num_qubits: + complete_wire_order = wire_order + list(range(circuit.num_qubits, total_wire_len)) + + if ( + circuit.clbits + and (reverse_bits or wire_order is not None) + and not set(wire_order or []).issubset(set(range(circuit.num_qubits))) + ): if cregbundle: warn( - "cregbundle set to False since either reverse_bits or wire_order has been set.", + "cregbundle set to False since either reverse_bits or wire_order " + "(over classical bit) has been set.", RuntimeWarning, 2, ) cregbundle = False + if output == "text": return _text_circuit_drawer( circuit, @@ -239,7 +250,7 @@ def circuit_drawer( fold=fold, initial_state=initial_state, cregbundle=cregbundle, - wire_order=wire_order, + wire_order=complete_wire_order, ) elif output == "latex": image = _latex_circuit_drawer( @@ -254,7 +265,7 @@ def circuit_drawer( with_layout=with_layout, initial_state=initial_state, cregbundle=cregbundle, - wire_order=wire_order, + wire_order=complete_wire_order, ) elif output == "latex_source": return _generate_latex_source( @@ -269,7 +280,7 @@ def circuit_drawer( with_layout=with_layout, initial_state=initial_state, cregbundle=cregbundle, - wire_order=wire_order, + wire_order=complete_wire_order, ) elif output == "mpl": image = _matplotlib_circuit_drawer( @@ -286,7 +297,7 @@ def circuit_drawer( ax=ax, initial_state=initial_state, cregbundle=cregbundle, - wire_order=wire_order, + wire_order=complete_wire_order, ) else: raise VisualizationError( @@ -351,7 +362,7 @@ def _text_circuit_drawer( TextDrawing: An instance that, when printed, draws the circuit in ascii art. Raises: - VisualizationError: When the filename extenstion is not .txt. + VisualizationError: When the filename extension is not .txt. """ qubits, clbits, nodes = _utils._get_layered_instructions( circuit, diff --git a/releasenotes/notes/relax_wire_order_restrictions-ffc0cfeacd7b8d4b.yaml b/releasenotes/notes/relax_wire_order_restrictions-ffc0cfeacd7b8d4b.yaml new file mode 100644 index 000000000000..c8fd6337fdb7 --- /dev/null +++ b/releasenotes/notes/relax_wire_order_restrictions-ffc0cfeacd7b8d4b.yaml @@ -0,0 +1,34 @@ +--- +features: + - | + Some restrictions when using ``wire_order`` in the circuit drawers had been relaxed. + Now, ``wire_order`` can list just qubits and, in that case, it can be used + with ``cregbundle=True``, since it will not affect the classical bits. + + .. code-block:: + + from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister + + qr = QuantumRegister(4, "q") + cr = ClassicalRegister(4, "c") + cr2 = ClassicalRegister(2, "ca") + circuit = QuantumCircuit(qr, cr, cr2) + circuit.h(0) + circuit.h(3) + circuit.x(1) + circuit.x(3).c_if(cr, 10) + circuit.draw('text', wire_order=[2, 3, 0, 1], cregbundle=True) + + .. parsed-literal:: + + q_2: ──────────── + ┌───┐ ┌───┐ + q_3: ┤ H ├─┤ X ├─ + ├───┤ └─╥─┘ + q_0: ┤ H ├───╫─── + ├───┤ ║ + q_1: ┤ X ├───╫─── + └───┘┌──╨──┐ + c: 4/═════╡ 0xa ╞ + └─────┘ + ca: 2/════════════ diff --git a/test/python/visualization/test_circuit_drawer.py b/test/python/visualization/test_circuit_drawer.py index 8e5462eca67e..168905498abf 100644 --- a/test/python/visualization/test_circuit_drawer.py +++ b/test/python/visualization/test_circuit_drawer.py @@ -16,7 +16,7 @@ import os from unittest.mock import patch -from qiskit import QuantumCircuit +from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister from qiskit.test import QiskitTestCase from qiskit.utils import optionals from qiskit import visualization @@ -103,15 +103,77 @@ def test_latex_output_file_correct_format(self): self.assertIn(im.format.lower(), filename.split(".")[-1]) os.remove(filename) + def test_wire_order(self): + """Test wire_order + See: https://github.com/Qiskit/qiskit-terra/pull/9893""" + qr = QuantumRegister(4, "q") + cr = ClassicalRegister(4, "c") + cr2 = ClassicalRegister(2, "ca") + circuit = QuantumCircuit(qr, cr, cr2) + circuit.h(0) + circuit.h(3) + circuit.x(1) + circuit.x(3).c_if(cr, 10) + + expected = "\n".join( + [ + " ", + " q_2: ────────────", + " ┌───┐ ┌───┐ ", + " q_3: ┤ H ├─┤ X ├─", + " ├───┤ └─╥─┘ ", + " q_0: ┤ H ├───╫───", + " ├───┤ ║ ", + " q_1: ┤ X ├───╫───", + " └───┘┌──╨──┐", + " c: 4/═════╡ 0xa ╞", + " └─────┘", + "ca: 2/════════════", + " ", + ] + ) + result = visualization.circuit_drawer(circuit, wire_order=[2, 3, 0, 1]) + self.assertEqual(result.__str__(), expected) + + def test_wire_order_cregbundle(self): + """Test wire_order with cregbundle=True + See: https://github.com/Qiskit/qiskit-terra/pull/9893""" + qr = QuantumRegister(4, "q") + cr = ClassicalRegister(4, "c") + cr2 = ClassicalRegister(2, "ca") + circuit = QuantumCircuit(qr, cr, cr2) + circuit.h(0) + circuit.h(3) + circuit.x(1) + circuit.x(3).c_if(cr, 10) + + expected = "\n".join( + [ + " ", + " q_2: ────────────", + " ┌───┐ ┌───┐ ", + " q_3: ┤ H ├─┤ X ├─", + " ├───┤ └─╥─┘ ", + " q_0: ┤ H ├───╫───", + " ├───┤ ║ ", + " q_1: ┤ X ├───╫───", + " └───┘┌──╨──┐", + " c: 4/═════╡ 0xa ╞", + " └─────┘", + "ca: 2/════════════", + " ", + ] + ) + result = visualization.circuit_drawer(circuit, wire_order=[2, 3, 0, 1], cregbundle=True) + self.assertEqual(result.__str__(), expected) + def test_wire_order_raises(self): """Verify we raise if using wire order incorrectly.""" circuit = QuantumCircuit(3, 3) circuit.x(1) - with self.assertRaisesRegex(VisualizationError, "the same length as"): - visualization.circuit_drawer(circuit, wire_order=[0, 1, 2]) - with self.assertRaisesRegex(VisualizationError, "one and only one entry"): + with self.assertRaisesRegex(VisualizationError, "should not have repeated elements"): visualization.circuit_drawer(circuit, wire_order=[2, 1, 0, 3, 1, 5]) with self.assertRaisesRegex(VisualizationError, "cannot be set when the reverse_bits"):