diff --git a/qiskit/visualization/latex.py b/qiskit/visualization/latex.py index bdefc21b44e5..654b472d617f 100644 --- a/qiskit/visualization/latex.py +++ b/qiskit/visualization/latex.py @@ -23,7 +23,13 @@ from qiskit.circuit.measure import Measure from qiskit.visualization.qcstyle import load_style from qiskit.circuit.tools.pi_check import pi_check -from .utils import get_gate_ctrl_text, get_param_str, get_bit_label, generate_latex_label +from .utils import ( + get_gate_ctrl_text, + get_param_str, + get_bit_label, + generate_latex_label, + get_condition_label, +) class QCircuitImage: @@ -78,15 +84,12 @@ def __init__( # image scaling self.scale = 1.0 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 = [] + self._ordered_bits = [] # Map from registers to the list they appear in the image self.img_regs = {} @@ -121,19 +124,19 @@ def __init__( self.plot_barriers = plot_barriers ################################# - self.qubit_list = qubits - self.clbit_list = clbits - self.ordered_bits = qubits + clbits + self._qubits = qubits + self._clbits = clbits + self._ordered_bits = qubits + clbits self.cregs = {reg: reg.size for reg in cregs} - self.bit_locations = { + 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} + 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 @@ -143,8 +146,8 @@ def __init__( if node.op.name not in {"measure"} and node.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)} + 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)} num_reg_bits = sum(reg.size for reg in self.cregs) if self.cregbundle: @@ -210,7 +213,7 @@ def _initialize_latex_array(self): self.wire_separation = 1.0 self._latex = [ [ - "\\cw" if isinstance(self.ordered_bits[j], Clbit) else "\\qw" + "\\cw" if isinstance(self._ordered_bits[j], Clbit) else "\\qw" for _ in range(self.img_depth + 1) ] for j in range(self.img_width) @@ -218,9 +221,9 @@ def _initialize_latex_array(self): self._latex.append([" "] * (self.img_depth + 1)) # quantum register - for ii, reg in enumerate(self.qubit_list): - register = self.bit_locations[reg]["register"] - index = self.bit_locations[reg]["index"] + for ii, reg in enumerate(self._qubits): + register = self._bit_locations[reg]["register"] + index = self._bit_locations[reg]["index"] qubit_label = get_bit_label("latex", register, index, qubit=True, layout=self.layout) qubit_label += " : " if self.initial_state: @@ -230,10 +233,10 @@ def _initialize_latex_array(self): # classical register offset = 0 - if self.clbit_list: - for ii in range(len(self.qubit_list), self.img_width): - register = self.bit_locations[self.ordered_bits[ii + offset]]["register"] - index = self.bit_locations[self.ordered_bits[ii + offset]]["index"] + if self._clbits: + for ii in range(len(self._qubits), self.img_width): + register = self._bit_locations[self._ordered_bits[ii + offset]]["register"] + index = self._bit_locations[self._ordered_bits[ii + offset]]["index"] clbit_label = get_bit_label( "latex", register, index, qubit=False, cregbundle=self.cregbundle ) @@ -328,9 +331,9 @@ def _get_image_depth(self): sum_column_widths = sum(1 + v / 3 for v in max_column_widths) max_reg_name = 3 - for reg in self.ordered_bits: - if self.bit_locations[reg]["register"] is not None: - max_reg_name = max(max_reg_name, len(self.bit_locations[reg]["register"].name)) + for reg in self._ordered_bits: + if self._bit_locations[reg]["register"] is not None: + max_reg_name = max(max_reg_name, len(self._bit_locations[reg]["register"].name)) sum_column_widths += 5 + max_reg_name / 3 # could be a fraction so ceil @@ -420,7 +423,7 @@ def _build_latex_array(self): def _build_multi_gate(self, op, gate_text, wire_list, cwire_list, col): """Add a multiple wire gate to the _latex list""" - cwire_start = len(self.qubit_list) + cwire_start = len(self._qubits) num_cols_op = 1 if isinstance(op, (SwapGate, RZZGate)): num_cols_op = self._build_symmetric_gate(op, gate_text, wire_list, col) @@ -524,19 +527,31 @@ def _build_measure(self, node, col): """Build a meter and the lines to the creg""" wire1 = self.img_regs[node.qargs[0]] self._latex[wire1][col] = "\\meter" + if self.cregbundle: - wire2 = len(self.qubit_list) - cregindex = self.img_regs[node.cargs[0]] - wire2 - for creg_size in self.cregs.values(): - if cregindex >= creg_size: - cregindex -= creg_size + wire2 = len(self._qubits) + prev_reg = None + idx_str = "" + cond_offset = 1.5 if node.op.condition else 0.0 + for i, reg in enumerate(self.cregs_bits): + # if it's a registerless bit + if reg is None: + if self._clbits[i] == node.cargs[0]: + break wire2 += 1 - else: + continue + # if it's a whole register or a bit in a register + if reg == self._bit_locations[node.cargs[0]]["register"]: + idx_str = str(self._bit_locations[node.cargs[0]]["index"]) break - cond_offset = 1.5 if node.op.condition else 0.0 + if self.cregbundle and prev_reg and prev_reg == reg: + continue + wire2 += 1 + prev_reg = reg + self._latex[wire2][col] = "\\dstick{_{_{\\hspace{%sem}%s}}} \\cw \\ar @{<=} [-%s,0]" % ( cond_offset, - str(cregindex), + idx_str, str(wire2 - wire1), ) else: @@ -553,11 +568,11 @@ def _build_barrier(self, node, col): if index - 1 == last: last = index else: - pos = self.img_regs[self.qubit_list[first]] + pos = self.img_regs[self._qubits[first]] self._latex[pos][col - 1] += " \\barrier[0em]{" + str(last - first) + "}" self._latex[pos][col] = "\\qw" first = last = index - pos = self.img_regs[self.qubit_list[first]] + pos = self.img_regs[self._qubits[first]] self._latex[pos][col - 1] += " \\barrier[0em]{" + str(last - first) + "}" self._latex[pos][col] = "\\qw" @@ -581,77 +596,70 @@ def _add_controls(self, wire_list, ctrlqargs, ctrl_state, col): def _add_condition(self, op, wire_list, col): """Add a condition to the _latex list""" - # if_value - a bit string for the condition # cwire - the wire number for the first wire for the condition register # or if cregbundle, wire number of the condition register itself # gap - the number of wires from cwire to the bottom gate qubit + label, clbit_mask, val_list = get_condition_label( + op.condition, self._clbits, self._bit_locations, self.cregbundle + ) + if not self.reverse_bits: + val_list = val_list[::-1] cond_is_bit = isinstance(op.condition[0], Clbit) - if cond_is_bit: - cond_reg = self.bit_locations[op.condition[0]]["register"] - if_value = op.condition[1] + cond_reg = ( + op.condition[0] if not cond_is_bit else self._bit_locations[op.condition[0]]["register"] + ) + # if cregbundle, add 1 to cwire for each register and each registerless bit, until + # the condition bit/register is found. If not cregbundle, add 1 to cwire for every + # bit until condition found. + cwire = len(self._qubits) + if self.cregbundle: + prev_reg = None + for i, reg in enumerate(self.cregs_bits): + # if it's a registerless bit + if reg is None: + if self._clbits[i] == op.condition[0]: + break + cwire += 1 + continue + # if it's a whole register or a bit in a register + if reg == cond_reg: + break + if self.cregbundle and prev_reg and prev_reg == reg: + continue + cwire += 1 + prev_reg = reg else: - cond_reg = op.condition[0] - creg_size = self.cregs[cond_reg] - if_value = format(op.condition[1], "b").zfill(creg_size) - if not self.reverse_bits: - if_value = if_value[::-1] - - cwire = len(self.qubit_list) - iter_cregs = iter(list(self.cregs)) if self.cregbundle else iter(self.cregs_bits) - for creg in iter_cregs: - if creg == cond_reg: - break - cwire += 1 + for bit in clbit_mask: + if bit == "1": + break + cwire += 1 gap = cwire - max(wire_list) meas_offset = -0.3 if isinstance(op, Measure) else 0.0 - if self.cregbundle: - # Print the condition value at the bottom and put bullet on creg line - if cond_is_bit: - ctrl_bit = ( - str(cond_reg.name) + "_" + str(self.bit_locations[op.condition[0]]["index"]) - ) - label = "T" if if_value is True else "F" - self._latex[cwire][col] = "\\control \\cw^(%s){^{\\mathtt{%s=%s}}} \\cwx[-%s]" % ( - meas_offset, - ctrl_bit, - label, - str(gap), - ) - else: - self._latex[cwire][col] = "\\control \\cw^(%s){^{\\mathtt{%s}}} \\cwx[-%s]" % ( - meas_offset, - str(hex(op.condition[1])), - str(gap), - ) + # Print the condition value at the bottom and put bullet on creg line + if cond_is_bit or self.cregbundle: + control = "\\control" if op.condition[1] else "\\controlo" + self._latex[cwire][col] = f"{control}" + " \\cw^(%s){^{\\mathtt{%s}}} \\cwx[-%s]" % ( + meas_offset, + label, + str(gap), + ) else: - # Add the open and closed buttons to indicate the condition value - if cond_is_bit: - extra_gap = list(cond_reg).index(op.condition[0]) - gap += extra_gap - control = "\\control" if if_value is True else "\\controlo" - self._latex[cwire + extra_gap][col] = ( - f"{control}" + " \\cw^(%s){^{\\mathtt{%s}}} \\cwx[-%s]" - ) % ( - meas_offset, - str(hex(op.condition[1])), - str(gap), - ) - else: - for i in range(creg_size - 1): - control = "\\control" if if_value[i] == "1" else "\\controlo" - self._latex[cwire + i][col] = f"{control} \\cw \\cwx[-" + str(gap) + "]" - gap = 1 - # Add (hex condition value) below the last cwire - control = "\\control" if if_value[creg_size - 1] == "1" else "\\controlo" - self._latex[creg_size + cwire - 1][col] = ( - f"{control}" + " \\cw^(%s){^{\\mathtt{%s}}} \\cwx[-%s]" - ) % ( - meas_offset, - str(hex(op.condition[1])), - str(gap), - ) + creg_size = op.condition[0].size + for i in range(creg_size - 1): + control = "\\control" if val_list[i] == "1" else "\\controlo" + self._latex[cwire + i][col] = f"{control} \\cw \\cwx[-" + str(gap) + "]" + gap = 1 + # Add (hex condition value) below the last cwire + control = "\\control" if val_list[creg_size - 1] == "1" else "\\controlo" + self._latex[creg_size + cwire - 1][col] = ( + f"{control}" + " \\cw^(%s){^{\\mathtt{%s}}} \\cwx[-%s]" + ) % ( + meas_offset, + label, + str(gap), + ) def _truncate_float(self, matchobj, ndigits=4): """Truncate long floats.""" diff --git a/qiskit/visualization/matplotlib.py b/qiskit/visualization/matplotlib.py index f4cb3a3b4f65..a8305b1bf444 100644 --- a/qiskit/visualization/matplotlib.py +++ b/qiskit/visualization/matplotlib.py @@ -27,7 +27,7 @@ except ImportError: HAS_PYLATEX = False -from qiskit.circuit import ControlledGate, Clbit +from qiskit.circuit import ControlledGate from qiskit.circuit import Measure from qiskit.circuit.library.standard_gates import ( SwapGate, @@ -43,6 +43,7 @@ get_gate_ctrl_text, get_param_str, get_bit_label, + get_condition_label, matplotlib_close_if_inline, ) from qiskit.circuit.tools.pi_check import pi_check @@ -114,10 +115,10 @@ def __init__( if bit not in self._bit_locations: self._bit_locations[bit] = {"register": None, "index": index} - self._qubit = qubits - self._clbit = clbits - self._qubit_dict = {} - self._clbit_dict = {} + self._qubits = qubits + self._clbits = clbits + self._qubits_dict = {} + self._clbits_dict = {} self._nodes = nodes self._scale = 1.0 if scale is None else scale @@ -270,7 +271,7 @@ def draw(self, filename=None, verbose=False): ``visualization.circuit_drawer`` and from ``QuantumCircuit.draw`` through circuit_drawer. """ # All information for the drawing is first loaded into self._data for the gates and into - # self._qubit_dict and self._clbit_dict for the qubits, clbits, and wires, + # self._qubits_dict and self._clbits_dict for the qubits, clbits, and wires, # followed by the coordinates for each gate. # get layer widths @@ -460,7 +461,7 @@ def _fix_double_script(bit_label): return bit_label # quantum register - for ii, reg in enumerate(self._qubit): + for ii, reg in enumerate(self._qubits): register = self._bit_locations[reg]["register"] index = self._bit_locations[reg]["index"] qubit_label = get_bit_label("mpl", register, index, qubit=True, layout=self._layout) @@ -470,7 +471,7 @@ def _fix_double_script(bit_label): if text_width > longest_bit_label_width: longest_bit_label_width = text_width pos = -ii - self._qubit_dict[ii] = { + self._qubits_dict[ii] = { "y": pos, "bit_label": qubit_label, "index": index, @@ -479,11 +480,11 @@ def _fix_double_script(bit_label): n_lines += 1 # classical register - if self._clbit: + if self._clbits: prev_creg = None idx = 0 - pos = y_off = -len(self._qubit) + 1 - for ii, reg in enumerate(self._clbit): + pos = y_off = -len(self._qubits) + 1 + for ii, reg in enumerate(self._clbits): register = self._bit_locations[reg]["register"] index = self._bit_locations[reg]["index"] if register is None or not self._cregbundle or prev_creg != register: @@ -503,7 +504,7 @@ def _fix_double_script(bit_label): if text_width > longest_bit_label_width: longest_bit_label_width = text_width pos = y_off - idx - self._clbit_dict[ii] = { + self._clbits_dict[ii] = { "y": pos, "bit_label": clbit_label, "index": index, @@ -516,9 +517,9 @@ def _get_coords(self, n_lines): """Load all the coordinate info needed to place the gates on the drawing""" # create the anchor arrays - for key, qubit in self._qubit_dict.items(): + for key, qubit in self._qubits_dict.items(): self._q_anchors[key] = Anchor(reg_num=n_lines, yind=qubit["y"], fold=self._fold) - for key, clbit in self._clbit_dict.items(): + for key, clbit in self._clbits_dict.items(): self._c_anchors[key] = Anchor(reg_num=n_lines, yind=clbit["y"], fold=self._fold) # get all the necessary coordinates for placing gates on the wires @@ -530,7 +531,7 @@ def _get_coords(self, n_lines): # get qubit index q_indxs = [] for qarg in node.qargs: - for index, reg in self._qubit_dict.items(): + for index, reg in self._qubits_dict.items(): if ( reg["register"] == self._bit_locations[qarg]["register"] and reg["index"] == self._bit_locations[qarg]["index"] @@ -541,7 +542,7 @@ def _get_coords(self, n_lines): # get clbit index c_indxs = [] for carg in node.cargs: - for index, reg in self._clbit_dict.items(): + for index, reg in self._clbits_dict.items(): if ( reg["register"] == self._bit_locations[carg]["register"] and reg["index"] == self._bit_locations[carg]["index"] @@ -575,7 +576,7 @@ def _get_coords(self, n_lines): barrier_offset = -1 if all(nd.op._directive for nd in layer) else 0 prev_anc = this_anc + layer_width + barrier_offset - 1 - anchors = [self._q_anchors[ii].get_index() for ii in self._qubit_dict] + anchors = [self._q_anchors[ii].get_index() for ii in self._qubits_dict] return max(anchors) if anchors else 0 def _get_text_width(self, text, fontsize, param=False): @@ -621,7 +622,7 @@ def _draw_regs_wires(self, num_folds, xmax, n_lines, max_anc): for fold_num in range(num_folds + 1): # quantum registers - for qubit in self._qubit_dict.values(): + for qubit in self._qubits_dict.values(): qubit_label = qubit["bit_label"] y = qubit["y"] - fold_num * (n_lines + 1) self._ax.text( @@ -640,7 +641,7 @@ def _draw_regs_wires(self, num_folds, xmax, n_lines, max_anc): # classical registers this_clbit_dict = {} - for clbit in self._clbit_dict.values(): + for clbit in self._clbits_dict.values(): clbit_label = clbit["bit_label"] clbit_reg = clbit["register"] y = clbit["y"] - fold_num * (n_lines + 1) @@ -754,7 +755,7 @@ def _draw_ops(self, verbose=False): if op.condition: cond_xy = [ self._c_anchors[ii].plot_coord(this_anc, layer_width, self._x_offset) - for ii in self._clbit_dict + for ii in self._clbits_dict ] self._condition(node, cond_xy) @@ -836,37 +837,19 @@ def _get_colors(self, node): def _condition(self, node, cond_xy): """Add a conditional to a gate""" - cond_is_bit = bool(isinstance(node.op.condition[0], Clbit)) - mask = 0 - qubit_b = min(self._data[node]["q_xy"], key=lambda xy: xy[1]) - if cond_is_bit: - for index, cbit in enumerate(self._clbit): - if cbit == node.op.condition[0]: - mask = 1 << index - break - else: - for index, cbit in enumerate(self._clbit): - if self._bit_locations[cbit]["register"] == node.op.condition[0]: - mask |= 1 << index - val = node.op.condition[1] - - # cbit list to consider - fmt_c = f"{{:0{len(cond_xy)}b}}" - cmask = list(fmt_c.format(mask))[::-1] - - # value - fmt_v = f"{{:0{cmask.count('1')}b}}" - vlist = list(fmt_v.format(val)) + label, clbit_mask, val_list = get_condition_label( + node.op.condition, self._clbits, self._bit_locations, self._cregbundle + ) if not self._reverse_bits: - vlist = vlist[::-1] + val_list = val_list[::-1] - # plot conditionals + # plot the conditionals v_ind = 0 xy_plot = [] - for xy, m in zip(cond_xy, cmask): + for xy, m in zip(cond_xy, clbit_mask): if m == "1": if xy not in xy_plot: - if vlist[v_ind] == "1" or self._cregbundle: + if node.op.condition[1] != 0 and (val_list[v_ind] == "1" or self._cregbundle): fc = self._style["lc"] else: fc = self._style["bg"] @@ -881,14 +864,12 @@ def _condition(self, node, cond_xy): self._ax.add_patch(box) xy_plot.append(xy) v_ind += 1 + + qubit_b = min(self._data[node]["q_xy"], key=lambda xy: xy[1]) clbit_b = min(xy_plot, key=lambda xy: xy[1]) + + # display the label at the bottom of the lowest conditional and draw the double line xpos, ypos = clbit_b - if cond_is_bit and self._cregbundle: - cond_reg = self._bit_locations[node.op.condition[0]]["register"] - ctrl_bit = self._bit_locations[node.op.condition[0]]["index"] - label = f"{cond_reg.name}_{ctrl_bit}={hex(val)}" - else: - label = hex(val) if isinstance(node.op, Measure): xpos += 0.3 self._ax.text( @@ -908,7 +889,9 @@ def _measure(self, node): """Draw the measure symbol and the line to the clbit""" qx, qy = self._data[node]["q_xy"][0] cx, cy = self._data[node]["c_xy"][0] - cid = self._clbit_dict[self._data[node]["c_indxs"][0]]["index"] + clbit_idx = self._clbits_dict[self._data[node]["c_indxs"][0]] + cid = clbit_idx["index"] + creg = clbit_idx["register"] # draw gate box self._gate(node) @@ -951,7 +934,7 @@ def _measure(self, node): ) self._ax.add_artist(arrowhead) # target - if self._cregbundle: + if self._cregbundle and creg is not None: self._ax.text( cx + 0.25, cy + 0.1, diff --git a/qiskit/visualization/text.py b/qiskit/visualization/text.py index c082924ca099..6eecdf9730bf 100644 --- a/qiskit/visualization/text.py +++ b/qiskit/visualization/text.py @@ -24,7 +24,12 @@ from qiskit.circuit import Measure from qiskit.circuit.library.standard_gates import IGate, RZZGate, SwapGate, SXGate, SXdgGate from qiskit.circuit.tools.pi_check import pi_check -from qiskit.visualization.utils import get_gate_ctrl_text, get_param_str, get_bit_label +from qiskit.visualization.utils import ( + get_gate_ctrl_text, + get_param_str, + get_bit_label, + get_condition_label, +) from .exceptions import VisualizationError @@ -1001,8 +1006,7 @@ def _node_to_gate(self, node, layer): if op.condition is not None: # conditional - op_cond = op.condition - layer.set_cl_multibox(op_cond[0], op_cond[1], top_connect="╨") + layer.set_cl_multibox(op.condition, top_connect="╨") conditional = True # add in a gate that operates over multiple qubits @@ -1016,7 +1020,7 @@ def add_connected_gate(node, gates, layer, current_cons): if isinstance(op, Measure): gate = MeasureFrom() layer.set_qubit(node.qargs[0], gate) - if self.cregbundle: + if self.cregbundle and self.bit_locations[node.cargs[0]]["register"] is not None: layer.set_clbit( node.cargs[0], MeasureTo(str(self.bit_locations[node.cargs[0]]["index"])), @@ -1147,6 +1151,7 @@ def __init__(self, qubits, clbits, reverse_bits=False, cregbundle=False, cregs=N cregs = [] if cregs is None else cregs self.qubits = qubits + self.clbits_raw = clbits # list of clbits ignoring cregbundle change below self._clbit_locations = { bit: {"register": register, "index": index} @@ -1163,8 +1168,11 @@ def __init__(self, qubits, clbits, reverse_bits=False, cregbundle=False, cregs=N for bit in clbits: if previous_creg and previous_creg == self._clbit_locations[bit]["register"]: continue - previous_creg = self._clbit_locations[bit]["register"] - self.clbits.append(previous_creg) + if self._clbit_locations[bit]["register"] is None: + self.clbits.append(bit) + else: + previous_creg = self._clbit_locations[bit]["register"] + self.clbits.append(previous_creg) else: self.clbits = clbits self.qubit_layer = [None] * len(qubits) @@ -1198,7 +1206,7 @@ def set_clbit(self, clbit, element): clbit (cbit): Element of self.clbits. element (DrawElement): Element to set in the clbit """ - if self.cregbundle: + if self.cregbundle and self._clbit_locations[clbit]["register"] is not None: self.clbit_layer[self.clbits.index(self._clbit_locations[clbit]["register"])] = element else: self.clbit_layer[self.clbits.index(clbit)] = element @@ -1329,53 +1337,58 @@ def _set_multibox( ) return bit_index - def set_cl_multibox(self, creg, val, top_connect="┴"): + def set_cl_multibox(self, condition, top_connect="┴"): """Sets the multi clbit box. Args: - creg (string): The affected classical register. - val (int): The value of the condition. + condition (list[Union(Clbit, ClassicalRegister), int]): The condition top_connect (char): The char to connect the box on the top. """ + label, clbit_mask, val_list = get_condition_label( + condition, self.clbits_raw, self._clbit_locations, self.cregbundle + ) + if not self.reverse_bits: + val_list = val_list[::-1] + if self.cregbundle: - if isinstance(creg, Clbit): - bit_reg = self._clbit_locations[creg]["register"] - bit_index = self._clbit_locations[creg]["index"] - label_bool = "= T" if val is True else "= F" - label = f"{bit_reg.name}_{bit_index} {label_bool}" - self.set_clbit(creg, BoxOnClWire(label=label, top_connect=top_connect)) + if isinstance(condition[0], Clbit): + # if it's a registerless Clbit + if self._clbit_locations[condition[0]]["register"] is None: + self.set_cond_bullets(label, val_list, [condition[0]]) + # if it's a single bit in a register + else: + self.set_clbit(condition[0], BoxOnClWire(label=label, top_connect=top_connect)) + # if it's a whole register else: - label = "%s" % str(hex(val)) - self.set_clbit(creg[0], BoxOnClWire(label=label, top_connect=top_connect)) + self.set_clbit(condition[0][0], BoxOnClWire(label=label, top_connect=top_connect)) else: - if isinstance(creg, Clbit): - clbit = [creg] - cond_bin = "1" if val is True else "0" - self.set_cond_bullets(cond_bin, clbit) - else: - clbit = [ - bit for bit in self.clbits if self._clbit_locations[bit]["register"] == creg - ] - cond_bin = bin(val)[2:].zfill(len(clbit)) - self.set_cond_bullets(cond_bin, clbits=clbit) + clbits = [] + for i, _ in enumerate(clbit_mask): + if clbit_mask[i] == "1": + clbits.append(self.clbits[i]) + self.set_cond_bullets(label, val_list, clbits) - def set_cond_bullets(self, val, clbits): + def set_cond_bullets(self, label, val_list, clbits): """Sets bullets for classical conditioning when cregbundle=False. Args: - val (int): The condition value. + label (str): String to display below the condition + val_list (list(int)): A list of bit values clbits (list[Clbit]): The list of classical bits on which the instruction is conditioned. """ - vlist = list(val) if self.reverse_bits else list(val[::-1]) for i, bit in enumerate(clbits): bot_connect = " " if bit == clbits[-1]: - bot_connect = "%s" % str(hex(int(val, 2))) - if vlist[i] == "1": - self.set_clbit(bit, ClBullet(top_connect="║", bot_connect=bot_connect)) - elif vlist[i] == "0": - self.set_clbit(bit, ClOpenBullet(top_connect="║", bot_connect=bot_connect)) + bot_connect = label + if val_list[i] == "1": + self.clbit_layer[self.clbits.index(bit)] = ClBullet( + top_connect="║", bot_connect=bot_connect + ) + elif val_list[i] == "0": + self.clbit_layer[self.clbits.index(bit)] = ClOpenBullet( + top_connect="║", bot_connect=bot_connect + ) def set_qu_multibox( self, diff --git a/qiskit/visualization/utils.py b/qiskit/visualization/utils.py index 5618298206fc..94436db786f1 100644 --- a/qiskit/visualization/utils.py +++ b/qiskit/visualization/utils.py @@ -220,6 +220,54 @@ def get_bit_label(drawer, register, index, qubit=True, layout=None, cregbundle=T return bit_label +def get_condition_label(condition, clbits, bit_locations, cregbundle): + """Get the label to display as a condition + + Args: + condition (Union[Clbit, ClassicalRegister], int): classical condition + clbits (list(Clbit)): the classical bits in the circuit + bit_locations (dict): the bits in the circuit with register and index + cregbundle (bool): if set True bundle classical registers + + Returns: + str: label to display for the condition + list(str): list of 1's and 0's with 1's indicating a bit that's part of the condition + list(str): list of 1's and 0's indicating values of condition at that position + """ + cond_is_bit = bool(isinstance(condition[0], Clbit)) + mask = 0 + if cond_is_bit: + for index, cbit in enumerate(clbits): + if cbit == condition[0]: + mask = 1 << index + break + else: + for index, cbit in enumerate(clbits): + if bit_locations[cbit]["register"] == condition[0]: + mask |= 1 << index + val = condition[1] + + # cbit list to consider + fmt_c = f"{{:0{len(clbits)}b}}" + clbit_mask = list(fmt_c.format(mask))[::-1] + + # value + fmt_v = f"{{:0{clbit_mask.count('1')}b}}" + vlist = list(fmt_v.format(val)) + + label = "" + if cond_is_bit and cregbundle: + cond_reg = bit_locations[condition[0]]["register"] + ctrl_bit = bit_locations[condition[0]]["index"] + truth = "0x1" if val else "0x0" + if cond_reg is not None: + label = f"{cond_reg.name}_{ctrl_bit}={truth}" + elif not cond_is_bit: + label = hex(val) + + return label, clbit_mask, vlist + + def generate_latex_label(label): """Convert a label to a valid latex string.""" if not HAS_PYLATEX: @@ -297,7 +345,7 @@ def _get_layered_instructions(circuit, reverse_bits=False, justify=None, idle_wi # Create a mapping of each register to the max layer number for all measure ops # with that register as the target. Then when an op with condition is seen, # it will be placed to the right of the measure op if the register matches. - measure_map = OrderedDict([(c, -1) for c in circuit.cregs]) + measure_map = OrderedDict([(c, -1) for c in clbits]) if justify == "none": for node in dag.topological_op_nodes(): @@ -374,6 +422,7 @@ def __init__(self, dag, justification, measure_map, reverse_bits): super().__init__() self.dag = dag self.qubits = dag.qubits + self.clbits = dag.clbits self.justification = justification self.measure_map = measure_map self.cregs = [self.dag.cregs[reg] for reg in self.dag.cregs] @@ -415,7 +464,7 @@ def slide_from_left(self, node, index): """Insert node into first layer where there is no conflict going l > r""" measure_layer = None if isinstance(node.op, Measure): - measure_reg = next(reg for reg in self.measure_map if node.cargs[0] in reg) + measure_bit = next(bit for bit in self.measure_map if node.cargs[0] == bit) if not self: inserted = True @@ -427,19 +476,22 @@ def slide_from_left(self, node, index): index_stop = -1 if node.op.condition: if isinstance(node.op.condition[0], Clbit): - cond_reg = [creg for creg in self.cregs if node.op.condition[0] in creg] - index_stop = self.measure_map[cond_reg[0]] + cond_bit = [clbit for clbit in self.clbits if node.op.condition[0] == clbit] + index_stop = self.measure_map[cond_bit[0]] else: - index_stop = self.measure_map[node.op.condition[0]] - elif node.cargs: + for bit in node.op.condition[0]: + max_index = -1 + if bit in self.measure_map: + if self.measure_map[bit] > max_index: + index_stop = max_index = self.measure_map[bit] + if node.cargs: for carg in node.cargs: try: - carg_reg = next(reg for reg in self.measure_map if carg in reg) - if self.measure_map[carg_reg] > index_stop: - index_stop = self.measure_map[carg_reg] + carg_bit = next(bit for bit in self.measure_map if carg == bit) + if self.measure_map[carg_bit] > index_stop: + index_stop = self.measure_map[carg_bit] except StopIteration: pass - while curr_index > index_stop: if self.is_found_in(node, self[curr_index]): break @@ -468,8 +520,8 @@ def slide_from_left(self, node, index): if isinstance(node.op, Measure): if not measure_layer: measure_layer = len(self) - 1 - if measure_layer > self.measure_map[measure_reg]: - self.measure_map[measure_reg] = measure_layer + if measure_layer > self.measure_map[measure_bit]: + self.measure_map[measure_bit] = measure_layer def slide_from_right(self, node, index): """Insert node into rightmost layer as long there is no conflict.""" diff --git a/releasenotes/notes/fix-bit-failures-circuit-drawers-cc502c9cb7f90e2b.yaml b/releasenotes/notes/fix-bit-failures-circuit-drawers-cc502c9cb7f90e2b.yaml new file mode 100644 index 000000000000..0efe0361afaf --- /dev/null +++ b/releasenotes/notes/fix-bit-failures-circuit-drawers-cc502c9cb7f90e2b.yaml @@ -0,0 +1,25 @@ +--- +fixes: + - | + Fixed an issue with the :func:`~qiskit.visualization.circuit_drawer` + function and :meth:`~qiskit.circuit.QuantumCircuit.draw` method of + :class:`~qiskit.circuit.QuantumCircuit`. When displaying a ``measure`` + instruction targeted on a classical bit instead of a register, using + the ``latex`` drawer option, the drawer would fail. +fixes: + - | + Fixed an issue with the :func:`~qiskit.visualization.circuit_drawer` + function and :meth:`~qiskit.circuit.QuantumCircuit.draw` method of + :class:`~qiskit.circuit.QuantumCircuit`. With any of the 3 drawer + options, ``mpl``, ``latex``, or ``text``, if a gate with a classical + condition was encountered that was conditioned on a classical bit + without a register, the drawer would fail. +fixes: + - | + Fixed an issue with the :func:`~qiskit.visualization.circuit_drawer` + function and :meth:`~qiskit.circuit.QuantumCircuit.draw` method of + :class:`~qiskit.circuit.QuantumCircuit`. With any of the 3 drawer + options, ``mpl``, ``latex``, or ``text``, if a gate with a classical + condition was conditioned on the same classical bit as a ``measure`` + and the bit that the measure targeted did not have a register, the + drawer would fail. diff --git a/test/ipynb/mpl/circuit/references/bit_conditional_bundle.png b/test/ipynb/mpl/circuit/references/bit_conditional_bundle.png index 79295f95e18b..3397ad9fd20c 100644 Binary files a/test/ipynb/mpl/circuit/references/bit_conditional_bundle.png and b/test/ipynb/mpl/circuit/references/bit_conditional_bundle.png differ diff --git a/test/ipynb/mpl/circuit/references/bit_conditional_no_bundle.png b/test/ipynb/mpl/circuit/references/bit_conditional_no_bundle.png index b366e2720956..c7853fe8a700 100644 Binary files a/test/ipynb/mpl/circuit/references/bit_conditional_no_bundle.png and b/test/ipynb/mpl/circuit/references/bit_conditional_no_bundle.png differ diff --git a/test/ipynb/mpl/circuit/references/measure_cond_bits_false.png b/test/ipynb/mpl/circuit/references/measure_cond_bits_false.png new file mode 100644 index 000000000000..35a6b8cfefe6 Binary files /dev/null and b/test/ipynb/mpl/circuit/references/measure_cond_bits_false.png differ diff --git a/test/ipynb/mpl/circuit/references/measure_cond_bits_right.png b/test/ipynb/mpl/circuit/references/measure_cond_bits_right.png new file mode 100644 index 000000000000..a989598c53cd Binary files /dev/null and b/test/ipynb/mpl/circuit/references/measure_cond_bits_right.png differ diff --git a/test/ipynb/mpl/circuit/references/measure_cond_bits_true.png b/test/ipynb/mpl/circuit/references/measure_cond_bits_true.png new file mode 100644 index 000000000000..f13566ba1c7b Binary files /dev/null and b/test/ipynb/mpl/circuit/references/measure_cond_bits_true.png differ diff --git a/test/ipynb/mpl/circuit/test_circuit_matplotlib_drawer.py b/test/ipynb/mpl/circuit/test_circuit_matplotlib_drawer.py index 900f69692cf5..9613ffd42fae 100644 --- a/test/ipynb/mpl/circuit/test_circuit_matplotlib_drawer.py +++ b/test/ipynb/mpl/circuit/test_circuit_matplotlib_drawer.py @@ -28,7 +28,7 @@ from qiskit.circuit.library import XGate, MCXGate, HGate, RZZGate, SwapGate, DCXGate, ZGate, SGate from qiskit.circuit.library import MCXVChain from qiskit.extensions import HamiltonianGate -from qiskit.circuit import Parameter +from qiskit.circuit import Parameter, Qubit, Clbit from qiskit.circuit.library import IQP from qiskit.quantum_info.random import random_unitary from qiskit.tools.visualization import HAS_MATPLOTLIB @@ -486,7 +486,7 @@ def test_scale(self): self.circuit_drawer(circuit, filename="scale_double.png", scale=2) def test_pi_param_expr(self): - """Text pi in circuit with parameter expression.""" + """Test pi in circuit with parameter expression.""" x, y = Parameter("x"), Parameter("y") circuit = QuantumCircuit(1) circuit.rx((pi - x) * (pi - y), 0) @@ -787,8 +787,6 @@ def test_figwidth(self): def test_registerless_one_bit(self): """Test circuit with one-bit registers and registerless bits.""" - from qiskit.circuit import Qubit, Clbit - qrx = QuantumRegister(2, "qrx") qry = QuantumRegister(1, "qry") crx = ClassicalRegister(2, "crx") @@ -809,6 +807,28 @@ def test_measures_with_conditions(self): self.circuit_drawer(circuit, cregbundle=False, filename="measure_cond_false.png") self.circuit_drawer(circuit, cregbundle=True, filename="measure_cond_true.png") + def test_conditions_measures_with_bits(self): + """Test that gates with conditions and measures work with bits""" + bits = [Qubit(), Qubit(), Clbit(), Clbit()] + cr = ClassicalRegister(2, "cr") + crx = ClassicalRegister(3, "cs") + circuit = QuantumCircuit(bits, cr, [Clbit()], crx) + circuit.x(0).c_if(crx[1], 0) + circuit.measure(0, bits[3]) + self.circuit_drawer(circuit, cregbundle=False, filename="measure_cond_bits_false.png") + self.circuit_drawer(circuit, cregbundle=True, filename="measure_cond_bits_true.png") + + def test_conditional_gates_right_of_measures_with_bits(self): + """Test that gates with conditions draw to right of measures when same bit""" + qr = QuantumRegister(3, "qr") + cr = ClassicalRegister(2, "cr") + circuit = QuantumCircuit(qr, cr) + circuit.h(qr[0]) + circuit.measure(qr[0], cr[1]) + circuit.h(qr[1]).c_if(cr[1], 0) + circuit.h(qr[2]).c_if(cr[0], 0) + self.circuit_drawer(circuit, cregbundle=False, filename="measure_cond_bits_right.png") + if __name__ == "__main__": unittest.main(verbosity=1) diff --git a/test/python/visualization/references/test_latex_cif_single_bit.tex b/test/python/visualization/references/test_latex_cif_single_bit.tex index 7b50bfdb5180..9977924a3194 100644 --- a/test/python/visualization/references/test_latex_cif_single_bit.tex +++ b/test/python/visualization/references/test_latex_cif_single_bit.tex @@ -8,7 +8,7 @@ \Qcircuit @C=1.0em @R=0.2em @!R { \\ \nghost{{qr}_{0} : } & \lstick{{qr}_{0} : } & \gate{\mathrm{H}} & \qw & \qw & \qw\\ \nghost{{qr}_{1} : } & \lstick{{qr}_{1} : } & \qw & \gate{\mathrm{X}} & \qw & \qw\\ - \nghost{{cr}_{0} : } & \lstick{{cr}_{0} : } & \cw & \control \cw^(0.0){^{\mathtt{0x1}}} \cwx[-1] & \cw & \cw\\ - \nghost{{cr}_{1} : } & \lstick{{cr}_{1} : } & \controlo \cw^(0.0){^{\mathtt{0x0}}} \cwx[-3] & \cw & \cw & \cw\\ + \nghost{{cr}_{0} : } & \lstick{{cr}_{0} : } & \cw & \control \cw^(0.0){^{\mathtt{}}} \cwx[-1] & \cw & \cw\\ + \nghost{{cr}_{1} : } & \lstick{{cr}_{1} : } & \controlo \cw^(0.0){^{\mathtt{}}} \cwx[-3] & \cw & \cw & \cw\\ \\ }} \end{document} \ No newline at end of file diff --git a/test/python/visualization/references/test_latex_cif_single_bit_bundle.tex b/test/python/visualization/references/test_latex_cif_single_bit_bundle.tex index 99d3d5b2e0af..ead346d7e6b2 100644 --- a/test/python/visualization/references/test_latex_cif_single_bit_bundle.tex +++ b/test/python/visualization/references/test_latex_cif_single_bit_bundle.tex @@ -8,6 +8,6 @@ \Qcircuit @C=1.0em @R=0.2em @!R { \\ \nghost{{qr}_{0} : } & \lstick{{qr}_{0} : } & \qw & \gate{\mathrm{H}} & \qw & \qw & \qw\\ \nghost{{qr}_{1} : } & \lstick{{qr}_{1} : } & \qw & \qw & \gate{\mathrm{X}} & \qw & \qw\\ - \nghost{\mathrm{cr : }} & \lstick{\mathrm{cr : }} & \lstick{/_{_{2}}} \cw & \control \cw^(0.0){^{\mathtt{cr_1=F}}} \cwx[-2] & \control \cw^(0.0){^{\mathtt{cr_0=T}}} \cwx[-1] & \cw & \cw\\ + \nghost{\mathrm{cr : }} & \lstick{\mathrm{cr : }} & \lstick{/_{_{2}}} \cw & \controlo \cw^(0.0){^{\mathtt{cr_1=0x0}}} \cwx[-2] & \control \cw^(0.0){^{\mathtt{cr_0=0x1}}} \cwx[-1] & \cw & \cw\\ \\ }} \end{document} \ No newline at end of file diff --git a/test/python/visualization/references/test_latex_meas_cond_bits_false.tex b/test/python/visualization/references/test_latex_meas_cond_bits_false.tex new file mode 100644 index 000000000000..1db61a8ddaeb --- /dev/null +++ b/test/python/visualization/references/test_latex_meas_cond_bits_false.tex @@ -0,0 +1,20 @@ +\documentclass[border=2px]{standalone} + +\usepackage[braket, qm]{qcircuit} +\usepackage{graphicx} + +\begin{document} +\scalebox{1.0}{ +\Qcircuit @C=1.0em @R=0.2em @!R { \\ + \nghost{{0} : } & \lstick{{0} : } & \gate{\mathrm{X}} & \meter & \qw & \qw\\ + \nghost{{1} : } & \lstick{{1} : } & \qw & \qw & \qw & \qw\\ + \nghost{{0} : } & \lstick{{0} : } & \cw & \cw & \cw & \cw\\ + \nghost{{1} : } & \lstick{{1} : } & \cw & \cw \ar @{<=} [-3,0] & \cw & \cw\\ + \nghost{{cr}_{0} : } & \lstick{{cr}_{0} : } & \cw & \cw & \cw & \cw\\ + \nghost{{cr}_{1} : } & \lstick{{cr}_{1} : } & \cw & \cw & \cw & \cw\\ + \nghost{{4} : } & \lstick{{4} : } & \cw & \cw & \cw & \cw\\ + \nghost{{cs}_{0} : } & \lstick{{cs}_{0} : } & \cw & \cw & \cw & \cw\\ + \nghost{{cs}_{1} : } & \lstick{{cs}_{1} : } & \controlo \cw^(0.0){^{\mathtt{}}} \cwx[-8] & \cw & \cw & \cw\\ + \nghost{{cs}_{2} : } & \lstick{{cs}_{2} : } & \cw & \cw & \cw & \cw\\ +\\ }} +\end{document} \ No newline at end of file diff --git a/test/python/visualization/references/test_latex_meas_cond_bits_true.tex b/test/python/visualization/references/test_latex_meas_cond_bits_true.tex new file mode 100644 index 000000000000..0ef5c66b0268 --- /dev/null +++ b/test/python/visualization/references/test_latex_meas_cond_bits_true.tex @@ -0,0 +1,17 @@ +\documentclass[border=2px]{standalone} + +\usepackage[braket, qm]{qcircuit} +\usepackage{graphicx} + +\begin{document} +\scalebox{1.0}{ +\Qcircuit @C=1.0em @R=0.2em @!R { \\ + \nghost{{0} : } & \lstick{{0} : } & \qw & \gate{\mathrm{X}} & \meter & \qw & \qw\\ + \nghost{{1} : } & \lstick{{1} : } & \qw & \qw & \qw & \qw & \qw\\ + \nghost{\mathrm{{0} : }} & \lstick{\mathrm{{0} : }} & \cw & \cw & \cw & \cw & \cw\\ + \nghost{\mathrm{{1} : }} & \lstick{\mathrm{{1} : }} & \cw & \cw & \dstick{_{_{\hspace{0.0em}}}} \cw \ar @{<=} [-3,0] & \cw & \cw\\ + \nghost{\mathrm{cr : }} & \lstick{\mathrm{cr : }} & \lstick{/_{_{2}}} \cw & \cw & \cw & \cw & \cw\\ + \nghost{\mathrm{{4} : }} & \lstick{\mathrm{{4} : }} & \cw & \cw & \cw & \cw & \cw\\ + \nghost{\mathrm{cs : }} & \lstick{\mathrm{cs : }} & \lstick{/_{_{3}}} \cw & \controlo \cw^(0.0){^{\mathtt{cs_1=0x0}}} \cwx[-6] & \cw & \cw & \cw\\ +\\ }} +\end{document} \ No newline at end of file diff --git a/test/python/visualization/test_circuit_latex.py b/test/python/visualization/test_circuit_latex.py index 3b302c42fcf0..c3e987fa44ac 100644 --- a/test/python/visualization/test_circuit_latex.py +++ b/test/python/visualization/test_circuit_latex.py @@ -25,6 +25,7 @@ from qiskit.circuit.library import XGate, MCXGate, RZZGate, SwapGate, DCXGate from qiskit.extensions import HamiltonianGate from qiskit.circuit import Parameter +from qiskit.circuit import Qubit, Clbit from qiskit.circuit.library import IQP from qiskit.quantum_info.random import random_unitary from .visualization import QiskitVisualizationTestCase @@ -576,8 +577,6 @@ def test_cif_single_bit_cregbundle(self): def test_registerless_one_bit(self): """Text circuit with one-bit registers and registerless bits.""" - from qiskit.circuit import Qubit, Clbit - filename = self._get_resource_path("test_latex_registerless_one_bit.tex") qrx = QuantumRegister(2, "qrx") qry = QuantumRegister(1, "qry") @@ -605,6 +604,21 @@ def test_measures_with_conditions(self): self.assertEqualToReference(filename1) self.assertEqualToReference(filename2) + def test_measures_with_conditions_with_bits(self): + """Condition and measure on single bits cregbundle true""" + filename1 = self._get_resource_path("test_latex_meas_cond_bits_false.tex") + filename2 = self._get_resource_path("test_latex_meas_cond_bits_true.tex") + bits = [Qubit(), Qubit(), Clbit(), Clbit()] + cr = ClassicalRegister(2, "cr") + crx = ClassicalRegister(3, "cs") + circuit = QuantumCircuit(bits, cr, [Clbit()], crx) + circuit.x(0).c_if(crx[1], 0) + circuit.measure(0, bits[3]) + circuit_drawer(circuit, cregbundle=False, filename=filename1, output="latex_source") + circuit_drawer(circuit, cregbundle=True, filename=filename2, output="latex_source") + self.assertEqualToReference(filename1) + self.assertEqualToReference(filename2) + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/test/python/visualization/test_circuit_text_drawer.py b/test/python/visualization/test_circuit_text_drawer.py index 836a46f4e050..090c5716beb7 100644 --- a/test/python/visualization/test_circuit_text_drawer.py +++ b/test/python/visualization/test_circuit_text_drawer.py @@ -20,7 +20,7 @@ import numpy from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, transpile -from qiskit.circuit import Gate, Parameter +from qiskit.circuit import Gate, Parameter, Qubit, Clbit from qiskit.quantum_info.operators import SuperOp from qiskit.quantum_info.random import random_unitary from qiskit.test import QiskitTestCase @@ -2904,7 +2904,6 @@ def test_text_conditional_measure(self): " 0x1 ", ] ) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) def test_text_bit_conditional(self): @@ -2924,9 +2923,9 @@ def test_text_bit_conditional(self): "qr_1: |0>──╫──┤ H ├", " ║ └─╥─┘", " cr_0: 0 ══■════╬══", - " 0x1 ║ ", + " ║ ", " cr_1: 0 ═══════o══", - " 0x0 ", + " ", ] ) @@ -2949,7 +2948,7 @@ def test_text_bit_conditional_cregbundle(self): "qr_1: |0>─────╫─────────┤ H ├────", " ║ └─╥─┘ ", " ┌────╨─────┐┌────╨─────┐", - " cr: 0 2/╡ cr_0 = T ╞╡ cr_1 = F ╞", + " cr: 0 2/╡ cr_0=0x1 ╞╡ cr_1=0x0 ╞", " └──────────┘└──────────┘", ] ) @@ -2959,6 +2958,78 @@ def test_text_bit_conditional_cregbundle(self): expected, ) + def test_text_condition_measure_bits_true(self): + """Condition and measure on single bits cregbundle true""" + + bits = [Qubit(), Qubit(), Clbit(), Clbit()] + cr = ClassicalRegister(2, "cr") + crx = ClassicalRegister(3, "cs") + circuit = QuantumCircuit(bits, cr, [Clbit()], crx) + circuit.x(0).c_if(crx[1], 0) + circuit.measure(0, bits[3]) + + expected = "\n".join( + [ + " ┌───┐ ┌─┐", + " 0: ───┤ X ├────┤M├", + " └─╥─┘ └╥┘", + " 1: ─────╫───────╫─", + " ║ ║ ", + " 0: ═════╬═══════╬═", + " ║ ║ ", + " 1: ═════╬═══════╩═", + " ║ ", + "cr: 2/═════╬═════════", + " ║ ", + " 4: ═════╬═════════", + " ┌────╨─────┐ ", + "cs: 3/╡ cs_1=0x0 ╞═══", + " └──────────┘ ", + ] + ) + self.assertEqual( + str(_text_circuit_drawer(circuit, cregbundle=True, initial_state=False)), expected + ) + + def test_text_condition_measure_bits_false(self): + """Condition and measure on single bits cregbundle false""" + + bits = [Qubit(), Qubit(), Clbit(), Clbit()] + cr = ClassicalRegister(2, "cr") + crx = ClassicalRegister(3, "cs") + circuit = QuantumCircuit(bits, cr, [Clbit()], crx) + circuit.x(0).c_if(crx[1], 0) + circuit.measure(0, bits[3]) + + expected = "\n".join( + [ + " ┌───┐┌─┐", + " 0: ┤ X ├┤M├", + " └─╥─┘└╥┘", + " 1: ──╫───╫─", + " ║ ║ ", + " 0: ══╬═══╬═", + " ║ ║ ", + " 1: ══╬═══╩═", + " ║ ", + "cr_0: ══╬═════", + " ║ ", + "cr_1: ══╬═════", + " ║ ", + " 4: ══╬═════", + " ║ ", + "cs_0: ══╬═════", + " ║ ", + "cs_1: ══o═════", + " ", + "cs_2: ════════", + " ", + ] + ) + self.assertEqual( + str(_text_circuit_drawer(circuit, cregbundle=False, initial_state=False)), expected + ) + def test_text_conditional_reverse_bits_1(self): """Classical condition on 2q2c circuit with cregbundle=False and reverse bits""" qr = QuantumRegister(2, "qr") @@ -4688,8 +4759,6 @@ def test_empty_noregs(self): def test_registerless_one_bit(self): """Text circuit with one-bit registers and registerless bits.""" - from qiskit.circuit import Qubit, Clbit - # fmt: off expected = "\n".join([" ", "qrx_0: ",