Repository: Qiskit/qiskit

Issue:
Add option in user config file to control `reverse_bits` in `circuit_drawer`
### What should we add?

Currently, the user config file supports `circuit_drawer` display options such as `output` (`"text"`, "`mpl`", etc.). Including the ability to change the `reverse_bits` option would allow users that prefer setting this option to `True` to not have to explicitly set this parameter every time a circuit is drawn.


Discussion:
I would like to work on this issue if you think there is value in adding this option.
If you've got a use-case for it, this definitely seems good to accept!  I'll assign the issue to you, thanks, but no pressure.
Hi @jakelishman. Thank you so much.

 I already have a working version of this, but I have a question before submitting a PR.
 
 While working on this, I realized that very recently, a new option was added to generalize how qubits are ordered when displayed by the drawer (using the `wire_order` option).
 
Reading through the [issue](https://github.com/Qiskit/qiskit-terra/issues/8063) where this was addressed, I noticed that @1ucian0 proposed to eventually eliminate the `reverse_bits` option, and just have something like `wire_order = 'reverse'` take care of it. It was decided not to include this change in the corresponding [PR](https://github.com/Qiskit/qiskit-terra/pull/8173), so I want to check if it is OK for me to move forward without making this change either. 

This wouldn't be too difficult to implement, but it is definitely much larger in scope than what I am proposing because removing `reverse_bits` from the drawer would be a breaking change.
We're fine to move ahead just adding a `reverse_bits` option to the configuration - even if the exact named option in the function were to go away in the future, it has an easy mapping onto the `wire_order` form, so the configuration-file option would still make sense.
Hi @jakelishman. I am sorry, this is my first time contributing to `qiskit-terra` and I am facing a linting issue. Where's a good place to ask about this? 

I did some searching, I see this problem is known: [#8052](https://github.com/Qiskit/qiskit-terra/issues/8052) but I don't know what I need to do to resolve it.

Thanks!
For now, you can just delete the lines starting `jax` and `jaxlib` from your `requirements-dev.txt` file (but don't commit those changes), then re-run `tox -e lint`.  Alternatively, you could bypass `tox` by doing `pip install black astroid==2.5.6 pylint==2.8.3` in your development environment, and then running the linters as:
```
black qiskit test tools examples setup.py
pylint -rn qiskit test tools
```

In general, I've got an open PR #8967 to split up the actual development requirements from our additional optional things, which will help a fair amount here.
I'm actually surprised there is still an issue with arm64 macOS. Looking at the binaries included in jaxlib there is an arm64 wheel available in the latest release for py3.8-py3.11 https://pypi.org/project/jaxlib/#files
Hi @jakelishman thank you so much, that worked.

I submitted PR #9186 that takes care of this, but I realized I have some issues with authorship of the first commits. I closed the PR because fixing it probably requires rewriting commit history, so I rather fix this by resubmitting the commits with the right user on a clean branch.

Resolving patch:
diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py
--- a/qiskit/circuit/quantumcircuit.py
+++ b/qiskit/circuit/quantumcircuit.py
@@ -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,
@@ -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
diff --git a/qiskit/user_config.py b/qiskit/user_config.py
--- a/qiskit/user_config.py
+++ b/qiskit/user_config.py
@@ -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
@@ -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
@@ -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",
diff --git a/qiskit/visualization/circuit/circuit_visualization.py b/qiskit/visualization/circuit/circuit_visualization.py
--- a/qiskit/visualization/circuit/circuit_visualization.py
+++ b/qiskit/visualization/circuit/circuit_visualization.py
@@ -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,
@@ -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
@@ -182,6 +184,7 @@ 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":
@@ -189,9 +192,14 @@ def circuit_drawer(
                 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."


Test patch:
diff --git a/test/python/test_user_config.py b/test/python/test_user_config.py
--- a/test/python/test_user_config.py
+++ b/test/python/test_user_config.py
@@ -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]
@@ -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
@@ -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,
@@ -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)
@@ -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,
diff --git a/test/python/visualization/test_circuit_text_drawer.py b/test/python/visualization/test_circuit_text_drawer.py
--- a/test/python/visualization/test_circuit_text_drawer.py
+++ b/test/python/visualization/test_circuit_text_drawer.py
@@ -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
 
@@ -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
@@ -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(