mirror of https://github.com/Qiskit/qiskit.git
Add option to user config to control `idle_wires` in circuit drawer (#12462)
* Add option to user config to control idle_wires in circuit drawer Co-Authored-By: diemilio <diemilio@live.com> * docs * 11339 * Update qiskit/visualization/circuit/circuit_visualization.py Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com> --------- Co-authored-by: diemilio <diemilio@live.com> Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>
This commit is contained in:
parent
bc685d3002
commit
b933f6d377
|
@ -1757,14 +1757,14 @@ class QuantumCircuit:
|
|||
this can be anything that :obj:`.append` will accept.
|
||||
qubits (list[Qubit|int]): qubits of self to compose onto.
|
||||
clbits (list[Clbit|int]): clbits of self to compose onto.
|
||||
front (bool): If True, front composition will be performed. This is not possible within
|
||||
front (bool): If ``True``, front composition will be performed. This is not possible within
|
||||
control-flow builder context managers.
|
||||
inplace (bool): If True, modify the object. Otherwise return composed circuit.
|
||||
inplace (bool): If ``True``, modify the object. Otherwise, return composed circuit.
|
||||
copy (bool): If ``True`` (the default), then the input is treated as shared, and any
|
||||
contained instructions will be copied, if they might need to be mutated in the
|
||||
future. You can set this to ``False`` if the input should be considered owned by
|
||||
the base circuit, in order to avoid unnecessary copies; in this case, it is not
|
||||
valid to use ``other`` afterwards, and some instructions may have been mutated in
|
||||
valid to use ``other`` afterward, and some instructions may have been mutated in
|
||||
place.
|
||||
var_remap (Mapping): mapping to use to rewrite :class:`.expr.Var` nodes in ``other`` as
|
||||
they are inlined into ``self``. This can be used to avoid naming conflicts.
|
||||
|
@ -2068,7 +2068,7 @@ class QuantumCircuit:
|
|||
|
||||
Args:
|
||||
other (QuantumCircuit): The other circuit to tensor this circuit with.
|
||||
inplace (bool): If True, modify the object. Otherwise return composed circuit.
|
||||
inplace (bool): If ``True``, modify the object. Otherwise return composed circuit.
|
||||
|
||||
Examples:
|
||||
|
||||
|
@ -2084,7 +2084,7 @@ class QuantumCircuit:
|
|||
tensored.draw('mpl')
|
||||
|
||||
Returns:
|
||||
QuantumCircuit: The tensored circuit (returns None if inplace==True).
|
||||
QuantumCircuit: The tensored circuit (returns ``None`` if ``inplace=True``).
|
||||
"""
|
||||
num_qubits = self.num_qubits + other.num_qubits
|
||||
num_clbits = self.num_clbits + other.num_clbits
|
||||
|
@ -3126,7 +3126,7 @@ class QuantumCircuit:
|
|||
reverse_bits: bool | None = None,
|
||||
justify: str | None = None,
|
||||
vertical_compression: str | None = "medium",
|
||||
idle_wires: bool = True,
|
||||
idle_wires: bool | 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
|
||||
|
@ -3157,7 +3157,7 @@ class QuantumCircuit:
|
|||
Args:
|
||||
output: 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
|
||||
|
@ -3203,7 +3203,9 @@ class QuantumCircuit:
|
|||
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``.
|
||||
in output visualization. Default is ``True`` 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
|
||||
physical layout. Default is ``True``.
|
||||
fold: Sets pagination. It can be disabled using -1. In ``text``,
|
||||
|
@ -3292,7 +3294,7 @@ class QuantumCircuit:
|
|||
Args:
|
||||
filter_function (callable): a function to filter out some instructions.
|
||||
Should take as input a tuple of (Instruction, list(Qubit), list(Clbit)).
|
||||
By default filters out "directives", such as barrier or snapshot.
|
||||
By default, filters out "directives", such as barrier or snapshot.
|
||||
|
||||
Returns:
|
||||
int: Total number of gate operations.
|
||||
|
@ -3314,7 +3316,7 @@ class QuantumCircuit:
|
|||
filter_function: A function to decide which instructions count to increase depth.
|
||||
Should take as a single positional input a :class:`CircuitInstruction`.
|
||||
Instructions for which the function returns ``False`` are ignored in the
|
||||
computation of the circuit depth. By default filters out "directives", such as
|
||||
computation of the circuit depth. By default, filters out "directives", such as
|
||||
:class:`.Barrier`.
|
||||
|
||||
Returns:
|
||||
|
@ -3445,7 +3447,7 @@ class QuantumCircuit:
|
|||
bits = self.qubits if unitary_only else (self.qubits + self.clbits)
|
||||
bit_indices: dict[Qubit | Clbit, int] = {bit: idx for idx, bit in enumerate(bits)}
|
||||
|
||||
# Start with each qubit or cbit being its own subgraph.
|
||||
# Start with each qubit or clbit being its own subgraph.
|
||||
sub_graphs = [[bit] for bit in range(len(bit_indices))]
|
||||
|
||||
num_sub_graphs = len(sub_graphs)
|
||||
|
@ -3816,7 +3818,7 @@ class QuantumCircuit:
|
|||
inplace (bool): All measurements inplace or return new circuit.
|
||||
|
||||
Returns:
|
||||
QuantumCircuit: Returns circuit with measurements when `inplace = False`.
|
||||
QuantumCircuit: Returns circuit with measurements when ``inplace = False``.
|
||||
"""
|
||||
from qiskit.converters.circuit_to_dag import circuit_to_dag
|
||||
|
||||
|
@ -5704,7 +5706,7 @@ class QuantumCircuit:
|
|||
* Statevector or vector of complex amplitudes to initialize to.
|
||||
* Labels of basis states of the Pauli eigenstates Z, X, Y. See
|
||||
:meth:`.Statevector.from_label`. Notice the order of the labels is reversed with
|
||||
respect to the qubit index to be applied to. Example label '01' initializes the
|
||||
respect to the qubit index to be applied to. Example label ``'01'`` initializes the
|
||||
qubit zero to :math:`|1\rangle` and the qubit one to :math:`|0\rangle`.
|
||||
* An integer that is used as a bitmap indicating which qubits to initialize to
|
||||
:math:`|1\rangle`. Example: setting params to 5 would initialize qubit 0 and qubit
|
||||
|
|
|
@ -31,6 +31,7 @@ class UserConfig:
|
|||
circuit_mpl_style = default
|
||||
circuit_mpl_style_path = ~/.qiskit:<default location>
|
||||
circuit_reverse_bits = True
|
||||
circuit_idle_wires = False
|
||||
transpile_optimization_level = 1
|
||||
parallel = False
|
||||
num_processes = 4
|
||||
|
@ -130,6 +131,18 @@ class UserConfig:
|
|||
if circuit_reverse_bits is not None:
|
||||
self.settings["circuit_reverse_bits"] = circuit_reverse_bits
|
||||
|
||||
# Parse circuit_idle_wires
|
||||
try:
|
||||
circuit_idle_wires = self.config_parser.getboolean(
|
||||
"default", "circuit_idle_wires", fallback=None
|
||||
)
|
||||
except ValueError as err:
|
||||
raise exceptions.QiskitUserConfigError(
|
||||
f"Value assigned to circuit_idle_wires is not valid. {str(err)}"
|
||||
)
|
||||
if circuit_idle_wires is not None:
|
||||
self.settings["circuit_idle_wires"] = circuit_idle_wires
|
||||
|
||||
# Parse transpile_optimization_level
|
||||
transpile_optimization_level = self.config_parser.getint(
|
||||
"default", "transpile_optimization_level", fallback=-1
|
||||
|
@ -191,6 +204,7 @@ def set_config(key, value, section=None, file_path=None):
|
|||
"circuit_mpl_style",
|
||||
"circuit_mpl_style_path",
|
||||
"circuit_reverse_bits",
|
||||
"circuit_idle_wires",
|
||||
"transpile_optimization_level",
|
||||
"parallel",
|
||||
"num_processes",
|
||||
|
|
|
@ -63,7 +63,7 @@ def circuit_drawer(
|
|||
reverse_bits: bool | None = None,
|
||||
justify: str | None = None,
|
||||
vertical_compression: str | None = "medium",
|
||||
idle_wires: bool = True,
|
||||
idle_wires: bool | 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
|
||||
|
@ -115,7 +115,7 @@ def circuit_drawer(
|
|||
|
||||
output: 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
|
||||
|
@ -141,7 +141,9 @@ def circuit_drawer(
|
|||
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``.
|
||||
in output visualization. Default is ``True`` 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
|
||||
physical layout. Default is ``True``.
|
||||
fold: Sets pagination. It can be disabled using -1. In ``text``,
|
||||
|
@ -200,6 +202,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)
|
||||
if config:
|
||||
default_output = config.get("circuit_drawer", "text")
|
||||
if default_output == "auto":
|
||||
|
@ -215,6 +218,9 @@ def circuit_drawer(
|
|||
if reverse_bits is None:
|
||||
reverse_bits = default_reverse_bits
|
||||
|
||||
if idle_wires is None:
|
||||
idle_wires = default_idle_wires
|
||||
|
||||
if wire_order is not None and reverse_bits:
|
||||
raise VisualizationError(
|
||||
"The wire_order option cannot be set when the reverse_bits option is True."
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
features_visualization:
|
||||
- |
|
||||
The user configuration file has a new option ``circuit_idle_wires``, which takes a Boolean
|
||||
value. This allows users to set their preferred default behavior of the ``idle_wires`` option
|
||||
of the circuit drawers :meth:`.QuantumCircuit.draw` and :func:`.circuit_drawer`. For example,
|
||||
adding a section to ``~/.qiskit/settings.conf`` with:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
[default]
|
||||
circuit_idle_wires = false
|
||||
|
||||
will change the default to display the bits in reverse order.
|
|
@ -94,6 +94,31 @@ class TestUserConfig(QiskitTestCase):
|
|||
config.read_config_file()
|
||||
self.assertEqual({"circuit_reverse_bits": False}, config.settings)
|
||||
|
||||
def test_invalid_circuit_idle_wires(self):
|
||||
test_config = """
|
||||
[default]
|
||||
circuit_idle_wires = Neither
|
||||
"""
|
||||
self.addCleanup(os.remove, self.file_path)
|
||||
with open(self.file_path, "w") as file:
|
||||
file.write(test_config)
|
||||
file.flush()
|
||||
config = user_config.UserConfig(self.file_path)
|
||||
self.assertRaises(exceptions.QiskitUserConfigError, config.read_config_file)
|
||||
|
||||
def test_circuit_idle_wires_valid(self):
|
||||
test_config = """
|
||||
[default]
|
||||
circuit_idle_wires = true
|
||||
"""
|
||||
self.addCleanup(os.remove, self.file_path)
|
||||
with open(self.file_path, "w") as file:
|
||||
file.write(test_config)
|
||||
file.flush()
|
||||
config = user_config.UserConfig(self.file_path)
|
||||
config.read_config_file()
|
||||
self.assertEqual({"circuit_idle_wires": True}, config.settings)
|
||||
|
||||
def test_optimization_level_valid(self):
|
||||
test_config = """
|
||||
[default]
|
||||
|
@ -152,6 +177,7 @@ class TestUserConfig(QiskitTestCase):
|
|||
circuit_mpl_style = default
|
||||
circuit_mpl_style_path = ~:~/.qiskit
|
||||
circuit_reverse_bits = false
|
||||
circuit_idle_wires = true
|
||||
transpile_optimization_level = 3
|
||||
suppress_packaging_warnings = true
|
||||
parallel = false
|
||||
|
@ -170,6 +196,7 @@ class TestUserConfig(QiskitTestCase):
|
|||
"circuit_mpl_style": "default",
|
||||
"circuit_mpl_style_path": ["~", "~/.qiskit"],
|
||||
"circuit_reverse_bits": False,
|
||||
"circuit_idle_wires": True,
|
||||
"transpile_optimization_level": 3,
|
||||
"num_processes": 15,
|
||||
"parallel_enabled": False,
|
||||
|
@ -184,6 +211,7 @@ class TestUserConfig(QiskitTestCase):
|
|||
user_config.set_config("circuit_mpl_style", "default", file_path=self.file_path)
|
||||
user_config.set_config("circuit_mpl_style_path", "~:~/.qiskit", file_path=self.file_path)
|
||||
user_config.set_config("circuit_reverse_bits", "false", file_path=self.file_path)
|
||||
user_config.set_config("circuit_idle_wires", "true", file_path=self.file_path)
|
||||
user_config.set_config("transpile_optimization_level", "3", file_path=self.file_path)
|
||||
user_config.set_config("parallel", "false", file_path=self.file_path)
|
||||
user_config.set_config("num_processes", "15", file_path=self.file_path)
|
||||
|
@ -198,6 +226,7 @@ class TestUserConfig(QiskitTestCase):
|
|||
"circuit_mpl_style": "default",
|
||||
"circuit_mpl_style_path": ["~", "~/.qiskit"],
|
||||
"circuit_reverse_bits": False,
|
||||
"circuit_idle_wires": True,
|
||||
"transpile_optimization_level": 3,
|
||||
"num_processes": 15,
|
||||
"parallel_enabled": False,
|
||||
|
|
|
@ -1150,15 +1150,16 @@ class TestBasisTranslatorWithTarget(QiskitTestCase):
|
|||
self.target.add_instruction(CXGate(), cx_props)
|
||||
|
||||
def test_2q_with_non_global_1q(self):
|
||||
"""Test translation works with a 2q gate on an non-global 1q basis."""
|
||||
"""Test translation works with a 2q gate on a non-global 1q basis."""
|
||||
qc = QuantumCircuit(2)
|
||||
qc.cz(0, 1)
|
||||
|
||||
bt_pass = BasisTranslator(std_eqlib, target_basis=None, target=self.target)
|
||||
output = bt_pass(qc)
|
||||
# We need a second run of BasisTranslator to correct gates outside of
|
||||
# the target basis. This is a known isssue, see:
|
||||
# https://docs.quantum.ibm.com/api/qiskit/release-notes/0.33#known-issues
|
||||
# We need a second run of BasisTranslator to correct gates outside
|
||||
# the target basis. This is a known issue, see:
|
||||
# https://github.com/Qiskit/qiskit/issues/11339
|
||||
# TODO: remove the second bt_pass call once fixed.
|
||||
output = bt_pass(output)
|
||||
expected = QuantumCircuit(2)
|
||||
expected.rz(pi, 1)
|
||||
|
|
|
@ -495,6 +495,58 @@ class TestTextDrawerGatesInCircuit(QiskitTestCase):
|
|||
test_reverse = str(circuit_drawer(circuit, output="text"))
|
||||
self.assertEqual(test_reverse, expected_reverse)
|
||||
|
||||
def test_text_idle_wires_read_from_config(self):
|
||||
"""Swap drawing with idle_wires set in the configuration file."""
|
||||
expected_with = "\n".join(
|
||||
[
|
||||
" ┌───┐",
|
||||
"q1_0: ┤ H ├",
|
||||
" └───┘",
|
||||
"q1_1: ─────",
|
||||
" ┌───┐",
|
||||
"q2_0: ┤ H ├",
|
||||
" └───┘",
|
||||
"q2_1: ─────",
|
||||
" ",
|
||||
]
|
||||
)
|
||||
expected_without = "\n".join(
|
||||
[
|
||||
" ┌───┐",
|
||||
"q1_0: ┤ H ├",
|
||||
" ├───┤",
|
||||
"q2_0: ┤ H ├",
|
||||
" └───┘",
|
||||
]
|
||||
)
|
||||
qr1 = QuantumRegister(2, "q1")
|
||||
qr2 = QuantumRegister(2, "q2")
|
||||
circuit = QuantumCircuit(qr1, qr2)
|
||||
circuit.h(qr1[0])
|
||||
circuit.h(qr2[0])
|
||||
|
||||
self.assertEqual(
|
||||
str(
|
||||
circuit_drawer(
|
||||
circuit,
|
||||
output="text",
|
||||
)
|
||||
),
|
||||
expected_with,
|
||||
)
|
||||
|
||||
config_content = """
|
||||
[default]
|
||||
circuit_idle_wires = false
|
||||
"""
|
||||
with tempfile.TemporaryDirectory() as dir_path:
|
||||
file_path = pathlib.Path(dir_path) / "qiskit.conf"
|
||||
with open(file_path, "w") as fptr:
|
||||
fptr.write(config_content)
|
||||
with unittest.mock.patch.dict(os.environ, {"QISKIT_SETTINGS": str(file_path)}):
|
||||
test_without = str(circuit_drawer(circuit, output="text"))
|
||||
self.assertEqual(test_without, expected_without)
|
||||
|
||||
def test_text_cswap(self):
|
||||
"""CSwap drawing."""
|
||||
expected = "\n".join(
|
||||
|
@ -514,6 +566,7 @@ class TestTextDrawerGatesInCircuit(QiskitTestCase):
|
|||
circuit.cswap(qr[0], qr[1], qr[2])
|
||||
circuit.cswap(qr[1], qr[0], qr[2])
|
||||
circuit.cswap(qr[2], qr[1], qr[0])
|
||||
|
||||
self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected)
|
||||
|
||||
def test_text_cswap_reverse_bits(self):
|
||||
|
@ -4223,7 +4276,6 @@ class TestTextInstructionWithBothWires(QiskitTestCase):
|
|||
cr6 = ClassicalRegister(6, "c")
|
||||
circuit = QuantumCircuit(qr6, cr6)
|
||||
circuit.append(inst, qr6[1:5], cr6[1:3])
|
||||
|
||||
self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected)
|
||||
|
||||
def test_text_2q_1c(self):
|
||||
|
@ -5668,7 +5720,6 @@ class TestTextPhase(QiskitTestCase):
|
|||
qry = QuantumRegister(1, "qry")
|
||||
crx = ClassicalRegister(2, "crx")
|
||||
circuit = QuantumCircuit(qrx, [Qubit(), Qubit()], qry, [Clbit(), Clbit()], crx)
|
||||
|
||||
self.assertEqual(circuit.draw(output="text", cregbundle=True).single_string(), expected)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue