def _get_qubit_index(self, qubit): """Get the index number for a quantum bit Args: qubit (tuple): The tuple of the bit of the form (register_name, bit_number) Returns: int: The index in the bit list Raises: VisualizationError: If the bit isn't found """ for i, bit in enumerate(self.qubit_list): if qubit == bit: qindex = i break else: raise exceptions.VisualizationError( "unable to find bit for operation") return qindex
def circuit_drawer(circuit, scale=0.7, filename=None, style=None, output=None, interactive=False, line_length=None, plot_barriers=True, reverse_bits=False): """Draw a quantum circuit to different formats (set by output parameter): 0. text: ASCII art TextDrawing that can be printed in the console. 1. latex: high-quality images, but heavy external software dependencies 2. matplotlib: purely in Python with no external dependencies Args: circuit (QuantumCircuit): the quantum circuit to draw scale (float): scale of image to draw (shrink if < 1) filename (str): file path to save image to style (dict or str): dictionary of style or file name of style file. This option is only used by the `mpl`, `latex`, and `latex_source` output types. If a str is passed in that is the path to a json file which contains that will be open, parsed, and then used just as the input dict. output (TextDrawing): Select the output method to use for drawing the circuit. Valid choices are `text`, `latex`, `latex_source`, `mpl`. Note if one is not specified it will use latex and if that fails fallback to mpl. However this behavior is deprecated and in a future release the default will change. interactive (bool): when set true show the circuit in a new window (for `mpl` this depends on the matplotlib backend being used supporting this). Note when used with either the `text` or the `latex_source` output type this has no effect and will be silently ignored. line_length (int): Sets the length of the lines generated by `text` output type. This useful when the drawing does not fit in the console. If None (default), it will try to guess the console width using shutil.get_terminal_size(). However, if you're running in jupyter the default line length is set to 80 characters. If you don't want pagination at all, set `line_length=-1`. reverse_bits (bool): When set to True reverse the bit order inside registers for the output visualization. plot_barriers (bool): Enable/disable drawing barriers in the output circuit. Defaults to True. Returns: PIL.Image: (output `latex`) an in-memory representation of the image of the circuit diagram. matplotlib.figure: (output `mpl`) a matplotlib figure object for the circuit diagram. String: (output `latex_source`). The LaTeX source code. TextDrawing: (output `text`). A drawing that can be printed as ascii art Raises: VisualizationError: when an invalid output method is selected ImportError: when the output methods requieres non-installed libraries. .. _style-dict-doc: The style dict kwarg contains numerous options that define the style of the output circuit visualization. While the style dict is used by the `mpl`, `latex`, and `latex_source` outputs some options in that are only used by the `mpl` output. These options are defined below, if it is only used by the `mpl` output it is marked as such: textcolor (str): The color code to use for text. Defaults to `'#000000'` (`mpl` only) subtextcolor (str): The color code to use for subtext. Defaults to `'#000000'` (`mpl` only) linecolor (str): The color code to use for lines. Defaults to `'#000000'` (`mpl` only) creglinecolor (str): The color code to use for classical register lines `'#778899'`(`mpl` only) gatetextcolor (str): The color code to use for gate text `'#000000'` (`mpl` only) gatefacecolor (str): The color code to use for gates. Defaults to `'#ffffff'` (`mpl` only) barrierfacecolor (str): The color code to use for barriers. Defaults to `'#bdbdbd'` (`mpl` only) backgroundcolor (str): The color code to use for the background. Defaults to `'#ffffff'` (`mpl` only) fontsize (int): The font size to use for text. Defaults to 13 (`mpl` only) subfontsize (int): The font size to use for subtext. Defaults to 8 (`mpl` only) displaytext (dict): A dictionary of the text to use for each element type in the output visualization. The default values are: { 'id': 'id', 'u0': 'U_0', 'u1': 'U_1', 'u2': 'U_2', 'u3': 'U_3', 'x': 'X', 'y': 'Y', 'z': 'Z', 'h': 'H', 's': 'S', 'sdg': 'S^\\dagger', 't': 'T', 'tdg': 'T^\\dagger', 'rx': 'R_x', 'ry': 'R_y', 'rz': 'R_z', 'reset': '\\left|0\\right\\rangle' } You must specify all the necessary values if using this. There is no provision for passing an incomplete dict in. (`mpl` only) displaycolor (dict): The color codes to use for each circuit element. By default all values default to the value of `gatefacecolor` and the keys are the same as `displaytext`. Also, just like `displaytext` there is no provision for an incomplete dict passed in. (`mpl` only) latexdrawerstyle (bool): When set to True enable latex mode which will draw gates like the `latex` output modes. (`mpl` only) usepiformat (bool): When set to True use radians for output (`mpl` only) fold (int): The number of circuit elements to fold the circuit at. Defaults to 20 (`mpl` only) cregbundle (bool): If set True bundle classical registers (`mpl` only) plotbarrier (bool): Enable/disable drawing barriers in the output circuit. Defaults to True. This is deprecated in the style dict and will be removed in a future release. Use the `plot_barriers` kwarg instead. showindex (bool): If set True draw an index. (`mpl` only) compress (bool): If set True draw a compressed circuit (`mpl` only) figwidth (int): The maximum width (in inches) for the output figure. (`mpl` only) dpi (int): The DPI to use for the output image. Defaults to 150 (`mpl` only) margin (list): `mpl` only creglinestyle (str): The style of line to use for classical registers. Choices are `'solid'`, `'doublet'`, or any valid matplotlib `linestyle` kwarg value. Defaults to `doublet`(`mpl` only) reversebits (bool): When set to True reverse the bit order inside registers for the output visualization. This is deprecated in the style dict and will be removed in a future release use the `reverse_bits` kwarg instead. """ image = None if style: if 'reversebits' in style: warnings.warn('The reversebits key in style is deprecated and will' 'not work in the future. Instead use the ' '``reverse_bits`` kwarg.', DeprecationWarning) reverse_bits = style.get('reversebits') if 'plotbarrier' in style: warnings.warn('The plotbarrier key in style is deprecated and will' 'not work in the future. Instead use the ' '``plot_barriers`` kwarg.', DeprecationWarning) plot_barriers = style.get('plotbarrier') if not output: warnings.warn('The current behavior for the default output will change' ' in a future release. Instead of trying latex and ' 'falling back to mpl on failure it will just use ' '"text" by default', DeprecationWarning) try: image = _latex_circuit_drawer(circuit, scale, filename, style) except (OSError, subprocess.CalledProcessError, FileNotFoundError): if _matplotlib.HAS_MATPLOTLIB: image = _matplotlib_circuit_drawer(circuit, scale, filename, style) else: raise ImportError('The default output needs matplotlib. ' 'Run "pip install matplotlib" before.') else: if output == 'text': return _text_circuit_drawer(circuit, filename=filename, line_length=line_length, reversebits=reverse_bits, plotbarriers=plot_barriers) elif output == 'latex': image = _latex_circuit_drawer(circuit, scale=scale, filename=filename, style=style, plot_barriers=plot_barriers, reverse_bits=reverse_bits) elif output == 'latex_source': return _generate_latex_source(circuit, filename=filename, scale=scale, style=style, plot_barriers=plot_barriers, reverse_bits=reverse_bits) elif output == 'mpl': image = _matplotlib_circuit_drawer(circuit, scale=scale, filename=filename, style=style, plot_barriers=plot_barriers, reverse_bits=reverse_bits) else: raise exceptions.VisualizationError( 'Invalid output type %s selected. The only valid choices ' 'are latex, latex_source, text, and mpl' % output) if image and interactive: image.show() return image
def _draw_ops(self, verbose=False): _force_next = 'measure barrier'.split() _wide_gate = 'u2 u3 cu2 cu3'.split() _barriers = {'coord': [], 'group': []} next_ops = self._ops.copy() if next_ops: next_ops.pop(0) this_anc = 0 # # 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._style.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._style.fold) # # draw gates # for i, (op, op_next) in enumerate( itertools.zip_longest(self._ops, next_ops)): # wide gate if op['name'] in _wide_gate: _iswide = True gw = 2 else: _iswide = False gw = 1 # get qreg index if 'qargs' in op.keys(): q_idxs = [] for qarg in op['qargs']: for index, reg in self._qreg_dict.items(): if (reg['group'] == qarg[0] and reg['index'] == qarg[1]): q_idxs.append(index) break else: q_idxs = [] # get creg index if 'cargs' in op.keys(): c_idxs = [] for carg in op['cargs']: for index, reg in self._creg_dict.items(): if (reg['group'] == carg[0] and reg['index'] == carg[1]): c_idxs.append(index) break else: c_idxs = [] # find empty space to place gate if not _barriers['group']: this_anc = max([q_anchors[ii].get_index() for ii in q_idxs]) while True: if op['name'] in _force_next or ( 'condition' in op.keys() and op['condition']) or \ not self._style.compress: occupied = self._qreg_dict.keys() else: occupied = q_idxs q_list = [ ii for ii in range(min(occupied), max(occupied) + 1) ] locs = [ q_anchors[jj].is_locatable(this_anc, gw) for jj in q_list ] if all(locs): for ii in q_list: if op['name'] in [ 'barrier', 'snapshot', 'load', 'save', 'noise' ] and not self.plot_barriers: q_anchors[ii].set_index(this_anc - 1, gw) else: q_anchors[ii].set_index(this_anc, gw) break else: this_anc += 1 # qreg coordinate q_xy = [q_anchors[ii].plot_coord(this_anc, gw) for ii in q_idxs] # creg coordinate c_xy = [c_anchors[ii].plot_coord(this_anc, gw) 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]) if verbose: print(i, op) # rotation parameter if 'op' in op.keys() and hasattr(op['op'], 'param'): param = self.param_parse(op['op'].params, self._style.pimode) else: param = None # conditional gate if 'condition' in op.keys() and op['condition']: c_xy = [ c_anchors[ii].plot_coord(this_anc, gw) for ii in self._creg_dict ] mask = 0 for index, cbit in enumerate(self._creg): if cbit.reg == 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' ]: # Go over all indices to add barriers across 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 op_next and op_next['name'] == 'barrier': continue else: if self.plot_barriers: self._barrier(_barriers, this_anc) _barriers['group'].clear() _barriers['coord'].clear() # # draw single qubit gates # elif len(q_xy) == 1: disp = op['name'] if param: self._gate(q_xy[0], wide=_iswide, text=disp, subtext='{}'.format(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'] in ['cx']: self._ctrl_qubit(q_xy[0]) self._tgt_qubit(q_xy[1]) # cz for latexmode elif op['name'] == 'cz': if self._style.latexmode: self._ctrl_qubit(q_xy[0]) self._ctrl_qubit(q_xy[1]) else: disp = op['name'].replace('c', '') self._ctrl_qubit(q_xy[0]) self._gate(q_xy[1], wide=_iswide, text=disp) # control gate elif op['name'] in ['cy', 'ch', 'cu3', 'crz']: disp = op['name'].replace('c', '') self._ctrl_qubit(q_xy[0]) if param: self._gate(q_xy[1], wide=_iswide, text=disp, subtext='{}'.format(param)) else: self._gate(q_xy[1], wide=_iswide, text=disp) # cu1 for latexmode elif op['name'] in ['cu1']: disp = op['name'].replace('c', '') self._ctrl_qubit(q_xy[0]) if self._style.latexmode: self._ctrl_qubit(q_xy[1]) self._subtext(qreg_b, param) else: self._gate(q_xy[1], wide=_iswide, text=disp, subtext='{}'.format(param)) # 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) # # draw multi-qubit gates (n=3) # elif len(q_xy) == 3: # cswap gate if op['name'] == 'cswap': self._ctrl_qubit(q_xy[0]) self._swap(q_xy[1]) self._swap(q_xy[2]) # ccx gate elif op['name'] == 'ccx': self._ctrl_qubit(q_xy[0]) self._ctrl_qubit(q_xy[1]) self._tgt_qubit(q_xy[2]) # add qubit-qubit wiring self._line(qreg_b, qreg_t) else: logger.critical('Invalid gate %s', op) raise exceptions.VisualizationError( 'invalid gate {}'.format(op)) # # 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._style.fold # window size if max_anc > self._style.fold > 0: self._cond['xmax'] = self._style.fold + 1 self._cond['ymax'] = (n_fold + 1) * (self._cond['n_lines'] + 1) - 1 else: self._cond['xmax'] = max_anc + 1 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._style.fold > 0: x_coord = ii % self._style.fold + 1 y_coord = -(ii // self._style.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)
def _build_latex_array(self, aliases=None): """Returns an array of strings containing \\LaTeX for this circuit. If aliases is not None, aliases contains a dict mapping the current qubits in the circuit to new qubit names. We will deduce the register names and sizes from aliases. """ columns = 1 is_occupied = [False] * self.img_width # Rename qregs if necessary if aliases: qregdata = {} for q in aliases.values(): if q[0] not in qregdata: qregdata[q[0]] = q[1] + 1 elif qregdata[q[0]] < q[1] + 1: qregdata[q[0]] = q[1] + 1 else: qregdata = self.qregs for _, op in enumerate(self.ops): if 'condition' in op and op['condition']: mask = self._get_mask(op['condition'][0]) cl_reg = self.clbit_list[self._ffs(mask)] if_reg = cl_reg[0] pos_2 = self.img_regs[cl_reg] if_value = format(op['condition'][1], 'b').zfill(self.cregs[if_reg])[::-1] if op['name'] not in ['measure', 'barrier', 'snapshot', 'load', 'save', 'noise']: nm = op['name'] qarglist = op['qargs'] if aliases is not None: qarglist = map(lambda x: aliases[x], qarglist) if len(qarglist) == 1: pos_1 = self.img_regs[(qarglist[0][0], qarglist[0][1])] if 'condition' in op and op['condition']: mask = self._get_mask(op['condition'][0]) cl_reg = self.clbit_list[self._ffs(mask)] if_reg = cl_reg[0] pos_2 = self.img_regs[cl_reg] for i in range(pos_1, pos_2 + self.cregs[if_reg]): if is_occupied[i] is False: is_occupied[i] = True else: columns += 1 is_occupied = [False] * self.img_width for j in range(pos_1, pos_2 + 1): is_occupied[j] = True break if nm == "x": self._latex[pos_1][columns] = "\\gate{X}" elif nm == "y": self._latex[pos_1][columns] = "\\gate{Y}" elif nm == "z": self._latex[pos_1][columns] = "\\gate{Z}" elif nm == "h": self._latex[pos_1][columns] = "\\gate{H}" elif nm == "s": self._latex[pos_1][columns] = "\\gate{S}" elif nm == "sdg": self._latex[pos_1][columns] = "\\gate{S^\\dag}" elif nm == "t": self._latex[pos_1][columns] = "\\gate{T}" elif nm == "tdg": self._latex[pos_1][columns] = "\\gate{T^\\dag}" elif nm == "u0": self._latex[pos_1][columns] = "\\gate{U_0(%s)}" % ( op["op"].params[0]) elif nm == "u1": self._latex[pos_1][columns] = "\\gate{U_1(%s)}" % ( op["op"].params[0]) elif nm == "u2": self._latex[pos_1][columns] = \ "\\gate{U_2\\left(%s,%s\\right)}" % ( op["op"].params[0], op["op"].params[1]) elif nm == "u3": self._latex[pos_1][columns] = ("\\gate{U_3(%s,%s,%s)}" % ( op["op"].params[0], op["op"].params[1], op["op"].params[2])) elif nm == "rx": self._latex[pos_1][columns] = "\\gate{R_x(%s)}" % ( op["op"].params[0]) elif nm == "ry": self._latex[pos_1][columns] = "\\gate{R_y(%s)}" % ( op["op"].params[0]) elif nm == "rz": self._latex[pos_1][columns] = "\\gate{R_z(%s)}" % ( op["op"].params[0]) gap = pos_2 - pos_1 for i in range(self.cregs[if_reg]): if if_value[i] == '1': self._latex[pos_2 + i][columns] = \ "\\control \\cw \\cwx[-" + str(gap) + "]" gap = 1 else: self._latex[pos_2 + i][columns] = \ "\\controlo \\cw \\cwx[-" + str(gap) + "]" gap = 1 else: if not is_occupied[pos_1]: is_occupied[pos_1] = True else: columns += 1 is_occupied = [False] * self.img_width is_occupied[pos_1] = True if nm == "x": self._latex[pos_1][columns] = "\\gate{X}" elif nm == "y": self._latex[pos_1][columns] = "\\gate{Y}" elif nm == "z": self._latex[pos_1][columns] = "\\gate{Z}" elif nm == "h": self._latex[pos_1][columns] = "\\gate{H}" elif nm == "s": self._latex[pos_1][columns] = "\\gate{S}" elif nm == "sdg": self._latex[pos_1][columns] = "\\gate{S^\\dag}" elif nm == "t": self._latex[pos_1][columns] = "\\gate{T}" elif nm == "tdg": self._latex[pos_1][columns] = "\\gate{T^\\dag}" elif nm == "u0": self._latex[pos_1][columns] = "\\gate{U_0(%s)}" % ( op["op"].params[0]) elif nm == "u1": self._latex[pos_1][columns] = "\\gate{U_1(%s)}" % ( op["op"].params[0]) elif nm == "u2": self._latex[pos_1][columns] = \ "\\gate{U_2\\left(%s,%s\\right)}" % ( op["op"].params[0], op["op"].params[1]) elif nm == "u3": self._latex[pos_1][columns] = ("\\gate{U_3(%s,%s,%s)}" % ( op["op"].params[0], op["op"].params[1], op["op"].params[2])) elif nm == "rx": self._latex[pos_1][columns] = "\\gate{R_x(%s)}" % ( op["op"].params[0]) elif nm == "ry": self._latex[pos_1][columns] = "\\gate{R_y(%s)}" % ( op["op"].params[0]) elif nm == "rz": self._latex[pos_1][columns] = "\\gate{R_z(%s)}" % ( op["op"].params[0]) elif nm == "reset": self._latex[pos_1][columns] = ( "\\push{\\rule{.6em}{0em}\\ket{0}\\" "rule{.2em}{0em}} \\qw") elif len(qarglist) == 2: pos_1 = self.img_regs[(qarglist[0][0], qarglist[0][1])] pos_2 = self.img_regs[(qarglist[1][0], qarglist[1][1])] if 'condition' in op and op['condition']: pos_3 = self.img_regs[(if_reg, 0)] temp = [pos_1, pos_2, pos_3] temp.sort(key=int) top = temp[0] bottom = temp[1] for i in range(top, pos_3 + 1): if is_occupied[i] is False: is_occupied[i] = True else: columns += 1 is_occupied = [False] * self.img_width for j in range(top, pos_3 + 1): is_occupied[j] = True break # symetric gates have angle labels if op['name'] in ['cu1']: columns += 1 is_occupied = [False] * self.img_width is_occupied[top] = True gap = pos_3 - bottom for i in range(self.cregs[if_reg]): if if_value[i] == '1': self._latex[pos_3 + i][columns] = \ "\\control \\cw \\cwx[-" + str(gap) + "]" gap = 1 else: self._latex[pos_3 + i][columns] = \ "\\controlo \\cw \\cwx[-" + str(gap) + "]" gap = 1 if nm == "cx": self._latex[pos_1][columns] = \ "\\ctrl{" + str(pos_2 - pos_1) + "}" self._latex[pos_2][columns] = "\\targ" elif nm == "cz": self._latex[pos_1][columns] = \ "\\ctrl{" + str(pos_2 - pos_1) + "}" self._latex[pos_2][columns] = "\\control\\qw" elif nm == "cy": self._latex[pos_1][columns] = \ "\\ctrl{" + str(pos_2 - pos_1) + "}" self._latex[pos_2][columns] = "\\gate{Y}" elif nm == "ch": self._latex[pos_1][columns] = \ "\\ctrl{" + str(pos_2 - pos_1) + "}" self._latex[pos_2][columns] = "\\gate{H}" elif nm == "swap": self._latex[pos_1][columns] = "\\qswap" self._latex[pos_2][columns] = \ "\\qswap \\qwx[" + str(pos_1 - pos_2) + "]" elif nm == "crz": self._latex[pos_1][columns] = \ "\\ctrl{" + str(pos_2 - pos_1) + "}" self._latex[pos_2][columns] = \ "\\gate{R_z(%s)}" % (op["op"].params[0]) elif nm == "cu1": self._latex[pos_1][columns - 1] = "\\ctrl{" + str( pos_2 - pos_1) + "}" self._latex[pos_2][columns - 1] = "\\control\\qw" self._latex[min(pos_1, pos_2)][columns] = \ "\\dstick{%s}\\qw" % (op["op"].params[0]) self._latex[max(pos_1, pos_2)][columns] = "\\qw" elif nm == "cu3": self._latex[pos_1][columns] = \ "\\ctrl{" + str(pos_2 - pos_1) + "}" self._latex[pos_2][columns] = \ "\\gate{U_3(%s,%s,%s)}" % (op["op"].params[0], op["op"].params[1], op["op"].params[2]) else: temp = [pos_1, pos_2] temp.sort(key=int) top = temp[0] bottom = temp[1] for i in range(top, bottom + 1): if is_occupied[i] is False: is_occupied[i] = True else: columns += 1 is_occupied = [False] * self.img_width for j in range(top, bottom + 1): is_occupied[j] = True break # symetric gates have angle labels if op['name'] in ['cu1']: columns += 1 is_occupied = [False] * self.img_width is_occupied[top] = True if nm == "cx": self._latex[pos_1][columns] = "\\ctrl{" + str( pos_2 - pos_1) + "}" self._latex[pos_2][columns] = "\\targ" elif nm == "cz": self._latex[pos_1][columns] = "\\ctrl{" + str( pos_2 - pos_1) + "}" self._latex[pos_2][columns] = "\\control\\qw" elif nm == "cy": self._latex[pos_1][columns] = "\\ctrl{" + str( pos_2 - pos_1) + "}" self._latex[pos_2][columns] = "\\gate{Y}" elif nm == "ch": self._latex[pos_1][columns] = "\\ctrl{" + str( pos_2 - pos_1) + "}" self._latex[pos_2][columns] = "\\gate{H}" elif nm == "swap": self._latex[pos_1][columns] = "\\qswap" self._latex[pos_2][columns] = \ "\\qswap \\qwx[" + str(pos_1 - pos_2) + "]" elif nm == "crz": self._latex[pos_1][columns] = "\\ctrl{" + str( pos_2 - pos_1) + "}" self._latex[pos_2][columns] = \ "\\gate{R_z(%s)}" % (op["op"].params[0]) elif nm == "cu1": self._latex[pos_1][columns - 1] = "\\ctrl{" + str( pos_2 - pos_1) + "}" self._latex[pos_2][columns - 1] = "\\control\\qw" self._latex[min(pos_1, pos_2)][columns] = \ "\\dstick{%s}\\qw" % (op["op"].params[0]) self._latex[max(pos_1, pos_2)][columns] = "\\qw" elif nm == "cu3": self._latex[pos_1][columns] = "\\ctrl{" + str( pos_2 - pos_1) + "}" self._latex[pos_2][columns] = ("\\gate{U_3(%s,%s,%s)}" % ( op["op"].params[0], op["op"].params[1], op["op"].params[2])) elif len(qarglist) == 3: pos_1 = self.img_regs[(qarglist[0][0], qarglist[0][1])] pos_2 = self.img_regs[(qarglist[1][0], qarglist[1][1])] pos_3 = self.img_regs[(qarglist[2][0], qarglist[2][1])] if 'condition' in op and op['condition']: pos_4 = self.img_regs[(if_reg, 0)] temp = [pos_1, pos_2, pos_3, pos_4] temp.sort(key=int) top = temp[0] bottom = temp[2] for i in range(top, pos_4 + 1): if is_occupied[i] is False: is_occupied[i] = True else: columns += 1 is_occupied = [False] * self.img_width for j in range(top, pos_4 + 1): is_occupied[j] = True break prev_column = [x[columns - 1] for x in self._latex] for item, prev_entry in enumerate(prev_column): if 'barrier' in prev_entry: span = re.search('barrier{(.*)}', prev_entry) if span and any(i in temp for i in range( item, int(span.group(1)))): self._latex[item][columns - 1] = \ prev_entry.replace( '\\barrier{', '\\barrier[-0.65em]{') gap = pos_4 - bottom for i in range(self.cregs[if_reg]): if if_value[i] == '1': self._latex[pos_4 + i][columns] = \ "\\control \\cw \\cwx[-" + str(gap) + "]" gap = 1 else: self._latex[pos_4 + i][columns] = \ "\\controlo \\cw \\cwx[-" + str(gap) + "]" gap = 1 if nm == "ccx": self._latex[pos_1][columns] = "\\ctrl{" + str( pos_2 - pos_1) + "}" self._latex[pos_2][columns] = "\\ctrl{" + str( pos_3 - pos_2) + "}" self._latex[pos_3][columns] = "\\targ" if nm == "cswap": self._latex[pos_1][columns] = "\\ctrl{" + str( pos_2 - pos_1) + "}" self._latex[pos_2][columns] = "\\qswap" self._latex[pos_3][columns] = \ "\\qswap \\qwx[" + str(pos_2 - pos_3) + "]" else: temp = [pos_1, pos_2, pos_3] temp.sort(key=int) top = temp[0] bottom = temp[2] for i in range(top, bottom + 1): if is_occupied[i] is False: is_occupied[i] = True else: columns += 1 is_occupied = [False] * self.img_width for j in range(top, bottom + 1): is_occupied[j] = True break prev_column = [x[columns - 1] for x in self._latex] for item, prev_entry in enumerate(prev_column): if 'barrier' in prev_entry: span = re.search('barrier{(.*)}', prev_entry) if span and any(i in temp for i in range( item, int(span.group(1)))): self._latex[item][columns - 1] = \ prev_entry.replace( '\\barrier{', '\\barrier[-0.65em]{') if nm == "ccx": self._latex[pos_1][columns] = "\\ctrl{" + str( pos_2 - pos_1) + "}" self._latex[pos_2][columns] = "\\ctrl{" + str( pos_3 - pos_2) + "}" self._latex[pos_3][columns] = "\\targ" if nm == "cswap": self._latex[pos_1][columns] = "\\ctrl{" + str( pos_2 - pos_1) + "}" self._latex[pos_2][columns] = "\\qswap" self._latex[pos_3][columns] = \ "\\qswap \\qwx[" + str(pos_2 - pos_3) + "]" elif op["name"] == "measure": if (len(op['cargs']) != 1 or len(op['qargs']) != 1 or op['op'].params): raise exceptions.VisualizationError("bad operation record") if 'condition' in op and op['condition']: raise exceptions.VisualizationError( "If controlled measures currently not supported.") qname, qindex = op['qargs'][0] cname, cindex = op['cargs'][0] if aliases: newq = aliases[(qname, qindex)] qname = newq[0] qindex = newq[1] pos_1 = self.img_regs[(qname, qindex)] pos_2 = self.img_regs[(cname, cindex)] for i in range(pos_1, pos_2 + 1): if is_occupied[i] is False: is_occupied[i] = True else: columns += 1 is_occupied = [False] * self.img_width for j in range(pos_1, pos_2 + 1): is_occupied[j] = True break try: self._latex[pos_1][columns] = "\\meter" prev_column = [x[columns - 1] for x in self._latex] for item, prev_entry in enumerate(prev_column): if 'barrier' in prev_entry: span = re.search('barrier{(.*)}', prev_entry) if span and ( item + int(span.group(1))) - pos_1 >= 0: self._latex[item][columns - 1] = \ prev_entry.replace( '\\barrier{', '\\barrier[-1.15em]{') self._latex[pos_2][columns] = \ "\\cw \\cwx[-" + str(pos_2 - pos_1) + "]" except Exception as e: raise exceptions.VisualizationError( 'Error during Latex building: %s' % str(e)) elif op['name'] in ["barrier", 'snapshot', 'load', 'save', 'noise']: if self.plot_barriers: qarglist = op['qargs'] indexes = [self._get_qubit_index(x) for x in qarglist] start_bit = self.qubit_list[min(indexes)] if aliases is not None: qarglist = map(lambda x: aliases[x], qarglist) start = self.img_regs[start_bit] span = len(op['qargs']) - 1 for i in range(start, start + span + 1): if is_occupied[i] is False: is_occupied[i] = True else: columns += 1 is_occupied = [False] * self.img_width for j in range(start, start + span + 1): is_occupied[j] = True break self._latex[start][columns] = "\\qw \\barrier{" + str( span) + "}" else: raise exceptions.VisualizationError("bad node data")
def _get_image_depth(self, aliases=None): """Get depth information for the circuit. Args: aliases (dict): dict mapping the current qubits in the circuit to new qubit names. Returns: int: number of columns in the circuit int: total size of columns in the circuit Raises: VisualizationError: if trying to draw unsupported gates """ columns = 2 # wires in the beginning and end is_occupied = [False] * self.img_width max_column_width = {} for op in self.ops: # useful information for determining row spacing boxed_gates = ['u0', 'u1', 'u2', 'u3', 'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'rx', 'ry', 'rz', 'ch', 'cy', 'crz', 'cu3'] target_gates = ['cx', 'ccx'] if op['name'] in boxed_gates: self.has_box = True if op['name'] in target_gates: self.has_target = True # useful information for determining column widths and final image # scaling if op['name'] not in ['measure', 'reset', 'barrier']: qarglist = op['qargs'] if aliases is not None: qarglist = map(lambda x: aliases[x], qarglist) if len(qarglist) == 1: pos_1 = self.img_regs[(qarglist[0][0], qarglist[0][1])] if 'condition' in op and op['condition']: mask = self._get_mask(op['condition'][0]) cl_reg = self.clbit_list[self._ffs(mask)] if_reg = cl_reg[0] pos_2 = self.img_regs[cl_reg] for i in range(pos_1, pos_2 + self.cregs[if_reg]): if is_occupied[i] is False: is_occupied[i] = True else: columns += 1 is_occupied = [False] * self.img_width for j in range(pos_1, pos_2 + 1): is_occupied[j] = True break else: if is_occupied[pos_1] is False: is_occupied[pos_1] = True else: columns += 1 is_occupied = [False] * self.img_width is_occupied[pos_1] = True elif len(qarglist) == 2: pos_1 = self.img_regs[(qarglist[0][0], qarglist[0][1])] pos_2 = self.img_regs[(qarglist[1][0], qarglist[1][1])] if 'condition' in op and op['condition']: mask = self._get_mask(op['condition'][0]) cl_reg = self.clbit_list[self._ffs(mask)] if_reg = cl_reg[0] pos_3 = self.img_regs[(if_reg, 0)] if pos_1 > pos_2: for i in range(pos_2, pos_3 + self.cregs[if_reg]): if is_occupied[i] is False: is_occupied[i] = True else: columns += 1 is_occupied = [False] * self.img_width for j in range(pos_2, pos_3 + 1): is_occupied[j] = True break else: for i in range(pos_1, pos_3 + self.cregs[if_reg]): if is_occupied[i] is False: is_occupied[i] = True else: columns += 1 is_occupied = [False] * self.img_width for j in range(pos_1, pos_3 + 1): is_occupied[j] = True break # symetric gates have angle labels if op['name'] in ['cu1']: columns += 1 is_occupied = [False] * self.img_width is_occupied[max(pos_1, pos_2)] = True else: temp = [pos_1, pos_2] temp.sort(key=int) top = temp[0] bottom = temp[1] for i in range(top, bottom + 1): if is_occupied[i] is False: is_occupied[i] = True else: columns += 1 is_occupied = [False] * self.img_width for j in range(top, bottom + 1): is_occupied[j] = True break # symetric gates have angle labels if op['name'] in ['cu1']: columns += 1 is_occupied = [False] * self.img_width is_occupied[top] = True elif len(qarglist) == 3: pos_1 = self.img_regs[(qarglist[0][0], qarglist[0][1])] pos_2 = self.img_regs[(qarglist[1][0], qarglist[1][1])] pos_3 = self.img_regs[(qarglist[2][0], qarglist[2][1])] if 'condition' in op and op['condition']: mask = self._get_mask(op['condition'][0]) cl_reg = self.clbit_list[self._ffs(mask)] if_reg = cl_reg[0] pos_4 = self.img_regs[(if_reg, 0)] temp = [pos_1, pos_2, pos_3, pos_4] temp.sort(key=int) top = temp[0] bottom = temp[2] for i in range(top, pos_4 + 1): if is_occupied[i] is False: is_occupied[i] = True else: columns += 1 is_occupied = [False] * self.img_width for j in range(top, pos_4 + 1): is_occupied[j] = True break else: temp = [pos_1, pos_2, pos_3] temp.sort(key=int) top = temp[0] bottom = temp[2] for i in range(top, bottom + 1): if is_occupied[i] is False: is_occupied[i] = True else: columns += 1 is_occupied = [False] * self.img_width for j in range(top, bottom + 1): is_occupied[j] = True break # update current column width arg_str_len = 0 for arg in op['op'].params: arg_str = re.sub(r'[-+]?\d*\.\d{2,}|\d{2,}', _truncate_float, str(arg)) arg_str_len += len(arg_str) if columns not in max_column_width: max_column_width[columns] = 0 max_column_width[columns] = max(arg_str_len, max_column_width[columns]) elif op['name'] == "measure": if len(op['cargs']) != 1 or len(op['qargs']) != 1: raise exceptions.VisualizationError("bad operation record") if 'condition' in op and op['condition']: raise exceptions.VisualizationError( 'conditional measures currently not supported.') qname, qindex = op['qargs'][0] cname, cindex = op['cargs'][0] if aliases: newq = aliases[(qname, qindex)] qname = newq[0] qindex = newq[1] pos_1 = self.img_regs[(qname, qindex)] pos_2 = self.img_regs[(cname, cindex)] temp = [pos_1, pos_2] temp.sort(key=int) [pos_1, pos_2] = temp for i in range(pos_1, pos_2 + 1): if is_occupied[i] is False: is_occupied[i] = True else: columns += 1 is_occupied = [False] * self.img_width for j in range(pos_1, pos_2 + 1): is_occupied[j] = True break # update current column width if columns not in max_column_width: max_column_width[columns] = 0 elif op['name'] == "reset": if 'conditional' in op and op['condition']: raise exceptions.VisualizationError( 'conditional reset currently not supported.') qname, qindex = op['qargs'][0] if aliases: newq = aliases[(qname, qindex)] qname = newq[0] qindex = newq[1] pos_1 = self.img_regs[(qname, qindex)] if is_occupied[pos_1] is False: is_occupied[pos_1] = True else: columns += 1 is_occupied = [False] * self.img_width is_occupied[pos_1] = True elif op['name'] in ["barrier", 'snapshot', 'load', 'save', 'noise']: if self.plot_barriers: qarglist = op['qargs'] indexes = [self._get_qubit_index(x) for x in qarglist] start_bit = self.qubit_list[min(indexes)] if aliases is not None: qarglist = map(lambda x: aliases[x], qarglist) start = self.img_regs[start_bit] span = len(op['qargs']) - 1 for i in range(start, start + span + 1): if is_occupied[i] is False: is_occupied[i] = True else: columns += 1 is_occupied = [False] * self.img_width for j in range(start, start + span + 1): is_occupied[j] = True break # update current column width if columns not in max_column_width: max_column_width[columns] = 0 else: raise exceptions.VisualizationError("bad node data") # every 3 characters is roughly one extra 'unit' of width in the cell # the gate name is 1 extra 'unit' # the qubit/cbit labels plus initial states is 2 more # the wires poking out at the ends is 2 more sum_column_widths = sum(1 + v / 3 for v in max_column_width.values()) return columns + 1, math.ceil(sum_column_widths) + 4