def _registers(self): # NOTE: formats of clbit and qubit are different! header = self._ast['header'] self._creg = [] for e in header['clbit_labels']: for i in range(e[1]): self._creg.append(Register(name=e[0], index=i)) if len(self._creg) != header['number_of_clbits']: raise _error.VisualizationError('internal error') self._qreg = [] for e in header['qubit_labels']: self._qreg.append(Register(name=e[0], index=e[1])) if len(self._qreg) != header['number_of_qubits']: raise _error.VisualizationError('internal error')
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 _error.VisualizationError("unable to find bit for operation") return qindex
def _convert_mask(self, mask): orig_clbit_list = [] for cr in self.orig_cregs: for i in range(self.orig_cregs[cr]): orig_clbit_list.append((cr, i)) bit_list = [(mask >> bit) & 1 for bit in range(len(orig_clbit_list) - 1, -1, -1)] converted_mask_list = [None] * len(bit_list) converted_mask = 0 for pos, bit in enumerate(reversed(bit_list)): new_pos = self.clbit_list.index(orig_clbit_list[pos]) converted_mask_list[new_pos] = bit if None in converted_mask_list: raise _error.VisualizationError('Reverse mask creation failed') converted_mask_list = list(reversed(converted_mask_list)) for bit in converted_mask_list: converted_mask = (converted_mask << 1) | bit return converted_mask
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.circuit['instructions']): if 'conditional' in op: mask = int(op['conditional']['mask'], 16) if self.reverse_bits: mask = self._convert_mask(mask) cl_reg = self.clbit_list[self._ffs(mask)] if_reg = cl_reg[0] pos_2 = self.img_regs[cl_reg] if_value = format(int(op['conditional']['val'], 16), 'b').zfill(self.cregs[if_reg])[::-1] if op['name'] not in [ 'measure', 'barrier', 'snapshot', 'load', 'save', 'noise' ]: nm = op['name'] qarglist = [self.qubit_list[i] for i in op['qubits']] 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 'conditional' in op: mask = int(op['conditional']['mask'], 16) if self.reverse_bits: mask = self._convert_mask(mask) 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["texparams"][0]) elif nm == "u1": self._latex[pos_1][columns] = "\\gate{U_1(%s)}" % ( op["texparams"][0]) elif nm == "u2": self._latex[pos_1][columns] = \ "\\gate{U_2\\left(%s,%s\\right)}" % ( op["texparams"][0], op["texparams"][1]) elif nm == "u3": self._latex[pos_1][columns] = ( "\\gate{U_3(%s,%s,%s)}" % (op["texparams"][0], op["texparams"][1], op["texparams"][2])) elif nm == "rx": self._latex[pos_1][columns] = "\\gate{R_x(%s)}" % ( op["texparams"][0]) elif nm == "ry": self._latex[pos_1][columns] = "\\gate{R_y(%s)}" % ( op["texparams"][0]) elif nm == "rz": self._latex[pos_1][columns] = "\\gate{R_z(%s)}" % ( op["texparams"][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["texparams"][0]) elif nm == "u1": self._latex[pos_1][columns] = "\\gate{U_1(%s)}" % ( op["texparams"][0]) elif nm == "u2": self._latex[pos_1][columns] = \ "\\gate{U_2\\left(%s,%s\\right)}" % ( op["texparams"][0], op["texparams"][1]) elif nm == "u3": self._latex[pos_1][columns] = ( "\\gate{U_3(%s,%s,%s)}" % (op["texparams"][0], op["texparams"][1], op["texparams"][2])) elif nm == "rx": self._latex[pos_1][columns] = "\\gate{R_x(%s)}" % ( op["texparams"][0]) elif nm == "ry": self._latex[pos_1][columns] = "\\gate{R_y(%s)}" % ( op["texparams"][0]) elif nm == "rz": self._latex[pos_1][columns] = "\\gate{R_z(%s)}" % ( op["texparams"][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 'conditional' in op: 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["texparams"][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["texparams"][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["texparams"][0], op["texparams"][1], op["texparams"][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["texparams"][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["texparams"][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["texparams"][0], op["texparams"][1], op["texparams"][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 'conditional' in op: 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['clbits']) != 1 or len(op['qubits']) != 1 or 'params' in op): raise _error.VisualizationError("bad operation record") if 'conditional' in op: raise _error.VisualizationError( "If controlled measures currently not supported.") qname, qindex = self.total_2_register_index( op['qubits'][0], self.qregs) cname, cindex = self.total_2_register_index( op['clbits'][0], self.cregs) 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 _error.VisualizationError( 'Error during Latex building: %s' % str(e)) elif op['name'] in [ "barrier", 'snapshot', 'load', 'save', 'noise' ]: if self.plot_barriers: qarglist = [self.qubit_list[i] for i in op['qubits']] if self.reverse_bits: qarglist = list(reversed(qarglist)) if aliases is not None: qarglist = map(lambda x: aliases[x], qarglist) start = self.img_regs[(qarglist[0][0], qarglist[0][1])] span = len(op['qubits']) - 1 self._latex[start][columns] += " \\barrier{" + str( span) + "}" else: raise _error.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.circuit['instructions']: # 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 = [self.qubit_list[i] for i in op['qubits']] 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 'conditional' in op: mask = int(op['conditional']['mask'], 16) if self.reverse_bits: mask = self._convert_mask(mask) 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 'conditional' in op: mask = int(op['conditional']['mask'], 16) if self.reverse_bits: mask = self._convert_mask(mask) 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 'conditional' in op: mask = int(op['conditional']['mask'], 16) if self.reverse_bits: mask = self._convert_mask(mask) 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['texparams']: arg_str = re.sub(r'[-+]?\d*\.\d{2,}|\d{2,}', _truncate_float, 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['clbits']) != 1 or len(op['qubits']) != 1: raise _error.VisualizationError("bad operation record") if 'conditional' in op: raise _error.VisualizationError( 'conditional measures currently not supported.') qname, qindex = self.total_2_register_index( op['qubits'][0], self.qregs) cname, cindex = self.total_2_register_index( op['clbits'][0], self.cregs) 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: raise _error.VisualizationError( 'conditional reset currently not supported.') qname, qindex = self.total_2_register_index( op['qubits'][0], self.qregs) 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'] == "barrier": pass else: raise _error.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
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() 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'].param, 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.name == 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' ]: q_group = self._qreg_dict[q_idxs[0]]['group'] if q_group not in _barriers['group']: _barriers['group'].append(q_group) _barriers['coord'].append(q_xy[0]) 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 _error.VisualizationError('invalid gate {}'.format(op)) # # adjust window size and draw horizontal lines # max_anc = max([q_anchors[ii].get_index() for ii in self._qreg_dict]) n_fold = (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 circuit_drawer(circuit, basis=None, scale=0.7, filename=None, style=None, output=None, interactive=False, line_length=None): """Draw a quantum circuit to different formats (set by output parameter): 0. text: ASCII art string 1. latex: high-quality images, but heavy external software dependencies 2. matplotlib: purely in Python with no external dependencies Defaults to an overcomplete basis, in order to not alter gates. Args: circuit (QuantumCircuit): the quantum circuit to draw basis (str): the basis to unroll to prior to drawing. Defaults to `"id,u0,u1,u2,u3,x,y,z,h,s,sdg,t,tdg,rx,ry,rz,cx,cy,cz,ch,crz,cu1, cu3,swap,ccx,cswap"` This option is deprecated and will be removed in the future. 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 (str): 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 (cannot inline in Jupyter). Note when used with the latex_source output type this has no effect line_length (int): sets the length of the lines generated by `text` Returns: PIL.Image: (outputs `latex` and `python`) an in-memory representation of the circuit diagram. String: (outputs `text` and `latex_source`). The ascii art or the LaTeX source code. Raises: VisualizationError: when an invalid output method is selected 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. 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. """ if basis is None: basis = ("id,u0,u1,u2,u3,x,y,z,h,s,sdg,t,tdg,rx,ry,rz," "cx,cy,cz,ch,crz,cu1,cu3,swap,ccx,cswap") else: warnings.warn('The basis kwarg is deprecated and the circuit drawer ' 'function will not be able to adjust basis gates itself ' 'in a future release', DeprecationWarning) im = None 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: im = _latex_circuit_drawer(circuit, basis, scale, filename, style) except (OSError, subprocess.CalledProcessError, FileNotFoundError): im = _matplotlib_circuit_drawer(circuit, basis, scale, filename, style) else: if output == 'text': reversebits = style['reversebits'] if style and 'reversebits' in style else False plotbarriers = style['plotbarriers'] if style and 'plotbarriers' in style else True return _text_circuit_drawer(circuit, filename=filename, basis=basis, line_length=line_length, reversebits=reversebits, plotbarriers=plotbarriers) elif output == 'latex': im = _latex_circuit_drawer(circuit, basis=basis, scale=scale, filename=filename, style=style) elif output == 'latex_source': return _generate_latex_source(circuit, basis=basis, filename=filename, scale=scale, style=style) elif output == 'mpl': im = _matplotlib_circuit_drawer(circuit, basis=basis, scale=scale, filename=filename, style=style) else: raise _error.VisualizationError( 'Invalid output type %s selected. The only valid choices ' 'are latex, latex_source, text, and mpl' % output) if im and interactive: im.show() return im
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 (cannot inline in Jupyter). Note when used with the latex_source output type this has no effect. 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 _error.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