Repository: Qiskit/qiskit

Issue:
Plotting a circuit with matplotlib interferes with global figure
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version**: 0.18.0.dev0+98b4a1f
- **Python version**: 3.8
- **Operating system**: Windows 10

### What is the current behavior?

Plotting a circuit diagram with matplotlib resizes the figure window


### Steps to reproduce the problem

Here is a minimal example. We create a matplotlib window with a specified size and layout and we require qiskit to draw into the specified axis `ax2`. 
```
import matplotlib.pyplot as plt
from qiskit import QuantumCircuit

Fig=plt.figure(1, figsize=(4,6))
plt.clf()
ax1=Fig.add_subplot(1,2,1)
ax2=Fig.add_subplot(1,2,2)
ax1.plot([1,2,3], [4,7,4])
print(Fig.get_size_inches())

circ = QuantumCircuit(2, name='test')
for ii in range(10):
    circ.h(1)
circ.cz(0,1)
circ.draw(ax=ax2, output='mpl')
print(Fig.get_size_inches())
```
After plotting the figure window has been resized.

### What is the expected behavior?

Plotting on a specified axis should not interface with the other axis on a figure and the figure itself.


### Suggested solutions

The problem is at this line: https://github.com/Qiskit/qiskit-terra/blob/main/qiskit/visualization/matplotlib.py#L912
There the global figure properties are updated.




Discussion:

Resolving patch:
diff --git a/qiskit/visualization/matplotlib.py b/qiskit/visualization/matplotlib.py
--- a/qiskit/visualization/matplotlib.py
+++ b/qiskit/visualization/matplotlib.py
@@ -14,7 +14,6 @@
 
 """mpl circuit visualization backend."""
 
-import collections
 import itertools
 import re
 from warnings import warn
@@ -30,79 +29,30 @@
     HAS_PYLATEX = False
 
 from qiskit.circuit import ControlledGate
+from qiskit.circuit import Measure
+from qiskit.circuit.library.standard_gates import (
+    SwapGate,
+    RZZGate,
+    U1Gate,
+    PhaseGate,
+    XGate,
+    ZGate,
+)
+from qiskit.extensions import Initialize
 from qiskit.visualization.qcstyle import load_style
 from qiskit.visualization.utils import get_gate_ctrl_text, get_param_str
-from qiskit.exceptions import MissingOptionalLibraryError
 from qiskit.circuit.tools.pi_check import pi_check
+from qiskit.exceptions import MissingOptionalLibraryError
 
 # Default gate width and height
 WID = 0.65
 HIG = 0.65
 
-BASE_SIZE = 3.01
 PORDER_GATE = 5
 PORDER_LINE = 3
 PORDER_REGLINE = 2
 PORDER_GRAY = 3
 PORDER_TEXT = 6
-PORDER_SUBP = 4
-
-
-class Anchor:
-    """Locate the anchors for the gates"""
-
-    def __init__(self, reg_num, yind, fold):
-        self.__yind = yind
-        self.__fold = fold
-        self.__reg_num = reg_num
-        self.__gate_placed = []
-        self.gate_anchor = 0
-
-    def plot_coord(self, index, gate_width, x_offset):
-        """Set the coord positions for an index"""
-        h_pos = index % self.__fold + 1
-        # check folding
-        if self.__fold > 0:
-            if h_pos + (gate_width - 1) > self.__fold:
-                index += self.__fold - (h_pos - 1)
-            x_pos = index % self.__fold + 0.5 * gate_width + 0.04
-            y_pos = self.__yind - (index // self.__fold) * (self.__reg_num + 1)
-        else:
-            x_pos = index + 0.5 * gate_width + 0.04
-            y_pos = self.__yind
-
-        # could have been updated, so need to store
-        self.gate_anchor = index
-        return x_pos + x_offset, y_pos
-
-    def is_locatable(self, index, gate_width):
-        """Determine if a gate has been placed"""
-        hold = [index + i for i in range(gate_width)]
-        for p in hold:
-            if p in self.__gate_placed:
-                return False
-        return True
-
-    def set_index(self, index, gate_width):
-        """Set the index for a gate"""
-        if self.__fold < 2:
-            _index = index
-        else:
-            h_pos = index % self.__fold + 1
-            if h_pos + (gate_width - 1) > self.__fold:
-                _index = index + self.__fold - (h_pos - 1) + 1
-            else:
-                _index = index
-        for ii in range(gate_width):
-            if _index + ii not in self.__gate_placed:
-                self.__gate_placed.append(_index + ii)
-        self.__gate_placed.sort()
-
-    def get_index(self):
-        """Getter for the index"""
-        if self.__gate_placed:
-            return self.__gate_placed[-1] + 1
-        return 0
 
 
 class MatplotlibDrawer:
@@ -137,19 +87,19 @@ def __init__(
             )
         from matplotlib import patches
 
-        self.patches_mod = patches
+        self._patches_mod = patches
         from matplotlib import pyplot as plt
 
-        self.plt_mod = plt
+        self._plt_mod = plt
         if not HAS_PYLATEX:
             raise MissingOptionalLibraryError(
                 libname="pylatexenc",
                 name="MatplotlibDrawer",
                 pip_install="pip install pylatexenc",
             )
-        self._clbit = []
-        self._qubit = []
-        self._registers(clbits, qubits)
+
+        # First load register and index info for the cregs and qregs,
+        # then add any bits which don't have registers associated with them.
         self._bit_locations = {
             bit: {"register": register, "index": index}
             for register in cregs + qregs
@@ -159,10 +109,13 @@ def __init__(
             if bit not in self._bit_locations:
                 self._bit_locations[bit] = {"register": None, "index": index}
 
-        self._qubit_dict = collections.OrderedDict()
-        self._clbit_dict = collections.OrderedDict()
+        self._qubit = qubits
+        self._clbit = clbits
+        self._qubit_dict = {}
+        self._clbit_dict = {}
         self._nodes = nodes
         self._scale = 1.0 if scale is None else scale
+
         self._style, def_font_ratio = load_style(style)
 
         # If font/subfont ratio changes from default, have to scale width calculations for
@@ -175,36 +128,40 @@ def __init__(
         self._fold = fold
         if self._fold < 2:
             self._fold = -1
+
         if ax is None:
-            self._return_fig = True
+            self._user_ax = False
             self._figure = plt.figure()
             self._figure.patch.set_facecolor(color=self._style["bg"])
             self._ax = self._figure.add_subplot(111)
         else:
-            self._return_fig = False
+            self._user_ax = True
             self._ax = ax
             self._figure = ax.get_figure()
         self._ax.axis("off")
         self._ax.set_aspect("equal")
         self._ax.tick_params(labelbottom=False, labeltop=False, labelleft=False, labelright=False)
+
         self._initial_state = initial_state
         self._cregbundle = cregbundle
-        self._set_cregbundle()
         self._global_phase = global_phase
 
-        self._n_lines = 0
-        self._xmax = 0
-        self._ymax = 0
-        self._x_offset = 0
-        self._reg_long_text = 0
-        self._style["fs"] *= self._scale
-        self._style["sfs"] *= self._scale
-        self._lwidth15 = 1.5 * self._scale
-        self._lwidth2 = 2.0 * self._scale
-        self._gate_width = {}
-
-        # these char arrays are for finding text_width when not
-        # using get_renderer method for the matplotlib backend
+        self._fs = self._style["fs"]
+        self._sfs = self._style["sfs"]
+        self._lwidth1 = 1.0
+        self._lwidth15 = 1.5
+        self._lwidth2 = 2.0
+        self._x_offset = 0.0
+
+        # _data per node with 'width', 'gate_text', 'raw_gate_text',
+        # 'ctrl_text', 'param', q_xy', 'c_xy', and 'c_indxs'
+        # and colors 'fc', 'ec', 'lc', 'sc', 'gt', and 'tc'
+        self._data = {}
+        self._layer_widths = []
+        self._q_anchors = {}
+        self._c_anchors = {}
+
+        # _char_list for finding text_width of names, labels, and params
         self._char_list = {
             " ": (0.0958, 0.0583),
             "!": (0.1208, 0.0729),
@@ -302,20 +259,108 @@ def __init__(
             "}": (0.1896, 0.1188),
         }
 
-    def _registers(self, clbit, qubit):
-        self._clbit = []
-        for r in clbit:
-            self._clbit.append(r)
-        self._qubit = []
-        for r in qubit:
-            self._qubit.append(r)
-
-    def _set_cregbundle(self):
-        """Sets the cregbundle to False if there is any instruction that
-        needs access to individual clbit."""
+    def draw(self, filename=None, verbose=False):
+        """Main entry point to 'matplotlib' ('mpl') drawer. Called from
+        ``visualization.circuit_drawer`` and from ``QuantumCircuit.draw`` through circuit_drawer.
+        """
+        # All information for the drawing is first loaded into self._data for the gates and into
+        # self._qubit_dict and self._clbit_dict for the qubits, clbits, and wires,
+        # followed by the coordinates for each gate.
+
+        # get layer widths
+        self._get_layer_widths()
+
+        # load the _qubit_dict and _clbit_dict with register info
+        n_lines = self._get_reg_names_and_numbers()
+
+        # load the coordinates for each gate and compute number of folds
+        max_anc = self._get_coords(n_lines)
+        num_folds = max(0, max_anc - 1) // self._fold if self._fold > 0 else 0
+
+        # The window size limits are computed, followed by one of the four possible ways
+        # of scaling the drawing.
+
+        # compute the window size
+        if max_anc > self._fold > 0:
+            xmax = self._fold + self._x_offset + 0.1
+            ymax = (num_folds + 1) * (n_lines + 1) - 1
+        else:
+            x_incr = 0.4 if not self._nodes else 0.9
+            xmax = max_anc + 1 + self._x_offset - x_incr
+            ymax = n_lines
+
+        xl = -self._style["margin"][0]
+        xr = xmax + self._style["margin"][1]
+        yb = -ymax - self._style["margin"][2] + 0.5
+        yt = self._style["margin"][3] + 0.5
+        self._ax.set_xlim(xl, xr)
+        self._ax.set_ylim(yb, yt)
+
+        # update figure size and, for backward compatibility,
+        # need to scale by a default value equal to (self._fs * 3.01 / 72 / 0.65)
+        base_fig_w = (xr - xl) * 0.8361111
+        base_fig_h = (yt - yb) * 0.8361111
+        scale = self._scale
+
+        # if user passes in an ax, this size takes priority over any other settings
+        if self._user_ax:
+            # from stackoverflow #19306510, get the bbox size for the ax and then reset scale
+            bbox = self._ax.get_window_extent().transformed(self._figure.dpi_scale_trans.inverted())
+            scale = bbox.width / base_fig_w / 0.8361111
+
+        # if scale not 1.0, use this scale factor
+        elif self._scale != 1.0:
+            self._figure.set_size_inches(base_fig_w * self._scale, base_fig_h * self._scale)
+
+        # if "figwidth" style param set, use this to scale
+        elif self._style["figwidth"] > 0.0:
+            # in order to get actual inches, need to scale by factor
+            adj_fig_w = self._style["figwidth"] * 1.282736
+            self._figure.set_size_inches(adj_fig_w, adj_fig_w * base_fig_h / base_fig_w)
+            scale = adj_fig_w / base_fig_w
+
+        # otherwise, display default size
+        else:
+            self._figure.set_size_inches(base_fig_w, base_fig_h)
+
+        # drawing will scale with 'set_size_inches', but fonts and linewidths do not
+        if scale != 1.0:
+            self._fs *= scale
+            self._sfs *= scale
+            self._lwidth1 = 1.0 * scale
+            self._lwidth15 = 1.5 * scale
+            self._lwidth2 = 2.0 * scale
+
+        # Once the scaling factor has been determined, the global phase, register names
+        # and numbers, wires, and gates are drawn
+        if self._global_phase:
+            self._plt_mod.text(
+                xl, yt, "Global Phase: %s" % pi_check(self._global_phase, output="mpl")
+            )
+        self._draw_regs_wires(num_folds, xmax, n_lines, max_anc)
+        self._draw_ops(verbose)
+
+        if filename:
+            self._figure.savefig(
+                filename,
+                dpi=self._style["dpi"],
+                bbox_inches="tight",
+                facecolor=self._figure.get_facecolor(),
+            )
+        if not self._user_ax:
+            from matplotlib import get_backend
+
+            if get_backend() in ["module://ipykernel.pylab.backend_inline", "nbAgg"]:
+                self._plt_mod.close(self._figure)
+            return self._figure
+
+    def _get_layer_widths(self):
+        """Compute the layer_widths for the layers"""
         for layer in self._nodes:
+            widest_box = WID
             for node in layer:
-                if node.cargs and node.op.name != "measure":
+                op = node.op
+                if self._cregbundle and node.cargs and not isinstance(op, Measure):
                     self._cregbundle = False
                     warn(
                         "Cregbundle set to False since an instruction needs to refer"
@@ -323,13 +368,245 @@ def _set_cregbundle(self):
                         RuntimeWarning,
                         2,
                     )
-                    break
+                self._data[node] = {}
+                self._data[node]["width"] = WID
+                num_ctrl_qubits = 0 if not hasattr(op, "num_ctrl_qubits") else op.num_ctrl_qubits
+                if op._directive or isinstance(op, Measure):
+                    self._data[node]["raw_gate_text"] = op.name
+                    continue
+
+                base_type = None if not hasattr(op, "base_gate") else op.base_gate
+                gate_text, ctrl_text, raw_gate_text = get_gate_ctrl_text(
+                    op, "mpl", style=self._style
+                )
+                self._data[node]["gate_text"] = gate_text
+                self._data[node]["ctrl_text"] = ctrl_text
+                self._data[node]["raw_gate_text"] = raw_gate_text
+                self._data[node]["param"] = ""
+
+                # if single qubit, no params, and no labels, layer_width is 1
+                if (
+                    (len(node.qargs) - num_ctrl_qubits) == 1
+                    and len(gate_text) < 3
+                    and (not hasattr(op, "params") or len(op.params) == 0)
+                    and ctrl_text is None
+                ):
+                    continue
+
+                if isinstance(op, SwapGate) or isinstance(base_type, SwapGate):
+                    continue
+
+                # small increments at end of the 3 _get_text_width calls are for small
+                # spacing adjustments between gates
+                ctrl_width = self._get_text_width(ctrl_text, fontsize=self._sfs) - 0.05
+
+                # get param_width, but 0 for gates with array params
+                if (
+                    hasattr(op, "params")
+                    and len(op.params) > 0
+                    and not any(isinstance(param, np.ndarray) for param in op.params)
+                ):
+                    param = get_param_str(op, "mpl", ndigits=3)
+                    if isinstance(op, Initialize):
+                        param = f"$[{param.replace('$', '')}]$"
+                    self._data[node]["param"] = param
+                    raw_param_width = self._get_text_width(param, fontsize=self._sfs, param=True)
+                    param_width = raw_param_width + 0.08
+                else:
+                    param_width = raw_param_width = 0.0
+
+                # get gate_width for sidetext symmetric gates
+                if isinstance(op, RZZGate) or isinstance(base_type, (U1Gate, PhaseGate, RZZGate)):
+                    if isinstance(base_type, PhaseGate):
+                        gate_text = "P"
+                    raw_gate_width = (
+                        self._get_text_width(gate_text + " ()", fontsize=self._sfs)
+                        + raw_param_width
+                    )
+                    gate_width = (raw_gate_width + 0.08) * 1.5
+
+                # otherwise, standard gate or multiqubit gate
+                else:
+                    raw_gate_width = self._get_text_width(gate_text, fontsize=self._fs)
+                    gate_width = raw_gate_width + 0.10
+                    # add .21 for the qubit numbers on the left of the multibit gates
+                    if len(node.qargs) - num_ctrl_qubits > 1:
+                        gate_width += 0.21
+
+                box_width = max(gate_width, ctrl_width, param_width, WID)
+                if box_width > widest_box:
+                    widest_box = box_width
+                self._data[node]["width"] = max(raw_gate_width, raw_param_width)
+
+            self._layer_widths.append(int(widest_box) + 1)
+
+    def _get_reg_names_and_numbers(self):
+        """Get all the info for drawing reg names and numbers"""
+        longest_reg_name_width = 0
+        n_lines = 0
+        initial_qbit = " |0>" if self._initial_state else ""
+        initial_cbit = " 0" if self._initial_state else ""
+
+        def _fix_double_script(reg_name):
+            words = reg_name.split(" ")
+            words = [word.replace("_", r"\_") if word.count("_") > 1 else word for word in words]
+            words = [
+                word.replace("^", r"\^{\ }") if word.count("^") > 1 else word for word in words
+            ]
+            reg_name = " ".join(words).replace(" ", "\\;")
+            return reg_name
+
+        # quantum register
+        for ii, reg in enumerate(self._qubit):
+            register = self._bit_locations[reg]["register"]
+            index = self._bit_locations[reg]["index"]
+
+            # show register name and number if more than 1 register
+            if len(self._qubit) > 1:
+                if self._layout is None:
+                    qubit_name = f"${{{register.name}}}_{{{index}}}$"
+                else:
+                    if self._layout[index]:
+                        virt_bit = self._layout[index]
+                        try:
+                            virt_reg = next(
+                                reg for reg in self._layout.get_registers() if virt_bit in reg
+                            )
+                            qubit_name = "${{{name}}}_{{{index}}} \\mapsto {{{physical}}}$".format(
+                                name=virt_reg.name,
+                                index=virt_reg[:].index(virt_bit),
+                                physical=index,
+                            )
+
+                        except StopIteration:
+                            qubit_name = "${{{name}}} \\mapsto {{{physical}}}$".format(
+                                name=virt_bit, physical=index
+                            )
+                    else:
+                        qubit_name = f"${{{index}}}$"
             else:
-                continue
-            break
+                qubit_name = f"{register.name}"
+
+            qubit_name = _fix_double_script(qubit_name) + initial_qbit
+            text_width = self._get_text_width(qubit_name, self._fs) * 1.15
+
+            if text_width > longest_reg_name_width:
+                longest_reg_name_width = text_width
+            pos = -ii
+            self._qubit_dict[ii] = {
+                "y": pos,
+                "reg_name": qubit_name,
+                "index": index,
+                "group": register,
+            }
+            n_lines += 1
+
+        # classical register
+        if self._clbit:
+            n_clbit = self._clbit.copy()
+            n_clbit.pop(0)
+            idx = 0
+            y_off = -len(self._qubit)
+            for ii, (reg, nreg) in enumerate(itertools.zip_longest(self._clbit, n_clbit)):
+                pos = y_off - idx
+                register = self._bit_locations[reg]["register"]
+                index = self._bit_locations[reg]["index"]
+
+                # if cregbundle show non-math reg name, if only 1 clbit, show math name
+                # else math name and number
+                if self._cregbundle:
+                    clbit_name = f"{register.name}"
+                else:
+                    clbit_name = f"${register.name}_{index}$"
+                clbit_name = _fix_double_script(clbit_name) + initial_cbit
+                text_width = self._get_text_width(register.name, self._fs) * 1.15
+                if text_width > longest_reg_name_width:
+                    longest_reg_name_width = text_width
+                self._clbit_dict[ii] = {
+                    "y": pos,
+                    "reg_name": clbit_name,
+                    "index": index,
+                    "group": register,
+                }
+                if self._cregbundle and not (
+                    not nreg or register != self._bit_locations[nreg]["register"]
+                ):
+                    continue
+
+                n_lines += 1
+                idx += 1
+
+        self._x_offset = -1.2 + longest_reg_name_width
+        return n_lines
+
+    def _get_coords(self, n_lines):
+        """Load all the coordinate info needed to place the gates on the drawing"""
+
+        # create the anchor arrays
+        for key, qubit in self._qubit_dict.items():
+            self._q_anchors[key] = Anchor(reg_num=n_lines, yind=qubit["y"], fold=self._fold)
+        for key, clbit in self._clbit_dict.items():
+            self._c_anchors[key] = Anchor(reg_num=n_lines, yind=clbit["y"], fold=self._fold)
+
+        # get all the necessary coordinates for placing gates on the wires
+        prev_anc = -1
+        for i, layer in enumerate(self._nodes):
+            layer_width = self._layer_widths[i]
+            this_anc = prev_anc + 1
+            for node in layer:
+                # get qubit index
+                q_indxs = []
+                for qarg in node.qargs:
+                    for index, reg in self._qubit_dict.items():
+                        if (
+                            reg["group"] == self._bit_locations[qarg]["register"]
+                            and reg["index"] == self._bit_locations[qarg]["index"]
+                        ):
+                            q_indxs.append(index)
+                            break
+
+                # get clbit index
+                c_indxs = []
+                for carg in node.cargs:
+                    for index, reg in self._clbit_dict.items():
+                        if (
+                            reg["group"] == self._bit_locations[carg]["register"]
+                            and reg["index"] == self._bit_locations[carg]["index"]
+                        ):
+                            c_indxs.append(index)
+                            break
+
+                # only add the gate to the anchors if it is going to be plotted.
+                if self._plot_barriers or not node.op._directive:
+                    for ii in q_indxs:
+                        self._q_anchors[ii].set_index(this_anc, layer_width)
+
+                # qubit coordinate
+                self._data[node]["q_xy"] = [
+                    self._q_anchors[ii].plot_coord(this_anc, layer_width, self._x_offset)
+                    for ii in q_indxs
+                ]
+                # clbit coordinate
+                self._data[node]["c_xy"] = [
+                    self._c_anchors[ii].plot_coord(this_anc, layer_width, self._x_offset)
+                    for ii in c_indxs
+                ]
+                # update index based on the value from plotting
+                this_anc = self._q_anchors[q_indxs[0]].gate_anchor
+                self._data[node]["c_indxs"] = c_indxs
+
+            # adjust the column if there have been barriers encountered, but not plotted
+            barrier_offset = 0
+            if not self._plot_barriers:
+                # only adjust if everything in the layer wasn't plotted
+                barrier_offset = -1 if all(nd.op._directive for nd in layer) else 0
+            prev_anc = this_anc + layer_width + barrier_offset - 1
+
+        anchors = [self._q_anchors[ii].get_index() for ii in self._qubit_dict]
+        return max(anchors) if anchors else 0
 
-    # This computes the width of a string in the default font
     def _get_text_width(self, text, fontsize, param=False):
+        """Compute the width of a string in the default font"""
         if not text:
             return 0.0
 
@@ -354,7 +631,7 @@ def _get_text_width(self, text, fontsize, param=False):
         if param:
             text = text.replace("-", "+")
 
-        f = 0 if fontsize == self._style["fs"] else 1
+        f = 0 if fontsize == self._fs else 1
         sum_text = 0.0
         for c in text:
             try:
@@ -366,264 +643,296 @@ def _get_text_width(self, text, fontsize, param=False):
             sum_text *= self._subfont_factor
         return sum_text
 
-    def _get_colors(self, op, gate_text):
-        base_name = None if not hasattr(op, "base_gate") else op.base_gate.name
-        color = None
-        if gate_text in self._style["dispcol"]:
-            color = self._style["dispcol"][gate_text]
-        elif op.name in self._style["dispcol"]:
-            color = self._style["dispcol"][op.name]
-        if color is not None:
-            # Backward compatibility for style dict using 'displaycolor' with
-            # gate color and no text color, so test for str first
-            if isinstance(color, str):
-                fc = color
-                gt = self._style["gt"]
-            else:
-                fc = color[0]
-                gt = color[1]
-        # Treat special case of classical gates in iqx style by making all
-        # controlled gates of x, dcx, and swap the classical gate color
-        elif self._style["name"] == "iqx" and base_name in ["x", "dcx", "swap"]:
-            color = self._style["dispcol"][base_name]
-            if isinstance(color, str):
-                fc = color
-                gt = self._style["gt"]
-            else:
-                fc = color[0]
-                gt = color[1]
-        else:
-            fc = self._style["gc"]
-            gt = self._style["gt"]
-
-        if self._style["name"] == "bw":
-            ec = self._style["ec"]
-            lc = self._style["lc"]
-        else:
-            ec = fc
-            lc = fc
-        # Subtext needs to be same color as gate text
-        sc = gt
-        return fc, ec, gt, self._style["tc"], sc, lc
-
-    def _multiqubit_gate(
-        self, node, xy, c_xy=None, fc=None, ec=None, gt=None, sc=None, text="", subtext=""
-    ):
-        xpos = min(x[0] for x in xy)
-        ypos = min(y[1] for y in xy)
-        ypos_max = max(y[1] for y in xy)
-        if c_xy:
-            cxpos = min(x[0] for x in c_xy)
-            cypos = min(y[1] for y in c_xy)
-            ypos = min(ypos, cypos)
-        fs = self._style["fs"]
-        sfs = self._style["sfs"]
-
-        wid = max(self._gate_width[node] + 0.21, WID)
+    def _draw_regs_wires(self, num_folds, xmax, n_lines, max_anc):
+        """Draw the register names and numbers, wires, and vertical lines at the ends"""
 
-        qubit_span = abs(ypos) - abs(ypos_max) + 1
-        height = HIG + (qubit_span - 1)
-        box = self.patches_mod.Rectangle(
-            xy=(xpos - 0.5 * wid, ypos - 0.5 * HIG),
-            width=wid,
-            height=height,
-            fc=fc,
-            ec=ec,
-            linewidth=self._lwidth15,
-            zorder=PORDER_GATE,
-        )
-        self._ax.add_patch(box)
-
-        # annotate inputs
-        for bit, y in enumerate([x[1] for x in xy]):
-            self._ax.text(
-                xpos + 0.07 - 0.5 * wid,
-                y,
-                str(bit),
-                ha="left",
-                va="center",
-                fontsize=fs,
-                color=gt,
-                clip_on=True,
-                zorder=PORDER_TEXT,
-            )
-        if c_xy:
-            # annotate classical inputs
-            for bit, y in enumerate([x[1] for x in c_xy]):
+        for fold_num in range(num_folds + 1):
+            # quantum registers
+            for qubit in self._qubit_dict.values():
+                qubit_name = qubit["reg_name"]
+                y = qubit["y"] - fold_num * (n_lines + 1)
                 self._ax.text(
-                    cxpos + 0.07 - 0.5 * wid,
+                    self._x_offset - 0.2,
                     y,
-                    str(bit),
-                    ha="left",
+                    qubit_name,
+                    ha="right",
                     va="center",
-                    fontsize=fs,
-                    color=gt,
+                    fontsize=1.25 * self._fs,
+                    color=self._style["tc"],
                     clip_on=True,
                     zorder=PORDER_TEXT,
                 )
-        if text:
-            if subtext:
+                # draw the qubit wire
+                self._line([self._x_offset, y], [xmax, y], zorder=PORDER_REGLINE)
+
+            # classical registers
+            this_clbit_dict = {}
+            for clbit in self._clbit_dict.values():
+                clbit_name = clbit["reg_name"]
+                y = clbit["y"] - fold_num * (n_lines + 1)
+                if y not in this_clbit_dict.keys():
+                    this_clbit_dict[y] = {"val": 1, "reg_name": clbit_name}
+                else:
+                    this_clbit_dict[y]["val"] += 1
+
+            for y, this_clbit in this_clbit_dict.items():
+                # cregbundle
+                if this_clbit["val"] > 1:
+                    self._ax.plot(
+                        [self._x_offset + 0.2, self._x_offset + 0.3],
+                        [y - 0.1, y + 0.1],
+                        color=self._style["cc"],
+                        zorder=PORDER_LINE,
+                    )
+                    self._ax.text(
+                        self._x_offset + 0.1,
+                        y + 0.1,
+                        str(this_clbit["val"]),
+                        ha="left",
+                        va="bottom",
+                        fontsize=0.8 * self._fs,
+                        color=self._style["tc"],
+                        clip_on=True,
+                        zorder=PORDER_TEXT,
+                    )
                 self._ax.text(
-                    xpos + 0.11,
-                    ypos + 0.4 * height,
-                    text,
-                    ha="center",
+                    self._x_offset - 0.2,
+                    y,
+                    this_clbit["reg_name"],
+                    ha="right",
                     va="center",
-                    fontsize=fs,
-                    color=gt,
+                    fontsize=1.25 * self._fs,
+                    color=self._style["tc"],
                     clip_on=True,
                     zorder=PORDER_TEXT,
                 )
-                self._ax.text(
-                    xpos + 0.11,
-                    ypos + 0.2 * height,
-                    subtext,
-                    ha="center",
-                    va="center",
-                    fontsize=sfs,
-                    color=sc,
-                    clip_on=True,
-                    zorder=PORDER_TEXT,
+                # draw the clbit wire
+                self._line(
+                    [self._x_offset, y],
+                    [xmax, y],
+                    lc=self._style["cc"],
+                    ls=self._style["cline"],
+                    zorder=PORDER_REGLINE,
                 )
-            else:
+
+            # lf vertical line at either end
+            feedline_r = num_folds > 0 and num_folds > fold_num
+            feedline_l = fold_num > 0
+            if feedline_l or feedline_r:
+                xpos_l = self._x_offset - 0.01
+                xpos_r = self._fold + self._x_offset + 0.1
+                ypos1 = -fold_num * (n_lines + 1)
+                ypos2 = -(fold_num + 1) * (n_lines) - fold_num + 1
+                if feedline_l:
+                    self._ax.plot(
+                        [xpos_l, xpos_l],
+                        [ypos1, ypos2],
+                        color=self._style["lc"],
+                        linewidth=self._lwidth15,
+                        zorder=PORDER_LINE,
+                    )
+                if feedline_r:
+                    self._ax.plot(
+                        [xpos_r, xpos_r],
+                        [ypos1, ypos2],
+                        color=self._style["lc"],
+                        linewidth=self._lwidth15,
+                        zorder=PORDER_LINE,
+                    )
+
+        # draw anchor index number
+        if self._style["index"]:
+            for layer_num in range(max_anc):
+                if self._fold > 0:
+                    x_coord = layer_num % self._fold + self._x_offset + 0.53
+                    y_coord = -(layer_num // self._fold) * (n_lines + 1) + 0.65
+                else:
+                    x_coord = layer_num + self._x_offset + 0.53
+                    y_coord = 0.65
                 self._ax.text(
-                    xpos + 0.11,
-                    ypos + 0.5 * (qubit_span - 1),
-                    text,
+                    x_coord,
+                    y_coord,
+                    str(layer_num + 1),
                     ha="center",
                     va="center",
-                    fontsize=fs,
-                    color=gt,
+                    fontsize=self._sfs,
+                    color=self._style["tc"],
                     clip_on=True,
                     zorder=PORDER_TEXT,
-                    wrap=True,
                 )
 
-    def _gate(self, node, xy, fc=None, ec=None, gt=None, sc=None, text="", subtext=""):
-        xpos, ypos = xy
-        fs = self._style["fs"]
-        sfs = self._style["sfs"]
+    def _draw_ops(self, verbose=False):
+        """Draw the gates in the circuit"""
+        prev_anc = -1
+        for i, layer in enumerate(self._nodes):
+            layer_width = self._layer_widths[i]
+            this_anc = prev_anc + 1
 
-        wid = max(self._gate_width[node], WID)
+            # draw the gates in this layer
+            for node in layer:
+                op = node.op
+                self._get_colors(node)
 
-        box = self.patches_mod.Rectangle(
-            xy=(xpos - 0.5 * wid, ypos - 0.5 * HIG),
-            width=wid,
-            height=HIG,
-            fc=fc,
-            ec=ec,
-            linewidth=self._lwidth15,
-            zorder=PORDER_GATE,
-        )
-        self._ax.add_patch(box)
+                if verbose:
+                    print(op)
 
-        if text:
-            if subtext:
-                self._ax.text(
-                    xpos,
-                    ypos + 0.15 * HIG,
-                    text,
-                    ha="center",
-                    va="center",
-                    fontsize=fs,
-                    color=gt,
-                    clip_on=True,
-                    zorder=PORDER_TEXT,
-                )
-                self._ax.text(
-                    xpos,
-                    ypos - 0.3 * HIG,
-                    subtext,
-                    ha="center",
-                    va="center",
-                    fontsize=sfs,
-                    color=sc,
-                    clip_on=True,
-                    zorder=PORDER_TEXT,
-                )
-            else:
-                self._ax.text(
-                    xpos,
-                    ypos,
-                    text,
-                    ha="center",
-                    va="center",
-                    fontsize=fs,
-                    color=gt,
-                    clip_on=True,
-                    zorder=PORDER_TEXT,
-                )
+                # add conditional
+                if op.condition:
+                    cond_xy = [
+                        self._c_anchors[ii].plot_coord(this_anc, layer_width, self._x_offset)
+                        for ii in self._clbit_dict
+                    ]
+                    self._condition(node, cond_xy)
 
-    def _sidetext(self, node, xy, tc=None, text=""):
-        xpos, ypos = xy
+                # draw measure
+                if isinstance(op, Measure):
+                    self._measure(node)
 
-        # 0.11 = the initial gap, add 1/2 text width to place on the right
-        text_width = self._gate_width[node]
-        xp = xpos + 0.11 + text_width / 2
+                # draw barriers, snapshots, etc.
+                elif op._directive:
+                    if self._plot_barriers:
+                        self._barrier(node)
+
+                # draw single qubit gates
+                elif len(self._data[node]["q_xy"]) == 1 and not node.cargs:
+                    self._gate(node)
+
+                # draw controlled gates
+                elif isinstance(op, ControlledGate):
+                    self._control_gate(node)
+
+                # draw multi-qubit gate as final default
+                else:
+                    self._multiqubit_gate(node)
+
+            # adjust the column if there have been barriers encountered, but not plotted
+            barrier_offset = 0
+            if not self._plot_barriers:
+                # only adjust if everything in the layer wasn't plotted
+                barrier_offset = -1 if all(nd.op._directive for nd in layer) else 0
+
+            prev_anc = this_anc + layer_width + barrier_offset - 1
+
+    def _get_colors(self, node):
+        """Get all the colors needed for drawing the circuit"""
+        op = node.op
+        base_name = None if not hasattr(op, "base_gate") else op.base_gate.name
+        color = None
+        if self._data[node]["raw_gate_text"] in self._style["dispcol"]:
+            color = self._style["dispcol"][self._data[node]["raw_gate_text"]]
+        elif op.name in self._style["dispcol"]:
+            color = self._style["dispcol"][op.name]
+        if color is not None:
+            # Backward compatibility for style dict using 'displaycolor' with
+            # gate color and no text color, so test for str first
+            if isinstance(color, str):
+                fc = color
+                gt = self._style["gt"]
+            else:
+                fc = color[0]
+                gt = color[1]
+        # Treat special case of classical gates in iqx style by making all
+        # controlled gates of x, dcx, and swap the classical gate color
+        elif self._style["name"] == "iqx" and base_name in ["x", "dcx", "swap"]:
+            color = self._style["dispcol"][base_name]
+            if isinstance(color, str):
+                fc = color
+                gt = self._style["gt"]
+            else:
+                fc = color[0]
+                gt = color[1]
+        else:
+            fc = self._style["gc"]
+            gt = self._style["gt"]
+
+        if self._style["name"] == "bw":
+            ec = self._style["ec"]
+            lc = self._style["lc"]
+        else:
+            ec = fc
+            lc = fc
+        # Subtext needs to be same color as gate text
+        sc = gt
+        self._data[node]["fc"] = fc
+        self._data[node]["ec"] = ec
+        self._data[node]["gt"] = gt
+        self._data[node]["tc"] = self._style["tc"]
+        self._data[node]["sc"] = sc
+        self._data[node]["lc"] = lc
+
+    def _condition(self, node, cond_xy):
+        """Add a conditional to a gate"""
+        mask = 0
+        qubit_b = min(self._data[node]["q_xy"], key=lambda xy: xy[1])
+        for index, cbit in enumerate(self._clbit):
+            if self._bit_locations[cbit]["register"] == node.op.condition[0]:
+                mask |= 1 << index
+        val = node.op.condition[1]
+
+        # cbit list to consider
+        fmt_c = f"{{:0{len(cond_xy)}b}}"
+        cmask = list(fmt_c.format(mask))[::-1]
+
+        # value
+        fmt_v = f"{{:0{cmask.count('1')}b}}"
+        vlist = list(fmt_v.format(val))
+        if not self._reverse_bits:
+            vlist = vlist[::-1]
+
+        # plot conditionals
+        v_ind = 0
+        xy_plot = []
+        for xy, m in zip(cond_xy, cmask):
+            if m == "1":
+                if xy not in xy_plot:
+                    if vlist[v_ind] == "1" or self._cregbundle:
+                        fc = self._style["lc"]
+                    else:
+                        fc = self._style["bg"]
+                    box = self._patches_mod.Circle(
+                        xy=xy,
+                        radius=WID * 0.15,
+                        fc=fc,
+                        ec=self._style["lc"],
+                        linewidth=self._lwidth15,
+                        zorder=PORDER_GATE,
+                    )
+                    self._ax.add_patch(box)
+                    xy_plot.append(xy)
+                v_ind += 1
+        clbit_b = min(xy_plot, key=lambda xy: xy[1])
+        xpos, ypos = clbit_b
         self._ax.text(
-            xp,
-            ypos + HIG,
-            text,
+            xpos,
+            ypos - 0.3 * HIG,
+            hex(val),
             ha="center",
             va="top",
-            fontsize=self._style["sfs"],
-            color=tc,
+            fontsize=self._sfs,
+            color=self._style["tc"],
             clip_on=True,
             zorder=PORDER_TEXT,
         )
+        self._line(qubit_b, clbit_b, lc=self._style["cc"], ls=self._style["cline"])
 
-    def _line(self, xy0, xy1, lc=None, ls=None, zorder=PORDER_LINE):
-        x0, y0 = xy0
-        x1, y1 = xy1
-        linecolor = self._style["lc"] if lc is None else lc
-        linestyle = "solid" if ls is None else ls
-
-        if linestyle == "doublet":
-            theta = np.arctan2(np.abs(x1 - x0), np.abs(y1 - y0))
-            dx = 0.05 * WID * np.cos(theta)
-            dy = 0.05 * WID * np.sin(theta)
-            self._ax.plot(
-                [x0 + dx, x1 + dx],
-                [y0 + dy, y1 + dy],
-                color=linecolor,
-                linewidth=self._lwidth2,
-                linestyle="solid",
-                zorder=zorder,
-            )
-            self._ax.plot(
-                [x0 - dx, x1 - dx],
-                [y0 - dy, y1 - dy],
-                color=linecolor,
-                linewidth=self._lwidth2,
-                linestyle="solid",
-                zorder=zorder,
-            )
-        else:
-            self._ax.plot(
-                [x0, x1],
-                [y0, y1],
-                color=linecolor,
-                linewidth=self._lwidth2,
-                linestyle=linestyle,
-                zorder=zorder,
-            )
-
-    def _measure(self, node, qxy, cxy, cid, fc=None, ec=None, gt=None, sc=None):
-        qx, qy = qxy
-        cx, cy = cxy
+    def _measure(self, node):
+        """Draw the measure symbol and the line to the clbit"""
+        qx, qy = self._data[node]["q_xy"][0]
+        cx, cy = self._data[node]["c_xy"][0]
+        cid = self._clbit_dict[self._data[node]["c_indxs"][0]]["index"]
 
         # draw gate box
-        self._gate(node, qxy, fc=fc, ec=ec, gt=gt, sc=sc)
+        self._gate(node)
 
         # add measure symbol
-        arc = self.patches_mod.Arc(
+        arc = self._patches_mod.Arc(
             xy=(qx, qy - 0.15 * HIG),
             width=WID * 0.7,
             height=HIG * 0.7,
             theta1=0,
             theta2=180,
             fill=False,
-            ec=gt,
+            ec=self._data[node]["gt"],
             linewidth=self._lwidth2,
             zorder=PORDER_GATE,
         )
@@ -631,13 +940,18 @@ def _measure(self, node, qxy, cxy, cid, fc=None, ec=None, gt=None, sc=None):
         self._ax.plot(
             [qx, qx + 0.35 * WID],
             [qy - 0.15 * HIG, qy + 0.20 * HIG],
-            color=gt,
+            color=self._data[node]["gt"],
             linewidth=self._lwidth2,
             zorder=PORDER_GATE,
         )
         # arrow
-        self._line(qxy, [cx, cy + 0.35 * WID], lc=self._style["cc"], ls=self._style["cline"])
-        arrowhead = self.patches_mod.Polygon(
+        self._line(
+            self._data[node]["q_xy"][0],
+            [cx, cy + 0.35 * WID],
+            lc=self._style["cc"],
+            ls=self._style["cline"],
+        )
+        arrowhead = self._patches_mod.Polygon(
             (
                 (cx - 0.20 * WID, cy + 0.35 * WID),
                 (cx + 0.20 * WID, cy + 0.35 * WID),
@@ -655,66 +969,216 @@ def _measure(self, node, qxy, cxy, cid, fc=None, ec=None, gt=None, sc=None):
                 str(cid),
                 ha="left",
                 va="bottom",
-                fontsize=0.8 * self._style["fs"],
+                fontsize=0.8 * self._fs,
                 color=self._style["tc"],
                 clip_on=True,
                 zorder=PORDER_TEXT,
             )
 
-    def _conditional(self, xy, istrue=False):
+    def _barrier(self, node):
+        """Draw a barrier"""
+        for xy in self._data[node]["q_xy"]:
+            xpos, ypos = xy
+            self._ax.plot(
+                [xpos, xpos],
+                [ypos + 0.5, ypos - 0.5],
+                linewidth=self._lwidth1,
+                linestyle="dashed",
+                color=self._style["lc"],
+                zorder=PORDER_TEXT,
+            )
+            box = self._patches_mod.Rectangle(
+                xy=(xpos - (0.3 * WID), ypos - 0.5),
+                width=0.6 * WID,
+                height=1,
+                fc=self._style["bc"],
+                ec=None,
+                alpha=0.6,
+                linewidth=self._lwidth15,
+                zorder=PORDER_GRAY,
+            )
+            self._ax.add_patch(box)
+
+    def _gate(self, node, xy=None):
+        """Draw a 1-qubit gate"""
+        if xy is None:
+            xy = self._data[node]["q_xy"][0]
         xpos, ypos = xy
+        wid = max(self._data[node]["width"], WID)
 
-        fc = self._style["lc"] if istrue else self._style["bg"]
-        box = self.patches_mod.Circle(
-            xy=(xpos, ypos),
-            radius=WID * 0.15,
-            fc=fc,
-            ec=self._style["lc"],
+        box = self._patches_mod.Rectangle(
+            xy=(xpos - 0.5 * wid, ypos - 0.5 * HIG),
+            width=wid,
+            height=HIG,
+            fc=self._data[node]["fc"],
+            ec=self._data[node]["ec"],
             linewidth=self._lwidth15,
             zorder=PORDER_GATE,
         )
         self._ax.add_patch(box)
 
-    def _ctrl_qubit(self, xy, fc=None, ec=None, tc=None, text="", text_top=None):
-        xpos, ypos = xy
-        box = self.patches_mod.Circle(
-            xy=(xpos, ypos),
-            radius=WID * 0.15,
-            fc=fc,
-            ec=ec,
-            linewidth=self._lwidth15,
-            zorder=PORDER_GATE,
-        )
-        self._ax.add_patch(box)
-        # display the control label at the top or bottom if there is one
-        if text_top is True:
-            self._ax.text(
-                xpos,
-                ypos + 0.7 * HIG,
-                text,
-                ha="center",
-                va="top",
-                fontsize=self._style["sfs"],
-                color=tc,
-                clip_on=True,
-                zorder=PORDER_TEXT,
-            )
-        elif text_top is False:
+        if "gate_text" in self._data[node]:
+            gate_ypos = ypos
+            if "param" in self._data[node] and self._data[node]["param"] != "":
+                gate_ypos = ypos + 0.15 * HIG
+                self._ax.text(
+                    xpos,
+                    ypos - 0.3 * HIG,
+                    self._data[node]["param"],
+                    ha="center",
+                    va="center",
+                    fontsize=self._sfs,
+                    color=self._data[node]["sc"],
+                    clip_on=True,
+                    zorder=PORDER_TEXT,
+                )
             self._ax.text(
                 xpos,
-                ypos - 0.3 * HIG,
-                text,
+                gate_ypos,
+                self._data[node]["gate_text"],
                 ha="center",
-                va="top",
-                fontsize=self._style["sfs"],
-                color=tc,
+                va="center",
+                fontsize=self._fs,
+                color=self._data[node]["gt"],
                 clip_on=True,
                 zorder=PORDER_TEXT,
             )
 
+    def _multiqubit_gate(self, node, xy=None):
+        """Draw a gate covering more than one qubit"""
+        op = node.op
+        if xy is None:
+            xy = self._data[node]["q_xy"]
+
+        # Swap gate
+        if isinstance(op, SwapGate):
+            self._swap(xy, self._data[node]["lc"])
+            return
+
+        # RZZ Gate
+        elif isinstance(op, RZZGate):
+            self._symmetric_gate(node, RZZGate)
+            return
+
+        c_xy = self._data[node]["c_xy"]
+        xpos = min([x[0] for x in xy])
+        ypos = min([y[1] for y in xy])
+        ypos_max = max([y[1] for y in xy])
+        if c_xy:
+            cxpos = min([x[0] for x in c_xy])
+            cypos = min([y[1] for y in c_xy])
+            ypos = min(ypos, cypos)
+
+        wid = max(self._data[node]["width"] + 0.21, WID)
+
+        qubit_span = abs(ypos) - abs(ypos_max) + 1
+        height = HIG + (qubit_span - 1)
+        box = self._patches_mod.Rectangle(
+            xy=(xpos - 0.5 * wid, ypos - 0.5 * HIG),
+            width=wid,
+            height=height,
+            fc=self._data[node]["fc"],
+            ec=self._data[node]["ec"],
+            linewidth=self._lwidth15,
+            zorder=PORDER_GATE,
+        )
+        self._ax.add_patch(box)
+
+        # annotate inputs
+        for bit, y in enumerate([x[1] for x in xy]):
+            self._ax.text(
+                xpos + 0.07 - 0.5 * wid,
+                y,
+                str(bit),
+                ha="left",
+                va="center",
+                fontsize=self._fs,
+                color=self._data[node]["gt"],
+                clip_on=True,
+                zorder=PORDER_TEXT,
+            )
+        if c_xy:
+            # annotate classical inputs
+            for bit, y in enumerate([x[1] for x in c_xy]):
+                self._ax.text(
+                    cxpos + 0.07 - 0.5 * wid,
+                    y,
+                    str(bit),
+                    ha="left",
+                    va="center",
+                    fontsize=self._fs,
+                    color=self._data[node]["gt"],
+                    clip_on=True,
+                    zorder=PORDER_TEXT,
+                )
+        if "gate_text" in self._data[node] and self._data[node]["gate_text"] != "":
+            gate_ypos = ypos + 0.5 * (qubit_span - 1)
+            if "param" in self._data[node] and self._data[node]["param"] != "":
+                gate_ypos = ypos + 0.4 * height
+                self._ax.text(
+                    xpos + 0.11,
+                    ypos + 0.2 * height,
+                    self._data[node]["param"],
+                    ha="center",
+                    va="center",
+                    fontsize=self._sfs,
+                    color=self._data[node]["sc"],
+                    clip_on=True,
+                    zorder=PORDER_TEXT,
+                )
+            self._ax.text(
+                xpos + 0.11,
+                gate_ypos,
+                self._data[node]["gate_text"],
+                ha="center",
+                va="center",
+                fontsize=self._fs,
+                color=self._data[node]["gt"],
+                clip_on=True,
+                zorder=PORDER_TEXT,
+            )
+
+    def _control_gate(self, node):
+        """Draw a controlled gate"""
+        op = node.op
+        base_type = None if not hasattr(op, "base_gate") else op.base_gate
+        xy = self._data[node]["q_xy"]
+        qubit_b = min(xy, key=lambda xy: xy[1])
+        qubit_t = max(xy, key=lambda xy: xy[1])
+        num_ctrl_qubits = op.num_ctrl_qubits
+        num_qargs = len(xy) - num_ctrl_qubits
+        self._set_ctrl_bits(
+            op.ctrl_state,
+            num_ctrl_qubits,
+            xy,
+            ec=self._data[node]["ec"],
+            tc=self._data[node]["tc"],
+            text=self._data[node]["ctrl_text"],
+            qargs=node.qargs,
+        )
+        self._line(qubit_b, qubit_t, lc=self._data[node]["lc"])
+
+        if isinstance(op, RZZGate) or isinstance(base_type, (U1Gate, PhaseGate, ZGate, RZZGate)):
+            self._symmetric_gate(node, base_type)
+
+        elif num_qargs == 1 and isinstance(base_type, XGate):
+            tgt_color = self._style["dispcol"]["target"]
+            tgt = tgt_color if isinstance(tgt_color, str) else tgt_color[0]
+            self._x_tgt_qubit(xy[num_ctrl_qubits], ec=self._data[node]["ec"], ac=tgt)
+
+        elif num_qargs == 1:
+            self._gate(node, xy[num_ctrl_qubits:][0])
+
+        elif isinstance(base_type, SwapGate):
+            self._swap(xy[num_ctrl_qubits:], self._data[node]["lc"])
+
+        else:
+            self._multiqubit_gate(node, xy[num_ctrl_qubits:])
+
     def _set_ctrl_bits(
         self, ctrl_state, num_ctrl_qubits, qbit, ec=None, tc=None, text="", qargs=None
     ):
+        """Determine which qubits are controls and whether they are open or closed"""
         # place the control label at the top or bottom of controls
         if text:
             qlist = [self._bit_locations[qubit]["index"] for qubit in qargs]
@@ -736,10 +1200,40 @@ def _set_ctrl_bits(
                     text_top = False
             self._ctrl_qubit(qbit[i], fc=fc_open_close, ec=ec, tc=tc, text=text, text_top=text_top)
 
+    def _ctrl_qubit(self, xy, fc=None, ec=None, tc=None, text="", text_top=None):
+        """Draw a control circle and if top or bottom control, draw control label"""
+        xpos, ypos = xy
+        box = self._patches_mod.Circle(
+            xy=(xpos, ypos),
+            radius=WID * 0.15,
+            fc=fc,
+            ec=ec,
+            linewidth=self._lwidth15,
+            zorder=PORDER_GATE,
+        )
+        self._ax.add_patch(box)
+        if text_top is None:
+            return
+
+        # display the control label at the top or bottom if there is one
+        ctrl_ypos = ypos + 0.7 * HIG if text_top else ypos - 0.3 * HIG
+        self._ax.text(
+            xpos,
+            ctrl_ypos,
+            text,
+            ha="center",
+            va="top",
+            fontsize=self._sfs,
+            color=tc,
+            clip_on=True,
+            zorder=PORDER_TEXT,
+        )
+
     def _x_tgt_qubit(self, xy, ec=None, ac=None):
+        """Draw the cnot target symbol"""
         linewidth = self._lwidth2
         xpos, ypos = xy
-        box = self.patches_mod.Circle(
+        box = self._patches_mod.Circle(
             xy=(xpos, ypos),
             radius=HIG * 0.35,
             fc=ec,
@@ -765,7 +1259,42 @@ def _x_tgt_qubit(self, xy, ec=None, ac=None):
             zorder=PORDER_GATE + 1,
         )
 
+    def _symmetric_gate(self, node, base_type):
+        """Draw symmetric gates for cz, cu1, cp, and rzz"""
+        op = node.op
+        xy = self._data[node]["q_xy"]
+        qubit_b = min(xy, key=lambda xy: xy[1])
+        qubit_t = max(xy, key=lambda xy: xy[1])
+        base_type = None if not hasattr(op, "base_gate") else op.base_gate
+        ec = self._data[node]["ec"]
+        tc = self._data[node]["tc"]
+        lc = self._data[node]["lc"]
+
+        # cz and mcz gates
+        if not isinstance(op, ZGate) and isinstance(base_type, ZGate):
+            num_ctrl_qubits = op.num_ctrl_qubits
+            self._ctrl_qubit(xy[-1], fc=ec, ec=ec, tc=tc)
+            self._line(qubit_b, qubit_t, lc=lc, zorder=PORDER_LINE + 1)
+
+        # cu1, cp, rzz, and controlled rzz gates (sidetext gates)
+        elif isinstance(op, RZZGate) or isinstance(base_type, (U1Gate, PhaseGate, RZZGate)):
+            num_ctrl_qubits = 0 if isinstance(op, RZZGate) else op.num_ctrl_qubits
+            gate_text = "P" if isinstance(base_type, PhaseGate) else self._data[node]["gate_text"]
+
+            self._ctrl_qubit(xy[num_ctrl_qubits], fc=ec, ec=ec, tc=tc)
+            if not isinstance(base_type, (U1Gate, PhaseGate)):
+                self._ctrl_qubit(xy[num_ctrl_qubits + 1], fc=ec, ec=ec, tc=tc)
+            self._sidetext(node, qubit_b, tc=tc, text=f"{gate_text} ({self._data[node]['param']})")
+            self._line(qubit_b, qubit_t, lc=lc)
+
     def _swap(self, xy, color=None):
+        """Draw a Swap gate"""
+        self._swap_cross(xy[0], color=color)
+        self._swap_cross(xy[1], color=color)
+        self._line(xy[0], xy[1], lc=color)
+
+    def _swap_cross(self, xy, color=None):
+        """Draw the Swap cross symbol"""
         xpos, ypos = xy
 
         self._ax.plot(
@@ -783,695 +1312,111 @@ def _swap(self, xy, color=None):
             zorder=PORDER_LINE + 1,
         )
 
-    def _barrier(self, config):
-        xys = config["coord"]
-        for xy in xys:
-            xpos, ypos = xy
-            self._ax.plot(
-                [xpos, xpos],
-                [ypos + 0.5, ypos - 0.5],
-                linewidth=self._scale,
-                linestyle="dashed",
-                color=self._style["lc"],
-                zorder=PORDER_TEXT,
-            )
-            box = self.patches_mod.Rectangle(
-                xy=(xpos - (0.3 * WID), ypos - 0.5),
-                width=0.6 * WID,
-                height=1,
-                fc=self._style["bc"],
-                ec=None,
-                alpha=0.6,
-                linewidth=self._lwidth15,
-                zorder=PORDER_GRAY,
-            )
-            self._ax.add_patch(box)
+    def _sidetext(self, node, xy, tc=None, text=""):
+        """Draw the sidetext for symmetric gates"""
+        xpos, ypos = xy
 
-    def draw(self, filename=None, verbose=False):
-        """Draw method called from circuit_drawer"""
-        self._draw_regs()
-        self._draw_ops(verbose)
-        _xl = -self._style["margin"][0]
-        _xr = self._xmax + self._style["margin"][1]
-        _yb = -self._ymax - self._style["margin"][2] + 1 - 0.5
-        _yt = self._style["margin"][3] + 0.5
-        self._ax.set_xlim(_xl, _xr)
-        self._ax.set_ylim(_yb, _yt)
-
-        # update figure size
-        fig_w = _xr - _xl
-        fig_h = _yt - _yb
-        if self._style["figwidth"] < 0.0:
-            self._style["figwidth"] = fig_w * BASE_SIZE * self._style["fs"] / 72 / WID
-        self._figure.set_size_inches(
-            self._style["figwidth"], self._style["figwidth"] * fig_h / fig_w
+        # 0.11 = the initial gap, add 1/2 text width to place on the right
+        xp = xpos + 0.11 + self._data[node]["width"] / 2
+        self._ax.text(
+            xp,
+            ypos + HIG,
+            text,
+            ha="center",
+            va="top",
+            fontsize=self._sfs,
+            color=tc,
+            clip_on=True,
+            zorder=PORDER_TEXT,
         )
-        if self._global_phase:
-            self.plt_mod.text(
-                _xl, _yt, "Global Phase: %s" % pi_check(self._global_phase, output="mpl")
-            )
-
-        if filename:
-            self._figure.savefig(
-                filename,
-                dpi=self._style["dpi"],
-                bbox_inches="tight",
-                facecolor=self._figure.get_facecolor(),
-            )
-        if self._return_fig:
-            from matplotlib import get_backend
-
-            if get_backend() in ["module://ipykernel.pylab.backend_inline", "nbAgg"]:
-                self.plt_mod.close(self._figure)
-            return self._figure
-
-    def _draw_regs(self):
-        longest_reg_name_width = 0
-        initial_qbit = " |0>" if self._initial_state else ""
-        initial_cbit = " 0" if self._initial_state else ""
-
-        def _fix_double_script(reg_name):
-            words = reg_name.split(" ")
-            words = [word.replace("_", r"\_") if word.count("_") > 1 else word for word in words]
-            words = [
-                word.replace("^", r"\^{\ }") if word.count("^") > 1 else word for word in words
-            ]
-            reg_name = " ".join(words).replace(" ", "\\;")
-            return reg_name
-
-        # quantum register
-        fs = self._style["fs"]
-        for ii, reg in enumerate(self._qubit):
-            register = self._bit_locations[reg]["register"]
-            index = self._bit_locations[reg]["index"]
-
-            if len(self._qubit) > 1:
-                if self._layout is None:
-                    qubit_name = f"${{{register.name}}}_{{{index}}}$"
-                else:
-                    if self._layout[index]:
-                        virt_bit = self._layout[index]
-                        try:
-                            virt_reg = next(
-                                reg for reg in self._layout.get_registers() if virt_bit in reg
-                            )
-                            qubit_name = "${{{name}}}_{{{index}}} \\mapsto {{{physical}}}$".format(
-                                name=virt_reg.name,
-                                index=virt_reg[:].index(virt_bit),
-                                physical=index,
-                            )
-
-                        except StopIteration:
-                            qubit_name = "${{{name}}} \\mapsto {{{physical}}}$".format(
-                                name=virt_bit, physical=index
-                            )
-                    else:
-                        qubit_name = f"${{{index}}}$"
-            else:
-                qubit_name = f"{register.name}"
-            qubit_name = _fix_double_script(qubit_name) + initial_qbit
-            text_width = self._get_text_width(qubit_name, fs) * 1.15
 
-            if text_width > longest_reg_name_width:
-                longest_reg_name_width = text_width
-            pos = -ii
-            self._qubit_dict[ii] = {
-                "y": pos,
-                "reg_name": qubit_name,
-                "index": index,
-                "group": register,
-            }
-            self._n_lines += 1
-
-        # classical register
-        if self._clbit:
-            n_clbit = self._clbit.copy()
-            n_clbit.pop(0)
-            idx = 0
-            y_off = -len(self._qubit)
-            for ii, (reg, nreg) in enumerate(itertools.zip_longest(self._clbit, n_clbit)):
-                pos = y_off - idx
-                register = self._bit_locations[reg]["register"]
-                index = self._bit_locations[reg]["index"]
-
-                if self._cregbundle:
-                    clbit_name = f"{register.name}"
-                    clbit_name = _fix_double_script(clbit_name) + initial_cbit
-                    text_width = self._get_text_width(register.name, fs) * 1.15
-                    if text_width > longest_reg_name_width:
-                        longest_reg_name_width = text_width
-                    self._clbit_dict[ii] = {
-                        "y": pos,
-                        "reg_name": clbit_name,
-                        "index": index,
-                        "group": register,
-                    }
-                    if not (not nreg or register != self._bit_locations[nreg]["register"]):
-                        continue
-                else:
-                    clbit_name = f"${register.name}_{{{index}}}$"
-                    clbit_name = _fix_double_script(clbit_name) + initial_cbit
-                    text_width = self._get_text_width(register.name, fs) * 1.15
-                    if text_width > longest_reg_name_width:
-                        longest_reg_name_width = text_width
-                    self._clbit_dict[ii] = {
-                        "y": pos,
-                        "reg_name": clbit_name,
-                        "index": index,
-                        "group": register,
-                    }
-                self._n_lines += 1
-                idx += 1
-
-        self._reg_long_text = longest_reg_name_width
-        self._x_offset = -1.2 + self._reg_long_text
+    def _line(self, xy0, xy1, lc=None, ls=None, zorder=PORDER_LINE):
+        """Draw a line from xy0 to xy1"""
+        x0, y0 = xy0
+        x1, y1 = xy1
+        linecolor = self._style["lc"] if lc is None else lc
+        linestyle = "solid" if ls is None else ls
 
-    def _draw_regs_sub(self, n_fold, feedline_l=False, feedline_r=False):
-        # quantum register
-        fs = self._style["fs"]
-        for qubit in self._qubit_dict.values():
-            qubit_name = qubit["reg_name"]
-            y = qubit["y"] - n_fold * (self._n_lines + 1)
-            self._ax.text(
-                self._x_offset - 0.2,
-                y,
-                qubit_name,
-                ha="right",
-                va="center",
-                fontsize=1.25 * fs,
-                color=self._style["tc"],
-                clip_on=True,
-                zorder=PORDER_TEXT,
+        if linestyle == "doublet":
+            theta = np.arctan2(np.abs(x1 - x0), np.abs(y1 - y0))
+            dx = 0.05 * WID * np.cos(theta)
+            dy = 0.05 * WID * np.sin(theta)
+            self._ax.plot(
+                [x0 + dx, x1 + dx],
+                [y0 + dy, y1 + dy],
+                color=linecolor,
+                linewidth=self._lwidth2,
+                linestyle="solid",
+                zorder=zorder,
             )
-            self._line([self._x_offset, y], [self._xmax, y], zorder=PORDER_REGLINE)
-
-        # classical register
-        this_clbit_dict = {}
-        for clbit in self._clbit_dict.values():
-            clbit_name = clbit["reg_name"]
-            y = clbit["y"] - n_fold * (self._n_lines + 1)
-            if y not in this_clbit_dict.keys():
-                this_clbit_dict[y] = {"val": 1, "reg_name": clbit_name}
-            else:
-                this_clbit_dict[y]["val"] += 1
-        for y, this_clbit in this_clbit_dict.items():
-            # cregbundle
-            if this_clbit["val"] > 1:
-                self._ax.plot(
-                    [self._x_offset + 0.2, self._x_offset + 0.3],
-                    [y - 0.1, y + 0.1],
-                    color=self._style["cc"],
-                    zorder=PORDER_LINE,
-                )
-                self._ax.text(
-                    self._x_offset + 0.1,
-                    y + 0.1,
-                    str(this_clbit["val"]),
-                    ha="left",
-                    va="bottom",
-                    fontsize=0.8 * fs,
-                    color=self._style["tc"],
-                    clip_on=True,
-                    zorder=PORDER_TEXT,
-                )
-            self._ax.text(
-                self._x_offset - 0.2,
-                y,
-                this_clbit["reg_name"],
-                ha="right",
-                va="center",
-                fontsize=1.25 * fs,
-                color=self._style["tc"],
-                clip_on=True,
-                zorder=PORDER_TEXT,
+            self._ax.plot(
+                [x0 - dx, x1 - dx],
+                [y0 - dy, y1 - dy],
+                color=linecolor,
+                linewidth=self._lwidth2,
+                linestyle="solid",
+                zorder=zorder,
             )
-            self._line(
-                [self._x_offset, y],
-                [self._xmax, y],
-                lc=self._style["cc"],
-                ls=self._style["cline"],
-                zorder=PORDER_REGLINE,
+        else:
+            self._ax.plot(
+                [x0, x1],
+                [y0, y1],
+                color=linecolor,
+                linewidth=self._lwidth2,
+                linestyle=linestyle,
+                zorder=zorder,
             )
 
-        # lf vertical line at either end
-        if feedline_l or feedline_r:
-            xpos_l = self._x_offset - 0.01
-            xpos_r = self._fold + self._x_offset + 0.1
-            ypos1 = -n_fold * (self._n_lines + 1)
-            ypos2 = -(n_fold + 1) * (self._n_lines) - n_fold + 1
-            if feedline_l:
-                self._ax.plot(
-                    [xpos_l, xpos_l],
-                    [ypos1, ypos2],
-                    color=self._style["lc"],
-                    linewidth=self._lwidth15,
-                    zorder=PORDER_LINE,
-                )
-            if feedline_r:
-                self._ax.plot(
-                    [xpos_r, xpos_r],
-                    [ypos1, ypos2],
-                    color=self._style["lc"],
-                    linewidth=self._lwidth15,
-                    zorder=PORDER_LINE,
-                )
-
-    def _draw_ops(self, verbose=False):
-        _standard_1q_gates = [
-            "x",
-            "y",
-            "z",
-            "id",
-            "h",
-            "r",
-            "s",
-            "sdg",
-            "t",
-            "tdg",
-            "rx",
-            "ry",
-            "rz",
-            "rxx",
-            "ryy",
-            "rzx",
-            "u1",
-            "u2",
-            "u3",
-            "u",
-            "swap",
-            "reset",
-            "sx",
-            "sxdg",
-            "p",
-        ]
-
-        # generate coordinate manager
-        q_anchors = {}
-        for key, qubit in self._qubit_dict.items():
-            q_anchors[key] = Anchor(reg_num=self._n_lines, yind=qubit["y"], fold=self._fold)
-        c_anchors = {}
-        for key, clbit in self._clbit_dict.items():
-            c_anchors[key] = Anchor(reg_num=self._n_lines, yind=clbit["y"], fold=self._fold)
-        #
-        # draw the ops
-        #
-        prev_anc = -1
-        fs = self._style["fs"]
-        sfs = self._style["sfs"]
-        for layer in self._nodes:
-            widest_box = 0.0
-            self._gate_width = {}
-            #
-            # compute the layer_width for this layer
-            #
-            for node in layer:
-                op = node.op
-                self._gate_width[node] = WID
-
-                if op._directive or op.name == "measure":
-                    continue
-
-                base_name = None if not hasattr(op, "base_gate") else op.base_gate.name
-                gate_text, ctrl_text, _ = get_gate_ctrl_text(op, "mpl", style=self._style)
-
-                # if a standard_gate, no params, and no labels, layer_width is 1
-                if not hasattr(op, "params") and (
-                    (op.name in _standard_1q_gates or base_name in _standard_1q_gates)
-                    and gate_text in (op.name, base_name)
-                    and ctrl_text is None
-                ):
-                    continue
-
-                # small increments at end of the 3 _get_text_width calls are for small
-                # spacing adjustments between gates
-                ctrl_width = self._get_text_width(ctrl_text, fontsize=sfs) - 0.05
-
-                # get param_width, but 0 for gates with array params
-                if (
-                    hasattr(op, "params")
-                    and not any(isinstance(param, np.ndarray) for param in op.params)
-                    and len(op.params) > 0
-                ):
-                    param = get_param_str(op, "mpl", ndigits=3)
-                    if op.name == "initialize":
-                        param = "[%s]" % param
-                    raw_param_width = self._get_text_width(param, fontsize=sfs, param=True)
-                    param_width = raw_param_width + 0.08
-                else:
-                    param_width = raw_param_width = 0.0
-
-                if op.name == "rzz" or base_name in ["u1", "p", "rzz"]:
-                    if base_name == "u1":
-                        tname = "U1"
-                    elif base_name == "p":
-                        tname = "P"
-                    else:
-                        tname = "ZZ"
-                    raw_gate_width = (
-                        self._get_text_width(tname + " ()", fontsize=sfs) + raw_param_width
-                    )
-                    gate_width = (raw_gate_width + 0.08) * 1.5
-                else:
-                    raw_gate_width = self._get_text_width(gate_text, fontsize=fs)
-                    gate_width = raw_gate_width + 0.10
-                    # add .21 for the qubit numbers on the left of the multibit gates
-                    if op.name not in _standard_1q_gates and base_name not in _standard_1q_gates:
-                        gate_width += 0.21
-
-                box_width = max(gate_width, ctrl_width, param_width, WID)
-                if box_width > widest_box:
-                    widest_box = box_width
-                self._gate_width[node] = max(raw_gate_width, raw_param_width)
-
-            layer_width = int(widest_box) + 1
-            this_anc = prev_anc + 1
-            #
-            # draw the gates in this layer
-            #
-            for node in layer:
-                op = node.op
-                base_name = None if not hasattr(op, "base_gate") else op.base_gate.name
-                gate_text, ctrl_text, raw_gate_text = get_gate_ctrl_text(
-                    op, "mpl", style=self._style
-                )
-                fc, ec, gt, tc, sc, lc = self._get_colors(op, raw_gate_text)
-
-                # get qubit index
-                q_idxs = []
-                for qarg in node.qargs:
-                    for index, reg in self._qubit_dict.items():
-                        if (
-                            reg["group"] == self._bit_locations[qarg]["register"]
-                            and reg["index"] == self._bit_locations[qarg]["index"]
-                        ):
-                            q_idxs.append(index)
-                            break
-
-                # get clbit index
-                c_idxs = []
-                for carg in node.cargs:
-                    for index, reg in self._clbit_dict.items():
-                        if (
-                            reg["group"] == self._bit_locations[carg]["register"]
-                            and reg["index"] == self._bit_locations[carg]["index"]
-                        ):
-                            c_idxs.append(index)
-                            break
-
-                # only add the gate to the anchors if it is going to be plotted.
-                # this prevents additional blank wires at the end of the line if
-                # the last instruction is a barrier type
-                if self._plot_barriers or not op._directive:
-                    for ii in q_idxs:
-                        q_anchors[ii].set_index(this_anc, layer_width)
 
-                # qubit coordinate
-                q_xy = [
-                    q_anchors[ii].plot_coord(this_anc, layer_width, self._x_offset) for ii in q_idxs
-                ]
-                # clbit coordinate
-                c_xy = [
-                    c_anchors[ii].plot_coord(this_anc, layer_width, self._x_offset) for ii in c_idxs
-                ]
-                # bottom and top point of qubit
-                qubit_b = min(q_xy, key=lambda xy: xy[1])
-                qubit_t = max(q_xy, key=lambda xy: xy[1])
-
-                # update index based on the value from plotting
-                this_anc = q_anchors[q_idxs[0]].gate_anchor
-
-                if verbose:
-                    print(op)
-
-                # load param
-                if (
-                    hasattr(op, "params")
-                    and len(op.params) > 0
-                    and not any(isinstance(param, np.ndarray) for param in op.params)
-                ):
-                    param = f"{get_param_str(op, 'mpl', ndigits=3)}"
-                else:
-                    param = ""
-
-                # conditional gate
-                if op.condition:
-                    c_xy = [
-                        c_anchors[ii].plot_coord(this_anc, layer_width, self._x_offset)
-                        for ii in self._clbit_dict
-                    ]
-                    mask = 0
-                    for index, cbit in enumerate(self._clbit):
-                        if self._bit_locations[cbit]["register"] == op.condition[0]:
-                            mask |= 1 << index
-                    val = op.condition[1]
-                    # cbit list to consider
-                    fmt_c = f"{{:0{len(c_xy)}b}}"
-                    cmask = list(fmt_c.format(mask))[::-1]
-                    # value
-                    fmt_v = f"{{:0{cmask.count('1')}b}}"
-                    vlist = list(fmt_v.format(val))
-                    if not self._reverse_bits:
-                        vlist = vlist[::-1]
-
-                    # plot conditionals
-                    v_ind = 0
-                    xy_plot = []
-                    for xy, m in zip(c_xy, cmask):
-                        if m == "1":
-                            if xy not in xy_plot:
-                                if vlist[v_ind] == "1" or self._cregbundle:
-                                    self._conditional(xy, istrue=True)
-                                else:
-                                    self._conditional(xy, istrue=False)
-                                xy_plot.append(xy)
-                            v_ind += 1
-                    clbit_b = sorted(xy_plot, key=lambda xy: xy[1])[0]
-                    xpos, ypos = clbit_b
-                    self._ax.text(
-                        xpos,
-                        ypos - 0.3 * HIG,
-                        hex(val),
-                        ha="center",
-                        va="top",
-                        fontsize=sfs,
-                        color=self._style["tc"],
-                        clip_on=True,
-                        zorder=PORDER_TEXT,
-                    )
-                    self._line(qubit_t, clbit_b, lc=self._style["cc"], ls=self._style["cline"])
-                #
-                # draw special gates
-                #
-                if op.name == "measure":
-                    vv = self._clbit_dict[c_idxs[0]]["index"]
-                    self._measure(node, q_xy[0], c_xy[0], vv, fc=fc, ec=ec, gt=gt, sc=sc)
-
-                elif op._directive:
-                    _barriers = {"coord": [], "group": []}
-                    for index, qbit in enumerate(q_idxs):
-                        q_group = self._qubit_dict[qbit]["group"]
-                        if q_group not in _barriers["group"]:
-                            _barriers["group"].append(q_group)
-                        _barriers["coord"].append(q_xy[index])
-                    if self._plot_barriers:
-                        self._barrier(_barriers)
-
-                elif op.name == "initialize":
-                    vec = f"$[{param.replace('$', '')}]$"
-                    if len(q_xy) == 1:
-                        self._gate(
-                            node, q_xy[0], fc=fc, ec=ec, gt=gt, sc=sc, text=gate_text, subtext=vec
-                        )
-                    else:
-                        self._multiqubit_gate(
-                            node, q_xy, fc=fc, ec=ec, gt=gt, sc=sc, text=gate_text, subtext=vec
-                        )
-                #
-                # draw single qubit gates
-                #
-                elif len(q_xy) == 1 and not node.cargs:
-                    self._gate(
-                        node,
-                        q_xy[0],
-                        fc=fc,
-                        ec=ec,
-                        gt=gt,
-                        sc=sc,
-                        text=gate_text,
-                        subtext=str(param),
-                    )
-                #
-                # draw controlled and special gates
-                #
-                # cz and mcz gates
-                elif op.name != "z" and base_name == "z":
-                    num_ctrl_qubits = op.num_ctrl_qubits
-                    self._set_ctrl_bits(
-                        op.ctrl_state,
-                        num_ctrl_qubits,
-                        q_xy,
-                        ec=ec,
-                        tc=tc,
-                        text=ctrl_text,
-                        qargs=node.qargs,
-                    )
-                    self._ctrl_qubit(q_xy[-1], fc=ec, ec=ec, tc=tc)
-                    self._line(qubit_b, qubit_t, lc=lc, zorder=PORDER_LINE + 1)
-
-                # cu1, cp, rzz, and controlled rzz gates (sidetext gates)
-                elif op.name == "rzz" or base_name in ["u1", "p", "rzz"]:
-                    num_ctrl_qubits = 0 if op.name == "rzz" else op.num_ctrl_qubits
-                    if op.name != "rzz":
-                        self._set_ctrl_bits(
-                            op.ctrl_state,
-                            num_ctrl_qubits,
-                            q_xy,
-                            ec=ec,
-                            tc=tc,
-                            text=ctrl_text,
-                            qargs=node.qargs,
-                        )
-                    self._ctrl_qubit(q_xy[num_ctrl_qubits], fc=ec, ec=ec, tc=tc)
-                    if base_name not in ["u1", "p"]:
-                        self._ctrl_qubit(q_xy[num_ctrl_qubits + 1], fc=ec, ec=ec, tc=tc)
-                    if base_name == "u1":
-                        if self._style["disptex"]["u1"].find("\\mathrm") >= 0:
-                            stext = self._style["disptex"]["u1"]
-                        else:
-                            stext = f"$\\mathrm{{{self._style['disptex']['u1']}}}$"
-                    elif base_name == "p":
-                        stext = "P"
-                    else:
-                        stext = "ZZ"
-                    self._sidetext(node, qubit_b, tc=tc, text=f"{stext} ({param})")
-                    self._line(qubit_b, qubit_t, lc=lc)
-
-                # swap gate
-                elif op.name == "swap":
-                    self._swap(q_xy[0], color=lc)
-                    self._swap(q_xy[1], color=lc)
-                    self._line(qubit_b, qubit_t, lc=lc)
-
-                # cswap gate
-                elif op.name != "swap" and base_name == "swap":
-                    num_ctrl_qubits = op.num_ctrl_qubits
-                    self._set_ctrl_bits(
-                        op.ctrl_state,
-                        num_ctrl_qubits,
-                        q_xy,
-                        ec=ec,
-                        tc=tc,
-                        text=ctrl_text,
-                        qargs=node.qargs,
-                    )
-                    self._swap(q_xy[num_ctrl_qubits], color=lc)
-                    self._swap(q_xy[num_ctrl_qubits + 1], color=lc)
-                    self._line(qubit_b, qubit_t, lc=lc)
+class Anchor:
+    """Locate the anchors for the gates"""
 
-                # all other controlled gates
-                elif isinstance(op, ControlledGate):
-                    num_ctrl_qubits = op.num_ctrl_qubits
-                    num_qargs = len(q_xy) - num_ctrl_qubits
-                    self._set_ctrl_bits(
-                        op.ctrl_state,
-                        num_ctrl_qubits,
-                        q_xy,
-                        ec=ec,
-                        tc=tc,
-                        text=ctrl_text,
-                        qargs=node.qargs,
-                    )
-                    self._line(qubit_b, qubit_t, lc=lc)
-                    if num_qargs == 1 and base_name == "x":
-                        tgt_color = self._style["dispcol"]["target"]
-                        tgt = tgt_color if isinstance(tgt_color, str) else tgt_color[0]
-                        self._x_tgt_qubit(q_xy[num_ctrl_qubits], ec=ec, ac=tgt)
-                    elif num_qargs == 1:
-                        self._gate(
-                            node,
-                            q_xy[num_ctrl_qubits],
-                            fc=fc,
-                            ec=ec,
-                            gt=gt,
-                            sc=sc,
-                            text=gate_text,
-                            subtext=f"{param}",
-                        )
-                    else:
-                        self._multiqubit_gate(
-                            node,
-                            q_xy[num_ctrl_qubits:],
-                            fc=fc,
-                            ec=ec,
-                            gt=gt,
-                            sc=sc,
-                            text=gate_text,
-                            subtext=f"{param}",
-                        )
+    def __init__(self, reg_num, yind, fold):
+        self._yind = yind
+        self._fold = fold
+        self._reg_num = reg_num
+        self._gate_placed = []
+        self.nxt_anchor_idx = 0
+        self.gate_anchor = 0
 
-                # draw multi-qubit gate as final default
-                else:
-                    self._multiqubit_gate(
-                        node,
-                        q_xy,
-                        c_xy,
-                        fc=fc,
-                        ec=ec,
-                        gt=gt,
-                        sc=sc,
-                        text=gate_text,
-                        subtext=f"{param}",
-                    )
+    def plot_coord(self, index, gate_width, x_offset):
+        """Set the coord positions for an index"""
+        h_pos = index % self._fold + 1
+        # check folding
+        if self._fold > 0:
+            if h_pos + (gate_width - 1) > self._fold:
+                index += self._fold - (h_pos - 1)
+            x_pos = index % self._fold + 0.5 * gate_width + 0.04
+            y_pos = self._yind - (index // self._fold) * (self._reg_num + 1)
+        else:
+            x_pos = index + 0.5 * gate_width + 0.04
+            y_pos = self._yind
 
-            # adjust the column if there have been barriers encountered, but not plotted
-            barrier_offset = 0
-            if not self._plot_barriers:
-                # only adjust if everything in the layer wasn't plotted
-                barrier_offset = -1 if all(op._directive for node in layer) else 0
+        # could have been updated, so need to store
+        self.gate_anchor = index
+        return x_pos + x_offset, y_pos
 
-            prev_anc = this_anc + layer_width + barrier_offset - 1
-        #
-        # adjust window size and draw horizontal lines
-        #
-        anchors = [q_anchors[ii].get_index() for ii in self._qubit_dict]
-        max_anc = max(anchors) if anchors else 0
-        n_fold = max(0, max_anc - 1) // self._fold if self._fold > 0 else 0
-
-        # window size
-        if max_anc > self._fold > 0:
-            self._xmax = self._fold + 1 + self._x_offset - 0.9
-            self._ymax = (n_fold + 1) * (self._n_lines + 1) - 1
+    def set_index(self, index, layer_width):
+        """Set the index for a gate"""
+        if self._fold < 2:
+            _index = index
         else:
-            x_incr = 0.4 if not self._nodes else 0.9
-            self._xmax = max_anc + 1 + self._x_offset - x_incr
-            self._ymax = self._n_lines
-
-        # add horizontal lines
-        for ii in range(n_fold + 1):
-            feedline_r = n_fold > 0 and n_fold > ii
-            feedline_l = ii > 0
-            self._draw_regs_sub(ii, feedline_l, feedline_r)
+            h_pos = index % self._fold + 1
+            if h_pos + (layer_width - 1) > self._fold:
+                _index = index + self._fold - (h_pos - 1) + 1
+            else:
+                _index = index
+        for ii in range(layer_width):
+            idx = _index + ii
+            if idx not in self._gate_placed:
+                self._gate_placed.append(idx)
+                self.nxt_anchor_idx = idx + 1
 
-        # draw anchor index number
-        if self._style["index"]:
-            for ii in range(max_anc):
-                if self._fold > 0:
-                    x_coord = ii % self._fold + self._reg_long_text - 0.67
-                    y_coord = -(ii // self._fold) * (self._n_lines + 1) + 0.7
-                else:
-                    x_coord = ii + self._reg_long_text - 0.67
-                    y_coord = 0.7
-                self._ax.text(
-                    x_coord,
-                    y_coord,
-                    str(ii + 1),
-                    ha="center",
-                    va="center",
-                    fontsize=sfs,
-                    color=self._style["tc"],
-                    clip_on=True,
-                    zorder=PORDER_TEXT,
-                )
+    def get_index(self):
+        """Getter for the index"""
+        if self._gate_placed:
+            return self._gate_placed[-1] + 1
+        return 0
 
 
 class HasMatplotlibWrapper:
diff --git a/qiskit/visualization/text.py b/qiskit/visualization/text.py
--- a/qiskit/visualization/text.py
+++ b/qiskit/visualization/text.py
@@ -783,7 +783,6 @@ def wire_names(self, with_initial_state=False):
                         label.format(
                             name=self.bit_locations[bit]["register"].name,
                             index=self.bit_locations[bit]["index"],
-                            physical="",
                         )
                     )
                 else:
diff --git a/qiskit/visualization/utils.py b/qiskit/visualization/utils.py
--- a/qiskit/visualization/utils.py
+++ b/qiskit/visualization/utils.py
@@ -271,11 +271,6 @@ def _get_gate_span(qubits, node, reverse_bits):
         else:
             return qubits[min_index : len(qubits)]
 
-    if node.cargs:
-        return qubits[min_index:]
-    if node.op.condition:
-        return qubits[min_index:]
-
     return qubits[min_index : max_index + 1]
 
 


Test patch:
diff --git a/test/ipynb/mpl/circuit/references/figwidth.png b/test/ipynb/mpl/circuit/references/figwidth.png
new file mode 100644
Binary files /dev/null and b/test/ipynb/mpl/circuit/references/figwidth.png differ
diff --git a/test/ipynb/mpl/circuit/references/global_phase.png b/test/ipynb/mpl/circuit/references/global_phase.png
Binary files a/test/ipynb/mpl/circuit/references/global_phase.png and b/test/ipynb/mpl/circuit/references/global_phase.png differ
diff --git a/test/ipynb/mpl/circuit/references/user_ax.png b/test/ipynb/mpl/circuit/references/user_ax.png
new file mode 100644
Binary files /dev/null and b/test/ipynb/mpl/circuit/references/user_ax.png differ
diff --git a/test/ipynb/mpl/circuit/references/user_style.png b/test/ipynb/mpl/circuit/references/user_style.png
Binary files a/test/ipynb/mpl/circuit/references/user_style.png and b/test/ipynb/mpl/circuit/references/user_style.png differ
diff --git a/test/ipynb/mpl/circuit/test_circuit_matplotlib_drawer.py b/test/ipynb/mpl/circuit/test_circuit_matplotlib_drawer.py
--- a/test/ipynb/mpl/circuit/test_circuit_matplotlib_drawer.py
+++ b/test/ipynb/mpl/circuit/test_circuit_matplotlib_drawer.py
@@ -621,6 +621,33 @@ def test_overwide_gates(self):
         circuit.initialize(initial_state)
         self.circuit_drawer(circuit, filename="wide_params.png")
 
+    def test_user_ax_subplot(self):
+        """Test for when user supplies ax for a subplot"""
+        import matplotlib.pyplot as plt
+
+        fig = plt.figure(1, figsize=(6, 4))
+        fig.patch.set_facecolor("white")
+        fig.add_subplot(1, 2, 1)
+        ax2 = fig.add_subplot(1, 2, 2)
+
+        circuit = QuantumCircuit(4)
+        circuit.h(0)
+        circuit.cx(0, 1)
+        circuit.h(1)
+        circuit.cx(1, 2)
+        plt.close(fig)
+        self.circuit_drawer(circuit, ax=ax2, filename="user_ax.png")
+
+    def test_figwidth(self):
+        """Test style dict 'figwidth'"""
+        circuit = QuantumCircuit(3)
+        circuit.h(0)
+        circuit.cx(0, 1)
+        circuit.x(1)
+        circuit.cx(1, 2)
+        circuit.x(2)
+        self.circuit_drawer(circuit, style={"figwidth": 5}, filename="figwidth.png")
+
 
 if __name__ == "__main__":
     unittest.main(verbosity=1)