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 _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 __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
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 __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
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)
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
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
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
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)}
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)