Skip to content

Commit

Permalink
Add option to user config to control reverse_bits in circuit drawer (#…
Browse files Browse the repository at this point in the history
…9211)

* add reverse_bits option to user_config

* check in circuit_visualization if circuit_reverse_bits option in present user configuration

* in quantumcircuit.py, change default value of reverse_bits to None

* reno

* update docstrings

* add tests to test_user_config.py, and run testing

* reno update

* Add conditional to  set `default_reverse_bits` to value in user config file only if `wire_order` option is not passed to drawer

This avoids confusion when user tries to pass `wire_order` to the drawer, but reverse_bits has been set to `True` in the user config file, which will then throw an error.

This means that the `wire_order` option passed to the drawer takes precedence over the `reverse_bits` option set in the user config file.

* Add test of loading default-override from config

* Reword release note

Co-authored-by: Jake Lishman <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 18, 2023
1 parent 5ef95e6 commit 954ce14
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 5 deletions.
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

0 comments on commit 954ce14

Please sign in to comment.