Example #1
0
    def _tgt_qubit(self, xy, fc=None, ec=None, ac=None,
                   add_width=None):
        if self._style.gc != DefaultStyle().gc:
            fc = self._style.gc
            ec = self._style.gc
        if fc is None:
            fc = self._style.dispcol['target']
        if ec is None:
            ec = self._style.lc
        if ac is None:
            ac = self._style.lc
        if add_width is None:
            add_width = 0.35

        linewidth = 2

        if self._style.dispcol['target'] == '#ffffff':
            add_width = self._style.colored_add_width

        xpos, ypos = xy

        box = patches.Circle(xy=(xpos, ypos), radius=HIG * 0.35,
                             fc=fc, ec=ec, linewidth=linewidth,
                             zorder=PORDER_GATE)
        self.ax.add_patch(box)
        # add '+' symbol
        self.ax.plot([xpos, xpos], [ypos - add_width * HIG,
                                    ypos + add_width * HIG],
                     color=ac, linewidth=linewidth, zorder=PORDER_GATE+1)

        self.ax.plot([xpos - add_width * HIG, xpos + add_width * HIG],
                     [ypos, ypos], color=ac, linewidth=linewidth,
                     zorder=PORDER_GATE+1)
Example #2
0
    def _gate(self, xy, fc=None, wide=False, text=None, subtext=None):
        xpos, ypos = xy

        if wide:
            if subtext:
                boxes_wide = round(max(len(subtext), len(text)) / 10, 1) or 1
                wid = WID * 1.5 * boxes_wide
            else:
                boxes_wide = round(len(text) / 10) or 1
                wid = WID * 2.2 * boxes_wide
        else:
            wid = WID
        if fc:
            _fc = fc
        elif self._style.gc != DefaultStyle().gc:
            _fc = self._style.gc
        elif text and text in self._style.dispcol:
            _fc = self._style.dispcol[text]
        else:
            _fc = self._style.gc

        box = patches.Rectangle(
            xy=(xpos - 0.5 * wid, ypos - 0.5 * HIG), width=wid, height=HIG,
            fc=_fc, ec=self._style.edge_color, linewidth=1.5, zorder=PORDER_GATE)
        self.ax.add_patch(box)

        if text:
            font_size = self._style.fs
            sub_font_size = self._style.sfs
            # check if gate is not unitary
            if text in ['reset']:
                disp_color = self._style.not_gate_lc
                sub_color = self._style.not_gate_lc
                font_size = self._style.math_fs

            else:
                disp_color = self._style.gt
                sub_color = self._style.sc

            if text in self._style.dispcol:
                disp_text = "${}$".format(self._style.disptex[text])
            else:
                disp_text = text

            if subtext:
                self.ax.text(xpos, ypos + 0.15 * HIG, disp_text, ha='center',
                             va='center', fontsize=font_size,
                             color=disp_color, clip_on=True,
                             zorder=PORDER_TEXT)
                self.ax.text(xpos, ypos - 0.3 * HIG, subtext, ha='center',
                             va='center', fontsize=sub_font_size,
                             color=sub_color, clip_on=True,
                             zorder=PORDER_TEXT)
            else:
                self.ax.text(xpos, ypos, disp_text, ha='center', va='center',
                             fontsize=font_size,
                             color=disp_color,
                             clip_on=True,
                             zorder=PORDER_TEXT)
Example #3
0
    def __init__(self,
                 qregs,
                 cregs,
                 ops,
                 scale=1.0,
                 style=None,
                 plot_barriers=True,
                 reverse_bits=False):

        if not HAS_MATPLOTLIB:
            raise ImportError('The class MatplotlibDrawer needs matplotlib. '
                              'Run "pip install matplotlib" before.')

        self._ast = None
        self._scale = DEFAULT_SCALE * scale
        self._creg = []
        self._qreg = []
        self._registers(cregs, qregs)
        self._ops = ops

        self._qreg_dict = collections.OrderedDict()
        self._creg_dict = collections.OrderedDict()
        self._cond = {
            'n_lines': 0,
            'xmax': 0,
            'ymax': 0,
        }
        config = user_config.get_config()
        if config and (style is None):
            config_style = config.get('circuit_mpl_style', 'default')
            if config_style == 'default':
                self._style = DefaultStyle()
            elif config_style == 'bw':
                self._style = BWStyle()
        elif style is False:
            self._style = BWStyle()
        else:
            self._style = DefaultStyle()

        self.plot_barriers = plot_barriers
        self.reverse_bits = reverse_bits
        if style:
            if isinstance(style, dict):
                self._style.set_style(style)
            elif isinstance(style, str):
                with open(style, 'r') as infile:
                    dic = json.load(infile)
                self._style.set_style(dic)

        self.figure = plt.figure()
        self.figure.patch.set_facecolor(color=self._style.bg)
        self.ax = self.figure.add_subplot(111)
        self.ax.axis('off')
        self.ax.set_aspect('equal')
        self.ax.tick_params(labelbottom=False,
                            labeltop=False,
                            labelleft=False,
                            labelright=False)

        self.x_offset = 0
Example #4
0
 def _ctrl_qubit(self, xy, fc=None, ec=None):
     if self._style.gc != DefaultStyle().gc:
         fc = self._style.gc
         ec = self._style.gc
     if fc is None:
         fc = self._style.lc
     if ec is None:
         ec = self._style.lc
     xpos, ypos = xy
     box = patches.Circle(xy=(xpos, ypos), radius=WID * 0.15,
                          fc=fc, ec=ec,
                          linewidth=1.5, zorder=PORDER_GATE)
     self.ax.add_patch(box)
Example #5
0
    def __init__(
        self,
        qubits,
        clbits,
        ops,
        scale,
        reverse_bits=False,
        plot_barriers=True,
        layout=None,
        initial_state=False,
        cregbundle=False,
        global_phase=None,
        qregs=None,
        cregs=None,
    ):
        """QCircuitImage initializer.

        Args:
            qubits (list[Qubit]): list of qubits
            clbits (list[Clbit]): list of clbits
            ops (list[list[DAGNode]]): list of circuit instructions, grouped by layer
            scale (float): image scaling
            reverse_bits (bool): when True, reverse the bit ordering of the registers
            plot_barriers (bool): Enable/disable drawing barriers in the output
               circuit. Defaults to True.
            layout (Layout or None): If present, the layout information will be
               included.
            initial_state (bool): Optional. Adds |0> in the beginning of the line. Default: `False`.
            cregbundle (bool): Optional. If set True bundle classical registers. Default: `False`.
            global_phase (float): Optional, the global phase for the circuit.
            qregs (list): List qregs present in the circuit.
            cregs (list): List of cregs present in the circuit.
        Raises:
            ImportError: If pylatexenc is not installed
        """
        # list of lists corresponding to layers of the circuit
        self.ops = ops

        # image scaling
        self.scale = 0.7 if scale is None else scale

        # Map of qregs to sizes
        self.qregs = {}

        # Map of cregs to sizes
        self.cregs = {}

        # List of qubits and cbits in order of appearance in code and image
        # May also include ClassicalRegisters if cregbundle=True
        self.ordered_bits = []

        # Map from registers to the list they appear in the image
        self.img_regs = {}

        # Array to hold the \\LaTeX commands to generate a circuit image.
        self._latex = []

        # Variable to hold image depth (width)
        self.img_depth = 0

        # Variable to hold image width (height)
        self.img_width = 0

        # Variable to hold total circuit depth
        self.sum_column_widths = 0

        # Variable to hold total circuit width
        self.sum_wire_heights = 0

        # em points of separation between circuit columns
        self.column_separation = 1

        # em points of separation between circuit wire
        self.wire_separation = 0

        # presence of "box" or "target" determines wire spacing
        self.has_box = False
        self.has_target = False
        self.layout = layout
        self.initial_state = initial_state
        self.reverse_bits = reverse_bits
        self.plot_barriers = plot_barriers

        #################################
        self.qubit_list = qubits
        self.ordered_bits = qubits + clbits
        self.cregs = {reg: reg.size for reg in cregs}

        self.bit_locations = {
            bit: {
                "register": register,
                "index": index
            }
            for register in cregs + qregs for index, bit in enumerate(register)
        }
        for index, bit in list(enumerate(qubits)) + list(enumerate(clbits)):
            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 self.cregbundle:
            self.img_width = len(qubits) + len(self.cregs)
        else:
            self.img_width = len(self.img_regs)
        self.global_phase = global_phase

        self._style = DefaultStyle().style
Example #6
0
    def _custom_multiqubit_gate(self, xy, cxy=None, fc=None, wide=True, text=None,
                                subtext=None):
        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 cxy:
            ypos = min([y[1] for y in cxy])
        if wide:
            if subtext:
                boxes_length = round(max([len(text), len(subtext)]) / 6) or 1
            else:
                boxes_length = math.ceil(len(text) / 6) or 1
            wid = WID * 2.5 * boxes_length
        else:
            wid = WID

        if fc:
            _fc = fc
        else:
            if self._style.name != 'bw':
                if self._style.gc != DefaultStyle().gc:
                    _fc = self._style.gc
                else:
                    _fc = self._style.dispcol['multi']
                _ec = self._style.dispcol['multi']
            else:
                _fc = self._style.gc

        qubit_span = abs(ypos) - abs(ypos_max) + 1
        height = HIG + (qubit_span - 1)
        box = patches.Rectangle(
            xy=(xpos - 0.5 * wid, ypos - .5 * HIG),
            width=wid, height=height,
            fc=_fc,
            ec=self._style.dispcol['multi'],
            linewidth=1.5, 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.45 * wid, y, str(bit), ha='left', va='center',
                         fontsize=self._style.fs, color=self._style.gt,
                         clip_on=True, zorder=PORDER_TEXT)

        if text:

            disp_text = text
            if subtext:
                self.ax.text(xpos, ypos + 0.5 * height, disp_text, ha='center',
                             va='center', fontsize=self._style.fs,
                             color=self._style.gt, clip_on=True,
                             zorder=PORDER_TEXT)
                self.ax.text(xpos, ypos + 0.3 * height, subtext, ha='center',
                             va='center', fontsize=self._style.sfs,
                             color=self._style.sc, clip_on=True,
                             zorder=PORDER_TEXT)
            else:
                self.ax.text(xpos, ypos + .5 * (qubit_span - 1), disp_text,
                             ha='center',
                             va='center',
                             fontsize=self._style.fs,
                             color=self._style.gt,
                             clip_on=True,
                             zorder=PORDER_TEXT,
                             wrap=True)
Example #7
0
class MatplotlibDrawer:
    def __init__(self, qregs, cregs, ops,
                 scale=1.0, style=None, plot_barriers=True,
                 reverse_bits=False, layout=None, fold=25, ax=None):

        if not HAS_MATPLOTLIB:
            raise ImportError('The class MatplotlibDrawer needs matplotlib. '
                              'Run "pip install matplotlib" before.')

        self._ast = None
        self._scale = DEFAULT_SCALE * scale
        self._creg = []
        self._qreg = []
        self._registers(cregs, qregs)
        self._ops = ops

        self._qreg_dict = collections.OrderedDict()
        self._creg_dict = collections.OrderedDict()
        self._cond = {
            'n_lines': 0,
            'xmax': 0,
            'ymax': 0,
        }
        config = user_config.get_config()
        if config and (style is None):
            config_style = config.get('circuit_mpl_style', 'default')
            if config_style == 'default':
                self._style = DefaultStyle()
            elif config_style == 'bw':
                self._style = BWStyle()
        elif style is False:
            self._style = BWStyle()
        else:
            self._style = DefaultStyle()

        self.plot_barriers = plot_barriers
        self.reverse_bits = reverse_bits
        self.layout = layout
        if style:
            if isinstance(style, dict):
                self._style.set_style(style)
            elif isinstance(style, str):
                with open(style, 'r') as infile:
                    dic = json.load(infile)
                self._style.set_style(dic)
        if ax is None:
            self.return_fig = True
            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.ax = ax
            self.figure = ax.get_figure()

        # TODO: self._style.fold should be removed after deprecation
        self.fold = self._style.fold or fold
        if self.fold < 2:
            self.fold = -1

        self.ax.axis('off')
        self.ax.set_aspect('equal')
        self.ax.tick_params(labelbottom=False, labeltop=False,
                            labelleft=False, labelright=False)

        self.x_offset = 0

    def _registers(self, creg, qreg):
        self._creg = []
        for r in creg:
            self._creg.append(r)
        self._qreg = []
        for r in qreg:
            self._qreg.append(r)

    @property
    def ast(self):
        return self._ast

    def _custom_multiqubit_gate(self, xy, cxy=None, fc=None, wide=True, text=None,
                                subtext=None):
        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 cxy:
            ypos = min([y[1] for y in cxy])
        if wide:
            if subtext:
                boxes_length = round(max([len(text), len(subtext)]) / 6) or 1
            else:
                boxes_length = math.ceil(len(text) / 6) or 1
            wid = WID * 2.5 * boxes_length
        else:
            wid = WID

        if fc:
            _fc = fc
        else:
            if self._style.name != 'bw':
                if self._style.gc != DefaultStyle().gc:
                    _fc = self._style.gc
                else:
                    _fc = self._style.dispcol['multi']
                _ec = self._style.dispcol['multi']
            else:
                _fc = self._style.gc

        qubit_span = abs(ypos) - abs(ypos_max) + 1
        height = HIG + (qubit_span - 1)
        box = patches.Rectangle(
            xy=(xpos - 0.5 * wid, ypos - .5 * HIG),
            width=wid, height=height,
            fc=_fc,
            ec=self._style.dispcol['multi'],
            linewidth=1.5, 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.45 * wid, y, str(bit), ha='left', va='center',
                         fontsize=self._style.fs, color=self._style.gt,
                         clip_on=True, zorder=PORDER_TEXT)

        if text:

            disp_text = text
            if subtext:
                self.ax.text(xpos, ypos + 0.5 * height, disp_text, ha='center',
                             va='center', fontsize=self._style.fs,
                             color=self._style.gt, clip_on=True,
                             zorder=PORDER_TEXT)
                self.ax.text(xpos, ypos + 0.3 * height, subtext, ha='center',
                             va='center', fontsize=self._style.sfs,
                             color=self._style.sc, clip_on=True,
                             zorder=PORDER_TEXT)
            else:
                self.ax.text(xpos, ypos + .5 * (qubit_span - 1), disp_text,
                             ha='center',
                             va='center',
                             fontsize=self._style.fs,
                             color=self._style.gt,
                             clip_on=True,
                             zorder=PORDER_TEXT,
                             wrap=True)

    def _gate(self, xy, fc=None, wide=False, text=None, subtext=None):
        xpos, ypos = xy

        if wide:
            if subtext:
                boxes_wide = round(max(len(subtext), len(text)) / 10, 1) or 1
                wid = WID * 1.5 * boxes_wide
            else:
                boxes_wide = round(len(text) / 10) or 1
                wid = WID * 2.2 * boxes_wide
        else:
            wid = WID
        if fc:
            _fc = fc
        elif self._style.gc != DefaultStyle().gc:
            _fc = self._style.gc
        elif text and text in self._style.dispcol:
            _fc = self._style.dispcol[text]
        else:
            _fc = self._style.gc

        box = patches.Rectangle(
            xy=(xpos - 0.5 * wid, ypos - 0.5 * HIG), width=wid, height=HIG,
            fc=_fc, ec=self._style.edge_color, linewidth=1.5, zorder=PORDER_GATE)
        self.ax.add_patch(box)

        if text:
            font_size = self._style.fs
            sub_font_size = self._style.sfs
            # check if gate is not unitary
            if text in ['reset']:
                disp_color = self._style.not_gate_lc
                sub_color = self._style.not_gate_lc
                font_size = self._style.math_fs

            else:
                disp_color = self._style.gt
                sub_color = self._style.sc

            if text in self._style.dispcol:
                disp_text = "${}$".format(self._style.disptex[text])
            else:
                disp_text = text

            if subtext:
                self.ax.text(xpos, ypos + 0.15 * HIG, disp_text, ha='center',
                             va='center', fontsize=font_size,
                             color=disp_color, clip_on=True,
                             zorder=PORDER_TEXT)
                self.ax.text(xpos, ypos - 0.3 * HIG, subtext, ha='center',
                             va='center', fontsize=sub_font_size,
                             color=sub_color, clip_on=True,
                             zorder=PORDER_TEXT)
            else:
                self.ax.text(xpos, ypos, disp_text, ha='center', va='center',
                             fontsize=font_size,
                             color=disp_color,
                             clip_on=True,
                             zorder=PORDER_TEXT)

    def _subtext(self, xy, text):
        xpos, ypos = xy

        self.ax.text(xpos, ypos - 0.3 * HIG, text, ha='center', va='top',
                     fontsize=self._style.sfs,
                     color=self._style.tc,
                     clip_on=True,
                     zorder=PORDER_TEXT)

    def _sidetext(self, xy, text):
        xpos, ypos = xy

        # 0.15 = the initial gap, each char means it needs to move
        # another 0.0375 over
        xp = xpos + 0.15 + (0.0375 * len(text))
        self.ax.text(xp, ypos+HIG, text, ha='center', va='top',
                     fontsize=self._style.sfs,
                     color=self._style.tc,
                     clip_on=True,
                     zorder=PORDER_TEXT)

    def _line(self, xy0, xy1, lc=None, ls=None, zorder=PORDER_LINE):
        x0, y0 = xy0
        x1, y1 = xy1
        if lc is None:
            linecolor = self._style.lc
        else:
            linecolor = lc
        if ls is None:
            linestyle = 'solid'
        else:
            linestyle = 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=2,
                         linestyle='solid',
                         zorder=zorder)
            self.ax.plot([x0 - dx, x1 - dx], [y0 - dy, y1 - dy],
                         color=linecolor,
                         linewidth=2,
                         linestyle='solid',
                         zorder=zorder)
        else:
            self.ax.plot([x0, x1], [y0, y1],
                         color=linecolor,
                         linewidth=2,
                         linestyle=linestyle,
                         zorder=zorder)

    def _measure(self, qxy, cxy, cid):
        qx, qy = qxy
        cx, cy = cxy

        self._gate(qxy, fc=self._style.dispcol['meas'])

        # add measure symbol
        arc = patches.Arc(xy=(qx, qy - 0.15 * HIG), width=WID * 0.7,
                          height=HIG * 0.7, theta1=0, theta2=180, fill=False,
                          ec=self._style.not_gate_lc, linewidth=2,
                          zorder=PORDER_GATE)
        self.ax.add_patch(arc)
        self.ax.plot([qx, qx + 0.35 * WID],
                     [qy - 0.15 * HIG, qy + 0.20 * HIG],
                     color=self._style.not_gate_lc, linewidth=2, zorder=PORDER_GATE)
        # arrow
        self._line(qxy, [cx, cy + 0.35 * WID], lc=self._style.cc,
                   ls=self._style.cline)
        arrowhead = patches.Polygon(((cx - 0.20 * WID, cy + 0.35 * WID),
                                     (cx + 0.20 * WID, cy + 0.35 * WID),
                                     (cx, cy)),
                                    fc=self._style.cc,
                                    ec=None)
        self.ax.add_artist(arrowhead)
        # target
        if self._style.bundle:
            self.ax.text(cx + .25, cy + .1, str(cid), ha='left', va='bottom',
                         fontsize=0.8 * self._style.fs,
                         color=self._style.tc,
                         clip_on=True,
                         zorder=PORDER_TEXT)

    def _conds(self, xy, istrue=False):
        xpos, ypos = xy

        if istrue:
            _fc = self._style.lc
        else:
            _fc = self._style.gc

        box = patches.Circle(xy=(xpos, ypos), radius=WID * 0.15,
                             fc=_fc, ec=self._style.lc,
                             linewidth=1.5, zorder=PORDER_GATE)
        self.ax.add_patch(box)

    def _ctrl_qubit(self, xy, fc=None, ec=None):
        if self._style.gc != DefaultStyle().gc:
            fc = self._style.gc
            ec = self._style.gc
        if fc is None:
            fc = self._style.lc
        if ec is None:
            ec = self._style.lc
        xpos, ypos = xy
        box = patches.Circle(xy=(xpos, ypos), radius=WID * 0.15,
                             fc=fc, ec=ec,
                             linewidth=1.5, zorder=PORDER_GATE)
        self.ax.add_patch(box)

    def _tgt_qubit(self, xy, fc=None, ec=None, ac=None,
                   add_width=None):
        if self._style.gc != DefaultStyle().gc:
            fc = self._style.gc
            ec = self._style.gc
        if fc is None:
            fc = self._style.dispcol['target']
        if ec is None:
            ec = self._style.lc
        if ac is None:
            ac = self._style.lc
        if add_width is None:
            add_width = 0.35

        linewidth = 2

        if self._style.dispcol['target'] == '#ffffff':
            add_width = self._style.colored_add_width

        xpos, ypos = xy

        box = patches.Circle(xy=(xpos, ypos), radius=HIG * 0.35,
                             fc=fc, ec=ec, linewidth=linewidth,
                             zorder=PORDER_GATE)
        self.ax.add_patch(box)
        # add '+' symbol
        self.ax.plot([xpos, xpos], [ypos - add_width * HIG,
                                    ypos + add_width * HIG],
                     color=ac, linewidth=linewidth, zorder=PORDER_GATE+1)

        self.ax.plot([xpos - add_width * HIG, xpos + add_width * HIG],
                     [ypos, ypos], color=ac, linewidth=linewidth,
                     zorder=PORDER_GATE+1)

    def _swap(self, xy):
        xpos, ypos = xy
        color = self._style.dispcol['swap']
        self.ax.plot([xpos - 0.20 * WID, xpos + 0.20 * WID],
                     [ypos - 0.20 * WID, ypos + 0.20 * WID],
                     color=color, linewidth=2, zorder=PORDER_LINE+1)
        self.ax.plot([xpos - 0.20 * WID, xpos + 0.20 * WID],
                     [ypos + 0.20 * WID, ypos - 0.20 * WID],
                     color=color, linewidth=2, zorder=PORDER_LINE+1)

    def _barrier(self, config, anc):
        xys = config['coord']
        group = config['group']
        y_reg = []
        for qreg in self._qreg_dict.values():
            if qreg['group'] in group:
                y_reg.append(qreg['y'])
        x0 = xys[0][0]

        box_y0 = min(y_reg) - int(anc / self.fold) * (self._cond['n_lines'] + 1) - 0.5
        box_y1 = max(y_reg) - int(anc / self.fold) * (self._cond['n_lines'] + 1) + 0.5
        box = patches.Rectangle(xy=(x0 - 0.3 * WID, box_y0),
                                width=0.6 * WID, height=box_y1 - box_y0,
                                fc=self._style.bc, ec=None, alpha=0.6,
                                linewidth=1.5, zorder=PORDER_GRAY)
        self.ax.add_patch(box)
        for xy in xys:
            xpos, ypos = xy
            self.ax.plot([xpos, xpos], [ypos + 0.5, ypos - 0.5],
                         linewidth=1, linestyle="dashed",
                         color=self._style.lc,
                         zorder=PORDER_TEXT)

    def _linefeed_mark(self, xy):
        xpos, ypos = xy

        self.ax.plot([xpos - .1, xpos - .1],
                     [ypos, ypos - self._cond['n_lines'] + 1],
                     color=self._style.lc, zorder=PORDER_LINE)
        self.ax.plot([xpos + .1, xpos + .1],
                     [ypos, ypos - self._cond['n_lines'] + 1],
                     color=self._style.lc, zorder=PORDER_LINE)

    def draw(self, filename=None, verbose=False):
        self._draw_regs()
        self._draw_ops(verbose)
        _xl = - self._style.margin[0]
        _xr = self._cond['xmax'] + self._style.margin[1]
        _yb = - self._cond['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 * self._scale * self._style.fs / 72 / WID
        self.figure.set_size_inches(self._style.figwidth, self._style.figwidth * fig_h / fig_w)
        if filename:
            self.figure.savefig(filename, dpi=self._style.dpi,
                                bbox_inches='tight')
        if self.return_fig:
            if get_backend() in ['module://ipykernel.pylab.backend_inline',
                                 'nbAgg']:
                plt.close(self.figure)
            return self.figure

    def _draw_regs(self):

        len_longest_label = 0
        # quantum register
        for ii, reg in enumerate(self._qreg):
            if len(self._qreg) > 1:
                if self.layout is None:
                    label = '${name}_{{{index}}}$'.format(name=reg.register.name, index=reg.index)
                else:
                    label = '$({name}_{{{index}}})\\ q_{{{physical}}}$'.format(
                        name=self.layout[reg.index].register.name,
                        index=self.layout[reg.index].index,
                        physical=reg.index)
            else:
                label = '${name}$'.format(name=reg.register.name)

            if len(label) > len_longest_label:
                len_longest_label = len(label)

            pos = -ii
            self._qreg_dict[ii] = {
                'y': pos,
                'label': label,
                'index': reg.index,
                'group': reg.register
            }
            self._cond['n_lines'] += 1
        # classical register
        if self._creg:
            n_creg = self._creg.copy()
            n_creg.pop(0)
            idx = 0
            y_off = -len(self._qreg)
            for ii, (reg, nreg) in enumerate(itertools.zip_longest(
                    self._creg, n_creg)):
                pos = y_off - idx
                if self._style.bundle:
                    label = '${}$'.format(reg.register.name)
                    self._creg_dict[ii] = {
                        'y': pos,
                        'label': label,
                        'index': reg.index,
                        'group': reg.register
                    }
                    if not (not nreg or reg.register != nreg.register):
                        continue
                else:
                    label = '${}_{{{}}}$'.format(reg.register.name, reg.index)
                    self._creg_dict[ii] = {
                        'y': pos,
                        'label': label,
                        'index': reg.index,
                        'group': reg.register
                    }
                if len(label) > len_longest_label:
                    len_longest_label = len(label)

                self._cond['n_lines'] += 1
                idx += 1

        # 7 is the length of the smallest possible label
        self.x_offset = -.5 + 0.18*(len_longest_label-7)

    def _draw_regs_sub(self, n_fold, feedline_l=False, feedline_r=False):
        # quantum register
        for qreg in self._qreg_dict.values():
            if n_fold == 0:
                label = qreg['label']
            else:
                label = qreg['label']
            y = qreg['y'] - n_fold * (self._cond['n_lines'] + 1)
            self.ax.text(self.x_offset, y, label, ha='right', va='center',
                         fontsize=1.25*self._style.fs,
                         color=self._style.tc,
                         clip_on=True,
                         zorder=PORDER_TEXT)
            self._line([self.x_offset + 0.5, y], [self._cond['xmax'], y],
                       zorder=PORDER_REGLINE)
        # classical register
        this_creg_dict = {}
        for creg in self._creg_dict.values():
            if n_fold == 0:
                label = creg['label']
            else:
                label = creg['label']
            y = creg['y'] - n_fold * (self._cond['n_lines'] + 1)
            if y not in this_creg_dict.keys():
                this_creg_dict[y] = {'val': 1, 'label': label}
            else:
                this_creg_dict[y]['val'] += 1
        for y, this_creg in this_creg_dict.items():
            # bundle
            if this_creg['val'] > 1:
                self.ax.plot([self.x_offset+1.1, self.x_offset+1.2], [y - .1, y + .1],
                             color=self._style.cc,
                             zorder=PORDER_LINE)
                self.ax.text(self.x_offset+1.0, y + .1, str(this_creg['val']), ha='left',
                             va='bottom',
                             fontsize=0.8 * self._style.fs,
                             color=self._style.tc,
                             clip_on=True,
                             zorder=PORDER_TEXT)
            self.ax.text(self.x_offset, y, this_creg['label'], ha='right', va='center',
                         fontsize=1.5*self._style.fs,
                         color=self._style.tc,
                         clip_on=True,
                         zorder=PORDER_TEXT)
            self._line([self.x_offset + 0.5, y], [self._cond['xmax'], y], lc=self._style.cc,
                       ls=self._style.cline, zorder=PORDER_REGLINE)

        # lf line
        if feedline_r:
            self._linefeed_mark((self.fold + 1 - 0.1,
                                 - n_fold * (self._cond['n_lines'] + 1)))
        if feedline_l:
            self._linefeed_mark((0.1,
                                 - n_fold * (self._cond['n_lines'] + 1)))

    def _draw_ops(self, verbose=False):
        _wide_gate = ['u2', 'u3', 'cu2', 'cu3', 'unitary', 'r']
        _barriers = {'coord': [], 'group': []}

        #
        # generate coordinate manager
        #
        q_anchors = {}
        for key, qreg in self._qreg_dict.items():
            q_anchors[key] = Anchor(reg_num=self._cond['n_lines'],
                                    yind=qreg['y'],
                                    fold=self.fold)
        c_anchors = {}
        for key, creg in self._creg_dict.items():
            c_anchors[key] = Anchor(reg_num=self._cond['n_lines'],
                                    yind=creg['y'],
                                    fold=self.fold)
        #
        # draw gates
        #
        prev_anc = -1
        for layer in self._ops:
            layer_width = 1

            for op in layer:

                if op.name in _wide_gate:
                    if op.type == 'op' and hasattr(op.op, 'params'):
                        param = self.param_parse(op.op.params)
                        if '$\\pi$' in param:
                            pi_count = param.count('pi')
                            len_param = len(param) - (5 * pi_count)
                        else:
                            len_param = len(param)
                        if len_param > len(op.name):
                            box_width = round(len(param) / 10)
                            # If more than 4 characters min width is 2
                            if box_width <= 1:
                                box_width = 2
                            if layer_width < box_width:
                                if box_width > 2:
                                    layer_width = box_width * 2
                                else:
                                    layer_width = 2
                    if layer_width < 2:
                        layer_width = 2

                # if custom gate with a longer than standard name determine
                # width
                elif op.name not in ['barrier', 'snapshot', 'load', 'save',
                                     'noise', 'cswap', 'swap', 'measure'] and len(
                                         op.name) >= 4:
                    box_width = math.ceil(len(op.name) / 6)

                    # handle params/subtext longer than op names
                    if op.type == 'op' and hasattr(op.op, 'params'):
                        param = self.param_parse(op.op.params)
                        if '$\\pi$' in param:
                            pi_count = param.count('pi')
                            len_param = len(param) - (5 * pi_count)
                        else:
                            len_param = len(param)
                        if len_param > len(op.name):
                            box_width = round(len(param) / 8)
                            # If more than 4 characters min width is 2
                            if box_width <= 1:
                                box_width = 2
                            if layer_width < box_width:
                                if box_width > 2:
                                    layer_width = box_width * 2
                                else:
                                    layer_width = 2
                            continue
                    # If more than 4 characters min width is 2
                    layer_width = math.ceil(box_width * WID * 2.5)

            this_anc = prev_anc + 1

            for op in layer:

                _iswide = op.name in _wide_gate
                if op.name not in ['barrier', 'snapshot', 'load', 'save',
                                   'noise', 'cswap', 'swap', 'measure',
                                   'reset'] and len(op.name) >= 4:
                    _iswide = True

                # get qreg index
                q_idxs = []
                for qarg in op.qargs:
                    for index, reg in self._qreg_dict.items():
                        if (reg['group'] == qarg.register and
                                reg['index'] == qarg.index):
                            q_idxs.append(index)
                            break

                # get creg index
                c_idxs = []
                for carg in op.cargs:
                    for index, reg in self._creg_dict.items():
                        if (reg['group'] == carg.register and
                                reg['index'] == 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 \
                        op.name not in ['barrier', 'snapshot', 'load', 'save',
                                        'noise']:

                    for ii in q_idxs:
                        q_anchors[ii].set_index(this_anc, layer_width)

                # qreg coordinate
                q_xy = [q_anchors[ii].plot_coord(this_anc, layer_width, self.x_offset)
                        for ii in q_idxs]
                # creg 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 qreg
                qreg_b = min(q_xy, key=lambda xy: xy[1])
                qreg_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)

                if op.type == 'op' and hasattr(op.op, 'params'):
                    param = self.param_parse(op.op.params)
                else:
                    param = None
                # conditional gate
                if op.condition:
                    c_xy = [c_anchors[ii].plot_coord(this_anc, layer_width, self.x_offset) for
                            ii in self._creg_dict]
                    mask = 0
                    for index, cbit in enumerate(self._creg):
                        if cbit.register == op.condition[0]:
                            mask |= (1 << index)
                    val = op.condition[1]
                    # cbit list to consider
                    fmt_c = '{{:0{}b}}'.format(len(c_xy))
                    cmask = list(fmt_c.format(mask))[::-1]
                    # value
                    fmt_v = '{{:0{}b}}'.format(cmask.count('1'))
                    vlist = list(fmt_v.format(val))[::-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._style.bundle:
                                    self._conds(xy, istrue=True)
                                else:
                                    self._conds(xy, istrue=False)
                                xy_plot.append(xy)
                            v_ind += 1
                    creg_b = sorted(xy_plot, key=lambda xy: xy[1])[0]
                    self._subtext(creg_b, hex(val))
                    self._line(qreg_t, creg_b, lc=self._style.cc,
                               ls=self._style.cline)
                #
                # draw special gates
                #
                if op.name == 'measure':
                    vv = self._creg_dict[c_idxs[0]]['index']
                    self._measure(q_xy[0], c_xy[0], vv)
                elif op.name in ['barrier', 'snapshot', 'load', 'save',
                                 'noise']:
                    _barriers = {'coord': [], 'group': []}
                    for index, qbit in enumerate(q_idxs):
                        q_group = self._qreg_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, this_anc)
                elif op.name == 'initialize':
                    vec = '[%s]' % param
                    self._custom_multiqubit_gate(q_xy, wide=_iswide,
                                                 text="|psi>",
                                                 subtext=vec)
                elif op.name == 'unitary':
                    # TODO(mtreinish): Look into adding the unitary to the
                    # subtext
                    self._custom_multiqubit_gate(q_xy, wide=_iswide,
                                                 text="U")
                #
                # draw single qubit gates
                #
                elif len(q_xy) == 1:
                    disp = op.name
                    if param:
                        self._gate(q_xy[0], wide=_iswide, text=disp,
                                   subtext=str(param))
                    else:
                        self._gate(q_xy[0], wide=_iswide, text=disp)
                #
                # draw multi-qubit gates (n=2)
                #
                elif len(q_xy) == 2:
                    # cx
                    if op.name == 'cx':
                        if self._style.dispcol['cx'] != '#ffffff':
                            add_width = self._style.colored_add_width
                        else:
                            add_width = None
                        self._ctrl_qubit(q_xy[0], fc=self._style.dispcol['cx'],
                                         ec=self._style.dispcol['cx'])
                        if self._style.name != 'bw':
                            self._tgt_qubit(q_xy[1], fc=self._style.dispcol['cx'],
                                            ec=self._style.dispcol['cx'],
                                            ac=self._style.dispcol['target'],
                                            add_width=add_width)
                        else:
                            self._tgt_qubit(q_xy[1], fc=self._style.dispcol['target'],
                                            ec=self._style.dispcol['cx'],
                                            ac=self._style.dispcol['cx'],
                                            add_width=add_width)
                        # add qubit-qubit wiring
                        self._line(qreg_b, qreg_t, lc=self._style.dispcol['cx'])
                    # cz for latexmode
                    elif op.name == 'cz':
                        disp = op.name.replace('c', '')
                        if self._style.name != 'bw':
                            color = self._style.dispcol['multi']
                            self._ctrl_qubit(q_xy[0],
                                             fc=color,
                                             ec=color)
                        else:
                            self._ctrl_qubit(q_xy[0])
                        self._gate(q_xy[1], wide=_iswide, text=disp, fc=color)
                        # add qubit-qubit wiring
                        if self._style.name != 'bw':
                            self._line(qreg_b, qreg_t,
                                       lc=self._style.dispcol['multi'])
                        else:
                            self._line(qreg_b, qreg_t, zorder=PORDER_LINE+1)
                    # control gate
                    elif op.name in ['cy', 'ch', 'cu3', 'cu1', 'crz']:
                        disp = op.name.replace('c', '')

                        color = None
                        if self._style.name != 'bw':
                            color = self._style.dispcol['multi']

                        self._ctrl_qubit(q_xy[0], fc=color, ec=color)
                        if param:
                            self._gate(q_xy[1], wide=_iswide,
                                       text=disp,
                                       fc=color,
                                       subtext='{}'.format(param))
                        else:
                            self._gate(q_xy[1], wide=_iswide, text=disp,
                                       fc=color)
                        # add qubit-qubit wiring
                        self._line(qreg_b, qreg_t, lc=color)

                    # rzz gate
                    elif op.name == 'rzz':
                        self._ctrl_qubit(q_xy[0])
                        self._ctrl_qubit(q_xy[1])
                        self._sidetext(qreg_b, text='zz({})'.format(param))

                        # add qubit-qubit wiring
                        self._line(qreg_b, qreg_t)
                    # swap gate
                    elif op.name == 'swap':
                        self._swap(q_xy[0])
                        self._swap(q_xy[1])
                        # add qubit-qubit wiring
                        self._line(qreg_b, qreg_t, lc=self._style.dispcol['swap'])
                    # Custom gate
                    else:
                        self._custom_multiqubit_gate(q_xy, c_xy, wide=_iswide,
                                                     text=op.name)
                #
                # draw multi-qubit gates (n=3)
                #
                elif len(q_xy) == 3:
                    # cswap gate
                    if op.name == 'cswap':
                        self._ctrl_qubit(q_xy[0],
                                         fc=self._style.dispcol['multi'],
                                         ec=self._style.dispcol['multi'])
                        self._swap(q_xy[1])
                        self._swap(q_xy[2])
                        # add qubit-qubit wiring
                        self._line(qreg_b, qreg_t, lc=self._style.dispcol['multi'])
                    # ccx gate
                    elif op.name == 'ccx':
                        self._ctrl_qubit(q_xy[0], fc=self._style.dispcol['multi'],
                                         ec=self._style.dispcol['multi'])
                        self._ctrl_qubit(q_xy[1], fc=self._style.dispcol['multi'],
                                         ec=self._style.dispcol['multi'])
                        if self._style.name != 'bw':
                            self._tgt_qubit(q_xy[2], fc=self._style.dispcol['multi'],
                                            ec=self._style.dispcol['multi'],
                                            ac=self._style.dispcol['target'])
                        else:
                            self._tgt_qubit(q_xy[2], fc=self._style.dispcol['target'],
                                            ec=self._style.dispcol['multi'],
                                            ac=self._style.dispcol['multi'])
                        # add qubit-qubit wiring
                        self._line(qreg_b, qreg_t, lc=self._style.dispcol['multi'])
                    # custom gate
                    else:
                        self._custom_multiqubit_gate(q_xy, c_xy, wide=_iswide,
                                                     text=op.name)

                # draw custom multi-qubit gate
                elif len(q_xy) > 3:
                    self._custom_multiqubit_gate(q_xy, c_xy, wide=_iswide,
                                                 text=op.name)
                else:
                    logger.critical('Invalid gate %s', op)
                    raise exceptions.VisualizationError('invalid gate {}'.format(op))

            # 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.name in
                                            ['barrier', 'snapshot', 'load', 'save', 'noise']
                                            for op in layer]) else 0
            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._qreg_dict]
        if anchors:
            max_anc = max(anchors)
        else:
            max_anc = 0
        n_fold = max(0, max_anc - 1) // self.fold
        # window size
        if max_anc > self.fold > 0:
            self._cond['xmax'] = self.fold + 1 + self.x_offset
            self._cond['ymax'] = (n_fold + 1) * (self._cond['n_lines'] + 1) - 1
        else:
            self._cond['xmax'] = max_anc + 1 + self.x_offset
            self._cond['ymax'] = self._cond['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)
        # draw gate number
        if self._style.index:
            for ii in range(max_anc):
                if self.fold > 0:
                    x_coord = ii % self.fold + 1
                    y_coord = - (ii // self.fold) * (self._cond['n_lines'] + 1) + 0.7
                else:
                    x_coord = ii + 1
                    y_coord = 0.7
                self.ax.text(x_coord, y_coord, str(ii + 1), ha='center',
                             va='center', fontsize=self._style.sfs,
                             color=self._style.tc, clip_on=True,
                             zorder=PORDER_TEXT)

    @staticmethod
    def param_parse(v):
        # create an empty list to store the parameters in
        param_parts = [None] * len(v)
        for i, e in enumerate(v):
            try:
                param_parts[i] = pi_check(e, output='mpl', ndigits=3)
            except TypeError:
                param_parts[i] = str(e)

            if param_parts[i].startswith('-'):
                param_parts[i] = '$-$' + param_parts[i][1:]

        param_parts = ', '.join(param_parts)
        return param_parts

    @staticmethod
    def format_numeric(val, tol=1e-5):
        if isinstance(val, complex):
            return str(val)
        elif complex(val).imag != 0:
            val = complex(val)
        abs_val = abs(val)
        if math.isclose(abs_val, 0.0, abs_tol=1e-100):
            return '0'
        if math.isclose(math.fmod(abs_val, 1.0),
                        0.0, abs_tol=tol) and 0.5 < abs_val < 9999.5:
            return str(int(val))
        if 0.1 <= abs_val < 100.0:
            return '{:.2f}'.format(val)
        return '{:.1e}'.format(val)

    @staticmethod
    def fraction(val, base=np.pi, n=100, tol=1e-5):
        abs_val = abs(val)
        for i in range(1, n):
            for j in range(1, n):
                if math.isclose(abs_val, i / j * base, rel_tol=tol):
                    if val < 0:
                        i *= -1
                    return fractions.Fraction(i, j)
        return None
Example #8
0
    def _load_style(self, style):
        current_style = DefaultStyle().style
        style_name = 'default'
        def_font_ratio = current_style['fs'] / current_style['sfs']

        config = user_config.get_config()
        if style is not None:
            if style is False:
                style_name = 'bw'
            elif isinstance(style, dict) and 'name' in style:
                style_name = style['name']
            elif isinstance(style, str):
                style_name = style
            elif config:
                style_name = config.get('circuit_mpl_style', 'default')
            elif not isinstance(style, (str, dict)):
                warn("style parameter '{}' must be a str or a dictionary."
                     " Will use default style.".format(style), UserWarning, 2)
        if style_name.endswith('.json'):
            style_name = style_name[:-5]

        # Search for file in 'styles' dir, then config_path, and finally 'cwd'
        style_path = []
        if style_name != 'default':
            style_name = style_name + '.json'
            spath = os.path.dirname(os.path.abspath(__file__))
            style_path.append(os.path.join(spath, 'styles', style_name))
            if config:
                config_path = config.get('circuit_mpl_style_path', '')
                if config_path:
                    for path in config_path:
                        style_path.append(os.path.normpath(os.path.join(path, style_name)))
            style_path.append(os.path.normpath(os.path.join('', style_name)))

            for path in style_path:
                exp_user = os.path.expanduser(path)
                if os.path.isfile(exp_user):
                    try:
                        with open(exp_user) as infile:
                            json_style = json.load(infile)
                        set_style(current_style, json_style)
                        break
                    except json.JSONDecodeError as e:
                        warn("Could not decode JSON in file '{}': {}. ".format(
                            path, str(e)) + "Will use default style.", UserWarning, 2)
                        break
                    except (OSError, FileNotFoundError):
                        warn("Error loading JSON file '{}'. Will use default style.".format(
                            path), UserWarning, 2)
                        break
            else:
                warn("Style JSON file '{}' not found in any of these locations: {}. Will use"
                     " default style.".format(style_name, ', '.join(style_path)), UserWarning, 2)

        if isinstance(style, dict):
            set_style(current_style, style)

        # If font/subfont ratio changes from default, have to scale width calculations for
        # subfont. Font change is auto scaled in the self._figure.set_size_inches call in draw()
        self._subfont_factor = current_style['sfs'] * def_font_ratio / current_style['fs']

        return current_style
Example #9
0
    def __init__(self, qubits, clbits, ops, scale, reverse_bits=False,
                 plot_barriers=True, layout=None, initial_state=False,
                 cregbundle=False, global_phase=None):
        """QCircuitImage initializer.

        Args:
            qubits (list[Qubit]): list of qubits
            clbits (list[Clbit]): list of clbits
            ops (list[list[DAGNode]]): list of circuit instructions, grouped by layer
            scale (float): image scaling
            reverse_bits: when True, reverse the bit ordering of the registers
            plot_barriers (bool): Enable/disable drawing barriers in the output
               circuit. Defaults to True.
            layout (Layout or None): If present, the layout information will be
               included.
            initial_state (bool): Optional. Adds |0> in the beginning of the line. Default: `False`.
            cregbundle (bool): Optional. If set True bundle classical registers. Default: `False`.
            global_phase (float): Optional, the global phase for the circuit.
        Raises:
            ImportError: If pylatexenc is not installed
        """
        # list of lists corresponding to layers of the circuit
        self.ops = ops

        # image scaling
        self.scale = 0.7 if scale is None else scale

        # Map of qregs to sizes
        self.qregs = {}

        # Map of cregs to sizes
        self.cregs = {}

        # List of qregs and cregs in order of appearance in code and image
        self.ordered_regs = []

        # Map from registers to the list they appear in the image
        self.img_regs = {}

        # Array to hold the \\LaTeX commands to generate a circuit image.
        self._latex = []

        # Variable to hold image depth (width)
        self.img_depth = 0

        # Variable to hold image width (height)
        self.img_width = 0

        # Variable to hold total circuit depth
        self.sum_column_widths = 0

        # Variable to hold total circuit width
        self.sum_wire_heights = 0

        # em points of separation between circuit columns
        self.column_separation = 1

        # em points of separation between circuit wire
        self.wire_separation = 0

        # presence of "box" or "target" determines wire spacing
        self.has_box = False
        self.has_target = False
        self.layout = layout
        self.initial_state = initial_state
        self.reverse_bits = reverse_bits
        self.plot_barriers = plot_barriers

        #################################
        self.qubit_list = qubits
        self.ordered_regs = qubits + clbits
        self.cregs, self.cregs_bits = self._get_register_specs(clbits)
        self.img_regs = {bit: ind for ind, bit in
                         enumerate(self.ordered_regs)}
        if 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
Example #10
0
    def __init__(self, qregs, cregs, ops,
                 scale=1.0, style=None, plot_barriers=True,
                 reverse_bits=False, layout=None, fold=25, ax=None,
                 initial_state=False, cregbundle=True):

        if not HAS_MATPLOTLIB:
            raise ImportError('The class MatplotlibDrawer needs matplotlib. '
                              'To install, run "pip install matplotlib".')

        self._ast = None
        self._scale = DEFAULT_SCALE * scale
        self._creg = []
        self._qreg = []
        self._registers(cregs, qregs)
        self._ops = ops

        self._qreg_dict = collections.OrderedDict()
        self._creg_dict = collections.OrderedDict()
        self._cond = {
            'n_lines': 0,
            'xmax': 0,
            'ymax': 0,
        }
        config = user_config.get_config()
        if config and (style is None):
            config_style = config.get('circuit_mpl_style', 'default')
            if config_style == 'default':
                self._style = DefaultStyle()
            elif config_style == 'bw':
                self._style = BWStyle()
        elif style is False:
            self._style = BWStyle()
        else:
            self._style = DefaultStyle()

        self.plot_barriers = plot_barriers
        self.reverse_bits = reverse_bits
        self.layout = layout
        self.initial_state = initial_state
        if style and 'cregbundle' in style.keys():
            self.cregbundle = style['cregbundle']
            del style['cregbundle']
            warn("The style dictionary key 'cregbundle' has been deprecated and will be removed"
                 " in a future release. cregbundle is now a parameter to draw()."
                 " Example: circuit.draw(output='mpl', cregbundle=False)", DeprecationWarning, 2)
        else:
            self.cregbundle = cregbundle

        if style:
            if isinstance(style, dict):
                self._style.set_style(style)
            elif isinstance(style, str):
                with open(style, 'r') as infile:
                    dic = json.load(infile)
                self._style.set_style(dic)

        if ax is None:
            self.return_fig = True
            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.ax = ax
            self.figure = ax.get_figure()

        self.x_offset = 0
        self._reg_long_text = 0

        # default is to use character table for text width,
        # but get_renderer will work with some mpl backends
        """fig = plt.figure()
        if hasattr(fig.canvas, 'get_renderer'):
            self.renderer = fig.canvas.get_renderer()
        else:
            self.renderer = None"""
        self.renderer = None

        self.fold = fold
        if self.fold < 2:
            self.fold = -1

        self.ax.axis('off')
        self.ax.set_aspect('equal')
        self.ax.tick_params(labelbottom=False, labeltop=False,
                            labelleft=False, labelright=False)

        # these char arrays are for finding text_width when not
        # using get_renderer method for the matplotlib backend
        self._latex_chars = ('$', '{', '}', '_', '\\left', '\\right',
                             '\\dagger', '\\rangle')
        self._latex_chars1 = ('\\mapsto', '\\pi', '\\;')
        self._char_list = {' ': (0.0958, 0.0583), '!': (0.1208, 0.0729), '"': (0.1396, 0.0875),
                           '#': (0.2521, 0.1562), '$': (0.1917, 0.1167), '%': (0.2854, 0.1771),
                           '&': (0.2333, 0.1458), "'": (0.0833, 0.0521), '(': (0.1167, 0.0729),
                           ')': (0.1167, 0.0729), '*': (0.15, 0.0938), '+': (0.25, 0.1562),
                           ',': (0.0958, 0.0583), '-': (0.1083, 0.0667), '.': (0.0958, 0.0604),
                           '/': (0.1021, 0.0625), '0': (0.1875, 0.1167), '1': (0.1896, 0.1167),
                           '2': (0.1917, 0.1188), '3': (0.1917, 0.1167), '4': (0.1917, 0.1188),
                           '5': (0.1917, 0.1167), '6': (0.1896, 0.1167), '7': (0.1917, 0.1188),
                           '8': (0.1896, 0.1188), '9': (0.1917, 0.1188), ':': (0.1021, 0.0604),
                           ';': (0.1021, 0.0604), '<': (0.25, 0.1542), '=': (0.25, 0.1562),
                           '>': (0.25, 0.1542), '?': (0.1583, 0.0979), '@': (0.2979, 0.1854),
                           'A': (0.2062, 0.1271), 'B': (0.2042, 0.1271), 'C': (0.2083, 0.1292),
                           'D': (0.2312, 0.1417), 'E': (0.1875, 0.1167), 'F': (0.1708, 0.1062),
                           'G': (0.2312, 0.1438), 'H': (0.225, 0.1396), 'I': (0.0875, 0.0542),
                           'J': (0.0875, 0.0542), 'K': (0.1958, 0.1208), 'L': (0.1667, 0.1042),
                           'M': (0.2583, 0.1604), 'N': (0.225, 0.1396), 'O': (0.2354, 0.1458),
                           'P': (0.1812, 0.1125), 'Q': (0.2354, 0.1458), 'R': (0.2083, 0.1292),
                           'S': (0.1896, 0.1188), 'T': (0.1854, 0.1125), 'U': (0.2208, 0.1354),
                           'V': (0.2062, 0.1271), 'W': (0.2958, 0.1833), 'X': (0.2062, 0.1271),
                           'Y': (0.1833, 0.1125), 'Z': (0.2042, 0.1271), '[': (0.1167, 0.075),
                           '\\': (0.1021, 0.0625), ']': (0.1167, 0.0729), '^': (0.2521, 0.1562),
                           '_': (0.1521, 0.0938), '`': (0.15, 0.0938), 'a': (0.1854, 0.1146),
                           'b': (0.1917, 0.1167), 'c': (0.1646, 0.1021), 'd': (0.1896, 0.1188),
                           'e': (0.1854, 0.1146), 'f': (0.1042, 0.0667), 'g': (0.1896, 0.1188),
                           'h': (0.1896, 0.1188), 'i': (0.0854, 0.0521), 'j': (0.0854, 0.0521),
                           'k': (0.1729, 0.1083), 'l': (0.0854, 0.0521), 'm': (0.2917, 0.1812),
                           'n': (0.1896, 0.1188), 'o': (0.1833, 0.1125), 'p': (0.1917, 0.1167),
                           'q': (0.1896, 0.1188), 'r': (0.125, 0.0771), 's': (0.1562, 0.0958),
                           't': (0.1167, 0.0729), 'u': (0.1896, 0.1188), 'v': (0.1771, 0.1104),
                           'w': (0.2458, 0.1521), 'x': (0.1771, 0.1104), 'y': (0.1771, 0.1104),
                           'z': (0.1562, 0.0979), '{': (0.1917, 0.1188), '|': (0.1, 0.0604),
                           '}': (0.1896, 0.1188)}
Example #11
0
class MatplotlibDrawer:
    def __init__(self, qregs, cregs, ops,
                 scale=1.0, style=None, plot_barriers=True,
                 reverse_bits=False, layout=None, fold=25, ax=None,
                 initial_state=False, cregbundle=True):

        if not HAS_MATPLOTLIB:
            raise ImportError('The class MatplotlibDrawer needs matplotlib. '
                              'To install, run "pip install matplotlib".')

        self._ast = None
        self._scale = DEFAULT_SCALE * scale
        self._creg = []
        self._qreg = []
        self._registers(cregs, qregs)
        self._ops = ops

        self._qreg_dict = collections.OrderedDict()
        self._creg_dict = collections.OrderedDict()
        self._cond = {
            'n_lines': 0,
            'xmax': 0,
            'ymax': 0,
        }
        config = user_config.get_config()
        if config and (style is None):
            config_style = config.get('circuit_mpl_style', 'default')
            if config_style == 'default':
                self._style = DefaultStyle()
            elif config_style == 'bw':
                self._style = BWStyle()
        elif style is False:
            self._style = BWStyle()
        else:
            self._style = DefaultStyle()

        self.plot_barriers = plot_barriers
        self.reverse_bits = reverse_bits
        self.layout = layout
        self.initial_state = initial_state
        if style and 'cregbundle' in style.keys():
            self.cregbundle = style['cregbundle']
            del style['cregbundle']
            warn("The style dictionary key 'cregbundle' has been deprecated and will be removed"
                 " in a future release. cregbundle is now a parameter to draw()."
                 " Example: circuit.draw(output='mpl', cregbundle=False)", DeprecationWarning, 2)
        else:
            self.cregbundle = cregbundle

        if style:
            if isinstance(style, dict):
                self._style.set_style(style)
            elif isinstance(style, str):
                with open(style, 'r') as infile:
                    dic = json.load(infile)
                self._style.set_style(dic)

        if ax is None:
            self.return_fig = True
            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.ax = ax
            self.figure = ax.get_figure()

        self.x_offset = 0
        self._reg_long_text = 0

        # default is to use character table for text width,
        # but get_renderer will work with some mpl backends
        """fig = plt.figure()
        if hasattr(fig.canvas, 'get_renderer'):
            self.renderer = fig.canvas.get_renderer()
        else:
            self.renderer = None"""
        self.renderer = None

        self.fold = fold
        if self.fold < 2:
            self.fold = -1

        self.ax.axis('off')
        self.ax.set_aspect('equal')
        self.ax.tick_params(labelbottom=False, labeltop=False,
                            labelleft=False, labelright=False)

        # these char arrays are for finding text_width when not
        # using get_renderer method for the matplotlib backend
        self._latex_chars = ('$', '{', '}', '_', '\\left', '\\right',
                             '\\dagger', '\\rangle')
        self._latex_chars1 = ('\\mapsto', '\\pi', '\\;')
        self._char_list = {' ': (0.0958, 0.0583), '!': (0.1208, 0.0729), '"': (0.1396, 0.0875),
                           '#': (0.2521, 0.1562), '$': (0.1917, 0.1167), '%': (0.2854, 0.1771),
                           '&': (0.2333, 0.1458), "'": (0.0833, 0.0521), '(': (0.1167, 0.0729),
                           ')': (0.1167, 0.0729), '*': (0.15, 0.0938), '+': (0.25, 0.1562),
                           ',': (0.0958, 0.0583), '-': (0.1083, 0.0667), '.': (0.0958, 0.0604),
                           '/': (0.1021, 0.0625), '0': (0.1875, 0.1167), '1': (0.1896, 0.1167),
                           '2': (0.1917, 0.1188), '3': (0.1917, 0.1167), '4': (0.1917, 0.1188),
                           '5': (0.1917, 0.1167), '6': (0.1896, 0.1167), '7': (0.1917, 0.1188),
                           '8': (0.1896, 0.1188), '9': (0.1917, 0.1188), ':': (0.1021, 0.0604),
                           ';': (0.1021, 0.0604), '<': (0.25, 0.1542), '=': (0.25, 0.1562),
                           '>': (0.25, 0.1542), '?': (0.1583, 0.0979), '@': (0.2979, 0.1854),
                           'A': (0.2062, 0.1271), 'B': (0.2042, 0.1271), 'C': (0.2083, 0.1292),
                           'D': (0.2312, 0.1417), 'E': (0.1875, 0.1167), 'F': (0.1708, 0.1062),
                           'G': (0.2312, 0.1438), 'H': (0.225, 0.1396), 'I': (0.0875, 0.0542),
                           'J': (0.0875, 0.0542), 'K': (0.1958, 0.1208), 'L': (0.1667, 0.1042),
                           'M': (0.2583, 0.1604), 'N': (0.225, 0.1396), 'O': (0.2354, 0.1458),
                           'P': (0.1812, 0.1125), 'Q': (0.2354, 0.1458), 'R': (0.2083, 0.1292),
                           'S': (0.1896, 0.1188), 'T': (0.1854, 0.1125), 'U': (0.2208, 0.1354),
                           'V': (0.2062, 0.1271), 'W': (0.2958, 0.1833), 'X': (0.2062, 0.1271),
                           'Y': (0.1833, 0.1125), 'Z': (0.2042, 0.1271), '[': (0.1167, 0.075),
                           '\\': (0.1021, 0.0625), ']': (0.1167, 0.0729), '^': (0.2521, 0.1562),
                           '_': (0.1521, 0.0938), '`': (0.15, 0.0938), 'a': (0.1854, 0.1146),
                           'b': (0.1917, 0.1167), 'c': (0.1646, 0.1021), 'd': (0.1896, 0.1188),
                           'e': (0.1854, 0.1146), 'f': (0.1042, 0.0667), 'g': (0.1896, 0.1188),
                           'h': (0.1896, 0.1188), 'i': (0.0854, 0.0521), 'j': (0.0854, 0.0521),
                           'k': (0.1729, 0.1083), 'l': (0.0854, 0.0521), 'm': (0.2917, 0.1812),
                           'n': (0.1896, 0.1188), 'o': (0.1833, 0.1125), 'p': (0.1917, 0.1167),
                           'q': (0.1896, 0.1188), 'r': (0.125, 0.0771), 's': (0.1562, 0.0958),
                           't': (0.1167, 0.0729), 'u': (0.1896, 0.1188), 'v': (0.1771, 0.1104),
                           'w': (0.2458, 0.1521), 'x': (0.1771, 0.1104), 'y': (0.1771, 0.1104),
                           'z': (0.1562, 0.0979), '{': (0.1917, 0.1188), '|': (0.1, 0.0604),
                           '}': (0.1896, 0.1188)}

    def _registers(self, creg, qreg):
        self._creg = []
        for r in creg:
            self._creg.append(r)
        self._qreg = []
        for r in qreg:
            self._qreg.append(r)

    @property
    def ast(self):
        return self._ast

    # This computes the width of a string in the default font
    def _get_text_width(self, text, fontsize):
        if not text:
            return 0.0

        if self.renderer:
            t = plt.text(0.5, 0.5, text, fontsize=fontsize)
            return t.get_window_extent(renderer=self.renderer).width / 60.0
        else:
            # if not using a get_renderer method, first remove
            # any latex chars before getting width
            for t in self._latex_chars1:
                text = text.replace(t, 'r')
            for t in self._latex_chars:
                text = text.replace(t, '')

            f = 0 if fontsize == self._style.fs else 1
            sum_text = 0.0
            for c in text:
                try:
                    sum_text += self._char_list[c][f]
                except KeyError:
                    # if non-ASCII char, use width of 'r', an average size
                    sum_text += self._char_list['r'][f]
            return sum_text

    def param_parse(self, v):
        # create an empty list to store the parameters in
        param_parts = [None] * len(v)
        for i, e in enumerate(v):
            try:
                param_parts[i] = pi_check(e, output='mpl', ndigits=3)
            except TypeError:
                param_parts[i] = str(e)

            if param_parts[i].startswith('-'):
                param_parts[i] = '$-$' + param_parts[i][1:]

        param_parts = ', '.join(param_parts)
        # remove $'s since "${}$".format will add them back on the outside
        param_parts = param_parts.replace('$', '')
        param_parts = param_parts.replace('-', u'\u02d7')
        return param_parts

    def _get_gate_ctrl_text(self, op):
        op_label = getattr(op.op, 'label', None)
        base_name = None if not hasattr(op.op, 'base_gate') else op.op.base_gate.name
        base_label = None if not hasattr(op.op, 'base_gate') else op.op.base_gate.label
        ctrl_text = None
        if base_label:
            gate_text = base_label
            ctrl_text = op_label
        elif op_label and isinstance(op.op, ControlledGate):
            gate_text = base_name
            ctrl_text = op_label
        elif op_label:
            gate_text = op_label
        elif base_name:
            gate_text = base_name
        else:
            gate_text = op.name

        if gate_text in self._style.disptex:
            gate_text = "${}$".format(self._style.disptex[gate_text])
        else:
            gate_text = "${}$".format(gate_text[0].upper() + gate_text[1:])

        # mathtext .format removes spaces so add them back and it changes
        # hyphen to wide minus sign, so use unicode hyphen to prevent that
        gate_text = gate_text.replace(' ', '\\;')
        gate_text = gate_text.replace('-', u'\u02d7')
        if ctrl_text:
            ctrl_text = "${}$".format(ctrl_text[0].upper() + ctrl_text[1:])
            ctrl_text = ctrl_text.replace(' ', '\\;')
            ctrl_text = ctrl_text.replace('-', u'\u02d7')
        return gate_text, ctrl_text

    def _get_colors(self, op):
        if op.name in self._style.dispcol:
            fc = self._style.dispcol[op.name]
        else:
            fc = self._style.gc
        if self._style.name != 'bw':
            ec = fc
            lc = fc
        else:
            ec = self._style.edge_color
            lc = self._style.lc
        if op.name == 'reset':
            gt = self._style.not_gate_lc
        else:
            gt = self._style.gt
        return fc, ec, gt, self._style.tc, self._style.sc, lc

    def _multiqubit_gate(self, xy, 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])

        # added .21 is for qubit numbers on the left side
        text_width = self._get_text_width(text, self._style.fs) + .21
        sub_width = self._get_text_width(subtext, self._style.sfs) + .21
        wid = max((text_width, sub_width, WID))

        qubit_span = abs(ypos) - abs(ypos_max) + 1
        height = HIG + (qubit_span - 1)
        box = patches.Rectangle(
            xy=(xpos - 0.5 * wid, ypos - .5 * HIG), width=wid, height=height,
            fc=fc, ec=ec, linewidth=1.5, 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 + .07 - 0.5 * wid, y, str(bit), ha='left', va='center',
                         fontsize=self._style.fs, color=gt,
                         clip_on=True, zorder=PORDER_TEXT)
        if text:
            if subtext:
                self.ax.text(xpos+.11, ypos + 0.4 * height, text, ha='center',
                             va='center', fontsize=self._style.fs,
                             color=gt, clip_on=True,
                             zorder=PORDER_TEXT)
                self.ax.text(xpos+.11, ypos + 0.2 * height, subtext, ha='center',
                             va='center', fontsize=self._style.sfs,
                             color=sc, clip_on=True,
                             zorder=PORDER_TEXT)
            else:
                self.ax.text(xpos+.11, ypos + .5 * (qubit_span - 1), text,
                             ha='center', va='center', fontsize=self._style.fs,
                             color=gt, clip_on=True,
                             zorder=PORDER_TEXT, wrap=True)

    def _gate(self, xy, fc=None, ec=None, gt=None, sc=None, text='', subtext=''):
        xpos, ypos = xy

        text_width = self._get_text_width(text, self._style.fs)
        sub_width = self._get_text_width(subtext, self._style.sfs)
        wid = max((text_width, sub_width, WID))

        box = patches.Rectangle(xy=(xpos - 0.5 * wid, ypos - 0.5 * HIG),
                                width=wid, height=HIG, fc=fc, ec=ec,
                                linewidth=1.5, zorder=PORDER_GATE)
        self.ax.add_patch(box)

        if text:
            if subtext:
                self.ax.text(xpos, ypos + 0.15 * HIG, text, ha='center',
                             va='center', fontsize=self._style.fs, color=gt,
                             clip_on=True, zorder=PORDER_TEXT)
                self.ax.text(xpos, ypos - 0.3 * HIG, subtext, ha='center',
                             va='center', fontsize=self._style.sfs, color=sc,
                             clip_on=True, zorder=PORDER_TEXT)
            else:
                self.ax.text(xpos, ypos, text, ha='center', va='center',
                             fontsize=self._style.fs, color=gt,
                             clip_on=True, zorder=PORDER_TEXT)

    def _sidetext(self, xy, tc=None, text=''):
        xpos, ypos = xy

        # 0.08 = the initial gap, add 1/2 text width to place on the right
        text_width = self._get_text_width(text, self._style.sfs)
        xp = xpos + 0.08 + text_width / 2
        self.ax.text(xp, ypos + HIG, text, ha='center', va='top',
                     fontsize=self._style.sfs, color=tc,
                     clip_on=True, zorder=PORDER_TEXT)

    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=2,
                         linestyle='solid', zorder=zorder)
            self.ax.plot([x0 - dx, x1 - dx], [y0 - dy, y1 - dy],
                         color=linecolor, linewidth=2,
                         linestyle='solid', zorder=zorder)
        else:
            self.ax.plot([x0, x1], [y0, y1],
                         color=linecolor, linewidth=2,
                         linestyle=linestyle, zorder=zorder)

    def _measure(self, qxy, cxy, cid, fc=None, ec=None, gt=None, sc=None):
        qx, qy = qxy
        cx, cy = cxy

        # draw gate box
        self._gate(qxy, fc=fc, ec=ec, gt=gt, sc=sc)

        # add measure symbol
        arc = patches.Arc(xy=(qx, qy - 0.15 * HIG), width=WID * 0.7,
                          height=HIG * 0.7, theta1=0, theta2=180, fill=False,
                          ec=self._style.not_gate_lc, linewidth=2, zorder=PORDER_GATE)
        self.ax.add_patch(arc)
        self.ax.plot([qx, qx + 0.35 * WID], [qy - 0.15 * HIG, qy + 0.20 * HIG],
                     color=self._style.not_gate_lc, linewidth=2, zorder=PORDER_GATE)
        # arrow
        self._line(qxy, [cx, cy + 0.35 * WID], lc=self._style.cc, ls=self._style.cline)
        arrowhead = patches.Polygon(((cx - 0.20 * WID, cy + 0.35 * WID),
                                     (cx + 0.20 * WID, cy + 0.35 * WID),
                                     (cx, cy)), fc=self._style.cc, ec=None)
        self.ax.add_artist(arrowhead)
        # target
        if self.cregbundle:
            self.ax.text(cx + .25, cy + .1, str(cid), ha='left', va='bottom',
                         fontsize=0.8 * self._style.fs, color=self._style.tc,
                         clip_on=True, zorder=PORDER_TEXT)

    def _conds(self, xy, istrue=False):
        xpos, ypos = xy

        fc = self._style.lc if istrue else self._style.bg
        box = patches.Circle(xy=(xpos, ypos), radius=WID * 0.15, fc=fc,
                             ec=self._style.lc, linewidth=1.5, 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 = patches.Circle(xy=(xpos, ypos), radius=WID * 0.15,
                             fc=fc, ec=ec, linewidth=1.5, 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=self._style.tc,
                         clip_on=True, zorder=PORDER_TEXT)
        elif text_top is False:
            self.ax.text(xpos, ypos - 0.3 * HIG, text, ha='center', va='top',
                         fontsize=self._style.sfs, color=tc,
                         clip_on=True, zorder=PORDER_TEXT)

    def _set_ctrl_bits(self, ctrl_state, num_ctrl_qubits, qbit, ec=None, tc=None,
                       text='', qargs=None):
        # place the control label at the top or bottom of controls
        if text:
            qlist = [qubit.index for qubit in qargs]
            ctbits = qlist[:num_ctrl_qubits]
            qubits = qlist[num_ctrl_qubits:]
            max_ctbit = max(ctbits)
            min_ctbit = min(ctbits)
            top = min(qubits) > min_ctbit

        # display the control qubits as open or closed based on ctrl_state
        cstate = "{0:b}".format(ctrl_state).rjust(num_ctrl_qubits, '0')[::-1]
        for i in range(num_ctrl_qubits):
            fc_open_close = ec if cstate[i] == '1' else self._style.bg
            text_top = None
            if text:
                if top and qlist[i] == min_ctbit:
                    text_top = True
                elif not top and qlist[i] == max_ctbit:
                    text_top = False
            self._ctrl_qubit(qbit[i], fc=fc_open_close, ec=ec, tc=tc,
                             text=text, text_top=text_top)

    def _x_tgt_qubit(self, xy, ec=None, ac=None):
        linewidth = 2
        xpos, ypos = xy
        box = patches.Circle(xy=(xpos, ypos), radius=HIG * 0.35,
                             fc=ec, ec=ec, linewidth=linewidth,
                             zorder=PORDER_GATE)
        self.ax.add_patch(box)

        # add '+' symbol
        self.ax.plot([xpos, xpos], [ypos - 0.2 * HIG, ypos + 0.2 * HIG],
                     color=ac, linewidth=linewidth, zorder=PORDER_GATE + 1)
        self.ax.plot([xpos - 0.2 * HIG, xpos + 0.2 * HIG], [ypos, ypos],
                     color=ac, linewidth=linewidth, zorder=PORDER_GATE + 1)

    def _swap(self, xy, color=None):
        xpos, ypos = xy

        self.ax.plot([xpos - 0.20 * WID, xpos + 0.20 * WID],
                     [ypos - 0.20 * WID, ypos + 0.20 * WID],
                     color=color, linewidth=2, zorder=PORDER_LINE + 1)
        self.ax.plot([xpos - 0.20 * WID, xpos + 0.20 * WID],
                     [ypos + 0.20 * WID, ypos - 0.20 * WID],
                     color=color, linewidth=2, 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=1, linestyle="dashed",
                         color=self._style.lc, zorder=PORDER_TEXT)
            box = patches.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=1.5, zorder=PORDER_GRAY)
            self.ax.add_patch(box)

    def _linefeed_mark(self, xy):
        xpos, ypos = xy

        self.ax.plot([xpos - .1, xpos - .1],
                     [ypos, ypos - self._cond['n_lines'] + 1],
                     color=self._style.lc, zorder=PORDER_LINE)
        self.ax.plot([xpos + .1, xpos + .1],
                     [ypos, ypos - self._cond['n_lines'] + 1],
                     color=self._style.lc, zorder=PORDER_LINE)

    def draw(self, filename=None, verbose=False):
        self._draw_regs()
        self._draw_ops(verbose)
        _xl = - self._style.margin[0]
        _xr = self._cond['xmax'] + self._style.margin[1]
        _yb = - self._cond['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 * self._scale * self._style.fs / 72 / WID
        self.figure.set_size_inches(self._style.figwidth, self._style.figwidth * fig_h / fig_w)

        if filename:
            self.figure.savefig(filename, dpi=self._style.dpi,
                                bbox_inches='tight', facecolor=self.figure.get_facecolor())
        if self.return_fig:
            if get_backend() in ['module://ipykernel.pylab.backend_inline',
                                 'nbAgg']:
                plt.close(self.figure)
            return self.figure

    def _draw_regs(self):
        longest_label_width = 0
        if self.initial_state:
            initial_qbit = ' |0>'
            initial_cbit = ' 0'
        else:
            initial_qbit = ''
            initial_cbit = ''

        def _fix_double_script(label):
            words = label.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]
            label = ' '.join(words).replace(' ', '\\;')
            return label

        # quantum register
        for ii, reg in enumerate(self._qreg):
            if len(self._qreg) > 1:
                if self.layout is None:
                    label = '${{{name}}}_{{{index}}}$'.format(name=reg.register.name,
                                                              index=reg.index)
                    label = _fix_double_script(label) + initial_qbit
                    text_width = self._get_text_width(label, self._style.fs)
                else:
                    label = '${{{name}}}_{{{index}}} \\mapsto {{{physical}}}$'.format(
                        name=self.layout[reg.index].register.name,
                        index=self.layout[reg.index].index, physical=reg.index)
                    label = _fix_double_script(label) + initial_qbit
                    text_width = self._get_text_width(label, self._style.fs)
            else:
                label = '${name}$'.format(name=reg.register.name)
                label = _fix_double_script(label) + initial_qbit
                text_width = self._get_text_width(label, self._style.fs)

            text_width = text_width * 1.15  # to account for larger font used
            if text_width > longest_label_width:
                longest_label_width = text_width

            pos = -ii
            self._qreg_dict[ii] = {
                'y': pos, 'label': label, 'index': reg.index, 'group': reg.register}
            self._cond['n_lines'] += 1

        # classical register
        if self._creg:
            n_creg = self._creg.copy()
            n_creg.pop(0)
            idx = 0
            y_off = -len(self._qreg)
            for ii, (reg, nreg) in enumerate(itertools.zip_longest(self._creg, n_creg)):
                pos = y_off - idx
                if self.cregbundle:
                    label = '${}$'.format(reg.register.name)
                    label = _fix_double_script(label) + initial_cbit
                    text_width = self._get_text_width(reg.register.name, self._style.fs) * 1.15
                    if text_width > longest_label_width:
                        longest_label_width = text_width
                    self._creg_dict[ii] = {'y': pos, 'label': label, 'index': reg.index,
                                           'group': reg.register}
                    if not (not nreg or reg.register != nreg.register):
                        continue
                else:
                    label = '${}_{{{}}}$'.format(reg.register.name, reg.index)
                    label = _fix_double_script(label) + initial_cbit
                    text_width = self._get_text_width(reg.register.name, self._style.fs) * 1.15
                    if text_width > longest_label_width:
                        longest_label_width = text_width
                    self._creg_dict[ii] = {'y': pos, 'label': label, 'index': reg.index,
                                           'group': reg.register}
                self._cond['n_lines'] += 1
                idx += 1

        self._reg_long_text = longest_label_width
        self.x_offset = -1.2 + self._reg_long_text

    def _draw_regs_sub(self, n_fold, feedline_l=False, feedline_r=False):
        # quantum register
        for qreg in self._qreg_dict.values():
            label = qreg['label']
            y = qreg['y'] - n_fold * (self._cond['n_lines'] + 1)
            self.ax.text(self.x_offset - 0.2, y, label, ha='right', va='center',
                         fontsize=1.25 * self._style.fs, color=self._style.tc,
                         clip_on=True, zorder=PORDER_TEXT)
            self._line([self.x_offset + 0.2, y], [self._cond['xmax'], y],
                       zorder=PORDER_REGLINE)

        # classical register
        this_creg_dict = {}
        for creg in self._creg_dict.values():
            label = creg['label']
            y = creg['y'] - n_fold * (self._cond['n_lines'] + 1)
            if y not in this_creg_dict.keys():
                this_creg_dict[y] = {'val': 1, 'label': label}
            else:
                this_creg_dict[y]['val'] += 1
        for y, this_creg in this_creg_dict.items():
            # cregbundle
            if this_creg['val'] > 1:
                self.ax.plot([self.x_offset + 0.64, self.x_offset + 0.74], [y - .1, y + .1],
                             color=self._style.cc, zorder=PORDER_LINE)
                self.ax.text(self.x_offset+0.54, y + .1, str(this_creg['val']), ha='left',
                             va='bottom', fontsize=0.8 * self._style.fs,
                             color=self._style.tc, clip_on=True, zorder=PORDER_TEXT)
            self.ax.text(self.x_offset - 0.2, y, this_creg['label'], ha='right', va='center',
                         fontsize=1.25 * self._style.fs, color=self._style.tc,
                         clip_on=True, zorder=PORDER_TEXT)
            self._line([self.x_offset + 0.2, y], [self._cond['xmax'], y], lc=self._style.cc,
                       ls=self._style.cline, zorder=PORDER_REGLINE)

        # lf line
        if feedline_r:
            self._linefeed_mark((self.fold + self.x_offset + 1 - 0.1,
                                 - n_fold * (self._cond['n_lines'] + 1)))
        if feedline_l:
            self._linefeed_mark((self.x_offset + 0.3,
                                 - n_fold * (self._cond['n_lines'] + 1)))

    def _draw_ops(self, verbose=False):
        _standard_gates = ['x', 'y', 'z', 'id', 'h', 'r', 's', 'sdg', 't', 'tdg', 'rx', 'ry', 'rz',
                           'rxx', 'ryy', 'rzx', 'u1', 'u2', 'u3', 'swap', 'reset']
        _barrier_gates = ['barrier', 'snapshot', 'load', 'save', 'noise']
        _barriers = {'coord': [], 'group': []}

        #
        # generate coordinate manager
        #
        q_anchors = {}
        for key, qreg in self._qreg_dict.items():
            q_anchors[key] = Anchor(reg_num=self._cond['n_lines'],
                                    yind=qreg['y'], fold=self.fold)
        c_anchors = {}
        for key, creg in self._creg_dict.items():
            c_anchors[key] = Anchor(reg_num=self._cond['n_lines'],
                                    yind=creg['y'], fold=self.fold)
        #
        # draw the ops
        #
        prev_anc = -1
        for layer in self._ops:
            widest_box = 0.0
            #
            # compute the layer_width for this layer
            #
            for op in layer:
                if op.name in [*_barrier_gates, 'measure']:
                    continue

                base_name = None if not hasattr(op.op, 'base_gate') else op.op.base_gate.name
                gate_text, ctrl_text = self._get_gate_ctrl_text(op)

                # if a standard_gate, no params, and no labels, layer_width is 1
                if (not hasattr(op.op, 'params') and
                        ((op.name in _standard_gates or base_name in _standard_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=self._style.sfs) - 0.05

                # get param_width, but 0 for gates with array params
                if (hasattr(op.op, 'params')
                        and not any([isinstance(param, np.ndarray) for param in op.op.params])
                        and len(op.op.params) > 0):
                    param = self.param_parse(op.op.params)
                    if op.name == 'initialize':
                        param = '[%s]' % param
                    param = "${}$".format(param)
                    param_width = self._get_text_width(param, fontsize=self._style.sfs) + 0.08
                else:
                    param_width = 0.0

                if op.name == 'cu1' or op.name == 'rzz' or base_name == 'rzz':
                    tname = 'U1' if op.name == 'cu1' else 'zz'
                    gate_width = (self._get_text_width(tname + ' ()$$',
                                                       fontsize=self._style.sfs)
                                  + param_width) * 1.5
                else:
                    gate_width = self._get_text_width(gate_text, fontsize=self._style.fs) + 0.10
                    # add .21 for the qubit numbers on the left of the multibit gates
                    if (op.name not in _standard_gates and base_name not in _standard_gates):
                        gate_width += 0.21

                box_width = max((gate_width, ctrl_width, param_width, WID))
                if box_width > widest_box:
                    widest_box = box_width

            layer_width = int(widest_box) + 1
            this_anc = prev_anc + 1
            #
            # draw the gates in this layer
            #
            for op in layer:
                base_name = None if not hasattr(op.op, 'base_gate') else op.op.base_gate.name
                gate_text, ctrl_text = self._get_gate_ctrl_text(op)
                fc, ec, gt, tc, sc, lc = self._get_colors(op)

                # get qreg index
                q_idxs = []
                for qarg in op.qargs:
                    for index, reg in self._qreg_dict.items():
                        if (reg['group'] == qarg.register and
                                reg['index'] == qarg.index):
                            q_idxs.append(index)
                            break

                # get creg index
                c_idxs = []
                for carg in op.cargs:
                    for index, reg in self._creg_dict.items():
                        if (reg['group'] == carg.register and
                                reg['index'] == 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 op.name not in _barrier_gates:
                    for ii in q_idxs:
                        q_anchors[ii].set_index(this_anc, layer_width)

                # qreg coordinate
                q_xy = [q_anchors[ii].plot_coord(this_anc, layer_width, self.x_offset)
                        for ii in q_idxs]
                # creg 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 qreg
                qreg_b = min(q_xy, key=lambda xy: xy[1])
                qreg_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 (op.type == 'op' and hasattr(op.op, 'params') and len(op.op.params) > 0
                        and not any([isinstance(param, np.ndarray) for param in op.op.params])):
                    param = "${}$".format(self.param_parse(op.op.params))
                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._creg_dict]
                    mask = 0
                    for index, cbit in enumerate(self._creg):
                        if cbit.register == op.condition[0]:
                            mask |= (1 << index)
                    val = op.condition[1]
                    # cbit list to consider
                    fmt_c = '{{:0{}b}}'.format(len(c_xy))
                    cmask = list(fmt_c.format(mask))[::-1]
                    # value
                    fmt_v = '{{:0{}b}}'.format(cmask.count('1'))
                    vlist = list(fmt_v.format(val))[::-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._conds(xy, istrue=True)
                                else:
                                    self._conds(xy, istrue=False)
                                xy_plot.append(xy)
                            v_ind += 1
                    creg_b = sorted(xy_plot, key=lambda xy: xy[1])[0]
                    xpos, ypos = creg_b
                    self.ax.text(xpos, ypos - 0.3 * HIG, hex(val), ha='center', va='top',
                                 fontsize=self._style.sfs, color=self._style.tc,
                                 clip_on=True, zorder=PORDER_TEXT)
                    self._line(qreg_t, creg_b, lc=self._style.cc,
                               ls=self._style.cline)
                #
                # draw special gates
                #
                if op.name == 'measure':
                    vv = self._creg_dict[c_idxs[0]]['index']
                    self._measure(q_xy[0], c_xy[0], vv, fc=fc, ec=ec, gt=gt, sc=sc)

                elif op.name in _barrier_gates:
                    _barriers = {'coord': [], 'group': []}
                    for index, qbit in enumerate(q_idxs):
                        q_group = self._qreg_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 = "$[{}]$".format(param.replace('$', ''))
                    self._multiqubit_gate(q_xy, fc=fc, ec=ec, gt=gt, sc=sc,
                                          text=gate_text, subtext=vec)
                #
                # draw single qubit gates
                #
                elif len(q_xy) == 1:
                    self._gate(q_xy[0], fc=fc, ec=ec, gt=gt, sc=sc,
                               text=gate_text, subtext=str(param))
                #
                # draw controlled and special gates
                #
                # cx gates
                elif isinstance(op.op, ControlledGate) and base_name == 'x':
                    num_ctrl_qubits = op.op.num_ctrl_qubits
                    self._set_ctrl_bits(op.op.ctrl_state, num_ctrl_qubits,
                                        q_xy, ec=ec, tc=tc, text=ctrl_text, qargs=op.qargs)
                    self._x_tgt_qubit(q_xy[num_ctrl_qubits], ec=ec,
                                      ac=self._style.dispcol['target'])
                    self._line(qreg_b, qreg_t, lc=lc)

                # cz gate
                elif op.name == 'cz':
                    num_ctrl_qubits = op.op.num_ctrl_qubits
                    self._set_ctrl_bits(op.op.ctrl_state, num_ctrl_qubits,
                                        q_xy, ec=ec, tc=tc, text=ctrl_text, qargs=op.qargs)
                    self._ctrl_qubit(q_xy[1], fc=ec, ec=ec, tc=tc)
                    self._line(qreg_b, qreg_t, lc=lc, zorder=PORDER_LINE + 1)

                # cu1, rzz, and controlled rzz gates (sidetext gates)
                elif (op.name == 'cu1' or op.name == 'rzz' or base_name == 'rzz'):
                    num_ctrl_qubits = 0 if op.name == 'rzz' else op.op.num_ctrl_qubits
                    if op.name != 'rzz':
                        self._set_ctrl_bits(op.op.ctrl_state, num_ctrl_qubits,
                                            q_xy, ec=ec, tc=tc, text=ctrl_text, qargs=op.qargs)
                    self._ctrl_qubit(q_xy[num_ctrl_qubits], fc=ec, ec=ec, tc=tc)
                    if op.name != 'cu1':
                        self._ctrl_qubit(q_xy[num_ctrl_qubits+1], fc=ec, ec=ec, tc=tc)
                    stext = self._style.disptex['u1'] if op.name == 'cu1' else 'zz'
                    self._sidetext(qreg_b, tc=tc,
                                   text='${}$'.format(stext)+' '+'({})'.format(param))
                    self._line(qreg_b, qreg_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(qreg_b, qreg_t, lc=lc)

                # cswap gate
                elif op.name != 'swap' and base_name == 'swap':
                    num_ctrl_qubits = op.op.num_ctrl_qubits
                    self._set_ctrl_bits(op.op.ctrl_state, num_ctrl_qubits,
                                        q_xy, ec=ec, tc=tc, text=ctrl_text, qargs=op.qargs)
                    self._swap(q_xy[num_ctrl_qubits], color=lc)
                    self._swap(q_xy[num_ctrl_qubits+1], color=lc)
                    self._line(qreg_b, qreg_t, lc=lc)

                # all other controlled gates
                elif isinstance(op.op, ControlledGate):
                    num_ctrl_qubits = op.op.num_ctrl_qubits
                    num_qargs = len(q_xy) - num_ctrl_qubits
                    self._set_ctrl_bits(op.op.ctrl_state, num_ctrl_qubits,
                                        q_xy, ec=ec, tc=tc, text=ctrl_text, qargs=op.qargs)
                    self._line(qreg_b, qreg_t, lc=lc)
                    if num_qargs == 1:
                        self._gate(q_xy[num_ctrl_qubits], fc=fc, ec=ec, gt=gt, sc=sc,
                                   text=gate_text, subtext='{}'.format(param))
                    else:
                        self._multiqubit_gate(q_xy[num_ctrl_qubits:], fc=fc, ec=ec, gt=gt,
                                              sc=sc, text=gate_text, subtext='{}'.format(param))

                # draw multi-qubit gate as final default
                else:
                    self._multiqubit_gate(q_xy, fc=fc, ec=ec, gt=gt, sc=sc,
                                          text=gate_text, subtext='{}'.format(param))

            # 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.name in _barrier_gates for op in layer]) else 0

            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._qreg_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._cond['xmax'] = self.fold + 1 + self.x_offset
            self._cond['ymax'] = (n_fold + 1) * (self._cond['n_lines'] + 1) - 1
        else:
            self._cond['xmax'] = max_anc + 1 + self.x_offset
            self._cond['ymax'] = self._cond['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)

        # 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.2
                    y_coord = - (ii // self.fold) * (self._cond['n_lines'] + 1) + 0.7
                else:
                    x_coord = ii + self._reg_long_text - 0.2
                    y_coord = 0.7
                self.ax.text(x_coord, y_coord, str(ii + 1), ha='center',
                             va='center', fontsize=self._style.sfs,
                             color=self._style.tc, clip_on=True, zorder=PORDER_TEXT)