Repository: Qiskit/qiskit

Issue:
[mpl and latex circuit drawer] The box needs to extend over the classical registers as well when an instruction has classical parameters
_Originally posted by @ajavadia in https://github.com/Qiskit/qiskit-terra/issues/2973#issuecomment-521635150_

> No mpl and latex also need to be fixed. The box needs to extend over the classical registers as well. But at least they draw something. I would say text is priority since that's the default drawer.




Discussion:
I think the part of this issue dealing with the latex drawer is the same as that of #3202 . Or am I missing something?

Resolving patch:
diff --git a/qiskit/visualization/latex.py b/qiskit/visualization/latex.py
--- a/qiskit/visualization/latex.py
+++ b/qiskit/visualization/latex.py
@@ -133,13 +133,21 @@ def __init__(
             if bit not in self.bit_locations:
                 self.bit_locations[bit] = {"register": None, "index": index}
 
+        self.cregbundle = cregbundle
+        # If there is any custom instruction that uses clasiscal bits
+        # then cregbundle is forced to be False.
+        for layer in self.ops:
+            for op in layer:
+                if op.name not in {"measure"} and op.cargs:
+                    self.cregbundle = False
+
         self.cregs_bits = [self.bit_locations[bit]["register"] for bit in clbits]
         self.img_regs = {bit: ind for ind, bit in enumerate(self.ordered_bits)}
-        if cregbundle:
+
+        if self.cregbundle:
             self.img_width = len(qubits) + len(self.cregs)
         else:
             self.img_width = len(self.img_regs)
-        self.cregbundle = cregbundle
         self.global_phase = global_phase
 
         self._style = DefaultStyle().style
@@ -463,30 +471,39 @@ def _build_latex_array(self):
                     gate_text = self._add_params_to_gate_text(op, gate_text)
                     gate_text = generate_latex_label(gate_text)
                     wire_list = [self.img_regs[qarg] for qarg in op.qargs]
+                    if op.cargs:
+                        cwire_list = [self.img_regs[carg] for carg in op.cargs]
+                    else:
+                        cwire_list = []
 
                     if op.op.condition:
                         self._add_condition(op, wire_list, column)
 
-                    if len(wire_list) == 1:
+                    if len(wire_list) == 1 and not op.cargs:
                         self._latex[wire_list[0]][column] = "\\gate{%s}" % gate_text
 
                     elif isinstance(op.op, ControlledGate):
                         num_cols_op = self._build_ctrl_gate(op, gate_text, wire_list, column)
                     else:
-                        num_cols_op = self._build_multi_gate(op, gate_text, wire_list, column)
+                        num_cols_op = self._build_multi_gate(
+                            op, gate_text, wire_list, cwire_list, column
+                        )
 
                 num_cols_layer = max(num_cols_layer, num_cols_op)
 
             column += num_cols_layer
 
-    def _build_multi_gate(self, op, gate_text, wire_list, col):
+    def _build_multi_gate(self, op, gate_text, wire_list, cwire_list, col):
         """Add a multiple wire gate to the _latex list"""
+        cwire_start = len(self.qubit_list)
         num_cols_op = 1
         if isinstance(op.op, (SwapGate, RZZGate)):
             num_cols_op = self._build_symmetric_gate(op, gate_text, wire_list, col)
         else:
             wire_min = min(wire_list)
             wire_max = max(wire_list)
+            if cwire_list and not self.cregbundle:
+                wire_max = max(cwire_list)
             wire_ind = wire_list.index(wire_min)
             self._latex[wire_min][col] = (
                 "\\multigate{%s}{%s}_" % (wire_max - wire_min, gate_text)
@@ -494,15 +511,20 @@ def _build_multi_gate(self, op, gate_text, wire_list, col):
                 + "{%s}" % wire_ind
             )
             for wire in range(wire_min + 1, wire_max + 1):
-                if wire in wire_list:
-                    wire_ind = wire_list.index(wire)
+                if wire < cwire_start:
+                    ghost_box = "\\ghost{%s}" % gate_text
+                    if wire in wire_list:
+                        wire_ind = wire_list.index(wire)
+                else:
+                    ghost_box = "\\cghost{%s}" % gate_text
+                    if wire in cwire_list:
+                        wire_ind = cwire_list.index(wire)
+                if wire in wire_list + cwire_list:
                     self._latex[wire][col] = (
-                        "\\ghost{%s}_" % gate_text
-                        + "<" * (len(str(wire_ind)) + 2)
-                        + "{%s}" % wire_ind
+                        ghost_box + "_" + "<" * (len(str(wire_ind)) + 2) + "{%s}" % wire_ind
                     )
                 else:
-                    self._latex[wire][col] = "\\ghost{%s}" % gate_text
+                    self._latex[wire][col] = ghost_box
         return num_cols_op
 
     def _build_ctrl_gate(self, op, gate_text, wire_list, col):
@@ -543,7 +565,7 @@ def _build_ctrl_gate(self, op, gate_text, wire_list, col):
                 else:
                     self._add_controls(wire_list, ctrlqargs, ctrl_state, col)
 
-                self._build_multi_gate(op, gate_text, wireqargs, col)
+                self._build_multi_gate(op, gate_text, wireqargs, [], col)
         return num_cols_op
 
     def _build_symmetric_gate(self, op, gate_text, wire_list, col):


Test patch:
diff --git a/test/python/visualization/references/test_latex_inst_with_cbits.tex b/test/python/visualization/references/test_latex_inst_with_cbits.tex
new file mode 100644
--- /dev/null
+++ b/test/python/visualization/references/test_latex_inst_with_cbits.tex
@@ -0,0 +1,30 @@
+% \documentclass[preview]{standalone}
+% If the image is too large to fit on this documentclass use
+\documentclass[draft]{beamer}
+% img_width = 8, img_depth = 3
+\usepackage[size=custom,height=12,width=10,scale=0.7]{beamerposter}
+% instead and customize the height and width (in cm) to fit.
+% Large images may run out of memory quickly.
+% To fix this use the LuaLaTeX compiler, which dynamically
+% allocates memory.
+\usepackage[braket, qm]{qcircuit}
+\usepackage{amsmath}
+\pdfmapfile{+sansmathaccent.map}
+% \usepackage[landscape]{geometry}
+% Comment out the above line if using the beamer documentclass.
+\begin{document}
+
+\begin{equation*}
+    \Qcircuit @C=1.0em @R=1.0em @!R {
+	 	\lstick{ {qr}_{0} :  } & \qw & \qw & \qw\\
+	 	\lstick{ {qr}_{1} :  } & \multigate{5}{\mathrm{instruction}}_<<<{0} & \qw & \qw\\
+	 	\lstick{ {qr}_{2} :  } & \ghost{\mathrm{instruction}}_<<<{1} & \qw & \qw\\
+	 	\lstick{ {qr}_{3} :  } & \ghost{\mathrm{instruction}} & \qw & \qw\\
+	 	\lstick{cr_{0}:} & \cghost{\mathrm{instruction}} & \cw & \cw\\
+	 	\lstick{cr_{1}:} & \cghost{\mathrm{instruction}}_<<<{1} & \cw & \cw\\
+	 	\lstick{cr_{2}:} & \cghost{\mathrm{instruction}}_<<<{0} & \cw & \cw\\
+	 	\lstick{cr_{3}:} & \cw & \cw & \cw\\
+	 }
+\end{equation*}
+
+\end{document}
\ No newline at end of file
diff --git a/test/python/visualization/test_circuit_latex.py b/test/python/visualization/test_circuit_latex.py
--- a/test/python/visualization/test_circuit_latex.py
+++ b/test/python/visualization/test_circuit_latex.py
@@ -532,6 +532,22 @@ def test_meas_condition(self):
 
         self.assertEqualToReference(filename)
 
+    def test_inst_with_cbits(self):
+        """Test custom instructions with classical bits"""
+
+        filename = self._get_resource_path("test_latex_inst_with_cbits.tex")
+        qinst = QuantumRegister(2, "q")
+        cinst = ClassicalRegister(2, "c")
+        inst = QuantumCircuit(qinst, cinst, name="instruction").to_instruction()
+
+        qr = QuantumRegister(4, "qr")
+        cr = ClassicalRegister(4, "cr")
+        circuit = QuantumCircuit(qr, cr)
+        circuit.append(inst, [qr[1], qr[2]], [cr[2], cr[1]])
+        circuit_drawer(circuit, filename=filename, output="latex_source")
+
+        self.assertEqualToReference(filename)
+
 
 if __name__ == "__main__":
     unittest.main(verbosity=2)