Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add wire_order option to all circuit drawers to allow defining the order of the wires #8173

Merged
merged 19 commits into from
Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1796,6 +1796,7 @@ def draw(
ax: Optional[typing.Any] = None,
initial_state: bool = False,
cregbundle: bool = True,
wire_order: list = None,
):
"""Draw the quantum circuit. Use the output parameter to choose the drawing format:

Expand Down Expand Up @@ -1873,10 +1874,13 @@ def draw(
specified, a new matplotlib Figure will be created and used.
Additionally, if specified there will be no returned Figure since
it is redundant.
initial_state (bool): optional. Adds ``|0>`` in the beginning of the wire.
initial_state (bool): Optional. Adds ``|0>`` in the beginning of the wire.
Default is False.
cregbundle (bool): optional. If set True, bundle classical registers.
cregbundle (bool): Optional. If set True, bundle classical registers.
Default is True.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).

Returns:
:class:`TextDrawing` or :class:`matplotlib.figure` or :class:`PIL.Image` or
Expand Down Expand Up @@ -1928,6 +1932,7 @@ def draw(
ax=ax,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)

def size(
Expand Down
72 changes: 67 additions & 5 deletions qiskit/visualization/circuit_visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import os
import subprocess
import tempfile
from warnings import warn

from qiskit import user_config
from qiskit.utils import optionals as _optionals
Expand Down Expand Up @@ -59,6 +60,7 @@ def circuit_drawer(
ax=None,
initial_state=False,
cregbundle=True,
wire_order=None,
):
"""Draw the quantum circuit. Use the output parameter to choose the drawing format:

Expand Down Expand Up @@ -137,10 +139,13 @@ def circuit_drawer(
specified, a new matplotlib Figure will be created and used.
Additionally, if specified there will be no returned Figure since
it is redundant.
initial_state (bool): optional. Adds ``|0>`` in the beginning of the wire.
initial_state (bool): Optional. Adds ``|0>`` in the beginning of the wire.
Default is False.
cregbundle (bool): optional. If set True, bundle classical registers.
cregbundle (bool): Optional. If set True, bundle classical registers.
Default is True.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).

Returns:
:class:`TextDrawing` or :class:`matplotlib.figure` or :class:`PIL.Image` or
Expand Down Expand Up @@ -185,6 +190,30 @@ def circuit_drawer(
if output is None:
output = default_output

if wire_order is not None and reverse_bits:
raise VisualizationError(
"The wire_order option cannot be set when the reverse_bits option is True."
)
if wire_order is not None and len(wire_order) != circuit.num_qubits + circuit.num_clbits:
raise VisualizationError(
"The wire_order list must be the same "
"length as the sum of the number of qubits and clbits in the circuit."
)
if wire_order is not None and set(wire_order) != set(
range(circuit.num_qubits + circuit.num_clbits)
):
raise VisualizationError(
"There must be one and only one entry in the "
"wire_order list for the index of each qubit and each clbit in the circuit."
)

if cregbundle and (reverse_bits or wire_order is not None):
cregbundle = False
warn(
"Cregbundle set to False since either reverse_bits or wire_order has been set.",
RuntimeWarning,
2,
)
if output == "text":
return _text_circuit_drawer(
circuit,
Expand All @@ -198,6 +227,7 @@ def circuit_drawer(
fold=fold,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)
elif output == "latex":
image = _latex_circuit_drawer(
Expand All @@ -212,6 +242,7 @@ def circuit_drawer(
with_layout=with_layout,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)
elif output == "latex_source":
return _generate_latex_source(
Expand All @@ -226,6 +257,7 @@ def circuit_drawer(
with_layout=with_layout,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)
elif output == "mpl":
image = _matplotlib_circuit_drawer(
Expand All @@ -242,6 +274,7 @@ def circuit_drawer(
ax=ax,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)
else:
raise VisualizationError(
Expand Down Expand Up @@ -271,6 +304,7 @@ def _text_circuit_drawer(
initial_state=True,
cregbundle=False,
encoding=None,
wire_order=None,
):
"""Draws a circuit using ascii art.

Expand All @@ -297,6 +331,9 @@ def _text_circuit_drawer(
Default: ``True``.
encoding (str): Optional. Sets the encoding preference of the output.
Default: ``sys.stdout.encoding``.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).

Returns:
TextDrawing: An instance that, when printed, draws the circuit in ascii art.
Expand All @@ -305,7 +342,11 @@ def _text_circuit_drawer(
VisualizationError: When the filename extenstion is not .txt.
"""
qubits, clbits, nodes = utils._get_layered_instructions(
circuit, reverse_bits=reverse_bits, justify=justify, idle_wires=idle_wires
circuit,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
wire_order=wire_order,
)
text_drawing = _text.TextDrawing(
qubits,
Expand Down Expand Up @@ -351,6 +392,7 @@ def _latex_circuit_drawer(
with_layout=True,
initial_state=False,
cregbundle=False,
wire_order=None,
):
"""Draw a quantum circuit based on latex (Qcircuit package)

Expand All @@ -374,6 +416,9 @@ def _latex_circuit_drawer(
Default: `False`.
cregbundle (bool): Optional. If set True, bundle classical registers.
Default: ``False``.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).

Returns:
PIL.Image: an in-memory representation of the circuit diagram
Expand All @@ -400,6 +445,7 @@ def _latex_circuit_drawer(
with_layout=with_layout,
initial_state=initial_state,
cregbundle=cregbundle,
wire_order=wire_order,
)

try:
Expand Down Expand Up @@ -465,6 +511,7 @@ def _generate_latex_source(
with_layout=True,
initial_state=False,
cregbundle=False,
wire_order=None,
):
"""Convert QuantumCircuit to LaTeX string.

Expand All @@ -486,12 +533,19 @@ def _generate_latex_source(
Default: `False`.
cregbundle (bool): Optional. If set True, bundle classical registers.
Default: ``False``.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).

Returns:
str: Latex string appropriate for writing to file.
"""
qubits, clbits, nodes = utils._get_layered_instructions(
circuit, reverse_bits=reverse_bits, justify=justify, idle_wires=idle_wires
circuit,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
wire_order=wire_order,
)
qcimg = _latex.QCircuitImage(
qubits,
Expand Down Expand Up @@ -537,6 +591,7 @@ def _matplotlib_circuit_drawer(
ax=None,
initial_state=False,
cregbundle=True,
wire_order=None,
):

"""Draw a quantum circuit based on matplotlib.
Expand Down Expand Up @@ -566,14 +621,21 @@ def _matplotlib_circuit_drawer(
Default: `False`.
cregbundle (bool): Optional. If set True bundle classical registers.
Default: ``True``.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).

Returns:
matplotlib.figure: a matplotlib figure object for the circuit diagram
if the ``ax`` kwarg is not set.
"""

qubits, clbits, nodes = utils._get_layered_instructions(
circuit, reverse_bits=reverse_bits, justify=justify, idle_wires=idle_wires
circuit,
reverse_bits=reverse_bits,
justify=justify,
idle_wires=idle_wires,
wire_order=wire_order,
)
if fold is None:
fold = 25
Expand Down
64 changes: 37 additions & 27 deletions qiskit/visualization/latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,8 @@ def __init__(
self._has_box = False
self._has_target = False

self._reverse_bits = reverse_bits
self._plot_barriers = plot_barriers
self._reverse_bits = reverse_bits
if with_layout:
self._layout = self._circuit._layout
else:
Expand Down Expand Up @@ -259,9 +259,7 @@ def _initialize_latex_array(self):
register = wire
index = self._wire_map[wire]
else:
register, bit_index, reg_index = get_bit_reg_index(
self._circuit, wire, self._reverse_bits
)
register, bit_index, reg_index = get_bit_reg_index(self._circuit, wire)
index = bit_index if register is None else reg_index

wire_label = get_wire_label(
Expand Down Expand Up @@ -623,46 +621,58 @@ def _add_condition(self, op, wire_list, col):
# or if cregbundle, wire number of the condition register itself
# gap - the number of wires from cwire to the bottom gate qubit

label, val_bits = get_condition_label_val(
op.condition, self._circuit, self._cregbundle, self._reverse_bits
)
label, val_bits = get_condition_label_val(op.condition, self._circuit, self._cregbundle)
cond_is_bit = isinstance(op.condition[0], Clbit)
cond_reg = op.condition[0]
if cond_is_bit:
register = get_bit_register(self._circuit, op.condition[0])
if register is not None:
cond_reg = register

if self._cregbundle:
cwire = self._wire_map[cond_reg]
else:
cwire = self._wire_map[op.condition[0] if cond_is_bit else cond_reg[0]]

gap = cwire - max(wire_list)
meas_offset = -0.3 if isinstance(op, Measure) else 0.0

# Print the condition value at the bottom and put bullet on creg line
# If condition is a bit or cregbundle true, print the condition value
# at the bottom and put bullet on creg line
if cond_is_bit or self._cregbundle:
cwire = (
self._wire_map[cond_reg] if self._cregbundle else self._wire_map[op.condition[0]]
)
gap = cwire - max(wire_list)
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),
)
# If condition is a register and cregbundle is false
else:
cond_len = op.condition[0].size - 1
# If reverse, start at highest reg bit and go down to 0
if self._reverse_bits:
cwire -= cond_len
gap -= cond_len
# Iterate through the reg bits down to the lowest one
for i in range(cond_len):
control = "\\control" if val_bits[i] == "1" else "\\controlo"
self._latex[cwire + i][col] = f"{control} \\cw \\cwx[-" + str(gap) + "]"
gap = 1
# First sort the val_bits in the order of the register bits in the circuit
cond_wires = []
cond_bits = []
for wire in self._wire_map:
reg, _, reg_index = get_bit_reg_index(self._circuit, wire)
if reg == cond_reg:
cond_bits.append(reg_index)
cond_wires.append(self._wire_map[wire])

gap = cond_wires[0] - max(wire_list)
prev_wire = cond_wires[0]
val_bits_sorted = [bit for _, bit in sorted(zip(cond_bits, val_bits))]

# Iterate over the wire values for the bits in the register
for i, wire in enumerate(cond_wires[:-1]):
if i > 0:
gap = wire - prev_wire
control = "\\control" if val_bits_sorted[i] == "1" else "\\controlo"
self._latex[wire][col] = f"{control} \\cw \\cwx[-" + str(gap) + "]"
prev_wire = wire

# Add (hex condition value) below the last cwire
control = "\\control" if val_bits[cond_len] == "1" else "\\controlo"
self._latex[cwire + cond_len][col] = (
if len(cond_wires) == 1: # Only one bit in register
gap = cond_wires[0] - max(wire_list)
else:
gap = cond_wires[-1] - prev_wire
control = "\\control" if val_bits_sorted[len(cond_wires) - 1] == "1" else "\\controlo"
self._latex[cond_wires[-1]][col] = (
f"{control}" + " \\cw^(%s){^{\\mathtt{%s}}} \\cwx[-%s]"
) % (
meas_offset,
Expand Down
13 changes: 5 additions & 8 deletions qiskit/visualization/matplotlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@ def __init__(
# subfont. Font change is auto scaled in the self._figure.set_size_inches call in draw()
self._subfont_factor = self._style["sfs"] * def_font_ratio / self._style["fs"]

self._reverse_bits = reverse_bits
self._plot_barriers = plot_barriers
self._reverse_bits = reverse_bits
if with_layout:
self._layout = self._circuit._layout
else:
Expand Down Expand Up @@ -503,9 +503,7 @@ def _set_bit_reg_info(self):
# otherwise, get the register from find_bit and use bit_index if
# it's a bit, or the index of the bit in the register if it's a reg
else:
register, bit_index, reg_index = get_bit_reg_index(
self._circuit, wire, self._reverse_bits
)
register, bit_index, reg_index = get_bit_reg_index(self._circuit, wire)
index = bit_index if register is None else reg_index

wire_label = get_wire_label(
Expand Down Expand Up @@ -883,7 +881,7 @@ def _get_colors(self, node):
def _condition(self, node, cond_xy):
"""Add a conditional to a gate"""
label, val_bits = get_condition_label_val(
node.op.condition, self._circuit, self._cregbundle, self._reverse_bits
node.op.condition, self._circuit, self._cregbundle
)
cond_bit_reg = node.op.condition[0]
cond_bit_val = int(node.op.condition[1])
Expand All @@ -895,8 +893,7 @@ def _condition(self, node, cond_xy):
# other cases, only one bit is shown.
if not self._cregbundle and isinstance(cond_bit_reg, ClassicalRegister):
for idx in range(cond_bit_reg.size):
rev_idx = cond_bit_reg.size - idx - 1 if self._reverse_bits else idx
cond_pos.append(cond_xy[self._wire_map[cond_bit_reg[rev_idx]] - first_clbit])
cond_pos.append(cond_xy[self._wire_map[cond_bit_reg[idx]] - first_clbit])

# If it's a register bit and cregbundle, need to use the register to find the location
elif self._cregbundle and isinstance(cond_bit_reg, Clbit):
Expand Down Expand Up @@ -952,7 +949,7 @@ 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]
register, _, reg_index = get_bit_reg_index(self._circuit, node.cargs[0], self._reverse_bits)
register, _, reg_index = get_bit_reg_index(self._circuit, node.cargs[0])

# draw gate box
self._gate(node)
Expand Down
Loading