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

Support for cregbundle in text circuit drawer #4274

Merged
merged 20 commits into from
Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 5 additions & 2 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ def qasm(self, formatted=False, filename=None):
def draw(self, output=None, scale=0.7, filename=None, style=None,
interactive=False, line_length=None, plot_barriers=True,
reverse_bits=False, justify=None, vertical_compression='medium', idle_wires=True,
with_layout=True, fold=None, ax=None, initial_state=False):
with_layout=True, fold=None, ax=None, initial_state=False, cregbundle=True):
"""Draw the quantum circuit.

**text**: ASCII art TextDrawing that can be printed in the console.
Expand Down Expand Up @@ -822,6 +822,8 @@ def draw(self, output=None, scale=0.7, filename=None, style=None,
initial_state (bool): Optional. Adds ``|0>`` in the beginning of the wire.
Only used by the ``text``, ``latex`` and ``latex_source`` outputs.
Default: ``False``.
cregbundle (bool): Optional. If set True bundle classical registers. Only used by
the ``text`` output. Default: ``True``.

Returns:
:class:`PIL.Image` or :class:`matplotlib.figure` or :class:`str` or
Expand Down Expand Up @@ -964,7 +966,8 @@ def draw(self, output=None, scale=0.7, filename=None, style=None,
with_layout=with_layout,
fold=fold,
ax=ax,
initial_state=initial_state)
initial_state=initial_state,
cregbundle=cregbundle)

def size(self):
"""Returns total number of gate operations in circuit.
Expand Down
19 changes: 14 additions & 5 deletions qiskit/visualization/circuit_visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ def circuit_drawer(circuit,
with_layout=True,
fold=None,
ax=None,
initial_state=False):
initial_state=False,
cregbundle=True):
"""Draw a quantum circuit to different formats (set by output parameter):

**text**: ASCII art TextDrawing that can be printed in the console.
Expand All @@ -78,7 +79,8 @@ def circuit_drawer(circuit,

Args:
circuit (QuantumCircuit): the quantum circuit to draw
scale (float): scale of image to draw (shrink if < 1)
scale (float): scale of image to draw (shrink if < 1). Only used by the ``mpl``,
``latex``, and ``latex_source`` outputs.
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`` output type. If a str is
Expand Down Expand Up @@ -137,6 +139,8 @@ def circuit_drawer(circuit,
initial_state (bool): Optional. Adds ``|0>`` in the beginning of the wire.
Only used by the ``text``, ``latex`` and ``latex_source`` outputs.
Default: ``False``.
cregbundle (bool): Optional. If set True bundle classical registers. Only used by
the ``text`` output. Default: ``True``.
Returns:
:class:`PIL.Image` or :class:`matplotlib.figure` or :class:`str` or
:class:`TextDrawing`:
Expand Down Expand Up @@ -286,7 +290,8 @@ def circuit_drawer(circuit,
idle_wires=idle_wires,
with_layout=with_layout,
fold=fold,
initial_state=initial_state)
initial_state=initial_state,
cregbundle=cregbundle)
elif output == 'latex':
image = _latex_circuit_drawer(circuit, scale=scale,
filename=filename, style=style,
Expand Down Expand Up @@ -406,7 +411,8 @@ def qx_color_scheme():

def _text_circuit_drawer(circuit, filename=None, line_length=None, reverse_bits=False,
plot_barriers=True, justify=None, vertical_compression='high',
idle_wires=True, with_layout=True, fold=None, initial_state=True):
idle_wires=True, with_layout=True, fold=None, initial_state=True,
cregbundle=False):
1ucian0 marked this conversation as resolved.
Show resolved Hide resolved
"""Draws a circuit using ascii art.

Args:
Expand All @@ -428,6 +434,8 @@ def _text_circuit_drawer(circuit, filename=None, line_length=None, reverse_bits=
`shutil.get_terminal_size()`. If you don't want pagination
at all, set `fold=-1`.
initial_state (bool): Optional. Adds |0> in the beginning of the line. Default: `True`.
cregbundle (bool): Optional. If set True bundle classical registers. Only used by
the ``text`` output. Default: ``False``.
Returns:
TextDrawing: An instances that, when printed, draws the circuit in ascii art.
"""
Expand All @@ -442,7 +450,8 @@ def _text_circuit_drawer(circuit, filename=None, line_length=None, reverse_bits=
if line_length:
warn('The parameter "line_length" is being replaced by "fold"', DeprecationWarning, 3)
fold = line_length
text_drawing = _text.TextDrawing(qregs, cregs, ops, layout=layout, initial_state=initial_state)
text_drawing = _text.TextDrawing(qregs, cregs, ops, layout=layout, initial_state=initial_state,
cregbundle=cregbundle)
text_drawing.plotbarriers = plot_barriers
text_drawing.line_length = fold
text_drawing.vertical_compression = vertical_compression
Expand Down
57 changes: 45 additions & 12 deletions qiskit/visualization/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,11 @@ class MeasureTo(DrawElement):
bot:
"""

def __init__(self):
def __init__(self, label=''):
super().__init__()
self.top_connect = " ║ "
self.mid_content = "═╩═"
self.bot_connect = " "
self.bot_connect = label
self.mid_bck = "═"


Expand Down Expand Up @@ -500,13 +500,15 @@ class TextDrawing():
""" The text drawing"""

def __init__(self, qregs, cregs, instructions, plotbarriers=True,
line_length=None, vertical_compression='high', layout=None, initial_state=True):
line_length=None, vertical_compression='high', layout=None, initial_state=True,
cregbundle=False):
self.qregs = qregs
self.cregs = cregs
self.instructions = instructions
self.layout = layout
self.initial_state = initial_state

self.cregbundle = cregbundle
self.plotbarriers = plotbarriers
self.line_length = line_length
if vertical_compression not in ['high', 'medium', 'low']:
Expand Down Expand Up @@ -641,9 +643,19 @@ def wire_names(self, with_initial_state=False):
index=self.layout[bit.index].index,
physical=bit.index))
clbit_labels = []
previous_creg = None
for bit in self.cregs:
label = '{name}_{index}: ' + initial_clbit_value
clbit_labels.append(label.format(name=bit.register.name, index=bit.index))
if self.cregbundle:
if previous_creg == bit.register:
continue
previous_creg = bit.register
label = '{name}: {initial_value}{size}/'
clbit_labels.append(label.format(name=bit.register.name,
initial_value=initial_clbit_value,
size=bit.register.size))
else:
label = '{name}_{index}: ' + initial_clbit_value
clbit_labels.append(label.format(name=bit.register.name, index=bit.index))
return qubit_labels + clbit_labels

def should_compress(self, top_line, bot_line):
Expand Down Expand Up @@ -798,6 +810,8 @@ def merge_lines(top, bot, icod="top"):
ret += "┬"
elif topc in "┘└" and botc in "─" and icod == 'top':
ret += "┴"
elif botc == " " and icod == 'top':
ret += topc
else:
ret += botc
return ret
Expand Down Expand Up @@ -899,7 +913,10 @@ def add_connected_gate(instruction, gates, layer, current_cons):
elif isinstance(instruction.op, MeasureInstruction):
gate = MeasureFrom()
layer.set_qubit(instruction.qargs[0], gate)
layer.set_clbit(instruction.cargs[0], MeasureTo())
if self.cregbundle:
layer.set_clbit(instruction.cargs[0], MeasureTo(str(instruction.cargs[0].index)))
else:
layer.set_clbit(instruction.cargs[0], MeasureTo())

elif isinstance(instruction.op, (BarrierInstruction, Snapshot)):
# barrier
Expand Down Expand Up @@ -1002,7 +1019,7 @@ def build_layers(self):
layers = [InputWire.fillup_layer(wire_names)]

for instruction_layer in self.instructions:
layer = Layer(self.qregs, self.cregs)
layer = Layer(self.qregs, self.cregs, self.cregbundle)

for instruction in instruction_layer:
layer, current_connections, connection_label = \
Expand All @@ -1018,12 +1035,22 @@ def build_layers(self):
class Layer:
""" A layer is the "column" of the circuit. """

def __init__(self, qregs, cregs):
def __init__(self, qregs, cregs, cregbundle=False):
self.qregs = qregs
self.cregs = cregs
if cregbundle:
self.cregs = []
previous_creg = None
for bit in cregs:
if previous_creg == bit.register:
continue
previous_creg = bit.register
self.cregs.append(bit.register)
else:
self.cregs = cregs
self.qubit_layer = [None] * len(qregs)
self.connections = []
self.clbit_layer = [None] * len(cregs)
self.cregbundle = cregbundle

@property
def full_layer(self):
Expand All @@ -1050,7 +1077,10 @@ def set_clbit(self, clbit, element):
clbit (cbit): Element of self.cregs.
element (DrawElement): Element to set in the clbit
"""
self.clbit_layer[self.cregs.index(clbit)] = element
if self.cregbundle:
self.clbit_layer[self.cregs.index(clbit.register)] = element
else:
self.clbit_layer[self.cregs.index(clbit)] = element

def _set_multibox(self, label, qubits=None, clbits=None, top_connect=None,
bot_connect=None, conditional=False, controlled_edge=None):
Expand Down Expand Up @@ -1154,8 +1184,11 @@ def set_cl_multibox(self, creg, label, top_connect='┴'):
label (string): The label for the multi clbit box.
top_connect (char): The char to connect the box on the top.
"""
clbit = [bit for bit in self.cregs if bit.register == creg]
self._set_multibox(label, clbits=clbit, top_connect=top_connect)
if self.cregbundle:
self.set_clbit(creg[0], BoxOnClWire(label=label, top_connect=top_connect))
else:
clbit = [bit for bit in self.cregs if bit.register == creg]
self._set_multibox(label, clbits=clbit, top_connect=top_connect)

def set_qu_multibox(self, bits, label, top_connect=None, bot_connect=None,
conditional=False, controlled_edge=None):
Expand Down
99 changes: 95 additions & 4 deletions test/python/visualization/test_circuit_text_drawer.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ def test_measure_to(self):
" "]
self.assertEqualElement(expected, element)

def test_measure_to_label(self):
""" MeasureTo element with cregbundle """
element = elements.MeasureTo('1')
expected = [" ║ ",
"═╩═",
" 1 "]
self.assertEqualElement(expected, element)

def test_measure_from(self):
""" MeasureFrom element. """
element = elements.MeasureFrom()
Expand Down Expand Up @@ -124,6 +132,44 @@ def test_text_no_pager(self):
class TestTextDrawerGatesInCircuit(QiskitTestCase):
""" Gate by gate checks in different settings."""

def test_text_measure_cregbundle(self):
""" The measure operator, using 3-bit-length registers with cregbundle=True. """
expected = '\n'.join([" ┌─┐ ",
"q_0: |0>┤M├──────",
" └╥┘┌─┐ ",
"q_1: |0>─╫─┤M├───",
" ║ └╥┘┌─┐",
"q_2: |0>─╫──╫─┤M├",
" ║ ║ └╥┘",
" c: 0 3/═╩══╩══╩═",
" 0 1 2 "])

qr = QuantumRegister(3, 'q')
cr = ClassicalRegister(3, 'c')
circuit = QuantumCircuit(qr, cr)
circuit.measure(qr, cr)
self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected)

def test_text_measure_cregbundle_2(self):
""" The measure operator, using 2 classical registers with cregbundle=True. """
expected = '\n'.join([" ┌─┐ ",
"q_0: |0>┤M├───",
" └╥┘┌─┐",
"q_1: |0>─╫─┤M├",
" ║ └╥┘",
"cA: 0 1/═╩══╬═",
" 0 ║ ",
"cB: 0 1/════╩═",
" 0 "])

qr = QuantumRegister(2, 'q')
cr_a = ClassicalRegister(1, 'cA')
cr_b = ClassicalRegister(1, 'cB')
circuit = QuantumCircuit(qr, cr_a, cr_b)
circuit.measure(qr[0], cr_a[0])
circuit.measure(qr[1], cr_b[0])
self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected)

def test_text_measure_1(self):
""" The measure operator, using 3-bit-length registers. """
expected = '\n'.join([' ┌─┐ ',
Expand Down Expand Up @@ -1142,6 +1188,28 @@ def test_text_measure_with_spaces(self):
class TestTextConditional(QiskitTestCase):
"""Gates with conditionals"""

def test_text_conditional_1_cregbundle(self):
""" Conditional drawing with 1-bit-length regs and cregbundle."""
qasm_string = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[1];
creg c0[1];
creg c1[1];
if(c0==1) x q[0];
if(c1==1) x q[0];
"""
expected = '\n'.join([" ┌───┐ ┌───┐ ",
"q_0: |0>─┤ X ├──┤ X ├─",
" ┌┴─┴─┴┐ └─┬─┘ ",
"c0: 0 1/╡ = 1 ╞═══╪═══",
" └─────┘┌──┴──┐",
"c1: 0 1/═══════╡ = 1 ╞",
" └─────┘"])

circuit = QuantumCircuit.from_qasm_str(qasm_string)
self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected)

def test_text_conditional_1(self):
""" Conditional drawing with 1-bit-length regs."""
qasm_string = """
Expand All @@ -1164,6 +1232,27 @@ def test_text_conditional_1(self):
circuit = QuantumCircuit.from_qasm_str(qasm_string)
self.assertEqual(str(_text_circuit_drawer(circuit)), expected)

def test_text_conditional_2_cregbundle(self):
""" Conditional drawing with 2-bit-length regs with cregbundle"""
qasm_string = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[1];
creg c0[2];
creg c1[2];
if(c0==2) x q[0];
if(c1==2) x q[0];
"""
expected = '\n'.join([" ┌───┐ ┌───┐ ",
"q_0: |0>─┤ X ├──┤ X ├─",
" ┌┴─┴─┴┐ └─┬─┘ ",
"c0: 0 2/╡ = 2 ╞═══╪═══",
" └─────┘┌──┴──┐",
"c1: 0 2/═══════╡ = 2 ╞",
" └─────┘"])
circuit = QuantumCircuit.from_qasm_str(qasm_string)
self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected)

def test_text_conditional_2(self):
""" Conditional drawing with 2-bit-length regs."""
qasm_string = """
Expand Down Expand Up @@ -2607,7 +2696,7 @@ def test_after_transpile(self):
[13, 12]]
qc_result = transpile(qc, basis_gates=['u1', 'u2', 'u3', 'cx', 'id'],
coupling_map=coupling_map, optimization_level=0, seed_transpiler=0)
self.assertEqual(qc_result.draw(output='text').single_string(), expected)
self.assertEqual(qc_result.draw(output='text', cregbundle=False).single_string(), expected)


class TestTextInitialValue(QiskitTestCase):
Expand All @@ -2631,7 +2720,8 @@ def test_draw_initial_value_default(self):
"c_1: ════╩═",
" "])

self.assertEqual(self.circuit.draw(output='text').single_string(), expected)
self.assertEqual(self.circuit.draw(output='text', cregbundle=False).single_string(),
expected)

def test_draw_initial_value_true(self):
""" Text drawer .draw(initial_state=True). """
Expand All @@ -2644,8 +2734,9 @@ def test_draw_initial_value_true(self):
" ║ ",
" c_1: 0 ════╩═",
" "])
self.assertEqual(self.circuit.draw(output='text', initial_state=True).single_string(),
expected)
self.assertEqual(self.circuit.draw(output='text',
initial_state=True,
cregbundle=False).single_string(), expected)

def test_initial_value_false(self):
""" Text drawer with initial_state parameter False. """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ def test_matplotlib_drawer(self):
def test_text_drawer(self):
filename = self._get_resource_path('current_textplot.txt')
qc = self.sample_circuit()
output = circuit_drawer(qc, filename=filename, output="text", fold=-1, initial_state=True)
output = circuit_drawer(qc, filename=filename, output="text", fold=-1, initial_state=True,
cregbundle=False)
self.assertFilesAreEqual(filename, self.text_reference)
os.remove(filename)
try:
Expand Down