Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to user config to control reverse_bits in circuit drawer #9211

Merged
merged 15 commits into from
Jan 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1752,7 +1752,7 @@ def draw(
style: Optional[Union[dict, str]] = None,
interactive: bool = False,
plot_barriers: bool = True,
reverse_bits: bool = False,
reverse_bits: bool = None,
justify: Optional[str] = None,
vertical_compression: Optional[str] = "medium",
idle_wires: bool = True,
Expand Down Expand Up @@ -1813,7 +1813,9 @@ def draw(
`latex_source` output type this has no effect and will be silently
ignored. Defaults to False.
reverse_bits (bool): when set to True, reverse the bit order inside
registers for the output visualization. Defaults to False.
registers for the output visualization. Defaults to False unless the
user config file (usually ``~/.qiskit/settings.conf``) has an
alternative value set. For example, ``circuit_reverse_bits = True``.
plot_barriers (bool): enable/disable drawing barriers in the output
circuit. Defaults to True.
justify (string): options are ``left``, ``right`` or ``none``. If
Expand Down
14 changes: 14 additions & 0 deletions qiskit/user_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class UserConfig:
circuit_drawer = mpl
circuit_mpl_style = default
circuit_mpl_style_path = ~/.qiskit:<default location>
circuit_reverse_bits = True
transpile_optimization_level = 1
parallel = False
num_processes = 4
Expand Down Expand Up @@ -117,6 +118,18 @@ def read_config_file(self):
)
self.settings["circuit_mpl_style_path"] = cpath_list

# Parse circuit_reverse_bits
try:
circuit_reverse_bits = self.config_parser.getboolean(
"default", "circuit_reverse_bits", fallback=None
)
except ValueError as err:
raise exceptions.QiskitUserConfigError(
f"Value assigned to circuit_reverse_bits is not valid. {str(err)}"
)
if circuit_reverse_bits is not None:
self.settings["circuit_reverse_bits"] = circuit_reverse_bits

# Parse transpile_optimization_level
transpile_optimization_level = self.config_parser.getint(
"default", "transpile_optimization_level", fallback=-1
Expand Down Expand Up @@ -177,6 +190,7 @@ def set_config(key, value, section=None, file_path=None):
"circuit_drawer",
"circuit_mpl_style",
"circuit_mpl_style_path",
"circuit_reverse_bits",
"transpile_optimization_level",
"parallel",
"num_processes",
Expand Down
12 changes: 10 additions & 2 deletions qiskit/visualization/circuit/circuit_visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def circuit_drawer(
output=None,
interactive=False,
plot_barriers=True,
reverse_bits=False,
reverse_bits=None,
justify=None,
vertical_compression="medium",
idle_wires=True,
Expand Down Expand Up @@ -111,7 +111,9 @@ def circuit_drawer(
`latex_source` output type this has no effect and will be silently
ignored. Defaults to False.
reverse_bits (bool): when set to True, reverse the bit order inside
registers for the output visualization. Defaults to False.
registers for the output visualization. Defaults to False unless the
user config file (usually ``~/.qiskit/settings.conf``) has an
alternative value set. For example, ``circuit_reverse_bits = True``.
plot_barriers (bool): enable/disable drawing barriers in the output
circuit. Defaults to True.
justify (string): options are ``left``, ``right`` or ``none``. If
Expand Down Expand Up @@ -182,16 +184,22 @@ def circuit_drawer(
config = user_config.get_config()
# Get default from config file else use text
default_output = "text"
default_reverse_bits = False
if config:
default_output = config.get("circuit_drawer", "text")
if default_output == "auto":
if _optionals.HAS_MATPLOTLIB:
default_output = "mpl"
else:
default_output = "text"
if wire_order is None:
default_reverse_bits = config.get("circuit_reverse_bits", False)
if output is None:
output = default_output

if reverse_bits is None:
reverse_bits = default_reverse_bits

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."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
features:
- |
The user configuration file has a new option ``circuit_reverse_bits``, which takes a Boolean
value. This allows users to set their preferred default behavior of the ``reverse_bits`` 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_reverse_bits = True

will change the default to display the bits in reverse order.
29 changes: 29 additions & 0 deletions test/python/test_user_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,31 @@ def test_circuit_drawer_valid(self):
config.read_config_file()
self.assertEqual({"circuit_drawer": "latex"}, config.settings)

def test_invalid_circuit_reverse_bits(self):
test_config = """
[default]
circuit_reverse_bits = 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_reverse_bits_valid(self):
test_config = """
[default]
circuit_reverse_bits = false
"""
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_reverse_bits": False}, config.settings)

def test_optimization_level_valid(self):
test_config = """
[default]
Expand Down Expand Up @@ -126,6 +151,7 @@ def test_all_options_valid(self):
circuit_drawer = latex
circuit_mpl_style = default
circuit_mpl_style_path = ~:~/.qiskit
circuit_reverse_bits = false
transpile_optimization_level = 3
suppress_packaging_warnings = true
parallel = false
Expand All @@ -143,6 +169,7 @@ def test_all_options_valid(self):
"circuit_drawer": "latex",
"circuit_mpl_style": "default",
"circuit_mpl_style_path": ["~", "~/.qiskit"],
"circuit_reverse_bits": False,
"transpile_optimization_level": 3,
"num_processes": 15,
"parallel_enabled": False,
Expand All @@ -156,6 +183,7 @@ def test_set_config_all_options_valid(self):
user_config.set_config("circuit_drawer", "latex", file_path=self.file_path)
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("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)
Expand All @@ -169,6 +197,7 @@ def test_set_config_all_options_valid(self):
"circuit_drawer": "latex",
"circuit_mpl_style": "default",
"circuit_mpl_style_path": ["~", "~/.qiskit"],
"circuit_reverse_bits": False,
"transpile_optimization_level": 3,
"num_processes": 15,
"parallel_enabled": False,
Expand Down
52 changes: 51 additions & 1 deletion test/python/visualization/test_circuit_text_drawer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@

""" `_text_circuit_drawer` "draws" a circuit in "ascii art" """

import pathlib
import os
import unittest
import tempfile
import unittest.mock
from codecs import encode
from math import pi

Expand All @@ -25,6 +27,7 @@
from qiskit.quantum_info.random import random_unitary
from qiskit.test import QiskitTestCase
from qiskit.transpiler.layout import Layout, TranspileLayout
from qiskit.visualization import circuit_drawer
from qiskit.visualization.circuit import text as elements
from qiskit.visualization.circuit.circuit_visualization import _text_circuit_drawer
from qiskit.extensions import UnitaryGate, HamiltonianGate
Expand Down Expand Up @@ -382,6 +385,53 @@ def test_text_swap_reverse_bits(self):
circuit.swap(qr1, qr2)
self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected)

def test_text_reverse_bits_read_from_config(self):
"""Swap drawing with reverse_bits set in the configuration file."""
expected_forward = "\n".join(
[
" ",
"q1_0: ─X────",
" │ ",
"q1_1: ─┼──X─",
" │ │ ",
"q2_0: ─X──┼─",
" │ ",
"q2_1: ────X─",
" ",
]
)
expected_reverse = "\n".join(
[
" ",
"q2_1: ────X─",
" │ ",
"q2_0: ─X──┼─",
" │ │ ",
"q1_1: ─┼──X─",
" │ ",
"q1_0: ─X────",
" ",
]
)
qr1 = QuantumRegister(2, "q1")
qr2 = QuantumRegister(2, "q2")
circuit = QuantumCircuit(qr1, qr2)
circuit.swap(qr1, qr2)

self.assertEqual(str(circuit_drawer(circuit, output="text")), expected_forward)

config_content = """
[default]
circuit_reverse_bits = true
"""
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_reverse = str(circuit_drawer(circuit, output="text"))
self.assertEqual(test_reverse, expected_reverse)

def test_text_cswap(self):
"""CSwap drawing."""
expected = "\n".join(
Expand Down