Repository: Qiskit/qiskit

Issue:
Reverse bit order in latex_circuit_drawer with conditional
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### What is the current behavior?

I found a bug in `latex_circuit_drawer` when conditional gate is drawn with `reversebits` option.
Index of registers is not correctly handled in `latex_circuit_drawer` when bit order is reversed.

### Steps to reproduce the problem

```
q1 = QuantumRegister(3, 'q1')
c1 = ClassicalRegister(3, 'c1')
qc = QuantumCircuit(q1, c1)
qc.x(q1[0]).c_if(c1, 2)
circuit_drawer(qc, style={"reversebits": True})
```
The results:
```
...
~/testbench/qiskit/tools/visualization/_circuit_visualization.py in _get_image_depth(self, aliases)
    611                         for i in range(pos_1, pos_2 + self.cregs[if_reg]):
    612                             if is_occupied[i] is False:
--> 613                                 is_occupied[i] = True
    614                             else:
    615                                 columns += 1

IndexError: list index out of range
```



Discussion:
I've done a lot of digging on this (it took much more than I was expecting) it looks like the fundamental issue here is that the mask generated by the json unroller for conditionals doesn't know things are reversed So the mask it writes assumes the regular order. So when we go to use that mask in the latex drawer (where the traceback here is and around L900) for figuring out the cbits to mark for writing out the latex it gets mixed up and out of order.

As for fixing it I'm thinking we'll either have to transform the bitmask generated by the json unroller to match the reversed bit order in the latex generator after it's called, or add an option to the json unroller to reverse the bitmasks when they're generated.

Resolving patch:
diff --git a/qiskit/tools/visualization/_circuit_visualization.py b/qiskit/tools/visualization/_circuit_visualization.py
--- a/qiskit/tools/visualization/_circuit_visualization.py
+++ b/qiskit/tools/visualization/_circuit_visualization.py
@@ -34,8 +34,8 @@
 from qiskit._qiskiterror import QISKitError
 from qiskit._quantumcircuit import QuantumCircuit
 from qiskit.wrapper import load_qasm_file
-
 from qiskit.dagcircuit import DAGCircuit
+from qiskit.tools.visualization._error import VisualizationError
 from qiskit.transpiler import transpile
 
 logger = logging.getLogger(__name__)
@@ -440,7 +440,11 @@ def __init__(self, circuit, scale, style=None):
             for item in self.header['clbit_labels']:
                 self.cregs[item[0]] = item[1]
         self.clbit_list = []
-        for cr in self.cregs:
+        cregs = self.cregs
+        if self._style.reverse:
+            self.orig_cregs = self.cregs
+            cregs = reversed(self.cregs)
+        for cr in cregs:
             for i in range(self.cregs[cr]):
                 self.clbit_list.append((cr, i))
         self.ordered_regs = [(item[0], item[1]) for
@@ -604,6 +608,8 @@ def _get_image_depth(self, aliases=None):
                                            qarglist[0][1])]
                     if 'conditional' in op:
                         mask = int(op['conditional']['mask'], 16)
+                        if self._style.reverse:
+                            mask = self._convert_mask(mask)
                         cl_reg = self.clbit_list[self._ffs(mask)]
                         if_reg = cl_reg[0]
                         pos_2 = self.img_regs[cl_reg]
@@ -629,6 +635,8 @@ def _get_image_depth(self, aliases=None):
 
                     if 'conditional' in op:
                         mask = int(op['conditional']['mask'], 16)
+                        if self._style.reverse:
+                            mask = self._convert_mask(mask)
                         cl_reg = self.clbit_list[self._ffs(mask)]
                         if_reg = cl_reg[0]
                         pos_3 = self.img_regs[(if_reg, 0)]
@@ -685,6 +693,8 @@ def _get_image_depth(self, aliases=None):
 
                     if 'conditional' in op:
                         mask = int(op['conditional']['mask'], 16)
+                        if self._style.reverse:
+                            mask = self._convert_mask(mask)
                         cl_reg = self.clbit_list[self._ffs(mask)]
                         if_reg = cl_reg[0]
                         pos_4 = self.img_regs[(if_reg, 0)]
@@ -842,6 +852,25 @@ def total_2_register_index(self, index, registers):
                 count += size
         raise ValueError('qubit index lies outside range of qubit registers')
 
+    def _convert_mask(self, mask):
+        orig_clbit_list = []
+        for cr in self.orig_cregs:
+            for i in range(self.orig_cregs[cr]):
+                orig_clbit_list.append((cr, i))
+        bit_list = [(mask >> bit) & 1 for bit in range(
+            len(orig_clbit_list) - 1, -1, -1)]
+        converted_mask_list = [None] * len(bit_list)
+        converted_mask = 0
+        for pos, bit in enumerate(reversed(bit_list)):
+            new_pos = self.clbit_list.index(orig_clbit_list[pos])
+            converted_mask_list[new_pos] = bit
+        if None in converted_mask_list:
+            raise VisualizationError('Reverse mask creation failed')
+        converted_mask_list = list(reversed(converted_mask_list))
+        for bit in converted_mask_list:
+            converted_mask = (converted_mask << 1) | bit
+        return converted_mask
+
     def _build_latex_array(self, aliases=None):
         """Returns an array of strings containing \\LaTeX for this circuit.
 
@@ -866,6 +895,8 @@ def _build_latex_array(self, aliases=None):
         for _, op in enumerate(self.circuit['instructions']):
             if 'conditional' in op:
                 mask = int(op['conditional']['mask'], 16)
+                if self._style.reverse:
+                    mask = self._convert_mask(mask)
                 cl_reg = self.clbit_list[self._ffs(mask)]
                 if_reg = cl_reg[0]
                 pos_2 = self.img_regs[cl_reg]
@@ -881,6 +912,8 @@ def _build_latex_array(self, aliases=None):
                                            qarglist[0][1])]
                     if 'conditional' in op:
                         mask = int(op['conditional']['mask'], 16)
+                        if self._style.reverse:
+                            mask = self._convert_mask(mask)
                         cl_reg = self.clbit_list[self._ffs(mask)]
                         if_reg = cl_reg[0]
                         pos_2 = self.img_regs[cl_reg]
@@ -1252,7 +1285,10 @@ def _ffs(self, mask):
         Returns:
             int: index of the first set bit.
         """
-        return (mask & (-mask)).bit_length() - 1
+        origin = (mask & (-mask)).bit_length()
+        if self._style.reverse:
+            return origin + 1
+        return origin - 1
 
 
 def _get_register_specs(bit_labels):


Test patch: