From 47c9291dbe83be6ce8833f38a077e273283dabe2 Mon Sep 17 00:00:00 2001 From: licheng 80054959 Date: Wed, 25 Sep 2019 09:56:54 +0000 Subject: [PATCH 01/37] Adding tutorials directory --- tutorials/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tutorials/.gitkeep diff --git a/tutorials/.gitkeep b/tutorials/.gitkeep new file mode 100644 index 000000000..e69de29bb From d23b239b73004d0f2f04633ae94c5709deccb9ee Mon Sep 17 00:00:00 2001 From: Bombenchris Date: Wed, 25 Sep 2019 14:48:40 +0200 Subject: [PATCH 02/37] test --- tutorials/Basic_qp1.ipynb | 82 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 tutorials/Basic_qp1.ipynb diff --git a/tutorials/Basic_qp1.ipynb b/tutorials/Basic_qp1.ipynb new file mode 100644 index 000000000..6fee9e1a4 --- /dev/null +++ b/tutorials/Basic_qp1.ipynb @@ -0,0 +1,82 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "from projectq import MainEngine\n", + "from projectq.ops import H,Measure" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(Note: This is the (slow) Python simulator.)\n" + ] + } + ], + "source": [ + "eng = MainEngine()\n", + "qubit = eng.allocate_qubit()\n", + "H | qubit\n", + "\n", + "Measure | qubit\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Measured:0\n" + ] + } + ], + "source": [ + "eng.flush()\n", + "print('Measured:{}'.format(int(qubit)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From c7356a493cc24728ae776e664ebe227f1ce897b1 Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Mon, 30 Sep 2019 09:19:50 +0200 Subject: [PATCH 03/37] BV Algorithm --- tutorials/Bernstein-Vazirani Algorithm.ipynb | 118 +++++++++++++++++++ tutorials/Bernstein-Vazirani algorithm.tex | 25 ++++ 2 files changed, 143 insertions(+) create mode 100644 tutorials/Bernstein-Vazirani Algorithm.ipynb create mode 100644 tutorials/Bernstein-Vazirani algorithm.tex diff --git a/tutorials/Bernstein-Vazirani Algorithm.ipynb b/tutorials/Bernstein-Vazirani Algorithm.ipynb new file mode 100644 index 000000000..c9d74106c --- /dev/null +++ b/tutorials/Bernstein-Vazirani Algorithm.ipynb @@ -0,0 +1,118 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# Goal: find the secret bit-string s\n", + "\n", + "from projectq import MainEngine\n", + "from projectq.ops import *\n", + "from projectq.meta import Compute, Uncompute\n", + "from projectq.backends import CircuitDrawer\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# set the secretnumber\n", + "secretnumber = '10111001001'\n", + "n = len(secretnumber)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(Note: This is the (slow) Python simulator.)\n" + ] + } + ], + "source": [ + "# initiate circuit\n", + "# drawing_engine = CircuitDrawer()\n", + "# eng = MainEngine(backend = drawing_engine)\n", + "eng = MainEngine()\n", + "qureg = eng.allocate_qureg(n + 1)\n", + "\n", + "# implement Algorithm\n", + "with Compute(eng):\n", + " X | qureg[n]\n", + " Barrier | qureg\n", + " All(H) | qureg\n", + "\n", + "Barrier | qureg\n", + "\n", + "for ii, yesno in enumerate(secretnumber):\n", + " if yesno == '1':\n", + " CNOT | (qureg[ii],qureg[n]) # different display sequence vs. Qiskit\n", + "\n", + "Barrier | qureg\n", + "\n", + "Uncompute(eng)\n", + "for i in range(n):\n", + " Measure | qureg[i]\n", + "\n", + "eng.flush()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Measured [1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1]\n" + ] + } + ], + "source": [ + "print(\"Measured {}\".format([ int(qubit) for qubit in qureg[:-1] ] ))\n", + "\n", + "with open('Bernstein-Vazirani algorithm.tex','w') as fd:\n", + " fd.write(drawing_engine.get_latex())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tutorials/Bernstein-Vazirani algorithm.tex b/tutorials/Bernstein-Vazirani algorithm.tex new file mode 100644 index 000000000..fba373517 --- /dev/null +++ b/tutorials/Bernstein-Vazirani algorithm.tex @@ -0,0 +1,25 @@ +\documentclass{standalone} +\usepackage[margin=1in]{geometry} +\usepackage[hang,small,bf]{caption} +\usepackage{tikz} +\usepackage{braket} +\usetikzlibrary{backgrounds,shadows.blur,fit,decorations.pathreplacing,shapes} + +\begin{document} +\begin{tikzpicture}[scale=0.8, transform shape] + +\tikzstyle{basicshadow}=[blur shadow={shadow blur steps=8, shadow xshift=0.7pt, shadow yshift=-0.7pt, shadow scale=1.02}]\tikzstyle{basic}=[draw,fill=white,basicshadow] +\tikzstyle{operator}=[basic,minimum size=1.5em] +\tikzstyle{phase}=[fill=black,shape=circle,minimum size=0.1cm,inner sep=0pt,outer sep=0pt,draw=black] +\tikzstyle{none}=[inner sep=0pt,outer sep=-.5pt,minimum height=0.5cm+1pt] +\tikzstyle{measure}=[operator,inner sep=0pt,minimum height=0.5cm, minimum width=0.75cm] +\tikzstyle{xstyle}=[circle,basic,minimum height=0.35cm,minimum width=0.35cm,inner sep=-1pt,very thin] +\tikzset{ +shadowed/.style={preaction={transform canvas={shift={(0.5pt,-0.5pt)}}, draw=gray, opacity=0.4}}, +} +\tikzstyle{swapstyle}=[inner sep=-1pt, outer sep=-1pt, minimum width=0pt] +\tikzstyle{edgestyle}=[very thin] + + +\end{tikzpicture} +\end{document} \ No newline at end of file From 28173297a019a00139ed74dd02dcf162876ddc20 Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Tue, 8 Oct 2019 11:03:09 +0200 Subject: [PATCH 04/37] add Matplotlib circuit drawer backend, this version works for H, CNOT, and Multi-CNOT. --- projectq/backends/_circuits/__init__.py | 3 + projectq/backends/_circuits/_drawer.py | 57 ++--- projectq/backends/_circuits/_plot.py | 312 ++++++++++++++++++++++++ projectq/ops/_command.py | 6 +- 4 files changed, 343 insertions(+), 35 deletions(-) create mode 100644 projectq/backends/_circuits/_plot.py diff --git a/projectq/backends/_circuits/__init__.py b/projectq/backends/_circuits/__init__.py index 1f22faec4..71560ddbb 100755 --- a/projectq/backends/_circuits/__init__.py +++ b/projectq/backends/_circuits/__init__.py @@ -13,4 +13,7 @@ # limitations under the License. from ._to_latex import to_latex +from ._plot import to_draw + from ._drawer import CircuitDrawer + diff --git a/projectq/backends/_circuits/_drawer.py b/projectq/backends/_circuits/_drawer.py index 269f592a2..23a90a901 100755 --- a/projectq/backends/_circuits/_drawer.py +++ b/projectq/backends/_circuits/_drawer.py @@ -23,8 +23,7 @@ from projectq.cengines import LastEngineException, BasicEngine from projectq.ops import FlushGate, Measure, Allocate, Deallocate from projectq.meta import get_control_count -from projectq.backends._circuits import to_latex - +from projectq.backends._circuits import to_latex, to_draw class CircuitItem(object): def __init__(self, gate, lines, ctrl_lines): @@ -152,6 +151,8 @@ def __init__(self, accept_input=False, default_measure=0): self._qubit_lines = dict() self._free_lines = [] self._map = dict() + + self._gates = [] # save a list of command in order def is_available(self, cmd): """ @@ -244,38 +245,6 @@ def _print_cmd(self, cmd): for l in all_lines: self._qubit_lines[l].append(item) - def get_latex(self): - """ - Return the latex document string representing the circuit. - - Simply write this string into a tex-file or, alternatively, pipe the - output directly to, e.g., pdflatex: - - .. code-block:: bash - - python3 my_circuit.py | pdflatex - - where my_circuit.py calls this function and prints it to the terminal. - """ - qubit_lines = dict() - - for line in range(len(self._qubit_lines)): - new_line = self._map[line] - qubit_lines[new_line] = [] - for cmd in self._qubit_lines[line]: - lines = [self._map[qb_id] for qb_id in cmd.lines] - ctrl_lines = [self._map[qb_id] for qb_id in cmd.ctrl_lines] - gate = cmd.gate - new_cmd = CircuitItem(gate, lines, ctrl_lines) - if gate == Allocate: - new_cmd.id = cmd.lines[0] - qubit_lines[new_line].append(new_cmd) - - circuit = [] - for lines in qubit_lines: - circuit.append(qubit_lines[lines]) - return to_latex(qubit_lines) - def receive(self, command_list): """ Receive a list of commands from the previous engine, print the @@ -286,8 +255,28 @@ def receive(self, command_list): potentially send on to the next engine). """ for cmd in command_list: + l = [] + g = str(cmd.gate) + l.append(str(cmd.label)) + if len(cmd.control_qubits) > 0: + for cq in cmd.control_qubits: + l.append(str(cq)) + + listOfStrings = ['','Allocate'] + + if not g in listOfStrings: + self._gates.append(tuple([g] + l)) + if not cmd.gate == FlushGate(): self._print_cmd(cmd) # (try to) send on if not self.is_last_engine: self.send([cmd]) + + def draw(self): + """ + Use Matplotlib to plot a quantum circuit. + """ + label = [str(self._map[id]) for id in self._map] + + return to_draw(self._gates,label) \ No newline at end of file diff --git a/projectq/backends/_circuits/_plot.py b/projectq/backends/_circuits/_plot.py new file mode 100644 index 000000000..ffd9a0766 --- /dev/null +++ b/projectq/backends/_circuits/_plot.py @@ -0,0 +1,312 @@ +# Copyright 2017 ProjectQ-Framework (www.projectq.ch) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np + +def to_draw(gates,labels=[],inits={},plot_labels=True,**kwargs): + """ + + :param gates: + :param label: + :param inits: + :param plot_labels: + :param kwargs: + :return: + """ + plot_params = dict(scale=1.0, fontsize=14.0, linewidth=1.0, + control_radius=0.05, not_radius=0.15, + swap_delta=0.08, label_buffer=0.0) + plot_params.update(kwargs) + scale = plot_params['scale'] + + # Create labels from gates. This will become slow if there are a lot + # of gates, in which case move to an ordered dictionary + # if not labels: + # labels = [] + # for i, gate in enumerate_gates(gates): + # for label in gate[1:]: + # if label not in labels: + # labels.append(label) + + nq = len(labels) + ng = len(gates) + wire_grid = np.arange(0.0, nq * scale, scale, dtype=float) + gate_grid = np.arange(0.0, ng * scale, scale, dtype=float) + + fig, ax = setup_figure(nq, ng, gate_grid, wire_grid, plot_params) + + measured = measured_wires(gates, labels) + draw_wires(ax, nq, gate_grid, wire_grid, plot_params, measured) + + if plot_labels: + draw_labels(ax, labels, inits, gate_grid, wire_grid, plot_params) + + draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params, measured) + return ax + + +def enumerate_gates(l, schedule=False): + "Enumerate the gates in a way that can take l as either a list of gates or a schedule" + if schedule: + for i, gates in enumerate(l): + for gate in gates: + yield i, gate + else: + for i, gate in enumerate(l): + yield i, gate + return + + +def measured_wires(l, labels, schedule=False): + "measured[i] = j means wire i is measured at step j" + # schedule is always false...not implemented. + measured = {} + for i, gate in enumerate_gates(l, schedule=schedule): + name, target = gate[:2] + j = get_flipped_index(target, labels) + if name.startswith('M'): + measured[j] = i + return measured + + +def draw_gates(ax, l, labels, gate_grid, wire_grid, plot_params, measured={}, schedule=False): + x_labels = {label: 0 for label in labels} + x_position = 0 + for i, gate in enumerate_gates(l, schedule=schedule): + if len(gate) > 2: # Controlled + qb_target = gate[1] + qb_control = gate[2] + + x_position = max(x_labels[qb_target], x_labels[qb_control]) + draw_controls(ax, x_position, gate, labels, gate_grid, wire_grid, plot_params, measured) + + x_labels[qb_target] = x_position + draw_target(ax, x_labels[qb_target], gate, labels, gate_grid, wire_grid, plot_params) + + # all the x betweeen control and target has to be added 1...for multi. qubit. + begin = min(qb_control, qb_target) + end = max(qb_control, qb_target) + + for itr, value in x_labels.items(): + if begin <= itr <= end: + x_labels[itr] = x_position + 1 + + else: + qb = gate[1] + + draw_target(ax, x_labels[qb], gate, labels, gate_grid, wire_grid, plot_params) + + x_labels[qb] = x_labels[qb] + 1 + + return + + +def draw_controls(ax, i, gate, labels, gate_grid, wire_grid, plot_params, measured={}): + linewidth = plot_params['linewidth'] + scale = plot_params['scale'] + control_radius = plot_params['control_radius'] + + # what about multi target, can't set 2 here.. + # make a case, specifically for multi target gate.. + name, target = gate[:2] + target_index = get_flipped_index(target, labels) + + # what about multi control + controls = gate[2:] + control_indices = get_flipped_indices(controls, labels) + gate_indices = control_indices + [target_index] + + min_wire = min(gate_indices) + max_wire = max(gate_indices) + line(ax, gate_grid[i], gate_grid[i], wire_grid[min_wire], wire_grid[max_wire], plot_params) + + ismeasured = False + for index in control_indices: + # what is this used for??? + if measured.get(index, 1000) < i: + ismeasured = True + if ismeasured: + dy = 0.04 # TODO: put in plot_params + line(ax, gate_grid[i] + dy, gate_grid[i] + dy, wire_grid[min_wire], wire_grid[max_wire], plot_params) + + for ci in control_indices: + x = gate_grid[i] + y = wire_grid[ci] + if name in ['SWAP']: + swapx(ax, x, y, plot_params) + else: + cdot(ax, x, y, plot_params) + return + + +def draw_target(ax, i, gate, labels, gate_grid, wire_grid, plot_params): + target_symbols = dict(CNOT='X', CPHASE='Z', NOP='', CX='X', CZ='Z') + name, target = gate[:2] + symbol = target_symbols.get(name, name) # override name with target_symbols, get(keyname,value) + + if symbol in ['X'] and len(gate) >= 3: + name = 'CNOT' + + x = gate_grid[i] + target_index = get_flipped_index(target, labels) + y = wire_grid[target_index] + + if not symbol: return + if name in ['CNOT', 'TOFFOLI']: + oplus(ax, x, y, plot_params) + elif name in ['CPHASE']: + cdot(ax, x, y, plot_params) + elif name in ['SWAP']: + swapx(ax, x, y, plot_params) + elif name in ['M']: + draw_mwires(ax, x, y, gate_grid, wire_grid, plot_params) + + text(ax, x, y, symbol, plot_params, box=True) + else: + text(ax, x, y, symbol, plot_params, box=True) + return + + +def line(ax, x1, x2, y1, y2, plot_params): + Line2D = matplotlib.lines.Line2D + line = Line2D((x1, x2), (y1, y2), + color='k', lw=plot_params['linewidth']) + ax.add_line(line) + + +def text(ax, x, y, textstr, plot_params, box=False): + linewidth = plot_params['linewidth'] + fontsize = plot_params['fontsize'] + + if box: + bbox = dict(ec='k', fc='w', fill=True, lw=linewidth) # draw gate box + else: + bbox = dict(ec='w', fc='w', fill=False, lw=linewidth) # draw the qubit box + ax.text(x, y, textstr, color='k', ha='center', va='center', bbox=bbox, size=fontsize) + return + + +def oplus(ax, x, y, plot_params): + Line2D = matplotlib.lines.Line2D + Circle = matplotlib.patches.Circle + not_radius = plot_params['not_radius'] + linewidth = plot_params['linewidth'] + c = Circle((x, y), not_radius, ec='k', + fc='w', fill=False, lw=linewidth) + ax.add_patch(c) + line(ax, x, x, y - not_radius, y + not_radius, plot_params) + return + + +def cdot(ax, x, y, plot_params): + Circle = matplotlib.patches.Circle + control_radius = plot_params['control_radius'] + scale = plot_params['scale'] + linewidth = plot_params['linewidth'] + c = Circle((x, y), control_radius * scale, + ec='k', fc='k', fill=True, lw=linewidth) + ax.add_patch(c) + return + + +def swapx(ax, x, y, plot_params): + d = plot_params['swap_delta'] + linewidth = plot_params['linewidth'] + line(ax, x - d, x + d, y - d, y + d, plot_params) + line(ax, x - d, x + d, y + d, y - d, plot_params) + return + + +def setup_figure(nq, ng, gate_grid, wire_grid, plot_params): + scale = plot_params['scale'] + fig = plt.figure( + figsize=(ng * scale, nq * scale), + facecolor='w', + edgecolor='w' + ) + ax = fig.add_subplot(1, 1, 1, frameon=True) + ax.set_axis_off() + offset = 0.5 * scale + ax.set_xlim(gate_grid[0] - offset, gate_grid[-1] + offset) + ax.set_ylim(wire_grid[0] - offset, wire_grid[-1] + offset) + ax.set_aspect('equal') + return fig, ax + + +def draw_wires(ax, nq, gate_grid, wire_grid, plot_params, measured={}): + scale = plot_params['scale'] + linewidth = plot_params['linewidth'] + xdata = (gate_grid[0] - scale, gate_grid[-1] + scale) + for i in range(nq): + line(ax, gate_grid[0] - scale, gate_grid[-1] + scale, wire_grid[i], wire_grid[i], plot_params) + return + + +def draw_mwires(ax, x, y, gate_grid, wire_grid, plot_params): + # Add the doubling for measured wires: + scale = plot_params['scale'] + dy = 0.04 # TODO: add to plot_params + + line(ax, x, gate_grid[-1] + scale, y + dy, y + dy, plot_params) + + # wired_grid indicate which qubit it belongs to + # gate_grid is the x-axes, x2=grid_grid[-1], so it will always draw the line to the end. + return + + +def draw_labels(ax, labels, inits, gate_grid, wire_grid, plot_params): + scale = plot_params['scale'] + label_buffer = plot_params['label_buffer'] + fontsize = plot_params['fontsize'] + nq = len(labels) + xdata = (gate_grid[0] - scale, gate_grid[-1] + scale) + for i in range(nq): + j = get_flipped_index(labels[i], labels) + text(ax, xdata[0] - label_buffer, wire_grid[j], render_label(labels[i], inits), plot_params) + return + + +def get_flipped_index(target, labels): + """Get qubit labels from the rest of the line,and return indices + + >>> get_flipped_index('q0', ['q0', 'q1']) + 1 + >>> get_flipped_index('q1', ['q0', 'q1']) + 0 + """ + nq = len(labels) + i = labels.index(target) + return nq - i - 1 + + +def get_flipped_indices(targets, labels): return [get_flipped_index(t, labels) for t in targets] + + +def render_label(label, inits={}): + """Slightly more flexible way to render labels. + + >>> render_label('q0') + '$|q0\\\\rangle$' + >>> render_label('q0', {'q0':'0'}) + '$|0\\\\rangle$' + """ + if label in inits: + s = inits[label] + if s is None: + return '' + else: + return r'$|%s\rangle$' % inits[label] + return r'$|%s\rangle$' % label diff --git a/projectq/ops/_command.py b/projectq/ops/_command.py index 5186502fa..7cb9086e8 100755 --- a/projectq/ops/_command.py +++ b/projectq/ops/_command.py @@ -106,11 +106,15 @@ def __init__(self, engine, gate, qubits, controls=(), tags=()): tags (list[object]): Tags associated with the command. """ + qubits = tuple([WeakQubitRef(qubit.engine, qubit.id) for qubit in qreg] for qreg in qubits) - + + # get the command gate and qubit self.gate = gate + self.label = qubits[0][0] + self.tags = list(tags) self.qubits = qubits # property self.control_qubits = controls # property From 6f3c77555cdc704cfb81b03c34876c88a1c40118 Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Tue, 8 Oct 2019 14:35:00 +0200 Subject: [PATCH 05/37] Delete the added unnecessary attributes in Command object --- projectq/backends/_circuits/_drawer.py | 3 ++- projectq/ops/_command.py | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/projectq/backends/_circuits/_drawer.py b/projectq/backends/_circuits/_drawer.py index 23a90a901..a04115fe4 100755 --- a/projectq/backends/_circuits/_drawer.py +++ b/projectq/backends/_circuits/_drawer.py @@ -257,7 +257,8 @@ def receive(self, command_list): for cmd in command_list: l = [] g = str(cmd.gate) - l.append(str(cmd.label)) + for q in cmd.qubits: + l.append(str(q[0])) # assume single target, the first element of q is the target qubit. if len(cmd.control_qubits) > 0: for cq in cmd.control_qubits: l.append(str(cq)) diff --git a/projectq/ops/_command.py b/projectq/ops/_command.py index 7cb9086e8..51fb37ae4 100755 --- a/projectq/ops/_command.py +++ b/projectq/ops/_command.py @@ -111,10 +111,7 @@ def __init__(self, engine, gate, qubits, controls=(), tags=()): for qubit in qreg] for qreg in qubits) - # get the command gate and qubit self.gate = gate - self.label = qubits[0][0] - self.tags = list(tags) self.qubits = qubits # property self.control_qubits = controls # property From e7bb09853bc261aab9b640620a19c7ac3d21bb11 Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Tue, 8 Oct 2019 15:40:28 +0200 Subject: [PATCH 06/37] Create the CircuitDrawerMatplotlib Class to handles drawing with matplotlib --- projectq/backends/__init__.py | 1 + projectq/backends/_circuits/__init__.py | 1 + projectq/backends/_circuits/_drawer.py | 174 ++++++++++++++++++++---- 3 files changed, 153 insertions(+), 23 deletions(-) diff --git a/projectq/backends/__init__.py b/projectq/backends/__init__.py index 6a3319779..9d57425e2 100755 --- a/projectq/backends/__init__.py +++ b/projectq/backends/__init__.py @@ -27,6 +27,7 @@ """ from ._printer import CommandPrinter from ._circuits import CircuitDrawer +from ._circuits import CircuitDrawerMatplotlib from ._sim import Simulator, ClassicalSimulator from ._resource import ResourceCounter from ._ibm import IBMBackend diff --git a/projectq/backends/_circuits/__init__.py b/projectq/backends/_circuits/__init__.py index 71560ddbb..1f72b94d8 100755 --- a/projectq/backends/_circuits/__init__.py +++ b/projectq/backends/_circuits/__init__.py @@ -16,4 +16,5 @@ from ._plot import to_draw from ._drawer import CircuitDrawer +from ._drawer import CircuitDrawerMatplotlib diff --git a/projectq/backends/_circuits/_drawer.py b/projectq/backends/_circuits/_drawer.py index a04115fe4..e2d17f042 100755 --- a/projectq/backends/_circuits/_drawer.py +++ b/projectq/backends/_circuits/_drawer.py @@ -48,7 +48,126 @@ def __eq__(self, other): def __ne__(self, other): return not self.__eq__(other) +class CircuitDrawerMatplotlib(BasicEngine): + """ + CircuitDrawerMatplotlib is a compiler engine which using Matplotlib library + for drawing quantum circuits + """ + def __init__(self, accept_input=False, default_measure=0): + """ + Initialize a circuit drawing engine(mpl) + """ + BasicEngine.__init__(self) + + self._accept_input = accept_input + self._default_measure = default_measure + self._qubit_lines = dict() + self._free_lines = [] + self._map = dict() + self._gates = [] + self._qubits = [] + + def is_available(self, cmd): + """ + Specialized implementation of is_available: Returns True if the + CircuitDrawer is the last engine (since it can print any command). + + Args: + cmd (Command): Command for which to check availability (all + Commands can be printed). + Returns: + availability (bool): True, unless the next engine cannot handle + the Command (if there is a next engine). + """ + try: + return BasicEngine.is_available(self, cmd) + except LastEngineException: + return True + + def _print_cmd(self, cmd): + """ + Add the command cmd to the circuit diagram, taking care of potential + measurements as specified in the __init__ function. + + Queries the user for measurement input if a measurement command + arrives if accept_input was set to True. Otherwise, it uses the + default_measure parameter to register the measurement outcome. + + Args: + cmd (Command): Command to add to the circuit diagram. + """ + if cmd.gate == Allocate: + qubit_id = cmd.qubits[0][0].id + if qubit_id not in self._map: + self._map[qubit_id] = qubit_id + self._qubit_lines[qubit_id] = [] + + if cmd.gate == Deallocate: + qubit_id = cmd.qubits[0][0].id + self._free_lines.append(qubit_id) + + if self.is_last_engine and cmd.gate == Measure: + assert(get_control_count(cmd) == 0) + for qureg in cmd.qubits: + for qubit in qureg: + if self._accept_input: + m = None + while m != '0' and m != '1' and m != 1 and m != 0: + prompt = ("Input measurement result (0 or 1) for " + "qubit " + str(qubit) + ": ") + m = input(prompt) + else: + m = self._default_measure + m = int(m) + self.main_engine.set_measurement_result(qubit, m) + all_lines = [qb.id for qr in cmd.all_qubits for qb in qr] + + gate = cmd.gate + lines = [qb.id for qr in cmd.qubits for qb in qr] + ctrl_lines = [qb.id for qb in cmd.control_qubits] + item = CircuitItem(gate, lines, ctrl_lines) + for l in all_lines: + self._qubit_lines[l].append(item) + + def receive(self, command_list): + """ + Receive a list of commands from the previous engine, print the + commands, and then send them on to the next engine. + + Args: + command_list (list): List of Commands to print (and + potentially send on to the next engine). + """ + for cmd in command_list: + l = [] + g = str(cmd.gate) + for q in cmd.qubits: + l.append(str(q[0])) # assume single target, the first element of q is the target qubit. + if len(cmd.control_qubits) > 0: + for cq in cmd.control_qubits: + l.append(str(cq)) + + listOfStrings = ['', 'Allocate'] + + if not g in listOfStrings: + self._gates.append(tuple([g] + l)) + + if not cmd.gate == FlushGate(): + self._print_cmd(cmd) + # (try to) send on + if not self.is_last_engine: + self.send([cmd]) + + def draw(self): + """ + Use Matplotlib to plot a quantum circuit. + """ + qubits = [str(self._map[id]) for id in self._map] + # extract all the allocated qubits from the circuit + + return to_draw(self._gates, qubits) + class CircuitDrawer(BasicEngine): """ CircuitDrawer is a compiler engine which generates TikZ code for drawing @@ -151,8 +270,6 @@ def __init__(self, accept_input=False, default_measure=0): self._qubit_lines = dict() self._free_lines = [] self._map = dict() - - self._gates = [] # save a list of command in order def is_available(self, cmd): """ @@ -245,6 +362,38 @@ def _print_cmd(self, cmd): for l in all_lines: self._qubit_lines[l].append(item) + def get_latex(self): + """ + Return the latex document string representing the circuit. + + Simply write this string into a tex-file or, alternatively, pipe the + output directly to, e.g., pdflatex: + + .. code-block:: bash + + python3 my_circuit.py | pdflatex + + where my_circuit.py calls this function and prints it to the terminal. + """ + qubit_lines = dict() + + for line in range(len(self._qubit_lines)): + new_line = self._map[line] + qubit_lines[new_line] = [] + for cmd in self._qubit_lines[line]: + lines = [self._map[qb_id] for qb_id in cmd.lines] + ctrl_lines = [self._map[qb_id] for qb_id in cmd.ctrl_lines] + gate = cmd.gate + new_cmd = CircuitItem(gate, lines, ctrl_lines) + if gate == Allocate: + new_cmd.id = cmd.lines[0] + qubit_lines[new_line].append(new_cmd) + + circuit = [] + for lines in qubit_lines: + circuit.append(qubit_lines[lines]) + return to_latex(qubit_lines) + def receive(self, command_list): """ Receive a list of commands from the previous engine, print the @@ -255,29 +404,8 @@ def receive(self, command_list): potentially send on to the next engine). """ for cmd in command_list: - l = [] - g = str(cmd.gate) - for q in cmd.qubits: - l.append(str(q[0])) # assume single target, the first element of q is the target qubit. - if len(cmd.control_qubits) > 0: - for cq in cmd.control_qubits: - l.append(str(cq)) - - listOfStrings = ['','Allocate'] - - if not g in listOfStrings: - self._gates.append(tuple([g] + l)) - if not cmd.gate == FlushGate(): self._print_cmd(cmd) # (try to) send on if not self.is_last_engine: self.send([cmd]) - - def draw(self): - """ - Use Matplotlib to plot a quantum circuit. - """ - label = [str(self._map[id]) for id in self._map] - - return to_draw(self._gates,label) \ No newline at end of file From d95a77744d8b4a974f7cd2da34aadc38ff04927c Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Thu, 10 Oct 2019 14:00:39 +0000 Subject: [PATCH 07/37] Deleted tutorials/.gitkeep --- tutorials/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tutorials/.gitkeep diff --git a/tutorials/.gitkeep b/tutorials/.gitkeep deleted file mode 100644 index e69de29bb..000000000 From 824f29ab4baadb780251516dbfb8d1837752513c Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Thu, 10 Oct 2019 17:52:55 +0200 Subject: [PATCH 08/37] update --- projectq/backends/_circuits/_drawer.py | 28 +- projectq/backends/_circuits/_plot.py | 344 +++++++++++++++---------- 2 files changed, 226 insertions(+), 146 deletions(-) diff --git a/projectq/backends/_circuits/_drawer.py b/projectq/backends/_circuits/_drawer.py index e2d17f042..621d82e80 100755 --- a/projectq/backends/_circuits/_drawer.py +++ b/projectq/backends/_circuits/_drawer.py @@ -56,16 +56,20 @@ class CircuitDrawerMatplotlib(BasicEngine): def __init__(self, accept_input=False, default_measure=0): """ Initialize a circuit drawing engine(mpl) + Args: + accept_input (bool): If accept_input is true, the printer queries + the user to input measurement results if the CircuitDrawerMPL is + the last engine. Otherwise, all measurements yield the result + default_measure (0 or 1). + default_measure (bool): Default value to use as measurement + results if accept_input is False and there is no underlying + backend to register real measurement results. """ BasicEngine.__init__(self) - self._accept_input = accept_input self._default_measure = default_measure - self._qubit_lines = dict() - self._free_lines = [] self._map = dict() self._gates = [] - self._qubits = [] def is_available(self, cmd): """ @@ -100,11 +104,9 @@ def _print_cmd(self, cmd): qubit_id = cmd.qubits[0][0].id if qubit_id not in self._map: self._map[qubit_id] = qubit_id - self._qubit_lines[qubit_id] = [] if cmd.gate == Deallocate: qubit_id = cmd.qubits[0][0].id - self._free_lines.append(qubit_id) if self.is_last_engine and cmd.gate == Measure: assert(get_control_count(cmd) == 0) @@ -121,15 +123,6 @@ def _print_cmd(self, cmd): m = int(m) self.main_engine.set_measurement_result(qubit, m) - all_lines = [qb.id for qr in cmd.all_qubits for qb in qr] - - gate = cmd.gate - lines = [qb.id for qr in cmd.qubits for qb in qr] - ctrl_lines = [qb.id for qb in cmd.control_qubits] - item = CircuitItem(gate, lines, ctrl_lines) - for l in all_lines: - self._qubit_lines[l].append(item) - def receive(self, command_list): """ Receive a list of commands from the previous engine, print the @@ -143,7 +136,8 @@ def receive(self, command_list): l = [] g = str(cmd.gate) for q in cmd.qubits: - l.append(str(q[0])) # assume single target, the first element of q is the target qubit. + l.append(str(q[0])) + # assume single target, 1st. element of q is the target qubit. if len(cmd.control_qubits) > 0: for cq in cmd.control_qubits: l.append(str(cq)) @@ -161,7 +155,7 @@ def receive(self, command_list): def draw(self): """ - Use Matplotlib to plot a quantum circuit. + Returns the plot of the quantum circuit """ qubits = [str(self._map[id]) for id in self._map] # extract all the allocated qubits from the circuit diff --git a/projectq/backends/_circuits/_plot.py b/projectq/backends/_circuits/_plot.py index ffd9a0766..47906179e 100644 --- a/projectq/backends/_circuits/_plot.py +++ b/projectq/backends/_circuits/_plot.py @@ -15,106 +15,105 @@ import matplotlib import matplotlib.pyplot as plt import numpy as np +from matplotlib.lines import Line2D +from matplotlib.patches import Circle def to_draw(gates,labels=[],inits={},plot_labels=True,**kwargs): """ - - :param gates: - :param label: - :param inits: - :param plot_labels: - :param kwargs: - :return: + Use Matplotlib to plot a quantum circuit. + Args: + gates (list): List of tuples for each gate in the quantum circuit. + (name,target,control1,control2...). Targets and controls initially + defined in terms of labels. + labels (list): Qubits' index in the quantum circuit + inits (dict): Initialization list of gates, optional + plot_labels (bool): If plot_labels is false, the qubits' label will not + be drawed. + **kwargs (dict): Can override plot_parameters """ plot_params = dict(scale=1.0, fontsize=14.0, linewidth=1.0, - control_radius=0.05, not_radius=0.15, + linebetween=0.04,control_radius=0.05, not_radius=0.15, swap_delta=0.08, label_buffer=0.0) plot_params.update(kwargs) scale = plot_params['scale'] - # Create labels from gates. This will become slow if there are a lot - # of gates, in which case move to an ordered dictionary - # if not labels: - # labels = [] - # for i, gate in enumerate_gates(gates): - # for label in gate[1:]: - # if label not in labels: - # labels.append(label) - - nq = len(labels) - ng = len(gates) - wire_grid = np.arange(0.0, nq * scale, scale, dtype=float) - gate_grid = np.arange(0.0, ng * scale, scale, dtype=float) + n_labels = len(labels) + n_gates = len(gates) + inits = {label: 0 for label in labels} - fig, ax = setup_figure(nq, ng, gate_grid, wire_grid, plot_params) + # create grid for the plot + wire_grid = np.arange(0.0, n_labels * scale, scale, dtype=float) + gate_grid = np.arange(0.0, n_gates * scale, scale, dtype=float) - measured = measured_wires(gates, labels) - draw_wires(ax, nq, gate_grid, wire_grid, plot_params, measured) + fig, ax = setup_figure(n_labels, n_gates, gate_grid, wire_grid, plot_params) + + draw_wires(ax, n_labels, gate_grid, wire_grid, plot_params) if plot_labels: draw_labels(ax, labels, inits, gate_grid, wire_grid, plot_params) - draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params, measured) + draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params) return ax +def draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params): + """ + matching the position of each gate to the figure and draw each gate + Args: + ax (AxesSubplot): axes object + gates (list): List of tuples for each gate in the quantum circuit. + labels (list): contains qubits' label + gate_grid (ndarray): grid for positioning gate + wire_grid (ndarray): grid for positioning wires + plot_params (dict): parameter for the figure + """ -def enumerate_gates(l, schedule=False): - "Enumerate the gates in a way that can take l as either a list of gates or a schedule" - if schedule: - for i, gates in enumerate(l): - for gate in gates: - yield i, gate - else: - for i, gate in enumerate(l): - yield i, gate - return - - -def measured_wires(l, labels, schedule=False): - "measured[i] = j means wire i is measured at step j" - # schedule is always false...not implemented. - measured = {} - for i, gate in enumerate_gates(l, schedule=schedule): - name, target = gate[:2] - j = get_flipped_index(target, labels) - if name.startswith('M'): - measured[j] = i - return measured - - -def draw_gates(ax, l, labels, gate_grid, wire_grid, plot_params, measured={}, schedule=False): + # initialize the position of gates as 0 for each label x_labels = {label: 0 for label in labels} x_position = 0 - for i, gate in enumerate_gates(l, schedule=schedule): - if len(gate) > 2: # Controlled + + for i, gate in enumerate(gates): + if len(gate) > 2: # case: multi-control or target gate + # it only works for single target gate qb_target = gate[1] qb_control = gate[2] x_position = max(x_labels[qb_target], x_labels[qb_control]) - draw_controls(ax, x_position, gate, labels, gate_grid, wire_grid, plot_params, measured) + draw_controls(ax, x_position, gate, labels, + gate_grid, wire_grid, plot_params) x_labels[qb_target] = x_position - draw_target(ax, x_labels[qb_target], gate, labels, gate_grid, wire_grid, plot_params) + draw_target(ax, x_labels[qb_target], gate, labels, + gate_grid, wire_grid, plot_params) - # all the x betweeen control and target has to be added 1...for multi. qubit. + # get the index of qubit between control and target qubit begin = min(qb_control, qb_target) end = max(qb_control, qb_target) + # update the position by adding 1 for itr, value in x_labels.items(): if begin <= itr <= end: x_labels[itr] = x_position + 1 - else: qb = gate[1] - draw_target(ax, x_labels[qb], gate, labels, gate_grid, wire_grid, plot_params) + draw_target(ax, x_labels[qb], gate, labels, + gate_grid, wire_grid, plot_params) x_labels[qb] = x_labels[qb] + 1 - return - +def draw_controls(ax, i, gate, labels, gate_grid, wire_grid, plot_params): + """ + draw the control qubit gate + Args: + ax (AxesSubplot): axes object + i (int): position of the control gate + gate (tuple): control qubit gate + labels (list): contains qubits' label + gate_grid (ndarray): grid for positioning gate + wire_grid (ndarray): grid for positioning wires + plot_params (dict): parameter for the figure + """ -def draw_controls(ax, i, gate, labels, gate_grid, wire_grid, plot_params, measured={}): linewidth = plot_params['linewidth'] scale = plot_params['scale'] control_radius = plot_params['control_radius'] @@ -124,38 +123,40 @@ def draw_controls(ax, i, gate, labels, gate_grid, wire_grid, plot_params, measur name, target = gate[:2] target_index = get_flipped_index(target, labels) - # what about multi control + # include multi-control gate controls = gate[2:] control_indices = get_flipped_indices(controls, labels) gate_indices = control_indices + [target_index] min_wire = min(gate_indices) max_wire = max(gate_indices) - line(ax, gate_grid[i], gate_grid[i], wire_grid[min_wire], wire_grid[max_wire], plot_params) - - ismeasured = False - for index in control_indices: - # what is this used for??? - if measured.get(index, 1000) < i: - ismeasured = True - if ismeasured: - dy = 0.04 # TODO: put in plot_params - line(ax, gate_grid[i] + dy, gate_grid[i] + dy, wire_grid[min_wire], wire_grid[max_wire], plot_params) + line(ax, gate_grid[i], gate_grid[i], + wire_grid[min_wire], wire_grid[max_wire], plot_params) for ci in control_indices: x = gate_grid[i] y = wire_grid[ci] - if name in ['SWAP']: + if name == 'SWAP': swapx(ax, x, y, plot_params) else: cdot(ax, x, y, plot_params) - return - def draw_target(ax, i, gate, labels, gate_grid, wire_grid, plot_params): + """ + draw the target gate in figure + Args: + ax (AxesSubplot): axes object + i (int): position of the target gate + gate (tuple): control qubit gate + labels (list): contains qubits' label + gate_grid (ndarray): grid for positioning gate + wire_grid (ndarray): grid for positioning wires + plot_params (dict): parameter for the figure + """ target_symbols = dict(CNOT='X', CPHASE='Z', NOP='', CX='X', CZ='Z') name, target = gate[:2] - symbol = target_symbols.get(name, name) # override name with target_symbols, get(keyname,value) + # override name with target_symbols, get(keyname,value) + symbol = target_symbols.get(name, name) if symbol in ['X'] and len(gate) >= 3: name = 'CNOT' @@ -167,77 +168,128 @@ def draw_target(ax, i, gate, labels, gate_grid, wire_grid, plot_params): if not symbol: return if name in ['CNOT', 'TOFFOLI']: oplus(ax, x, y, plot_params) - elif name in ['CPHASE']: + elif name == 'CPHASE': cdot(ax, x, y, plot_params) - elif name in ['SWAP']: + elif name == 'SWAP': swapx(ax, x, y, plot_params) - elif name in ['M']: + elif name == 'Measure': draw_mwires(ax, x, y, gate_grid, wire_grid, plot_params) + symbol = 'M' # it should be updated by new measurement symbol + text(ax, x, y, symbol, plot_params, box=True) else: text(ax, x, y, symbol, plot_params, box=True) - return - def line(ax, x1, x2, y1, y2, plot_params): - Line2D = matplotlib.lines.Line2D + """ + draw line in the plot, begin at (x1, y1) and end at (x2, y2) + Args: + ax (AxesSubplot): axes object + x1 (float): x_1 coordinate + x2 (float): x_2 coordinate + y1 (float): y_1 coordinate + y2 (float): y_2 coordinate + plot_params (dict): parameter for the figure + """ line = Line2D((x1, x2), (y1, y2), color='k', lw=plot_params['linewidth']) ax.add_line(line) def text(ax, x, y, textstr, plot_params, box=False): + """ + draw the name of gate or qubit and draw the rectangle box at (x, y) + Args: + ax (AxesSubplot): axes object + x (float): x coordinate + y (float): y coordinate + textstr (str): text of the gate and box + plot_params (dict): parameter for the text + box (bool): draw the rectangle box if box is True + """ linewidth = plot_params['linewidth'] fontsize = plot_params['fontsize'] if box: - bbox = dict(ec='k', fc='w', fill=True, lw=linewidth) # draw gate box + # draw gate box + bbox = dict(ec='k', fc='w', fill=True, lw=linewidth) else: - bbox = dict(ec='w', fc='w', fill=False, lw=linewidth) # draw the qubit box - ax.text(x, y, textstr, color='k', ha='center', va='center', bbox=bbox, size=fontsize) - return - + # draw the qubit box + bbox = dict(ec='w', fc='w', fill=False, lw=linewidth) + # draw the text + ax.text(x, y, textstr, color='k', ha='center', va='center', + bbox=bbox, size=fontsize) def oplus(ax, x, y, plot_params): - Line2D = matplotlib.lines.Line2D - Circle = matplotlib.patches.Circle + """ + Draw the Symbol for control gate + Args: + ax (AxesSubplot): axes object + x (float): x coordinate + y (float): y coordinate + plot_params (dict): parameter for the text + """ not_radius = plot_params['not_radius'] linewidth = plot_params['linewidth'] + c = Circle((x, y), not_radius, ec='k', fc='w', fill=False, lw=linewidth) ax.add_patch(c) - line(ax, x, x, y - not_radius, y + not_radius, plot_params) - return + line(ax, x, x, y - not_radius, y + not_radius, plot_params) def cdot(ax, x, y, plot_params): - Circle = matplotlib.patches.Circle + """ + draw the control dot for control gate + Args: + ax (AxesSubplot): axes object + x (float): x coordinate + y (float): y coordinate + plot_params (dict): parameter for the text + """ control_radius = plot_params['control_radius'] scale = plot_params['scale'] linewidth = plot_params['linewidth'] + c = Circle((x, y), control_radius * scale, ec='k', fc='k', fill=True, lw=linewidth) ax.add_patch(c) - return - def swapx(ax, x, y, plot_params): + """ + draw the SwapX symbol + Args: + ax (AxesSubplot): axes object + x (float): x coordinate + y (float): y coordinate + plot_params (dict): parameter for the text + """ d = plot_params['swap_delta'] linewidth = plot_params['linewidth'] line(ax, x - d, x + d, y - d, y + d, plot_params) line(ax, x - d, x + d, y + d, y - d, plot_params) - return - -def setup_figure(nq, ng, gate_grid, wire_grid, plot_params): +def setup_figure(n_labels, n_gates, gate_grid, wire_grid, plot_params): + """ + Create the figure and set up the parameter of figure + Args: + n_labels (int): number of labels representing qubits + n_gates (int): number of gates to be drawed + gate_grid (ndarray): grid for positioning gates + wire_grid (ndarray): grid for positioning wires + plot_params (dict): parameter for the figure + Returns: + return the Figure and AxesSubplot object + """ scale = plot_params['scale'] fig = plt.figure( - figsize=(ng * scale, nq * scale), + figsize=(n_gates * scale, n_labels * scale), facecolor='w', edgecolor='w' ) - ax = fig.add_subplot(1, 1, 1, frameon=True) + + ax = plt.subplot() ax.set_axis_off() offset = 0.5 * scale ax.set_xlim(gate_grid[0] - offset, gate_grid[-1] + offset) @@ -245,68 +297,102 @@ def setup_figure(nq, ng, gate_grid, wire_grid, plot_params): ax.set_aspect('equal') return fig, ax - -def draw_wires(ax, nq, gate_grid, wire_grid, plot_params, measured={}): +def draw_wires(ax, n_labels, gate_grid, wire_grid, plot_params): + """ + draw the circuit wire + Args: + ax (AxesSubplot): axes object + n_labels (int): number of qubit + gate_grid (ndarray): grid for positioning gates + wire_grid (ndarray): grid for positioning wires + plot_params (dict): parameter for the figure + """ scale = plot_params['scale'] linewidth = plot_params['linewidth'] xdata = (gate_grid[0] - scale, gate_grid[-1] + scale) - for i in range(nq): - line(ax, gate_grid[0] - scale, gate_grid[-1] + scale, wire_grid[i], wire_grid[i], plot_params) - return + for i in range(n_labels): + line(ax, gate_grid[0] - scale, gate_grid[-1] + scale, + wire_grid[i], wire_grid[i], plot_params) def draw_mwires(ax, x, y, gate_grid, wire_grid, plot_params): - # Add the doubling for measured wires: + """ + Add the doubling for measured wires + Args: + ax (AxesSubplot): axes object + x (float): x coordinate + y (float): y coordinate + gate_grid (ndarray): grid for positioning gate + wire_grid (ndarray): grid for positioning wires + plot_params (dict): parameter for the figure + """ scale = plot_params['scale'] - dy = 0.04 # TODO: add to plot_params + dy = plot_params['linebetween'] + # gate_grid indicate x-axes line(ax, x, gate_grid[-1] + scale, y + dy, y + dy, plot_params) - # wired_grid indicate which qubit it belongs to - # gate_grid is the x-axes, x2=grid_grid[-1], so it will always draw the line to the end. - return - - def draw_labels(ax, labels, inits, gate_grid, wire_grid, plot_params): + """ + draw the qubit label + Args: + ax (AxesSubplot): axes object + labels (list): labels of the qubit to be drawed + inits (list): Initialization of qubits + gate_grid (ndarray): grid for positioning gate + wire_grid (ndarray): grid for positioning wires + plot_params (dict): parameter for the figure + """ scale = plot_params['scale'] label_buffer = plot_params['label_buffer'] fontsize = plot_params['fontsize'] - nq = len(labels) + n_labels = len(labels) + if inits is None: + inits = {label: 0 for label in labels} xdata = (gate_grid[0] - scale, gate_grid[-1] + scale) - for i in range(nq): + for i in range(n_labels): j = get_flipped_index(labels[i], labels) - text(ax, xdata[0] - label_buffer, wire_grid[j], render_label(labels[i], inits), plot_params) - return - + text(ax, xdata[0] - label_buffer, wire_grid[j], + render_label(labels[i], inits), plot_params) def get_flipped_index(target, labels): - """Get qubit labels from the rest of the line,and return indices + """ + flip the index of the target qubit in order to match the coordination >>> get_flipped_index('q0', ['q0', 'q1']) 1 >>> get_flipped_index('q1', ['q0', 'q1']) 0 + + Args: + target (str): target qubit + labels (list): contains all labels of qubits """ - nq = len(labels) - i = labels.index(target) - return nq - i - 1 + n_labels = len(labels) + i = labels.index(target) -def get_flipped_indices(targets, labels): return [get_flipped_index(t, labels) for t in targets] + return n_labels - i - 1 +def get_flipped_indices(targets, labels): + """ + flip the index of the target qubit for multi targets + Args: + target (str): target qubit + labels (list): contains all labels of qubits + """ + return [get_flipped_index(t, labels) for t in targets] def render_label(label, inits={}): - """Slightly more flexible way to render labels. - - >>> render_label('q0') - '$|q0\\\\rangle$' - >>> render_label('q0', {'q0':'0'}) - '$|0\\\\rangle$' + """ + render qubit label as |0> + Args: + label: label of the qubit + inits (list): initial qubits """ if label in inits: s = inits[label] if s is None: return '' - else: - return r'$|%s\rangle$' % inits[label] - return r'$|%s\rangle$' % label + return r'$|{}\rangle$'.format(s) + return r'$|{}\rangle$'.format(label) From 5e46daeb826c279fba6559b5731e3edd47e9adf8 Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Fri, 11 Oct 2019 15:44:20 +0200 Subject: [PATCH 09/37] fix measurement gate --- projectq/backends/_circuits/_plot.py | 33 +++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/projectq/backends/_circuits/_plot.py b/projectq/backends/_circuits/_plot.py index 47906179e..19d68635a 100644 --- a/projectq/backends/_circuits/_plot.py +++ b/projectq/backends/_circuits/_plot.py @@ -17,6 +17,7 @@ import numpy as np from matplotlib.lines import Line2D from matplotlib.patches import Circle +from matplotlib.patches import Arc def to_draw(gates,labels=[],inits={},plot_labels=True,**kwargs): """ @@ -174,13 +175,39 @@ def draw_target(ax, i, gate, labels, gate_grid, wire_grid, plot_params): swapx(ax, x, y, plot_params) elif name == 'Measure': draw_mwires(ax, x, y, gate_grid, wire_grid, plot_params) + + measure(ax, x, y, plot_params) - symbol = 'M' # it should be updated by new measurement symbol - - text(ax, x, y, symbol, plot_params, box=True) else: text(ax, x, y, symbol, plot_params, box=True) +def measure(ax, x, y, plot_params): + """ + drawing the measure gate + Args: + ax (AxesSubplot): axes object + x (float): x coordinate + y (float): y coordinate + plot_params: + + Returns: + + """ + HIG = 0.65 + WID = 0.65 + s = ''.ljust(5) + + # add box + text(ax, x, y, s, plot_params, box=True) + # add measure symbol + arc = Arc(xy=(x, y - 0.15 * HIG), width=WID * 0.7, + height=HIG * 0.7, theta1=0, theta2=180, + fill=False, linewidth=1,zorder=5) + ax.add_patch(arc) + ax.plot([x, x + 0.35 * WID], + [y - 0.15 * HIG, y + 0.20 * HIG], color='k', + linewidth=1, zorder=5) + def line(ax, x1, x2, y1, y2, plot_params): """ draw line in the plot, begin at (x1, y1) and end at (x2, y2) From 2082351c6100167a49246189e61cd413c4956f2a Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Fri, 11 Oct 2019 15:48:14 +0200 Subject: [PATCH 10/37] Delete unrelated files. --- tutorials/Basic_qp1.ipynb | 82 ------------- tutorials/Bernstein-Vazirani Algorithm.ipynb | 118 ------------------- tutorials/Bernstein-Vazirani algorithm.tex | 25 ---- 3 files changed, 225 deletions(-) delete mode 100644 tutorials/Basic_qp1.ipynb delete mode 100644 tutorials/Bernstein-Vazirani Algorithm.ipynb delete mode 100644 tutorials/Bernstein-Vazirani algorithm.tex diff --git a/tutorials/Basic_qp1.ipynb b/tutorials/Basic_qp1.ipynb deleted file mode 100644 index 6fee9e1a4..000000000 --- a/tutorials/Basic_qp1.ipynb +++ /dev/null @@ -1,82 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "from projectq import MainEngine\n", - "from projectq.ops import H,Measure" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(Note: This is the (slow) Python simulator.)\n" - ] - } - ], - "source": [ - "eng = MainEngine()\n", - "qubit = eng.allocate_qubit()\n", - "H | qubit\n", - "\n", - "Measure | qubit\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Measured:0\n" - ] - } - ], - "source": [ - "eng.flush()\n", - "print('Measured:{}'.format(int(qubit)))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tutorials/Bernstein-Vazirani Algorithm.ipynb b/tutorials/Bernstein-Vazirani Algorithm.ipynb deleted file mode 100644 index c9d74106c..000000000 --- a/tutorials/Bernstein-Vazirani Algorithm.ipynb +++ /dev/null @@ -1,118 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Goal: find the secret bit-string s\n", - "\n", - "from projectq import MainEngine\n", - "from projectq.ops import *\n", - "from projectq.meta import Compute, Uncompute\n", - "from projectq.backends import CircuitDrawer\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# set the secretnumber\n", - "secretnumber = '10111001001'\n", - "n = len(secretnumber)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(Note: This is the (slow) Python simulator.)\n" - ] - } - ], - "source": [ - "# initiate circuit\n", - "# drawing_engine = CircuitDrawer()\n", - "# eng = MainEngine(backend = drawing_engine)\n", - "eng = MainEngine()\n", - "qureg = eng.allocate_qureg(n + 1)\n", - "\n", - "# implement Algorithm\n", - "with Compute(eng):\n", - " X | qureg[n]\n", - " Barrier | qureg\n", - " All(H) | qureg\n", - "\n", - "Barrier | qureg\n", - "\n", - "for ii, yesno in enumerate(secretnumber):\n", - " if yesno == '1':\n", - " CNOT | (qureg[ii],qureg[n]) # different display sequence vs. Qiskit\n", - "\n", - "Barrier | qureg\n", - "\n", - "Uncompute(eng)\n", - "for i in range(n):\n", - " Measure | qureg[i]\n", - "\n", - "eng.flush()" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Measured [1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1]\n" - ] - } - ], - "source": [ - "print(\"Measured {}\".format([ int(qubit) for qubit in qureg[:-1] ] ))\n", - "\n", - "with open('Bernstein-Vazirani algorithm.tex','w') as fd:\n", - " fd.write(drawing_engine.get_latex())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tutorials/Bernstein-Vazirani algorithm.tex b/tutorials/Bernstein-Vazirani algorithm.tex deleted file mode 100644 index fba373517..000000000 --- a/tutorials/Bernstein-Vazirani algorithm.tex +++ /dev/null @@ -1,25 +0,0 @@ -\documentclass{standalone} -\usepackage[margin=1in]{geometry} -\usepackage[hang,small,bf]{caption} -\usepackage{tikz} -\usepackage{braket} -\usetikzlibrary{backgrounds,shadows.blur,fit,decorations.pathreplacing,shapes} - -\begin{document} -\begin{tikzpicture}[scale=0.8, transform shape] - -\tikzstyle{basicshadow}=[blur shadow={shadow blur steps=8, shadow xshift=0.7pt, shadow yshift=-0.7pt, shadow scale=1.02}]\tikzstyle{basic}=[draw,fill=white,basicshadow] -\tikzstyle{operator}=[basic,minimum size=1.5em] -\tikzstyle{phase}=[fill=black,shape=circle,minimum size=0.1cm,inner sep=0pt,outer sep=0pt,draw=black] -\tikzstyle{none}=[inner sep=0pt,outer sep=-.5pt,minimum height=0.5cm+1pt] -\tikzstyle{measure}=[operator,inner sep=0pt,minimum height=0.5cm, minimum width=0.75cm] -\tikzstyle{xstyle}=[circle,basic,minimum height=0.35cm,minimum width=0.35cm,inner sep=-1pt,very thin] -\tikzset{ -shadowed/.style={preaction={transform canvas={shift={(0.5pt,-0.5pt)}}, draw=gray, opacity=0.4}}, -} -\tikzstyle{swapstyle}=[inner sep=-1pt, outer sep=-1pt, minimum width=0pt] -\tikzstyle{edgestyle}=[very thin] - - -\end{tikzpicture} -\end{document} \ No newline at end of file From 66e7613c0f77872688827c513af9badd1a371e6a Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Mon, 14 Oct 2019 11:42:49 +0200 Subject: [PATCH 11/37] fix Toffoli gate position issue and change the qubit position from 'str' to 'int' --- projectq/backends/_circuits/_drawer.py | 6 ++--- projectq/backends/_circuits/_plot.py | 32 +++++++++++++++++--------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/projectq/backends/_circuits/_drawer.py b/projectq/backends/_circuits/_drawer.py index 621d82e80..b2a746961 100755 --- a/projectq/backends/_circuits/_drawer.py +++ b/projectq/backends/_circuits/_drawer.py @@ -136,11 +136,11 @@ def receive(self, command_list): l = [] g = str(cmd.gate) for q in cmd.qubits: - l.append(str(q[0])) + l.append(q[0].id) # assume single target, 1st. element of q is the target qubit. if len(cmd.control_qubits) > 0: for cq in cmd.control_qubits: - l.append(str(cq)) + l.append(cq.id) listOfStrings = ['', 'Allocate'] @@ -157,7 +157,7 @@ def draw(self): """ Returns the plot of the quantum circuit """ - qubits = [str(self._map[id]) for id in self._map] + qubits = [self._map[id] for id in self._map] # extract all the allocated qubits from the circuit return to_draw(self._gates, qubits) diff --git a/projectq/backends/_circuits/_plot.py b/projectq/backends/_circuits/_plot.py index 19d68635a..c0d83a6ad 100644 --- a/projectq/backends/_circuits/_plot.py +++ b/projectq/backends/_circuits/_plot.py @@ -38,9 +38,11 @@ def to_draw(gates,labels=[],inits={},plot_labels=True,**kwargs): plot_params.update(kwargs) scale = plot_params['scale'] + if len(inits) == 0: + inits = {label: 0 for label in labels} + n_labels = len(labels) n_gates = len(gates) - inits = {label: 0 for label in labels} # create grid for the plot wire_grid = np.arange(0.0, n_labels * scale, scale, dtype=float) @@ -78,7 +80,22 @@ def draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params): qb_target = gate[1] qb_control = gate[2] - x_position = max(x_labels[qb_target], x_labels[qb_control]) + # get the index of qubit between control and target qubit + begin = min(qb_control, qb_target) + end = max(qb_control, qb_target) + + # check the max position between control and target gate + MaxPosition = max(x_labels[qb_target], x_labels[qb_control]) + CheckMax = False + for x in range(begin, end + 1): + if x_labels[x] > MaxPosition: + CheckMax = True + break + if CheckMax: + x_position = max(x_labels.values()) + else: + x_position = max(x_labels[qb_target], x_labels[qb_control]) + draw_controls(ax, x_position, gate, labels, gate_grid, wire_grid, plot_params) @@ -86,10 +103,6 @@ def draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params): draw_target(ax, x_labels[qb_target], gate, labels, gate_grid, wire_grid, plot_params) - # get the index of qubit between control and target qubit - begin = min(qb_control, qb_target) - end = max(qb_control, qb_target) - # update the position by adding 1 for itr, value in x_labels.items(): if begin <= itr <= end: @@ -120,7 +133,7 @@ def draw_controls(ax, i, gate, labels, gate_grid, wire_grid, plot_params): control_radius = plot_params['control_radius'] # what about multi target, can't set 2 here.. - # make a case, specifically for multi target gate.. + # ToDo: make a case, specifically for multi target gate.. name, target = gate[:2] target_index = get_flipped_index(target, labels) @@ -188,10 +201,7 @@ def measure(ax, x, y, plot_params): ax (AxesSubplot): axes object x (float): x coordinate y (float): y coordinate - plot_params: - - Returns: - + plot_params (dict): parameter for the figure """ HIG = 0.65 WID = 0.65 From f33dcdd8ad607a64ffe782aef71e887557addaea Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Tue, 22 Oct 2019 09:53:13 +0200 Subject: [PATCH 12/37] Pytest for drawer_mpl --- projectq/tests/_drawmpl_test.py | 39 ++++++++++++++++++++ projectq/tests/baseline/test_drawer_mpl.png | Bin 0 -> 15283 bytes 2 files changed, 39 insertions(+) create mode 100644 projectq/tests/_drawmpl_test.py create mode 100644 projectq/tests/baseline/test_drawer_mpl.png diff --git a/projectq/tests/_drawmpl_test.py b/projectq/tests/_drawmpl_test.py new file mode 100644 index 000000000..eccae2314 --- /dev/null +++ b/projectq/tests/_drawmpl_test.py @@ -0,0 +1,39 @@ +# Copyright 2017 ProjectQ-Framework (www.projectq.ch) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest +from projectq import MainEngine +from projectq.ops import * +from projectq.backends import CircuitDrawerMatplotlib + +''' + To generate the baseline images, run the tests with '--mpl-generate-path' + hen run the tests simply with '--mpl' +''' +@pytest.mark.mpl_image_compare +def test_drawer_mpl(): + drawer = CircuitDrawerMatplotlib() + eng = MainEngine(engine_list=[drawer]) + ctrl = eng.allocate_qureg(2) + qureg = eng.allocate_qureg(3) + + CNOT | (qureg[0], qureg[2]) + Rx(1.0) | qureg[0] + CNOT | (qureg[1], qureg[2]) + C(X, 2) | (ctrl[0], ctrl[1], qureg[2]) + All(Measure) | qureg + + eng.flush() + fig, ax = drawer.draw() + return fig \ No newline at end of file diff --git a/projectq/tests/baseline/test_drawer_mpl.png b/projectq/tests/baseline/test_drawer_mpl.png new file mode 100644 index 0000000000000000000000000000000000000000..1ed3930785e97ce5dbbcaf2c581b3b32593de026 GIT binary patch literal 15283 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 literal 0 HcmV?d00001 From 9a00d161dbb9bd09dcbb4b5d5807cfcf4a10cb25 Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Tue, 22 Oct 2019 16:58:40 +0200 Subject: [PATCH 13/37] Tests for _plot function --- projectq/backends/_circuits/_plot.py | 24 +++-- projectq/backends/_circuits/_plot_test.py | 90 ++++++++++++++++++ .../_circuits/baseline/test_complex_CNOT.png | Bin 0 -> 3096 bytes .../_circuits/baseline/test_complex_CNOT2.png | Bin 0 -> 2963 bytes .../baseline/test_draw_multi_gates.png | Bin 0 -> 4562 bytes .../baseline/test_draw_single_gates.png | Bin 0 -> 2442 bytes .../baseline/test_gates_position.png | Bin 0 -> 2440 bytes .../baseline/test_gates_position2.png | Bin 0 -> 8034 bytes .../_circuits/baseline/test_measure_gate.png | Bin 0 -> 1840 bytes .../_circuits/baseline/test_qubit_numbers.png | Bin 0 -> 6099 bytes .../_circuits/baseline/test_simple_CNOT.png | Bin 0 -> 2362 bytes projectq/tests/_drawmpl_test.py | 10 +- 12 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 projectq/backends/_circuits/_plot_test.py create mode 100644 projectq/backends/_circuits/baseline/test_complex_CNOT.png create mode 100644 projectq/backends/_circuits/baseline/test_complex_CNOT2.png create mode 100644 projectq/backends/_circuits/baseline/test_draw_multi_gates.png create mode 100644 projectq/backends/_circuits/baseline/test_draw_single_gates.png create mode 100644 projectq/backends/_circuits/baseline/test_gates_position.png create mode 100644 projectq/backends/_circuits/baseline/test_gates_position2.png create mode 100644 projectq/backends/_circuits/baseline/test_measure_gate.png create mode 100644 projectq/backends/_circuits/baseline/test_qubit_numbers.png create mode 100644 projectq/backends/_circuits/baseline/test_simple_CNOT.png diff --git a/projectq/backends/_circuits/_plot.py b/projectq/backends/_circuits/_plot.py index c0d83a6ad..f9e4296b1 100644 --- a/projectq/backends/_circuits/_plot.py +++ b/projectq/backends/_circuits/_plot.py @@ -33,7 +33,7 @@ def to_draw(gates,labels=[],inits={},plot_labels=True,**kwargs): **kwargs (dict): Can override plot_parameters """ plot_params = dict(scale=1.0, fontsize=14.0, linewidth=1.0, - linebetween=0.04,control_radius=0.05, not_radius=0.15, + linebetween=0.06,control_radius=0.05, not_radius=0.15, swap_delta=0.08, label_buffer=0.0) plot_params.update(kwargs) scale = plot_params['scale'] @@ -47,6 +47,8 @@ def to_draw(gates,labels=[],inits={},plot_labels=True,**kwargs): # create grid for the plot wire_grid = np.arange(0.0, n_labels * scale, scale, dtype=float) gate_grid = np.arange(0.0, n_gates * scale, scale, dtype=float) + if len(gate_grid) == 0: + gate_grid = wire_grid fig, ax = setup_figure(n_labels, n_gates, gate_grid, wire_grid, plot_params) @@ -56,7 +58,7 @@ def to_draw(gates,labels=[],inits={},plot_labels=True,**kwargs): draw_labels(ax, labels, inits, gate_grid, wire_grid, plot_params) draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params) - return ax + return fig, ax def draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params): """ @@ -205,7 +207,7 @@ def measure(ax, x, y, plot_params): """ HIG = 0.65 WID = 0.65 - s = ''.ljust(5) + s = ''.ljust(2) # define box size # add box text(ax, x, y, s, plot_params, box=True) @@ -320,15 +322,21 @@ def setup_figure(n_labels, n_gates, gate_grid, wire_grid, plot_params): return the Figure and AxesSubplot object """ scale = plot_params['scale'] + width = n_gates * scale + height = n_labels * scale + if width == 0: + width = height + fig = plt.figure( - figsize=(n_gates * scale, n_labels * scale), + figsize=(width, height), facecolor='w', edgecolor='w' ) ax = plt.subplot() ax.set_axis_off() - offset = 0.5 * scale + offset = scale + ax.set_xlim(gate_grid[0] - offset, gate_grid[-1] + offset) ax.set_ylim(wire_grid[0] - offset, wire_grid[-1] + offset) ax.set_aspect('equal') @@ -346,10 +354,10 @@ def draw_wires(ax, n_labels, gate_grid, wire_grid, plot_params): """ scale = plot_params['scale'] linewidth = plot_params['linewidth'] - xdata = (gate_grid[0] - scale, gate_grid[-1] + scale) + x_pos = (gate_grid[0] - 0.5 * scale, gate_grid[-1] + 2 * scale) for i in range(n_labels): - line(ax, gate_grid[0] - scale, gate_grid[-1] + scale, + line(ax, x_pos[0], x_pos[-1], wire_grid[i], wire_grid[i], plot_params) def draw_mwires(ax, x, y, gate_grid, wire_grid, plot_params): @@ -367,7 +375,7 @@ def draw_mwires(ax, x, y, gate_grid, wire_grid, plot_params): dy = plot_params['linebetween'] # gate_grid indicate x-axes - line(ax, x, gate_grid[-1] + scale, y + dy, y + dy, plot_params) + line(ax, x, gate_grid[-1] + 2 * scale, y + dy, y + dy, plot_params) def draw_labels(ax, labels, inits, gate_grid, wire_grid, plot_params): """ diff --git a/projectq/backends/_circuits/_plot_test.py b/projectq/backends/_circuits/_plot_test.py new file mode 100644 index 000000000..19536abca --- /dev/null +++ b/projectq/backends/_circuits/_plot_test.py @@ -0,0 +1,90 @@ +# Copyright 2017 ProjectQ-Framework (www.projectq.ch) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" + Tests for projectq.backends._circuits._plot.py. + To generate the baseline images, run the tests with '--mpl-generate-path' + Then run the tests simply with '--mpl' +""" +import pytest +from projectq import MainEngine +from projectq.ops import * +from projectq.backends import CircuitDrawerMatplotlib + +import projectq.backends._circuits._plot as _plot + +@pytest.mark.mpl_image_compare +def test_draw_single_gates(): + allocate_qubit = [0,1,2,3] + gates = [('H',0)] + fig, ax = _plot.to_draw(gates, allocate_qubit) + return fig + +@pytest.mark.mpl_image_compare +def test_draw_multi_gates(): + allocate_qubit = [0,1,2,3] + gates = [('H',0), ('H',0)] + fig, ax = _plot.to_draw(gates, allocate_qubit) + return fig + +@pytest.mark.mpl_image_compare +def test_gates_position(): + allocate_qubit = [0,1,2,3] + gates = [('H',3)] + fig, ax = _plot.to_draw(gates, allocate_qubit) + return fig + +@pytest.mark.mpl_image_compare +def test_gates_position2(): + allocate_qubit = [0,1,2,3] + gates = [('H',3), ('X',1,0), ('H',2), ('H',2),('H',2),('X',3,0)] + fig, ax = _plot.to_draw(gates, allocate_qubit) + return fig + +@pytest.mark.mpl_image_compare +def test_simple_CNOT(): + allocate_qubit = [0,1] + gates = [('X',1,0)] + fig, ax = _plot.to_draw(gates, allocate_qubit) + return fig + +@pytest.mark.mpl_image_compare +def test_complex_CNOT(): + allocate_qubit = [0,1,2,3] + gates = [('X',3,0)] + fig, ax = _plot.to_draw(gates, allocate_qubit) + return fig + +@pytest.mark.mpl_image_compare +def test_complex_CNOT2(): + allocate_qubit = [0,1,2,3] + gates = [('X',0,3)] + fig, ax = _plot.to_draw(gates, allocate_qubit) + return fig + +@pytest.mark.mpl_image_compare +def test_qubit_numbers(): + # set up qubit numbers without quantum gates + allocate_qureg = [0, 1, 2, 3, 4] + gates = [] + fig, ax = _plot.to_draw(gates, allocate_qureg) + return fig + +@pytest.mark.mpl_image_compare +def test_measure_gate(): + # set up qubit numbers without quantum gates + allocate_qureg = [0] + gates = [('Measure',0)] + fig, ax = _plot.to_draw(gates, allocate_qureg) + return fig \ No newline at end of file diff --git a/projectq/backends/_circuits/baseline/test_complex_CNOT.png b/projectq/backends/_circuits/baseline/test_complex_CNOT.png new file mode 100644 index 0000000000000000000000000000000000000000..ba6c9365c7ed14dfd07ffceba0a78d139b9b43ed GIT binary patch literal 3096 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 Q_ZJA_s*x_?^6iIz1K14!%K!iX literal 0 HcmV?d00001 diff --git a/projectq/backends/_circuits/baseline/test_draw_single_gates.png b/projectq/backends/_circuits/baseline/test_draw_single_gates.png new file mode 100644 index 0000000000000000000000000000000000000000..1c8d4878d1e8a696be61e973e7cda2c29dc4ae50 GIT binary patch literal 2442 zcmeHJX;4$=8cvHMm8KNbumnh?Qk7LiWt9{p#jsfd7Q!?QixN_nkcCY#c*{k=L;*3f zNXQt4l%*h|U?Gswl%Q8j3}Qqm!~)Gi2oVAZk>wuRncLpp&fJ;)?wL9Bo%5XYKJW8A z@3$3w5*h&BZm}H%0)bJ-{n0>s3Iuwq#6TZ-in`XP0h%ooKU9PPaOD`BDg@r&Njr|A zfIvpEoA+B^A-Dtpbf+StsNuu}YGy3?G$ z{>O^tZ##Q?Ev=^4Pd|HoH9I0QG9WWEb6j*1jW%&~bgbC- z0IpW6#baYurp});rl+TQEN*SBt)v+X1#o@yO{l^pUYLdxmIO{+*VP7l%!iwu70j|8yOj~VFvaAc_=>e@$R#w zfwggh3*ciJSpFB^I%BM?tXy)}SBLMD;w=$0Z$Kf3%iSw!!j#SZ*wE0h&)%M1U<8w? z;|zPx`+kv>6#sppacj=Xd`q!*9!fz<@ zkL{Q`-SAL-c>L6ZQqtn|GlpEm@^Y*3&Nd;A^HA%7hW-O^7=p%-NF)^pV*U92;q?&= z7{X^(%4aX8k9H7?UGW2Z&N8>|kwXRT?d^qyfSyOoZG(e@S#Oq7B+tK&zGrvY$_uj1 z$eO|5Sof-IwEQS3Daq1}#O0d-#j-W;W47}0CJu*_Tk|SX7JlZ;8BRxso7@-h-cl}N!>+hp zG=w-5QaxFVY?(~PVR`X#3whSBP)kCFbe#E`uE{)MV{Q2F388gGjYtdq{xO5JW#bRAZ6W>>FTYL4>G4HcW4t2qm z2VXuM9T|xODxcT+BqzEnBV}CVGTZ|3$h*p9_AMx%PWI7F*Q_;4Wl^~^PI$#Oh-|$u z^P;Vl@U)JGaKeY5Jjq<--peJ@jhH!7*@n5^I1R_kA_?DIs;jG00AWn~$b@qtL?ZFC zo*WMqv|qE!)bxYYma(Xud2CHZ=8`dMRQa`LKfxY89LryQ(x0SedDvRPjw9fBm0 zNF3AXefq0VbXb^iN9x!`L4i@fdbDG5tSd7tI@&@$z)b90mZ6-QE{(FG32i?({CPe;s*cYDE^9lx1?I2w>w7LQ?z;S@gHCg zh3KHF=Pf4?=X47yKFmE0j>TXqXH}{~28mjVG# zI#_&&FTqtJaeUg*$pSra=l~p1h2-aM_^4DpETl@lw$!FTfXR@xy<`a36&OPgFx1_@ zMhY>99uvLj>){^s3#4DT03_H3M!ugl7q_su*w>k^x^lnnOIge|q0zs3|D@ml4FO^R z0!CY+8D4x~fJ5kEO?lF-X@C#aNC99GkfV$On{RAWR8>_C^Rl()i94}BC0Ix>ckO=+ z7I@M#@~;!3@Z#n)SFp+LLpfWNB)DrMw r{@!0y|EBe?X8n6p{r&NxVnfe*U%O$UUOyZ-H-S*dQ2%DXm@oeguD&S3 literal 0 HcmV?d00001 diff --git a/projectq/backends/_circuits/baseline/test_gates_position.png b/projectq/backends/_circuits/baseline/test_gates_position.png new file mode 100644 index 0000000000000000000000000000000000000000..070df4677356b25f7252812a7ff8c948113256d9 GIT binary patch literal 2440 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 V^YyQ_VNd}c*jn2o%B?(-{sWoi%KiWV literal 0 HcmV?d00001 diff --git a/projectq/backends/_circuits/baseline/test_simple_CNOT.png b/projectq/backends/_circuits/baseline/test_simple_CNOT.png new file mode 100644 index 0000000000000000000000000000000000000000..ef7440144438200af9b10691fb0909ca3fb79942 GIT binary patch literal 2362 zcmdT`dpy(YAOBWzo5prrQd*;Qk%ndz8Z+07a!F@M&2hhN*;p=-+?CrQ!%=R(V@wHK z$;NM%X>_DqGIXK*XwLWi_51z#`}aJr*XQ}Xp3n3A@w{H|%jc7E!O3=y{62XA z0QT71S-F7O7mNW}X>doFS@nQPifU=^CJU}OS>JT;Rl3~Xp~SgEi}jn8RZv24I+o5Q3fajU1T7QMlsRT`|mx991);z?Ueiv%c9}Q>T zN$j_JReY=2J=N~theM4H>FCHiIy!O}XdW+z{D^1qr_gAgKwvzF>*(xkBu`__V%G`m zj8;}cbppxgHvo2#b_V!vH%e?hRn&RZrekCQK(>tfIP?hO8uFW;Hsmg0> zYpd>hH8`l5kdRPPT3RX`fAfX_)KpX?O2%KmwyUkJHJVI$w}i#(=;$aR5aDY!2Q)Mk zl6r2YDIOFq9Yi3q&zE8UoNOVNm{Yt`f^+loU~yaDVf&3j8-(>^3k~rMM-A zyu`{%t+0RvOSdE%oxThB8q+x(=I!nMUGih|&Nl}F!74KH@$g`ePr1r!Y~jVEqG5j^ zLGo$)!~6Fs6%`fgD|5@s*#!j!)}0sPzRttq;^G>D`8v<~`bgKWU*}~%#PE^J%gYH- z6t>9PS@}**P93!5{{8IH(NXK@XHhg7^2`}^^Z2csmB(soYkP{(0j2R?0UNgB)W;zL z*3_U+)n`;@uwpriVq(?l!ZdwMJwM{rUS`jI)#OB!#KipiA0;c!ukQzB8;JyW`gA+L2_ zERKBf1oG#6T}rSL2!?pF<=qfN&wWzGx<~Nn%C&i}CLc$h?#kkDIK=HMCy+>swa_q> zyuADccX#QxBWyN@qnwzS2tm;83EjswHhWilQy`qkkM4C4Mq8W7PK(IMNVNCsW0{sD z66xzlfnM07B7bKh(H^Mj>WX*Hsl&9Zr5-(h--=AuW3gD(%_=G?nnp&dd3ky0DQ0Em zP9Bf9y1L3;C867I-UI-2S+(9#mL3s!J2#gm6nc1d#i_5Ytrg6ZK+AJfx+(9GIAx@PIBv7(zz* z{1lKc$;ruqn14Bp>JDV>H_k0CuH{ouLptd_SD!BO>*mY`R#){)N=jziQ}#8)xa#TZ zmI{y{=lT}LnD2K%_Z6+_4#8}*HF!RIcWjhvnEt;WG0+s~Zzu;>Q%jqfnNexP{!%*# zR(y$u=o3D~ud}m;u_-O|rG&N3U$uWmtb(Thj#9FP*gG zAU4i|NK}tnnc4%SXJp`PZDs1ObZ3LI_Ueq`*HU@HCNI?+Up_d3IxtI+XhV`Jj@4jQ(OGAR^tr>Aoo8uaV#HebGc*)`$(bXSM~<>v0LrKRQL)Xyuas-s*S zX)-Z2l>^?-%}tidN$uX9S5{UgVy7f0OT}zUIz=~xLSgl*Z1?{hv^}}dG{5nnSG=@j z<>CUDhd^Km1orU{Q9Iqv!2ur>bleh;XEyK8vU%Xy0*#K2rkcERA&&a-5TJdS-`!^a z(hm)tY~BO13N3k-%H?uv4Lm(OxI$s30|Pa@aoVA8Ins_mQ1I@G3wr#pu@RP8iPY32 zJ>LXXUe(c|VN>PYPIRgoTLA4Ru|MZvRYe6ZvO6O~ZgzI|sX(Ci>C-2t;@hq@ooPz2 z8U;j2)g@iXV7P{d*Z=XT_zP&a=Rhs^>hGNgu`|SCosQ&Z z0J(#{mG|!>GIMf{(&=4r#d40W_#nNGp;6vEb#kbY;ymNVh zUT+INd|3M^pv^p!lUGnU3@F3lmM!5TE%ro5%@=NFH4YAk*R-}qS5QmTZueP&pVL(x z`c>n~i4xtQU4`*P4wE_8bSA&aw$K`VXsEK7Hyh9mHVINh1iU}{z?hE{$}t~lY5Ey= h=AYS8|7R3<+3j-L@9s%T)Za4^U~lbYRd0Fq)?WpzC+PqH literal 0 HcmV?d00001 diff --git a/projectq/tests/_drawmpl_test.py b/projectq/tests/_drawmpl_test.py index eccae2314..99a8554f4 100644 --- a/projectq/tests/_drawmpl_test.py +++ b/projectq/tests/_drawmpl_test.py @@ -12,15 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +''' + Tests for projectq.backends._circuits._drawer.py. + To generate the baseline images, run the tests with '--mpl-generate-path' + Then run the tests simply with '--mpl' +''' + import pytest from projectq import MainEngine from projectq.ops import * from projectq.backends import CircuitDrawerMatplotlib -''' - To generate the baseline images, run the tests with '--mpl-generate-path' - hen run the tests simply with '--mpl' -''' @pytest.mark.mpl_image_compare def test_drawer_mpl(): drawer = CircuitDrawerMatplotlib() From 83065dfcd4c30f033e37e3dc35cb1e31bfa06ff0 Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Tue, 29 Oct 2019 15:39:44 +0100 Subject: [PATCH 14/37] Fix the R(angle) gate drawing --- projectq/backends/_circuits/_drawer.py | 13 ++++++++++--- projectq/backends/_circuits/_plot.py | 6 +++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/projectq/backends/_circuits/_drawer.py b/projectq/backends/_circuits/_drawer.py index b2a746961..27b35ec22 100755 --- a/projectq/backends/_circuits/_drawer.py +++ b/projectq/backends/_circuits/_drawer.py @@ -74,7 +74,8 @@ def __init__(self, accept_input=False, default_measure=0): def is_available(self, cmd): """ Specialized implementation of is_available: Returns True if the - CircuitDrawer is the last engine (since it can print any command). + CircuitDrawerMatplotlib is the last engine + (since it can print any command). Args: cmd (Command): Command for which to check availability (all @@ -132,9 +133,15 @@ def receive(self, command_list): command_list (list): List of Commands to print (and potentially send on to the next engine). """ + for cmd in command_list: l = [] - g = str(cmd.gate) + # split the gate string "Gate()" at '(' get the gate name + g = str(cmd.gate).split('(')[0] + # case for R(1.57094543) Gate + if hasattr(cmd.gate, 'angle'): + g = g + '({0:.2f})'.format(cmd.gate.angle) + for q in cmd.qubits: l.append(q[0].id) # assume single target, 1st. element of q is the target qubit. @@ -174,7 +181,7 @@ class CircuitDrawer(BasicEngine): After initializing the CircuitDrawer, it can also be given the mapping from qubit IDs to wire location (via the :meth:`set_qubit_locations` function): - +ยทยท .. code-block:: python circuit_backend = CircuitDrawer() diff --git a/projectq/backends/_circuits/_plot.py b/projectq/backends/_circuits/_plot.py index f9e4296b1..3db8049b1 100644 --- a/projectq/backends/_circuits/_plot.py +++ b/projectq/backends/_circuits/_plot.py @@ -174,7 +174,7 @@ def draw_target(ax, i, gate, labels, gate_grid, wire_grid, plot_params): # override name with target_symbols, get(keyname,value) symbol = target_symbols.get(name, name) - if symbol in ['X'] and len(gate) >= 3: + if symbol in ['X'] and len(gate) == 3: name = 'CNOT' x = gate_grid[i] @@ -207,12 +207,12 @@ def measure(ax, x, y, plot_params): """ HIG = 0.65 WID = 0.65 - s = ''.ljust(2) # define box size + s = ''.ljust(3) # define box size # add box text(ax, x, y, s, plot_params, box=True) # add measure symbol - arc = Arc(xy=(x, y - 0.15 * HIG), width=WID * 0.7, + arc = Arc(xy=(x, y - 0.15 * HIG), width=WID * 0.60, height=HIG * 0.7, theta1=0, theta2=180, fill=False, linewidth=1,zorder=5) ax.add_patch(arc) From 6f110566f9a19da5a77c4a0dabf2eb58c0e91329 Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Tue, 29 Oct 2019 15:42:34 +0100 Subject: [PATCH 15/37] added test for is_available and QFT gate --- projectq/tests/_drawmpl_test.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/projectq/tests/_drawmpl_test.py b/projectq/tests/_drawmpl_test.py index 99a8554f4..63ce91015 100644 --- a/projectq/tests/_drawmpl_test.py +++ b/projectq/tests/_drawmpl_test.py @@ -14,19 +14,27 @@ ''' Tests for projectq.backends._circuits._drawer.py. - To generate the baseline images, run the tests with '--mpl-generate-path' + + To generate the baseline images + run the tests with '--mpl-generate-path=baseline' + Then run the tests simply with '--mpl' ''' import pytest from projectq import MainEngine from projectq.ops import * +from projectq.backends import Simulator from projectq.backends import CircuitDrawerMatplotlib +from projectq.cengines import DecompositionRuleSet, AutoReplacer +import projectq.setups.decompositions @pytest.mark.mpl_image_compare def test_drawer_mpl(): drawer = CircuitDrawerMatplotlib() - eng = MainEngine(engine_list=[drawer]) + rule_set = DecompositionRuleSet(modules=[projectq.setups.decompositions]) + eng = MainEngine(backend=Simulator(), engine_list=[AutoReplacer(rule_set), + drawer]) ctrl = eng.allocate_qureg(2) qureg = eng.allocate_qureg(3) @@ -34,6 +42,7 @@ def test_drawer_mpl(): Rx(1.0) | qureg[0] CNOT | (qureg[1], qureg[2]) C(X, 2) | (ctrl[0], ctrl[1], qureg[2]) + QFT | qureg All(Measure) | qureg eng.flush() From 9805b5efd47953b09d5090e424cbca8633cd16bd Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Tue, 29 Oct 2019 16:49:02 +0100 Subject: [PATCH 16/37] fix drawing distance between gates when gate_length >2 --- projectq/backends/_circuits/_plot.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/projectq/backends/_circuits/_plot.py b/projectq/backends/_circuits/_plot.py index 3db8049b1..de0d62146 100644 --- a/projectq/backends/_circuits/_plot.py +++ b/projectq/backends/_circuits/_plot.py @@ -75,6 +75,7 @@ def draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params): # initialize the position of gates as 0 for each label x_labels = {label: 0 for label in labels} x_position = 0 + CheckGateLength = False # keep track of the last gate length for i, gate in enumerate(gates): if len(gate) > 2: # case: multi-control or target gate @@ -106,16 +107,23 @@ def draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params): gate_grid, wire_grid, plot_params) # update the position by adding 1 + distance = 2 if len(gate[0]) > 2 else 1 + CheckGateLength = True if distance == 2 else False for itr, value in x_labels.items(): if begin <= itr <= end: - x_labels[itr] = x_position + 1 + x_labels[itr] = x_position + distance + else: qb = gate[1] + # if the last gate length > 2 + if CheckGateLength == True: + x_labels[qb] = x_labels[qb] - 1 draw_target(ax, x_labels[qb], gate, labels, gate_grid, wire_grid, plot_params) x_labels[qb] = x_labels[qb] + 1 + CheckGateLength = False def draw_controls(ax, i, gate, labels, gate_grid, wire_grid, plot_params): """ From 56a8403285b7a134bef0236131597814614754a4 Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Tue, 29 Oct 2019 16:50:41 +0100 Subject: [PATCH 17/37] new test png for pytest mpl --- projectq/tests/baseline/test_drawer_mpl.png | Bin 15283 -> 22084 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/projectq/tests/baseline/test_drawer_mpl.png b/projectq/tests/baseline/test_drawer_mpl.png index 1ed3930785e97ce5dbbcaf2c581b3b32593de026..d93e0c2def8f8a28ad6583fb563411749590ac5d 100644 GIT binary patch literal 22084 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 From f3eab276ad29f24a0656418c53290b133d3d7bba Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Mon, 4 Nov 2019 17:39:21 +0100 Subject: [PATCH 18/37] added Swap gates and CSwap gate with multi-control and multi-target. --- projectq/backends/_circuits/_drawer.py | 19 ++- projectq/backends/_circuits/_drawer_test.py | 4 +- projectq/backends/_circuits/_plot.py | 179 +++++++++++++------- 3 files changed, 137 insertions(+), 65 deletions(-) diff --git a/projectq/backends/_circuits/_drawer.py b/projectq/backends/_circuits/_drawer.py index 27b35ec22..68ef164e4 100755 --- a/projectq/backends/_circuits/_drawer.py +++ b/projectq/backends/_circuits/_drawer.py @@ -85,6 +85,7 @@ def is_available(self, cmd): the Command (if there is a next engine). """ try: + return BasicEngine.is_available(self, cmd) except LastEngineException: return True @@ -135,24 +136,32 @@ def receive(self, command_list): """ for cmd in command_list: - l = [] + target = [] + control = [] + gate = [] # split the gate string "Gate()" at '(' get the gate name g = str(cmd.gate).split('(')[0] # case for R(1.57094543) Gate if hasattr(cmd.gate, 'angle'): g = g + '({0:.2f})'.format(cmd.gate.angle) + gate.append(g) + gate = tuple(gate) for q in cmd.qubits: - l.append(q[0].id) + target.append(q[0].id) # assume single target, 1st. element of q is the target qubit. if len(cmd.control_qubits) > 0: for cq in cmd.control_qubits: - l.append(cq.id) + control.append(cq.id) listOfStrings = ['', 'Allocate'] - + T = tuple(target) + C = tuple(control) if not g in listOfStrings: - self._gates.append(tuple([g] + l)) + if len(C) == 0: + self._gates.append(gate + (T,)) + else: + self._gates.append(gate + (T,) + (C,)) if not cmd.gate == FlushGate(): self._print_cmd(cmd) diff --git a/projectq/backends/_circuits/_drawer_test.py b/projectq/backends/_circuits/_drawer_test.py index 7df4bd0ee..9baa5d0ea 100755 --- a/projectq/backends/_circuits/_drawer_test.py +++ b/projectq/backends/_circuits/_drawer_test.py @@ -27,8 +27,8 @@ from projectq.meta import Control import projectq.backends._circuits._drawer as _drawer -from projectq.backends._circuits._drawer import CircuitItem, CircuitDrawer - +from projectq.backends._circuits._drawer import CircuitItem, CircuitDrawer\ + , CircuitDrawerMatplotlib def test_drawer_getlatex(): old_latex = _drawer.to_latex diff --git a/projectq/backends/_circuits/_plot.py b/projectq/backends/_circuits/_plot.py index de0d62146..930da1ae3 100644 --- a/projectq/backends/_circuits/_plot.py +++ b/projectq/backends/_circuits/_plot.py @@ -79,16 +79,20 @@ def draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params): for i, gate in enumerate(gates): if len(gate) > 2: # case: multi-control or target gate - # it only works for single target gate + qb_target = gate[1] qb_control = gate[2] + tar_max = max(qb_target) + tar_min = min(qb_target) + ctr_max = max(qb_control) + ctr_min = min(qb_control) # get the index of qubit between control and target qubit - begin = min(qb_control, qb_target) - end = max(qb_control, qb_target) + begin = min(ctr_min, tar_min) + end = max(ctr_max, tar_max) # check the max position between control and target gate - MaxPosition = max(x_labels[qb_target], x_labels[qb_control]) + MaxPosition = max(x_labels[tar_max], x_labels[ctr_max]) CheckMax = False for x in range(begin, end + 1): if x_labels[x] > MaxPosition: @@ -97,17 +101,20 @@ def draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params): if CheckMax: x_position = max(x_labels.values()) else: - x_position = max(x_labels[qb_target], x_labels[qb_control]) + x_position = MaxPosition draw_controls(ax, x_position, gate, labels, gate_grid, wire_grid, plot_params) - x_labels[qb_target] = x_position - draw_target(ax, x_labels[qb_target], gate, labels, - gate_grid, wire_grid, plot_params) + for i in qb_target: + x_labels[i] = x_position + draw_target(ax, x_labels, gate, labels, + gate_grid, wire_grid, plot_params) + draw_lines(ax, x_labels, gate, labels, + gate_grid, wire_grid, plot_params) # update the position by adding 1 - distance = 2 if len(gate[0]) > 2 else 1 + distance = 2 if len(gate[0]) > 4 else 1 CheckGateLength = True if distance == 2 else False for itr, value in x_labels.items(): if begin <= itr <= end: @@ -117,14 +124,69 @@ def draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params): qb = gate[1] # if the last gate length > 2 if CheckGateLength == True: - x_labels[qb] = x_labels[qb] - 1 + for q in qb: + x_labels[q] = x_labels[q] - 1 - draw_target(ax, x_labels[qb], gate, labels, + draw_target(ax, x_labels, gate, labels, gate_grid, wire_grid, plot_params) + draw_lines(ax, x_labels, gate, labels, + gate_grid, wire_grid, plot_params) + + if len(qb) > 1: + begin = min(qb) + end = max(qb) + for itr, value in x_labels.items(): + if begin <= itr <= end: + x_labels[itr] = x_labels[itr] + 1 + else: + for q in qb: + x_labels[q] = x_labels[q] + 1 - x_labels[qb] = x_labels[qb] + 1 CheckGateLength = False +def draw_lines(ax, x_labels, gate, labels, gate_grid, wire_grid, plot_params): + """ + draw the wires of connection between gates and control qubits + Args: + ax (AxesSubplot): axes object + x_labels (dict): the x position of each qubit + gate (tuple): control qubit gate + labels (list): contains qubits' label + gate_grid (ndarray): grid for positioning gate + wire_grid (ndarray): grid for positioning wires + plot_params (dict): parameter for the figure + """ + + if len(gate) == 3: + name, targets, controls = gate + + tar_indices = get_flipped_indices(targets,labels) + + # include multi-control gate + ctr_indices = get_flipped_indices(controls,labels) + + i = x_labels[targets[0]] + tar_max = max(tar_indices) + tar_min = min(tar_indices) + ctr_max = max(ctr_indices) + ctr_min = min(ctr_indices) + + min_wire = min(tar_min, ctr_min) + max_wire = max(tar_max, ctr_max) + line(ax, gate_grid[i], gate_grid[i], + wire_grid[min_wire], wire_grid[max_wire], plot_params) + else: + name, targets = gate + tar_indices = [get_flipped_index(targets, labels)] \ + if isinstance(targets, int) else get_flipped_indices(targets, + labels) + i = x_labels[targets] if isinstance(targets, int) \ + else x_labels[targets[0]] + tar_max = max(tar_indices) + tar_min = min(tar_indices) + line(ax, gate_grid[i], gate_grid[i], + wire_grid[tar_min], wire_grid[tar_max], plot_params) + def draw_controls(ax, i, gate, labels, gate_grid, wire_grid, plot_params): """ draw the control qubit gate @@ -138,71 +200,72 @@ def draw_controls(ax, i, gate, labels, gate_grid, wire_grid, plot_params): plot_params (dict): parameter for the figure """ - linewidth = plot_params['linewidth'] - scale = plot_params['scale'] - control_radius = plot_params['control_radius'] + name, targets, controls = gate - # what about multi target, can't set 2 here.. - # ToDo: make a case, specifically for multi target gate.. - name, target = gate[:2] - target_index = get_flipped_index(target, labels) + tar_indices = get_flipped_indices(targets, labels) # include multi-control gate - controls = gate[2:] - control_indices = get_flipped_indices(controls, labels) - gate_indices = control_indices + [target_index] + ctr_indices = get_flipped_indices(controls,labels) + + tar_max = max(tar_indices) + tar_min = min(tar_indices) + ctr_max = max(ctr_indices) + ctr_min = min(ctr_indices) - min_wire = min(gate_indices) - max_wire = max(gate_indices) - line(ax, gate_grid[i], gate_grid[i], - wire_grid[min_wire], wire_grid[max_wire], plot_params) + min_wire = min(tar_min,ctr_min) + max_wire = max(tar_max,ctr_max) - for ci in control_indices: + for ci in ctr_indices: x = gate_grid[i] y = wire_grid[ci] - if name == 'SWAP': - swapx(ax, x, y, plot_params) - else: - cdot(ax, x, y, plot_params) + cdot(ax, x, y, plot_params) -def draw_target(ax, i, gate, labels, gate_grid, wire_grid, plot_params): +def draw_target(ax, x_labels, gate, labels, gate_grid, wire_grid, plot_params): """ draw the target gate in figure Args: ax (AxesSubplot): axes object - i (int): position of the target gate + x_labels (dict): the x position of each qubit gate (tuple): control qubit gate labels (list): contains qubits' label gate_grid (ndarray): grid for positioning gate wire_grid (ndarray): grid for positioning wires plot_params (dict): parameter for the figure """ - target_symbols = dict(CNOT='X', CPHASE='Z', NOP='', CX='X', CZ='Z') - name, target = gate[:2] - # override name with target_symbols, get(keyname,value) - symbol = target_symbols.get(name, name) - - if symbol in ['X'] and len(gate) == 3: - name = 'CNOT' - - x = gate_grid[i] - target_index = get_flipped_index(target, labels) - y = wire_grid[target_index] - - if not symbol: return - if name in ['CNOT', 'TOFFOLI']: - oplus(ax, x, y, plot_params) - elif name == 'CPHASE': - cdot(ax, x, y, plot_params) - elif name == 'SWAP': - swapx(ax, x, y, plot_params) - elif name == 'Measure': - draw_mwires(ax, x, y, gate_grid, wire_grid, plot_params) - - measure(ax, x, y, plot_params) + if len(gate) == 3: + name, targets, controls = gate else: - text(ax, x, y, symbol, plot_params, box=True) + name, targets = gate + + target_indices = get_flipped_indices(targets, labels) + + if name == 'X' and len(gate) == 3: + name = 'CNOT' + + for qb in targets: + i = x_labels[qb] + x = gate_grid[i] + + target_index = get_flipped_index(qb, labels) + y = wire_grid[target_index] + + if name in ['CNOT', 'TOFFOLI']: + oplus(ax, x, y, plot_params) + elif name == 'CPHASE': + cdot(ax, x, y, plot_params) + elif name == 'Swap': + y1, y2 = target_indices + # line(ax, gate_grid[i], gate_grid[i], + # wire_grid[y1], wire_grid[y2], plot_params) + swapx(ax, x, y, plot_params) + + elif name == 'Measure': + draw_mwires(ax, x, y, gate_grid, wire_grid, plot_params) + measure(ax, x, y, plot_params) + else: + text(ax, x, y, name, plot_params, box=True) + def measure(ax, x, y, plot_params): """ @@ -431,7 +494,7 @@ def get_flipped_indices(targets, labels): """ flip the index of the target qubit for multi targets Args: - target (str): target qubit + targets (tuple): target qubit labels (list): contains all labels of qubits """ return [get_flipped_index(t, labels) for t in targets] From e7caedad211ffa5bc3b58b00c13bb7c5e71d804e Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Tue, 5 Nov 2019 11:08:58 +0100 Subject: [PATCH 19/37] update test and comments --- projectq/backends/_circuits/_plot.py | 19 +++++++++---------- projectq/tests/_drawmpl_test.py | 5 ++++- projectq/tests/baseline/test_drawer_mpl.png | Bin 22084 -> 24432 bytes 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/projectq/backends/_circuits/_plot.py b/projectq/backends/_circuits/_plot.py index 930da1ae3..3218fe096 100644 --- a/projectq/backends/_circuits/_plot.py +++ b/projectq/backends/_circuits/_plot.py @@ -72,7 +72,7 @@ def draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params): plot_params (dict): parameter for the figure """ - # initialize the position of gates as 0 for each label + # initialize the position of gates as 0 for each qubit label x_labels = {label: 0 for label in labels} x_position = 0 CheckGateLength = False # keep track of the last gate length @@ -113,7 +113,8 @@ def draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params): gate_grid, wire_grid, plot_params) draw_lines(ax, x_labels, gate, labels, gate_grid, wire_grid, plot_params) - # update the position by adding 1 + + # update x position between control and target qubit distance = 2 if len(gate[0]) > 4 else 1 CheckGateLength = True if distance == 2 else False for itr, value in x_labels.items(): @@ -121,8 +122,9 @@ def draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params): x_labels[itr] = x_position + distance else: + # get target qubit (tuple) qb = gate[1] - # if the last gate length > 2 + # if the last gate length > 4 if CheckGateLength == True: for q in qb: x_labels[q] = x_labels[q] - 1 @@ -177,11 +179,10 @@ def draw_lines(ax, x_labels, gate, labels, gate_grid, wire_grid, plot_params): wire_grid[min_wire], wire_grid[max_wire], plot_params) else: name, targets = gate - tar_indices = [get_flipped_index(targets, labels)] \ - if isinstance(targets, int) else get_flipped_indices(targets, - labels) - i = x_labels[targets] if isinstance(targets, int) \ - else x_labels[targets[0]] + + tar_indices = get_flipped_indices(targets, labels) + i = x_labels[targets[0]] # use the first target qubit position + tar_max = max(tar_indices) tar_min = min(tar_indices) line(ax, gate_grid[i], gate_grid[i], @@ -256,8 +257,6 @@ def draw_target(ax, x_labels, gate, labels, gate_grid, wire_grid, plot_params): cdot(ax, x, y, plot_params) elif name == 'Swap': y1, y2 = target_indices - # line(ax, gate_grid[i], gate_grid[i], - # wire_grid[y1], wire_grid[y2], plot_params) swapx(ax, x, y, plot_params) elif name == 'Measure': diff --git a/projectq/tests/_drawmpl_test.py b/projectq/tests/_drawmpl_test.py index 63ce91015..3d78befa6 100644 --- a/projectq/tests/_drawmpl_test.py +++ b/projectq/tests/_drawmpl_test.py @@ -17,7 +17,7 @@ To generate the baseline images run the tests with '--mpl-generate-path=baseline' - + Then run the tests simply with '--mpl' ''' @@ -38,6 +38,9 @@ def test_drawer_mpl(): ctrl = eng.allocate_qureg(2) qureg = eng.allocate_qureg(3) + Swap | (qureg[0], qureg[2]) + C(Swap) | (qureg[0], qureg[1], qureg[2]) + CNOT | (qureg[0], qureg[2]) Rx(1.0) | qureg[0] CNOT | (qureg[1], qureg[2]) diff --git a/projectq/tests/baseline/test_drawer_mpl.png b/projectq/tests/baseline/test_drawer_mpl.png index d93e0c2def8f8a28ad6583fb563411749590ac5d..69358c44eee29f385bf703d5db14f30c89b133f8 100644 GIT binary patch literal 24432 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 literal 22084 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 Date: Tue, 5 Nov 2019 13:57:44 -0500 Subject: [PATCH 20/37] Address comments in _drawer.py --- projectq/backends/_circuits/_drawer.py | 96 ++++++++++---------------- 1 file changed, 36 insertions(+), 60 deletions(-) diff --git a/projectq/backends/_circuits/_drawer.py b/projectq/backends/_circuits/_drawer.py index 68ef164e4..59b9c44c3 100755 --- a/projectq/backends/_circuits/_drawer.py +++ b/projectq/backends/_circuits/_drawer.py @@ -21,7 +21,7 @@ from builtins import input from projectq.cengines import LastEngineException, BasicEngine -from projectq.ops import FlushGate, Measure, Allocate, Deallocate +from projectq.ops import (SwapGate, FlushGate, Measure, Allocate, Deallocate) from projectq.meta import get_control_count from projectq.backends._circuits import to_latex, to_draw @@ -74,7 +74,7 @@ def __init__(self, accept_input=False, default_measure=0): def is_available(self, cmd): """ Specialized implementation of is_available: Returns True if the - CircuitDrawerMatplotlib is the last engine + CircuitDrawerMatplotlib is the last engine (since it can print any command). Args: @@ -85,46 +85,15 @@ def is_available(self, cmd): the Command (if there is a next engine). """ try: - + # General multi-target qubit gates are not supported yet + if (not isinstance(cmd.gate, SwapGate) + and len([qubit for qureg in cmd.qubits + for qubit in qureg]) > 1): + return False return BasicEngine.is_available(self, cmd) except LastEngineException: return True - def _print_cmd(self, cmd): - """ - Add the command cmd to the circuit diagram, taking care of potential - measurements as specified in the __init__ function. - - Queries the user for measurement input if a measurement command - arrives if accept_input was set to True. Otherwise, it uses the - default_measure parameter to register the measurement outcome. - - Args: - cmd (Command): Command to add to the circuit diagram. - """ - if cmd.gate == Allocate: - qubit_id = cmd.qubits[0][0].id - if qubit_id not in self._map: - self._map[qubit_id] = qubit_id - - if cmd.gate == Deallocate: - qubit_id = cmd.qubits[0][0].id - - if self.is_last_engine and cmd.gate == Measure: - assert(get_control_count(cmd) == 0) - for qureg in cmd.qubits: - for qubit in qureg: - if self._accept_input: - m = None - while m != '0' and m != '1' and m != 1 and m != 0: - prompt = ("Input measurement result (0 or 1) for " - "qubit " + str(qubit) + ": ") - m = input(prompt) - else: - m = self._default_measure - m = int(m) - self.main_engine.set_measurement_result(qubit, m) - def receive(self, command_list): """ Receive a list of commands from the previous engine, print the @@ -136,35 +105,42 @@ def receive(self, command_list): """ for cmd in command_list: - target = [] - control = [] - gate = [] # split the gate string "Gate()" at '(' get the gate name g = str(cmd.gate).split('(')[0] # case for R(1.57094543) Gate if hasattr(cmd.gate, 'angle'): g = g + '({0:.2f})'.format(cmd.gate.angle) - gate.append(g) - gate = tuple(gate) - - for q in cmd.qubits: - target.append(q[0].id) - # assume single target, 1st. element of q is the target qubit. - if len(cmd.control_qubits) > 0: - for cq in cmd.control_qubits: - control.append(cq.id) - - listOfStrings = ['', 'Allocate'] - T = tuple(target) - C = tuple(control) - if not g in listOfStrings: - if len(C) == 0: - self._gates.append(gate + (T,)) + + gate_names_ignore_list = ['', 'Allocate', 'Deallocate'] + if g not in gate_names_ignore_list: + T = tuple(qubit.id for qureg in cmd.qubits for qubit in qureg) + + if len(cmd.control_qubits) > 0: + self._gates.append( + (g, T, tuple(qubit.id for qubit in cmd.control_qubits))) else: - self._gates.append(gate + (T,) + (C,)) + self._gates.append((g, T)) + + if cmd.gate == Allocate: + qubit_id = cmd.qubits[0][0].id + if qubit_id not in self._map: + self._map[qubit_id] = qubit_id + + elif self.is_last_engine and cmd.gate == Measure: + assert (get_control_count(cmd) == 0) + for qureg in cmd.qubits: + for qubit in qureg: + if self._accept_input: + m = None + while m != '0' and m != '1' and m != 1 and m != 0: + prompt = ('Input measurement result (0 or 1) ' + 'for qubit ' + str(qubit) + ': ') + m = input(prompt) + else: + m = self._default_measure + m = int(m) + self.main_engine.set_measurement_result(qubit, m) - if not cmd.gate == FlushGate(): - self._print_cmd(cmd) # (try to) send on if not self.is_last_engine: self.send([cmd]) From 4c090b34ab652de73952bca08828a1f20f4fb8c6 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Tue, 5 Nov 2019 14:02:34 -0500 Subject: [PATCH 21/37] Reindent and reformat parts of _drawer.py --- projectq/backends/_circuits/_drawer.py | 52 +++++++++++++------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/projectq/backends/_circuits/_drawer.py b/projectq/backends/_circuits/_drawer.py index 59b9c44c3..df4ae5913 100755 --- a/projectq/backends/_circuits/_drawer.py +++ b/projectq/backends/_circuits/_drawer.py @@ -11,13 +11,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - """ Contains a compiler engine which generates TikZ Latex code describing the circuit. """ -import sys - from builtins import input from projectq.cengines import LastEngineException, BasicEngine @@ -25,6 +22,7 @@ from projectq.meta import get_control_count from projectq.backends._circuits import to_latex, to_draw + class CircuitItem(object): def __init__(self, gate, lines, ctrl_lines): """ @@ -41,16 +39,17 @@ def __init__(self, gate, lines, ctrl_lines): self.id = -1 def __eq__(self, other): - return (self.gate == other.gate and self.lines == other.lines and - self.ctrl_lines == other.ctrl_lines and - self.id == other.id) + return (self.gate == other.gate and self.lines == other.lines + and self.ctrl_lines == other.ctrl_lines + and self.id == other.id) def __ne__(self, other): return not self.__eq__(other) + class CircuitDrawerMatplotlib(BasicEngine): """ - CircuitDrawerMatplotlib is a compiler engine which using Matplotlib library + CircuitDrawerMatplotlib is a compiler engine which using Matplotlib library for drawing quantum circuits """ def __init__(self, accept_input=False, default_measure=0): @@ -58,9 +57,9 @@ def __init__(self, accept_input=False, default_measure=0): Initialize a circuit drawing engine(mpl) Args: accept_input (bool): If accept_input is true, the printer queries - the user to input measurement results if the CircuitDrawerMPL is - the last engine. Otherwise, all measurements yield the result - default_measure (0 or 1). + the user to input measurement results if the CircuitDrawerMPL + is the last engine. Otherwise, all measurements yield the + result default_measure (0 or 1). default_measure (bool): Default value to use as measurement results if accept_input is False and there is no underlying backend to register real measurement results. @@ -70,7 +69,7 @@ def __init__(self, accept_input=False, default_measure=0): self._default_measure = default_measure self._map = dict() self._gates = [] - + def is_available(self, cmd): """ Specialized implementation of is_available: Returns True if the @@ -106,20 +105,22 @@ def receive(self, command_list): for cmd in command_list: # split the gate string "Gate()" at '(' get the gate name - g = str(cmd.gate).split('(')[0] + gate_name = str(cmd.gate).split('(')[0] # case for R(1.57094543) Gate if hasattr(cmd.gate, 'angle'): - g = g + '({0:.2f})'.format(cmd.gate.angle) + gate_name = gate_name + '({0:.2f})'.format(cmd.gate.angle) - gate_names_ignore_list = ['', 'Allocate', 'Deallocate'] - if g not in gate_names_ignore_list: - T = tuple(qubit.id for qureg in cmd.qubits for qubit in qureg) + if (cmd.gate not in [Allocate, Deallocate] + and not isinstance(cmd.gate, FlushGate)): + targets = tuple(qubit.id for qureg in cmd.qubits + for qubit in qureg) if len(cmd.control_qubits) > 0: self._gates.append( - (g, T, tuple(qubit.id for qubit in cmd.control_qubits))) + (gate_name, targets, + tuple(qubit.id for qubit in cmd.control_qubits))) else: - self._gates.append((g, T)) + self._gates.append((gate_name, targets)) if cmd.gate == Allocate: qubit_id = cmd.qubits[0][0].id @@ -127,12 +128,12 @@ def receive(self, command_list): self._map[qubit_id] = qubit_id elif self.is_last_engine and cmd.gate == Measure: - assert (get_control_count(cmd) == 0) + assert get_control_count(cmd) == 0 for qureg in cmd.qubits: for qubit in qureg: if self._accept_input: m = None - while m != '0' and m != '1' and m != 1 and m != 0: + while m not in ('0', '1', 1, 0): prompt = ('Input measurement result (0 or 1) ' 'for qubit ' + str(qubit) + ': ') m = input(prompt) @@ -151,9 +152,10 @@ def draw(self): """ qubits = [self._map[id] for id in self._map] # extract all the allocated qubits from the circuit - + return to_draw(self._gates, qubits) - + + class CircuitDrawer(BasicEngine): """ CircuitDrawer is a compiler engine which generates TikZ code for drawing @@ -294,7 +296,7 @@ def set_qubit_locations(self, id_to_loc): raise RuntimeError("set_qubit_locations() has to be called before" " applying gates!") - for k in range(min(id_to_loc), max(id_to_loc)+1): + for k in range(min(id_to_loc), max(id_to_loc) + 1): if k not in id_to_loc: raise RuntimeError("set_qubit_locations(): Invalid id_to_loc " "mapping provided. All ids in the provided" @@ -325,12 +327,12 @@ def _print_cmd(self, cmd): self._free_lines.append(qubit_id) if self.is_last_engine and cmd.gate == Measure: - assert(get_control_count(cmd) == 0) + assert get_control_count(cmd) == 0 for qureg in cmd.qubits: for qubit in qureg: if self._accept_input: m = None - while m != '0' and m != '1' and m != 1 and m != 0: + while m not in ('0', '1', 1, 0): prompt = ("Input measurement result (0 or 1) for " "qubit " + str(qubit) + ": ") m = input(prompt) From 3f86dc3e7cc91ea36146438b90d5c0f423780ed1 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Tue, 5 Nov 2019 15:32:22 -0500 Subject: [PATCH 22/37] Address comments in _plot.py - Minor tweaks, code cleanup, rewrites, etc. --- projectq/backends/_circuits/_plot.py | 288 +++++++++++++-------------- 1 file changed, 141 insertions(+), 147 deletions(-) diff --git a/projectq/backends/_circuits/_plot.py b/projectq/backends/_circuits/_plot.py index 3218fe096..9cc765fb1 100644 --- a/projectq/backends/_circuits/_plot.py +++ b/projectq/backends/_circuits/_plot.py @@ -12,14 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -import matplotlib import matplotlib.pyplot as plt import numpy as np from matplotlib.lines import Line2D from matplotlib.patches import Circle from matplotlib.patches import Arc -def to_draw(gates,labels=[],inits={},plot_labels=True,**kwargs): + +def to_draw(gates, labels=None, inits=None, plot_labels=True, **kwargs): """ Use Matplotlib to plot a quantum circuit. Args: @@ -32,15 +32,18 @@ def to_draw(gates,labels=[],inits={},plot_labels=True,**kwargs): be drawed. **kwargs (dict): Can override plot_parameters """ + if labels is None: + labels = [] + + if inits is None: + inits = {label: 0 for label in labels} + plot_params = dict(scale=1.0, fontsize=14.0, linewidth=1.0, - linebetween=0.06,control_radius=0.05, not_radius=0.15, + linebetween=0.06, control_radius=0.05, not_radius=0.15, swap_delta=0.08, label_buffer=0.0) plot_params.update(kwargs) scale = plot_params['scale'] - if len(inits) == 0: - inits = {label: 0 for label in labels} - n_labels = len(labels) n_gates = len(gates) @@ -50,17 +53,18 @@ def to_draw(gates,labels=[],inits={},plot_labels=True,**kwargs): if len(gate_grid) == 0: gate_grid = wire_grid - fig, ax = setup_figure(n_labels, n_gates, gate_grid, wire_grid, plot_params) + fig, axes = setup_figure(n_labels, n_gates, gate_grid, wire_grid, plot_params) - draw_wires(ax, n_labels, gate_grid, wire_grid, plot_params) + draw_wires(axes, n_labels, gate_grid, wire_grid, plot_params) if plot_labels: - draw_labels(ax, labels, inits, gate_grid, wire_grid, plot_params) + draw_labels(axes, labels, inits, gate_grid, wire_grid, plot_params) - draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params) - return fig, ax + draw_gates(axes, gates, labels, gate_grid, wire_grid, plot_params) + return fig, axes -def draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params): + +def draw_gates(axes, gates, labels, gate_grid, wire_grid, plot_params): """ matching the position of each gate to the figure and draw each gate Args: @@ -75,13 +79,11 @@ def draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params): # initialize the position of gates as 0 for each qubit label x_labels = {label: 0 for label in labels} x_position = 0 - CheckGateLength = False # keep track of the last gate length + check_gate_length = False # keep track of the last gate length - for i, gate in enumerate(gates): + for gate in gates: if len(gate) > 2: # case: multi-control or target gate - - qb_target = gate[1] - qb_control = gate[2] + gate_name, qb_target, qb_control = gate tar_max = max(qb_target) tar_min = min(qb_target) ctr_max = max(qb_control) @@ -92,61 +94,61 @@ def draw_gates(ax, gates, labels, gate_grid, wire_grid, plot_params): end = max(ctr_max, tar_max) # check the max position between control and target gate - MaxPosition = max(x_labels[tar_max], x_labels[ctr_max]) - CheckMax = False - for x in range(begin, end + 1): - if x_labels[x] > MaxPosition: - CheckMax = True + max_position = max(x_labels[tar_max], x_labels[ctr_max]) + check_max = False + for qb_id in range(begin, end + 1): + if x_labels[qb_id] > max_position: + check_max = True break - if CheckMax: + if check_max: x_position = max(x_labels.values()) else: - x_position = MaxPosition + x_position = max_position - draw_controls(ax, x_position, gate, labels, + draw_controls(axes, x_position, gate, labels, gate_grid, wire_grid, plot_params) - for i in qb_target: - x_labels[i] = x_position + for qb_id in qb_target: + x_labels[qb_id] = x_position - draw_target(ax, x_labels, gate, labels, - gate_grid, wire_grid, plot_params) - draw_lines(ax, x_labels, gate, labels, + draw_target(axes, x_labels, gate, labels, gate_grid, wire_grid, plot_params) + draw_lines(axes, x_labels, gate, labels, + gate_grid, wire_grid, plot_params) # update x position between control and target qubit - distance = 2 if len(gate[0]) > 4 else 1 - CheckGateLength = True if distance == 2 else False - for itr, value in x_labels.items(): + distance = 2 if len(gate_name) > 4 else 1 + check_gate_length = (distance == 2) + for itr in x_labels: if begin <= itr <= end: x_labels[itr] = x_position + distance else: # get target qubit (tuple) - qb = gate[1] + _, target_qubits = gate # if the last gate length > 4 - if CheckGateLength == True: - for q in qb: - x_labels[q] = x_labels[q] - 1 + if check_gate_length: + for qb_id in target_qubits: + x_labels[qb_id] = x_labels[qb_id] - 1 - draw_target(ax, x_labels, gate, labels, - gate_grid, wire_grid, plot_params) - draw_lines(ax, x_labels, gate, labels, + draw_target(axes, x_labels, gate, labels, gate_grid, wire_grid, plot_params) + draw_lines(axes, x_labels, gate, labels, + gate_grid, wire_grid, plot_params) - if len(qb) > 1: - begin = min(qb) - end = max(qb) - for itr, value in x_labels.items(): + if len(target_qubits) > 1: + begin = min(target_qubits) + end = max(target_qubits) + for itr in x_labels: if begin <= itr <= end: x_labels[itr] = x_labels[itr] + 1 else: - for q in qb: - x_labels[q] = x_labels[q] + 1 + qb_id = target_qubits[0] + x_labels[qb_id] = x_labels[qb_id] + 1 - CheckGateLength = False + check_gate_length = False -def draw_lines(ax, x_labels, gate, labels, gate_grid, wire_grid, plot_params): +def draw_lines(axes, x_labels, gate, labels, gate_grid, wire_grid, plot_params): """ draw the wires of connection between gates and control qubits Args: @@ -160,12 +162,12 @@ def draw_lines(ax, x_labels, gate, labels, gate_grid, wire_grid, plot_params): """ if len(gate) == 3: - name, targets, controls = gate + _, targets, controls = gate - tar_indices = get_flipped_indices(targets,labels) + tar_indices = get_flipped_indices(targets, labels) # include multi-control gate - ctr_indices = get_flipped_indices(controls,labels) + ctr_indices = get_flipped_indices(controls, labels) i = x_labels[targets[0]] tar_max = max(tar_indices) @@ -175,20 +177,21 @@ def draw_lines(ax, x_labels, gate, labels, gate_grid, wire_grid, plot_params): min_wire = min(tar_min, ctr_min) max_wire = max(tar_max, ctr_max) - line(ax, gate_grid[i], gate_grid[i], - wire_grid[min_wire], wire_grid[max_wire], plot_params) + line(axes, (gate_grid[i], gate_grid[i]), + (wire_grid[min_wire], wire_grid[max_wire]), plot_params) else: - name, targets = gate + _, targets = gate - tar_indices = get_flipped_indices(targets, labels) + tar_indices = get_flipped_indices(targets, labels) i = x_labels[targets[0]] # use the first target qubit position tar_max = max(tar_indices) tar_min = min(tar_indices) - line(ax, gate_grid[i], gate_grid[i], - wire_grid[tar_min], wire_grid[tar_max], plot_params) + line(axes, (gate_grid[i], gate_grid[i]), + (wire_grid[tar_min], wire_grid[tar_max]), plot_params) + -def draw_controls(ax, i, gate, labels, gate_grid, wire_grid, plot_params): +def draw_controls(axes, i, gate, labels, gate_grid, wire_grid, plot_params): """ draw the control qubit gate Args: @@ -201,27 +204,16 @@ def draw_controls(ax, i, gate, labels, gate_grid, wire_grid, plot_params): plot_params (dict): parameter for the figure """ - name, targets, controls = gate - - tar_indices = get_flipped_indices(targets, labels) + _, _, controls = gate # include multi-control gate - ctr_indices = get_flipped_indices(controls,labels) - - tar_max = max(tar_indices) - tar_min = min(tar_indices) - ctr_max = max(ctr_indices) - ctr_min = min(ctr_indices) + ctr_indices = get_flipped_indices(controls, labels) - min_wire = min(tar_min,ctr_min) - max_wire = max(tar_max,ctr_max) + for cidx in ctr_indices: + cdot(axes, gate_grid[i], wire_grid[cidx], plot_params) - for ci in ctr_indices: - x = gate_grid[i] - y = wire_grid[ci] - cdot(ax, x, y, plot_params) -def draw_target(ax, x_labels, gate, labels, gate_grid, wire_grid, plot_params): +def draw_target(axes, x_labels, gate, labels, gate_grid, wire_grid, plot_params): """ draw the target gate in figure Args: @@ -233,40 +225,34 @@ def draw_target(ax, x_labels, gate, labels, gate_grid, wire_grid, plot_params): wire_grid (ndarray): grid for positioning wires plot_params (dict): parameter for the figure """ + # pylint: disable=invalid-name if len(gate) == 3: - name, targets, controls = gate + name, targets, _ = gate else: name, targets = gate - target_indices = get_flipped_indices(targets, labels) - - if name == 'X' and len(gate) == 3: - name = 'CNOT' - - for qb in targets: - i = x_labels[qb] + for qb_id in targets: + i = x_labels[qb_id] x = gate_grid[i] - target_index = get_flipped_index(qb, labels) + target_index = get_flipped_index(qb_id, labels) y = wire_grid[target_index] - if name in ['CNOT', 'TOFFOLI']: - oplus(ax, x, y, plot_params) + if name in ('X', 'CNOT', 'TOFFOLI'): + oplus(axes, x, y, plot_params) elif name == 'CPHASE': - cdot(ax, x, y, plot_params) + cdot(axes, x, y, plot_params) elif name == 'Swap': - y1, y2 = target_indices - swapx(ax, x, y, plot_params) - + swapx(axes, x, y, plot_params) elif name == 'Measure': - draw_mwires(ax, x, y, gate_grid, wire_grid, plot_params) - measure(ax, x, y, plot_params) + draw_mwires(axes, x, y, gate_grid, wire_grid, plot_params) + measure(axes, x, y, plot_params) else: - text(ax, x, y, name, plot_params, box=True) + text(axes, x, y, name, plot_params, box=True) -def measure(ax, x, y, plot_params): +def measure(axes, x, y, plot_params): """ drawing the measure gate Args: @@ -275,22 +261,24 @@ def measure(ax, x, y, plot_params): y (float): y coordinate plot_params (dict): parameter for the figure """ - HIG = 0.65 - WID = 0.65 - s = ''.ljust(3) # define box size + # pylint: disable=invalid-name + + height = 0.65 + width = 0.65 # add box - text(ax, x, y, s, plot_params, box=True) + text(axes, x, y, ' ', plot_params, box=True) # add measure symbol - arc = Arc(xy=(x, y - 0.15 * HIG), width=WID * 0.60, - height=HIG * 0.7, theta1=0, theta2=180, - fill=False, linewidth=1,zorder=5) - ax.add_patch(arc) - ax.plot([x, x + 0.35 * WID], - [y - 0.15 * HIG, y + 0.20 * HIG], color='k', - linewidth=1, zorder=5) + arc = Arc(xy=(x, y - 0.15 * height), width=width * 0.60, + height=height * 0.7, theta1=0, theta2=180, + fill=False, linewidth=1, zorder=5) + axes.add_patch(arc) + axes.plot([x, x + 0.35 * width], + [y - 0.15 * height, y + 0.20 * height], color='k', + linewidth=1, zorder=5) + -def line(ax, x1, x2, y1, y2, plot_params): +def line(axes, xdata, ydata, plot_params): """ draw line in the plot, begin at (x1, y1) and end at (x2, y2) Args: @@ -301,12 +289,11 @@ def line(ax, x1, x2, y1, y2, plot_params): y2 (float): y_2 coordinate plot_params (dict): parameter for the figure """ - line = Line2D((x1, x2), (y1, y2), - color='k', lw=plot_params['linewidth']) - ax.add_line(line) + axes.add_line(Line2D(xdata, ydata, + color='k', lw=plot_params['linewidth'])) -def text(ax, x, y, textstr, plot_params, box=False): +def text(axes, x, y, textstr, plot_params, box=False): """ draw the name of gate or qubit and draw the rectangle box at (x, y) Args: @@ -317,6 +304,8 @@ def text(ax, x, y, textstr, plot_params, box=False): plot_params (dict): parameter for the text box (bool): draw the rectangle box if box is True """ + # pylint: disable=invalid-name + linewidth = plot_params['linewidth'] fontsize = plot_params['fontsize'] @@ -327,10 +316,11 @@ def text(ax, x, y, textstr, plot_params, box=False): # draw the qubit box bbox = dict(ec='w', fc='w', fill=False, lw=linewidth) # draw the text - ax.text(x, y, textstr, color='k', ha='center', va='center', - bbox=bbox, size=fontsize) + axes.text(x, y, textstr, color='k', ha='center', va='center', + bbox=bbox, size=fontsize) + -def oplus(ax, x, y, plot_params): +def oplus(axes, x, y, plot_params): """ Draw the Symbol for control gate Args: @@ -339,16 +329,17 @@ def oplus(ax, x, y, plot_params): y (float): y coordinate plot_params (dict): parameter for the text """ + # pylint: disable=invalid-name + not_radius = plot_params['not_radius'] linewidth = plot_params['linewidth'] - c = Circle((x, y), not_radius, ec='k', - fc='w', fill=False, lw=linewidth) - ax.add_patch(c) + axes.add_patch(Circle((x, y), not_radius, ec='k', + fc='w', fill=False, lw=linewidth)) - line(ax, x, x, y - not_radius, y + not_radius, plot_params) + line(axes, (x, x), (y - not_radius, y + not_radius), plot_params) -def cdot(ax, x, y, plot_params): +def cdot(axes, x, y, plot_params): """ draw the control dot for control gate Args: @@ -357,15 +348,16 @@ def cdot(ax, x, y, plot_params): y (float): y coordinate plot_params (dict): parameter for the text """ + # pylint: disable=invalid-name + control_radius = plot_params['control_radius'] scale = plot_params['scale'] linewidth = plot_params['linewidth'] - c = Circle((x, y), control_radius * scale, - ec='k', fc='k', fill=True, lw=linewidth) - ax.add_patch(c) + axes.add_patch(Circle((x, y), control_radius * scale, + ec='k', fc='k', fill=True, lw=linewidth)) -def swapx(ax, x, y, plot_params): +def swapx(axes, x, y, plot_params): """ draw the SwapX symbol Args: @@ -374,10 +366,11 @@ def swapx(ax, x, y, plot_params): y (float): y coordinate plot_params (dict): parameter for the text """ + # pylint: disable=invalid-name + d = plot_params['swap_delta'] - linewidth = plot_params['linewidth'] - line(ax, x - d, x + d, y - d, y + d, plot_params) - line(ax, x - d, x + d, y + d, y - d, plot_params) + line(axes, (x - d, x + d), (y - d, y + d), plot_params) + line(axes, (x - d, x + d), (y + d, y - d), plot_params) def setup_figure(n_labels, n_gates, gate_grid, wire_grid, plot_params): """ @@ -394,25 +387,25 @@ def setup_figure(n_labels, n_gates, gate_grid, wire_grid, plot_params): scale = plot_params['scale'] width = n_gates * scale height = n_labels * scale - if width == 0: + if width == 0: width = height - + fig = plt.figure( figsize=(width, height), facecolor='w', edgecolor='w' ) - ax = plt.subplot() - ax.set_axis_off() - offset = scale - - ax.set_xlim(gate_grid[0] - offset, gate_grid[-1] + offset) - ax.set_ylim(wire_grid[0] - offset, wire_grid[-1] + offset) - ax.set_aspect('equal') - return fig, ax + axes = plt.subplot() + axes.set_axis_off() + offset = scale + + axes.set_xlim(gate_grid[0] - offset, gate_grid[-1] + offset) + axes.set_ylim(wire_grid[0] - offset, wire_grid[-1] + offset) + axes.set_aspect('equal') + return fig, axes -def draw_wires(ax, n_labels, gate_grid, wire_grid, plot_params): +def draw_wires(axes, n_labels, gate_grid, wire_grid, plot_params): """ draw the circuit wire Args: @@ -422,15 +415,16 @@ def draw_wires(ax, n_labels, gate_grid, wire_grid, plot_params): wire_grid (ndarray): grid for positioning wires plot_params (dict): parameter for the figure """ + # pylint: disable=invalid-name + scale = plot_params['scale'] - linewidth = plot_params['linewidth'] x_pos = (gate_grid[0] - 0.5 * scale, gate_grid[-1] + 2 * scale) for i in range(n_labels): - line(ax, x_pos[0], x_pos[-1], - wire_grid[i], wire_grid[i], plot_params) + line(axes, (x_pos[0], x_pos[-1]), + (wire_grid[i], wire_grid[i]), plot_params) -def draw_mwires(ax, x, y, gate_grid, wire_grid, plot_params): +def draw_mwires(axes, x, y, gate_grid, wire_grid, plot_params): """ Add the doubling for measured wires Args: @@ -441,13 +435,15 @@ def draw_mwires(ax, x, y, gate_grid, wire_grid, plot_params): wire_grid (ndarray): grid for positioning wires plot_params (dict): parameter for the figure """ + # pylint: disable=invalid-name + scale = plot_params['scale'] dy = plot_params['linebetween'] # gate_grid indicate x-axes - line(ax, x, gate_grid[-1] + 2 * scale, y + dy, y + dy, plot_params) + line(axes, (x, gate_grid[-1] + 2 * scale), (y + dy, y + dy), plot_params) -def draw_labels(ax, labels, inits, gate_grid, wire_grid, plot_params): +def draw_labels(axes, labels, inits, gate_grid, wire_grid, plot_params): """ draw the qubit label Args: @@ -460,14 +456,13 @@ def draw_labels(ax, labels, inits, gate_grid, wire_grid, plot_params): """ scale = plot_params['scale'] label_buffer = plot_params['label_buffer'] - fontsize = plot_params['fontsize'] n_labels = len(labels) if inits is None: inits = {label: 0 for label in labels} xdata = (gate_grid[0] - scale, gate_grid[-1] + scale) for i in range(n_labels): j = get_flipped_index(labels[i], labels) - text(ax, xdata[0] - label_buffer, wire_grid[j], + text(axes, xdata[0] - label_buffer, wire_grid[j], render_label(labels[i], inits), plot_params) def get_flipped_index(target, labels): @@ -498,7 +493,7 @@ def get_flipped_indices(targets, labels): """ return [get_flipped_index(t, labels) for t in targets] -def render_label(label, inits={}): +def render_label(label, inits): """ render qubit label as |0> Args: @@ -506,8 +501,7 @@ def render_label(label, inits={}): inits (list): initial qubits """ if label in inits: - s = inits[label] - if s is None: + if inits[label] is None: return '' - return r'$|{}\rangle$'.format(s) + return r'$|{}\rangle$'.format(inits[label]) return r'$|{}\rangle$'.format(label) From 97411b465279706d4e6bfbcedd4f4a236d01afab Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Tue, 5 Nov 2019 15:38:11 -0500 Subject: [PATCH 23/37] Reindent and reformat _plot.py --- projectq/backends/_circuits/_plot.py | 108 +++++++++++++++++---------- 1 file changed, 69 insertions(+), 39 deletions(-) diff --git a/projectq/backends/_circuits/_plot.py b/projectq/backends/_circuits/_plot.py index 9cc765fb1..4951d178b 100644 --- a/projectq/backends/_circuits/_plot.py +++ b/projectq/backends/_circuits/_plot.py @@ -38,9 +38,14 @@ def to_draw(gates, labels=None, inits=None, plot_labels=True, **kwargs): if inits is None: inits = {label: 0 for label in labels} - plot_params = dict(scale=1.0, fontsize=14.0, linewidth=1.0, - linebetween=0.06, control_radius=0.05, not_radius=0.15, - swap_delta=0.08, label_buffer=0.0) + plot_params = dict(scale=1.0, + fontsize=14.0, + linewidth=1.0, + linebetween=0.06, + control_radius=0.05, + not_radius=0.15, + swap_delta=0.08, + label_buffer=0.0) plot_params.update(kwargs) scale = plot_params['scale'] @@ -53,7 +58,8 @@ def to_draw(gates, labels=None, inits=None, plot_labels=True, **kwargs): if len(gate_grid) == 0: gate_grid = wire_grid - fig, axes = setup_figure(n_labels, n_gates, gate_grid, wire_grid, plot_params) + fig, axes = setup_figure(n_labels, n_gates, gate_grid, wire_grid, + plot_params) draw_wires(axes, n_labels, gate_grid, wire_grid, plot_params) @@ -105,16 +111,16 @@ def draw_gates(axes, gates, labels, gate_grid, wire_grid, plot_params): else: x_position = max_position - draw_controls(axes, x_position, gate, labels, - gate_grid, wire_grid, plot_params) + draw_controls(axes, x_position, gate, labels, gate_grid, wire_grid, + plot_params) for qb_id in qb_target: x_labels[qb_id] = x_position - draw_target(axes, x_labels, gate, labels, - gate_grid, wire_grid, plot_params) - draw_lines(axes, x_labels, gate, labels, - gate_grid, wire_grid, plot_params) + draw_target(axes, x_labels, gate, labels, gate_grid, wire_grid, + plot_params) + draw_lines(axes, x_labels, gate, labels, gate_grid, wire_grid, + plot_params) # update x position between control and target qubit distance = 2 if len(gate_name) > 4 else 1 @@ -131,10 +137,10 @@ def draw_gates(axes, gates, labels, gate_grid, wire_grid, plot_params): for qb_id in target_qubits: x_labels[qb_id] = x_labels[qb_id] - 1 - draw_target(axes, x_labels, gate, labels, - gate_grid, wire_grid, plot_params) - draw_lines(axes, x_labels, gate, labels, - gate_grid, wire_grid, plot_params) + draw_target(axes, x_labels, gate, labels, gate_grid, wire_grid, + plot_params) + draw_lines(axes, x_labels, gate, labels, gate_grid, wire_grid, + plot_params) if len(target_qubits) > 1: begin = min(target_qubits) @@ -148,7 +154,9 @@ def draw_gates(axes, gates, labels, gate_grid, wire_grid, plot_params): check_gate_length = False -def draw_lines(axes, x_labels, gate, labels, gate_grid, wire_grid, plot_params): + +def draw_lines(axes, x_labels, gate, labels, gate_grid, wire_grid, + plot_params): """ draw the wires of connection between gates and control qubits Args: @@ -183,7 +191,7 @@ def draw_lines(axes, x_labels, gate, labels, gate_grid, wire_grid, plot_params): _, targets = gate tar_indices = get_flipped_indices(targets, labels) - i = x_labels[targets[0]] # use the first target qubit position + i = x_labels[targets[0]] # use the first target qubit position tar_max = max(tar_indices) tar_min = min(tar_indices) @@ -213,7 +221,8 @@ def draw_controls(axes, i, gate, labels, gate_grid, wire_grid, plot_params): cdot(axes, gate_grid[i], wire_grid[cidx], plot_params) -def draw_target(axes, x_labels, gate, labels, gate_grid, wire_grid, plot_params): +def draw_target(axes, x_labels, gate, labels, gate_grid, wire_grid, + plot_params): """ draw the target gate in figure Args: @@ -269,13 +278,19 @@ def measure(axes, x, y, plot_params): # add box text(axes, x, y, ' ', plot_params, box=True) # add measure symbol - arc = Arc(xy=(x, y - 0.15 * height), width=width * 0.60, - height=height * 0.7, theta1=0, theta2=180, - fill=False, linewidth=1, zorder=5) + arc = Arc(xy=(x, y - 0.15 * height), + width=width * 0.60, + height=height * 0.7, + theta1=0, + theta2=180, + fill=False, + linewidth=1, + zorder=5) axes.add_patch(arc) - axes.plot([x, x + 0.35 * width], - [y - 0.15 * height, y + 0.20 * height], color='k', - linewidth=1, zorder=5) + axes.plot([x, x + 0.35 * width], [y - 0.15 * height, y + 0.20 * height], + color='k', + linewidth=1, + zorder=5) def line(axes, xdata, ydata, plot_params): @@ -289,8 +304,7 @@ def line(axes, xdata, ydata, plot_params): y2 (float): y_2 coordinate plot_params (dict): parameter for the figure """ - axes.add_line(Line2D(xdata, ydata, - color='k', lw=plot_params['linewidth'])) + axes.add_line(Line2D(xdata, ydata, color='k', lw=plot_params['linewidth'])) def text(axes, x, y, textstr, plot_params, box=False): @@ -316,8 +330,14 @@ def text(axes, x, y, textstr, plot_params, box=False): # draw the qubit box bbox = dict(ec='w', fc='w', fill=False, lw=linewidth) # draw the text - axes.text(x, y, textstr, color='k', ha='center', va='center', - bbox=bbox, size=fontsize) + axes.text(x, + y, + textstr, + color='k', + ha='center', + va='center', + bbox=bbox, + size=fontsize) def oplus(axes, x, y, plot_params): @@ -334,11 +354,12 @@ def oplus(axes, x, y, plot_params): not_radius = plot_params['not_radius'] linewidth = plot_params['linewidth'] - axes.add_patch(Circle((x, y), not_radius, ec='k', - fc='w', fill=False, lw=linewidth)) + axes.add_patch( + Circle((x, y), not_radius, ec='k', fc='w', fill=False, lw=linewidth)) line(axes, (x, x), (y - not_radius, y + not_radius), plot_params) + def cdot(axes, x, y, plot_params): """ draw the control dot for control gate @@ -354,8 +375,14 @@ def cdot(axes, x, y, plot_params): scale = plot_params['scale'] linewidth = plot_params['linewidth'] - axes.add_patch(Circle((x, y), control_radius * scale, - ec='k', fc='k', fill=True, lw=linewidth)) + axes.add_patch( + Circle((x, y), + control_radius * scale, + ec='k', + fc='k', + fill=True, + lw=linewidth)) + def swapx(axes, x, y, plot_params): """ @@ -372,6 +399,7 @@ def swapx(axes, x, y, plot_params): line(axes, (x - d, x + d), (y - d, y + d), plot_params) line(axes, (x - d, x + d), (y + d, y - d), plot_params) + def setup_figure(n_labels, n_gates, gate_grid, wire_grid, plot_params): """ Create the figure and set up the parameter of figure @@ -390,11 +418,7 @@ def setup_figure(n_labels, n_gates, gate_grid, wire_grid, plot_params): if width == 0: width = height - fig = plt.figure( - figsize=(width, height), - facecolor='w', - edgecolor='w' - ) + fig = plt.figure(figsize=(width, height), facecolor='w', edgecolor='w') axes = plt.subplot() axes.set_axis_off() @@ -405,6 +429,7 @@ def setup_figure(n_labels, n_gates, gate_grid, wire_grid, plot_params): axes.set_aspect('equal') return fig, axes + def draw_wires(axes, n_labels, gate_grid, wire_grid, plot_params): """ draw the circuit wire @@ -421,8 +446,9 @@ def draw_wires(axes, n_labels, gate_grid, wire_grid, plot_params): x_pos = (gate_grid[0] - 0.5 * scale, gate_grid[-1] + 2 * scale) for i in range(n_labels): - line(axes, (x_pos[0], x_pos[-1]), - (wire_grid[i], wire_grid[i]), plot_params) + line(axes, (x_pos[0], x_pos[-1]), (wire_grid[i], wire_grid[i]), + plot_params) + def draw_mwires(axes, x, y, gate_grid, wire_grid, plot_params): """ @@ -443,6 +469,7 @@ def draw_mwires(axes, x, y, gate_grid, wire_grid, plot_params): # gate_grid indicate x-axes line(axes, (x, gate_grid[-1] + 2 * scale), (y + dy, y + dy), plot_params) + def draw_labels(axes, labels, inits, gate_grid, wire_grid, plot_params): """ draw the qubit label @@ -465,6 +492,7 @@ def draw_labels(axes, labels, inits, gate_grid, wire_grid, plot_params): text(axes, xdata[0] - label_buffer, wire_grid[j], render_label(labels[i], inits), plot_params) + def get_flipped_index(target, labels): """ flip the index of the target qubit in order to match the coordination @@ -484,6 +512,7 @@ def get_flipped_index(target, labels): return n_labels - i - 1 + def get_flipped_indices(targets, labels): """ flip the index of the target qubit for multi targets @@ -493,6 +522,7 @@ def get_flipped_indices(targets, labels): """ return [get_flipped_index(t, labels) for t in targets] + def render_label(label, inits): """ render qubit label as |0> @@ -503,5 +533,5 @@ def render_label(label, inits): if label in inits: if inits[label] is None: return '' - return r'$|{}\rangle$'.format(inits[label]) + return r'$|{}\rangle$'.format(inits[label]) return r'$|{}\rangle$'.format(label) From a01c44a66e74dc251af4dd7b6e4b9c68931df94d Mon Sep 17 00:00:00 2001 From: Li Cheng Date: Thu, 7 Nov 2019 14:18:29 +0100 Subject: [PATCH 24/37] update tests --- projectq/backends/_circuits/_plot.py | 8 ------ projectq/backends/_circuits/_plot_test.py | 23 ++++++++++-------- .../_circuits/baseline/test_complex_CNOT.png | Bin 3096 -> 3096 bytes .../baseline/test_draw_single_gates.png | Bin 2442 -> 1413 bytes .../baseline/test_gates_position2.png | Bin 8034 -> 8037 bytes .../_circuits/baseline/test_measure_gate.png | Bin 1840 -> 1792 bytes 6 files changed, 13 insertions(+), 18 deletions(-) diff --git a/projectq/backends/_circuits/_plot.py b/projectq/backends/_circuits/_plot.py index 4951d178b..ff57d30f7 100644 --- a/projectq/backends/_circuits/_plot.py +++ b/projectq/backends/_circuits/_plot.py @@ -32,8 +32,6 @@ def to_draw(gates, labels=None, inits=None, plot_labels=True, **kwargs): be drawed. **kwargs (dict): Can override plot_parameters """ - if labels is None: - labels = [] if inits is None: inits = {label: 0 for label in labels} @@ -250,8 +248,6 @@ def draw_target(axes, x_labels, gate, labels, gate_grid, wire_grid, if name in ('X', 'CNOT', 'TOFFOLI'): oplus(axes, x, y, plot_params) - elif name == 'CPHASE': - cdot(axes, x, y, plot_params) elif name == 'Swap': swapx(axes, x, y, plot_params) elif name == 'Measure': @@ -484,8 +480,6 @@ def draw_labels(axes, labels, inits, gate_grid, wire_grid, plot_params): scale = plot_params['scale'] label_buffer = plot_params['label_buffer'] n_labels = len(labels) - if inits is None: - inits = {label: 0 for label in labels} xdata = (gate_grid[0] - scale, gate_grid[-1] + scale) for i in range(n_labels): j = get_flipped_index(labels[i], labels) @@ -531,7 +525,5 @@ def render_label(label, inits): inits (list): initial qubits """ if label in inits: - if inits[label] is None: - return '' return r'$|{}\rangle$'.format(inits[label]) return r'$|{}\rangle$'.format(label) diff --git a/projectq/backends/_circuits/_plot_test.py b/projectq/backends/_circuits/_plot_test.py index 19536abca..bfb6099ac 100644 --- a/projectq/backends/_circuits/_plot_test.py +++ b/projectq/backends/_circuits/_plot_test.py @@ -14,7 +14,10 @@ """ Tests for projectq.backends._circuits._plot.py. - To generate the baseline images, run the tests with '--mpl-generate-path' + + To generate the baseline images, + run the tests with '--mpl-generate-path=baseline' + Then run the tests simply with '--mpl' """ import pytest @@ -26,50 +29,50 @@ @pytest.mark.mpl_image_compare def test_draw_single_gates(): - allocate_qubit = [0,1,2,3] - gates = [('H',0)] + allocate_qubit = [0] + gates = [('H',(0,))] fig, ax = _plot.to_draw(gates, allocate_qubit) return fig @pytest.mark.mpl_image_compare def test_draw_multi_gates(): allocate_qubit = [0,1,2,3] - gates = [('H',0), ('H',0)] + gates = [('H',(0,)), ('H',(0,))] fig, ax = _plot.to_draw(gates, allocate_qubit) return fig @pytest.mark.mpl_image_compare def test_gates_position(): allocate_qubit = [0,1,2,3] - gates = [('H',3)] + gates = [('H',(3,))] fig, ax = _plot.to_draw(gates, allocate_qubit) return fig @pytest.mark.mpl_image_compare def test_gates_position2(): allocate_qubit = [0,1,2,3] - gates = [('H',3), ('X',1,0), ('H',2), ('H',2),('H',2),('X',3,0)] + gates = [('H',(3,)), ('X',(1,),(0,)), ('H',(2,)), ('H',(2,)),('H',(2,)),('X',(3,),(0,))] fig, ax = _plot.to_draw(gates, allocate_qubit) return fig @pytest.mark.mpl_image_compare def test_simple_CNOT(): allocate_qubit = [0,1] - gates = [('X',1,0)] + gates = [('X',(1,),(0,))] fig, ax = _plot.to_draw(gates, allocate_qubit) return fig @pytest.mark.mpl_image_compare def test_complex_CNOT(): allocate_qubit = [0,1,2,3] - gates = [('X',3,0)] + gates = [('X',(3,),(0,))] fig, ax = _plot.to_draw(gates, allocate_qubit) return fig @pytest.mark.mpl_image_compare def test_complex_CNOT2(): allocate_qubit = [0,1,2,3] - gates = [('X',0,3)] + gates = [('X',(0,),(3,))] fig, ax = _plot.to_draw(gates, allocate_qubit) return fig @@ -85,6 +88,6 @@ def test_qubit_numbers(): def test_measure_gate(): # set up qubit numbers without quantum gates allocate_qureg = [0] - gates = [('Measure',0)] + gates = [('Measure',(0,))] fig, ax = _plot.to_draw(gates, allocate_qureg) return fig \ No newline at end of file diff --git a/projectq/backends/_circuits/baseline/test_complex_CNOT.png b/projectq/backends/_circuits/baseline/test_complex_CNOT.png index ba6c9365c7ed14dfd07ffceba0a78d139b9b43ed..a723f242c7e507b10bba2016040fd4dca78f6bb3 100644 GIT binary patch delta 2173 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 diff --git a/projectq/backends/_circuits/baseline/test_gates_position2.png b/projectq/backends/_circuits/baseline/test_gates_position2.png index 3bb12ef0a3dc7871aa7ad3868188aaf651396dc4..a7b4eb6af84de7fab49a48c0dc9a7ed1c26cb50c 100644 GIT binary patch literal 8037 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 literal 1840 zcmcIl`#;-v7f-4q^|-`bLrcVMZ4p5TMcWK*#tToyttv!?P(dSg2@R#1h)cR@yc!Xm zmZ_#0ik6}tm(rR`1$C)Vu~IX#O-ij&&$mDAFW7mV%j=x;(|Mow`Fsik{LZMW8mfXo zAa&eX&vQVG`C<@dz^6#Lcpw~1#p3V~K(Zk*EFeFWayBd#1kxmaG4TD9b@4zEL-Puy z1t!PR(uq_Oh(e^LBqq}mYt?yr>9#3*R>aJE1Inup1}d>VhaJqYia~5$0l*e#Vx|SmyTI z_0+1d(bdX$77N};?8(-Ldm^y6P5({(-ed8r{@(Quy|NuE>+YVY{|#}EYnSKPHtI@0 z(24dusG73&Htf7e&zQsH>O98oF;3?7z&GdHmC9b$&>5V;d^TJ4qk^GjZ{%yK?L?V# zTqK#RhfVUT**81VWp`U!gG%s6ImfWu8*iB9<$5pt7xo1Z(_o>l$DQQYI6WkxI4|!& zXU3n{*4}emmX|ve5)~6;j(9s!=TA;d?5{vyvU7D+MdV46n8CslmQW;m-gI%WF>7bD zKSYt^J6w*&U|3RVG(IS(y1xF<$jAt!0E@}^BMP5LAQ;Q(?jDT2+l|pvxmn>QyRFkn zPt45Blxk~h%PykP=oe#SXX4_l*ladJ>R*Wg*G&}ios%tpnmY>Ba&>k6<86@b;(M>M5 zfT?NX{ajy9Pr5$c@Lnbh=@#4FU@|Z7{zZBi{zT(M`ljcZGb#fE12=iRLfK;X@-WXK zv?=;4jU%qtcVvr7ACE7~*Ct+!j{1=)`)URM4GY6bBoa?g`PQocfm2tmTyf;KUYZsI zxZWEmWNj{X`#bR|a~GR?Hqx{~)DDT(vXYV#J|N)K+S*!6&OU?w;Wu5y?v@o|K(*$>~=oQ35~awQJYn$BrYB%)X^lSFgJ3*_@Zl z94w1Z!E*tuKW2M$W^O5mKThO1*`7FIROctaQYbb6jIS0JO2y)Hl_%0|r3osZld8@0 zGt!N83Jx9sgL3wF1IO2B@7LG)+np^fj)yTRAdyIfq07k^y!w4eBnx_0))KY7k)ik- zwu~fnveIP>J<&~XXPe_vIwUilnW>(>US0(c(8o?+X7B=ZekdAXV)SOXK2 z5a06=5hW2FY1L1kx+ZwvaBy>bi21gzG|SD{jj?C&BzdvaXQ01=*gX4cW(Lwcs|Kta z;E`9q;AD=mFr=Xh6)A?uB9|_m4GGZ)!(+V4)0c027WIfkx25TKIU!Ios&71 zAk@Aky>lv|wzBfDAq>WA(tNYAeIMPoMiciI{O9HBme%zgr(=S5Q8M~SocWJg0a z4Gr%jpA?^*gGJ|SzsV$%AzfWvcKi{P)wcjo=aY6?1}z^Cg5d^g($}JJ3}X*tZ*L#H zqSkHSJ6+tz)Vx<+4R%hN2bQ6T$-et|XO(>y+C|{(?PX_w+O~;y(^0K%XwU@43G$_H z%%KmvP|agI4&)9G54Y)cAS0i_p^zVIYRqFRe^3L3KBXN^`+Y_W4Q`{z20Kyn&2j#d ziW|SY!+xWK4GWuXSuN&p?ukU|0)gOxZv8m9k@CjLI`Y{C)XHyDLbknSW2m)(kx`)N z=j+gg?%ruA0?{z-L+`4pa?7-L2__KyEG#V8?u{Z*iJ_sPbd&ep@U};_P*_w^QE_(@ zH#v028aSEu_V$e3zbr53gB9{j_Ca>0LZ4AXzsf&$`4LLbEBy(=Kc%0n-vdpUN zb9;irve~>S{E2jTYxS~4YWoE{!xB_@5!5KiWQow00tPN3;U`}gytY^(DT(Z65SIr^ z5^8;Mcx~#M*dkOE+$yfM*1|q|^k}8U6yxZ49pJ;z1Xh<7I?3nrS8x5gHc@xKzMl7| zcff5}5aE{3>(cmRaO4mwUm>h1r03pxs1n#*AcE%ifJDLkS84aB5{PyNUjuI?nf39R rei^%4lW1eE`@NR{hPeJO>OMF?mRAcKG Date: Thu, 16 Jan 2020 17:27:23 +0100 Subject: [PATCH 25/37] Move matplotlib drawer into its own file + add test coverage --- projectq/backends/_circuits/__init__.py | 2 +- projectq/backends/_circuits/_drawer.py | 113 +-------------- .../backends/_circuits/_drawer_matplotlib.py | 133 ++++++++++++++++++ projectq/backends/_circuits/_drawer_test.py | 3 +- 4 files changed, 137 insertions(+), 114 deletions(-) create mode 100644 projectq/backends/_circuits/_drawer_matplotlib.py diff --git a/projectq/backends/_circuits/__init__.py b/projectq/backends/_circuits/__init__.py index 1f72b94d8..be22d24d2 100755 --- a/projectq/backends/_circuits/__init__.py +++ b/projectq/backends/_circuits/__init__.py @@ -16,5 +16,5 @@ from ._plot import to_draw from ._drawer import CircuitDrawer -from ._drawer import CircuitDrawerMatplotlib +from ._drawer_matplotlib import CircuitDrawerMatplotlib diff --git a/projectq/backends/_circuits/_drawer.py b/projectq/backends/_circuits/_drawer.py index df4ae5913..f9778dcc4 100755 --- a/projectq/backends/_circuits/_drawer.py +++ b/projectq/backends/_circuits/_drawer.py @@ -18,9 +18,9 @@ from builtins import input from projectq.cengines import LastEngineException, BasicEngine -from projectq.ops import (SwapGate, FlushGate, Measure, Allocate, Deallocate) +from projectq.ops import FlushGate, Measure, Allocate, Deallocate from projectq.meta import get_control_count -from projectq.backends._circuits import to_latex, to_draw +from projectq.backends._circuits import to_latex class CircuitItem(object): @@ -47,115 +47,6 @@ def __ne__(self, other): return not self.__eq__(other) -class CircuitDrawerMatplotlib(BasicEngine): - """ - CircuitDrawerMatplotlib is a compiler engine which using Matplotlib library - for drawing quantum circuits - """ - def __init__(self, accept_input=False, default_measure=0): - """ - Initialize a circuit drawing engine(mpl) - Args: - accept_input (bool): If accept_input is true, the printer queries - the user to input measurement results if the CircuitDrawerMPL - is the last engine. Otherwise, all measurements yield the - result default_measure (0 or 1). - default_measure (bool): Default value to use as measurement - results if accept_input is False and there is no underlying - backend to register real measurement results. - """ - BasicEngine.__init__(self) - self._accept_input = accept_input - self._default_measure = default_measure - self._map = dict() - self._gates = [] - - def is_available(self, cmd): - """ - Specialized implementation of is_available: Returns True if the - CircuitDrawerMatplotlib is the last engine - (since it can print any command). - - Args: - cmd (Command): Command for which to check availability (all - Commands can be printed). - Returns: - availability (bool): True, unless the next engine cannot handle - the Command (if there is a next engine). - """ - try: - # General multi-target qubit gates are not supported yet - if (not isinstance(cmd.gate, SwapGate) - and len([qubit for qureg in cmd.qubits - for qubit in qureg]) > 1): - return False - return BasicEngine.is_available(self, cmd) - except LastEngineException: - return True - - def receive(self, command_list): - """ - Receive a list of commands from the previous engine, print the - commands, and then send them on to the next engine. - - Args: - command_list (list): List of Commands to print (and - potentially send on to the next engine). - """ - - for cmd in command_list: - # split the gate string "Gate()" at '(' get the gate name - gate_name = str(cmd.gate).split('(')[0] - # case for R(1.57094543) Gate - if hasattr(cmd.gate, 'angle'): - gate_name = gate_name + '({0:.2f})'.format(cmd.gate.angle) - - if (cmd.gate not in [Allocate, Deallocate] - and not isinstance(cmd.gate, FlushGate)): - targets = tuple(qubit.id for qureg in cmd.qubits - for qubit in qureg) - - if len(cmd.control_qubits) > 0: - self._gates.append( - (gate_name, targets, - tuple(qubit.id for qubit in cmd.control_qubits))) - else: - self._gates.append((gate_name, targets)) - - if cmd.gate == Allocate: - qubit_id = cmd.qubits[0][0].id - if qubit_id not in self._map: - self._map[qubit_id] = qubit_id - - elif self.is_last_engine and cmd.gate == Measure: - assert get_control_count(cmd) == 0 - for qureg in cmd.qubits: - for qubit in qureg: - if self._accept_input: - m = None - while m not in ('0', '1', 1, 0): - prompt = ('Input measurement result (0 or 1) ' - 'for qubit ' + str(qubit) + ': ') - m = input(prompt) - else: - m = self._default_measure - m = int(m) - self.main_engine.set_measurement_result(qubit, m) - - # (try to) send on - if not self.is_last_engine: - self.send([cmd]) - - def draw(self): - """ - Returns the plot of the quantum circuit - """ - qubits = [self._map[id] for id in self._map] - # extract all the allocated qubits from the circuit - - return to_draw(self._gates, qubits) - - class CircuitDrawer(BasicEngine): """ CircuitDrawer is a compiler engine which generates TikZ code for drawing diff --git a/projectq/backends/_circuits/_drawer_matplotlib.py b/projectq/backends/_circuits/_drawer_matplotlib.py new file mode 100644 index 000000000..83c07004f --- /dev/null +++ b/projectq/backends/_circuits/_drawer_matplotlib.py @@ -0,0 +1,133 @@ +# Copyright 2020 ProjectQ-Framework (www.projectq.ch) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Contains a compiler engine which generates matplotlib figures describing the +circuit. +""" + +from builtins import input + +from projectq.cengines import LastEngineException, BasicEngine +from projectq.ops import (SwapGate, FlushGate, Measure, Allocate, Deallocate) +from projectq.meta import get_control_count +from projectq.backends._circuits import to_draw + + +class CircuitDrawerMatplotlib(BasicEngine): + """ + CircuitDrawerMatplotlib is a compiler engine which using Matplotlib library + for drawing quantum circuits + """ + def __init__(self, accept_input=False, default_measure=0): + """ + Initialize a circuit drawing engine(mpl) + Args: + accept_input (bool): If accept_input is true, the printer queries + the user to input measurement results if the CircuitDrawerMPL + is the last engine. Otherwise, all measurements yield the + result default_measure (0 or 1). + default_measure (bool): Default value to use as measurement + results if accept_input is False and there is no underlying + backend to register real measurement results. + """ + BasicEngine.__init__(self) + self._accept_input = accept_input + self._default_measure = default_measure + self._map = dict() + self._gates = [] + + def is_available(self, cmd): + """ + Specialized implementation of is_available: Returns True if the + CircuitDrawerMatplotlib is the last engine + (since it can print any command). + + Args: + cmd (Command): Command for which to check availability (all + Commands can be printed). + Returns: + availability (bool): True, unless the next engine cannot handle + the Command (if there is a next engine). + """ + try: + # General multi-target qubit gates are not supported yet + if (not isinstance(cmd.gate, SwapGate) + and len([qubit for qureg in cmd.qubits + for qubit in qureg]) > 1): + return False + return BasicEngine.is_available(self, cmd) + except LastEngineException: + return True + + def receive(self, command_list): + """ + Receive a list of commands from the previous engine, print the + commands, and then send them on to the next engine. + + Args: + command_list (list): List of Commands to print (and + potentially send on to the next engine). + """ + + for cmd in command_list: + # split the gate string "Gate()" at '(' get the gate name + gate_name = str(cmd.gate).split('(')[0] + # case for R(1.57094543) Gate + if hasattr(cmd.gate, 'angle'): + gate_name = gate_name + '({0:.2f})'.format(cmd.gate.angle) + + if (cmd.gate not in [Allocate, Deallocate] + and not isinstance(cmd.gate, FlushGate)): + targets = tuple(qubit.id for qureg in cmd.qubits + for qubit in qureg) + + if len(cmd.control_qubits) > 0: + self._gates.append( + (gate_name, targets, + tuple(qubit.id for qubit in cmd.control_qubits))) + else: + self._gates.append((gate_name, targets)) + + if cmd.gate == Allocate: + qubit_id = cmd.qubits[0][0].id + if qubit_id not in self._map: + self._map[qubit_id] = qubit_id + + elif self.is_last_engine and cmd.gate == Measure: + assert get_control_count(cmd) == 0 + for qureg in cmd.qubits: + for qubit in qureg: + if self._accept_input: + m = None + while m not in ('0', '1', 1, 0): + prompt = ('Input measurement result (0 or 1) ' + 'for qubit ' + str(qubit) + ': ') + m = input(prompt) + else: + m = self._default_measure + m = int(m) + self.main_engine.set_measurement_result(qubit, m) + + # (try to) send on + if not self.is_last_engine: + self.send([cmd]) + + def draw(self): + """ + Returns the plot of the quantum circuit + """ + qubits = [self._map[id] for id in self._map] + # extract all the allocated qubits from the circuit + + return to_draw(self._gates, qubits) diff --git a/projectq/backends/_circuits/_drawer_test.py b/projectq/backends/_circuits/_drawer_test.py index 9baa5d0ea..7492eb834 100755 --- a/projectq/backends/_circuits/_drawer_test.py +++ b/projectq/backends/_circuits/_drawer_test.py @@ -27,8 +27,7 @@ from projectq.meta import Control import projectq.backends._circuits._drawer as _drawer -from projectq.backends._circuits._drawer import CircuitItem, CircuitDrawer\ - , CircuitDrawerMatplotlib +from projectq.backends._circuits._drawer import CircuitItem, CircuitDrawer def test_drawer_getlatex(): old_latex = _drawer.to_latex From 830fcdee43cf1e85dcd6174cae2c8ac8863998df Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Thu, 16 Jan 2020 18:40:48 +0100 Subject: [PATCH 26/37] Use regular expressions to rewrite and shorten gate names --- .../backends/_circuits/_drawer_matplotlib.py | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/projectq/backends/_circuits/_drawer_matplotlib.py b/projectq/backends/_circuits/_drawer_matplotlib.py index 83c07004f..2b84672f8 100644 --- a/projectq/backends/_circuits/_drawer_matplotlib.py +++ b/projectq/backends/_circuits/_drawer_matplotlib.py @@ -17,6 +17,7 @@ """ from builtins import input +import re from projectq.cengines import LastEngineException, BasicEngine from projectq.ops import (SwapGate, FlushGate, Measure, Allocate, Deallocate) @@ -81,11 +82,23 @@ def receive(self, command_list): """ for cmd in command_list: - # split the gate string "Gate()" at '(' get the gate name - gate_name = str(cmd.gate).split('(')[0] - # case for R(1.57094543) Gate - if hasattr(cmd.gate, 'angle'): - gate_name = gate_name + '({0:.2f})'.format(cmd.gate.angle) + param_str = '' + gate_name = str(cmd.gate) + if '(' in gate_name: + (gate_name, param_str) = re.search(r'(.+)\((.*)\)', + gate_name).groups() + params = re.findall(r'([^,]+)', param_str) + params_str_list = [] + for param in params: + try: + params_str_list.append('{0:.2f}'.format(float(param))) + except ValueError: + if len(param) < 8: + params_str_list.append(param) + else: + params_str_list.append(param[:5] + '...') + + gate_name += '(' + ','.join(params_str_list) + ')' if (cmd.gate not in [Allocate, Deallocate] and not isinstance(cmd.gate, FlushGate)): From 9df3377a3d458b4d36b6400fd578ce2758cd6586 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Mon, 27 Jan 2020 16:50:25 +0100 Subject: [PATCH 27/37] Change internal storage format for CircuitDrawerMatplotlib --- .../backends/_circuits/_drawer_matplotlib.py | 181 ++++++++++++------ .../_circuits/_drawer_matplotlib_test.py | 149 ++++++++++++++ 2 files changed, 267 insertions(+), 63 deletions(-) create mode 100644 projectq/backends/_circuits/_drawer_matplotlib_test.py diff --git a/projectq/backends/_circuits/_drawer_matplotlib.py b/projectq/backends/_circuits/_drawer_matplotlib.py index 2b84672f8..1bf000566 100644 --- a/projectq/backends/_circuits/_drawer_matplotlib.py +++ b/projectq/backends/_circuits/_drawer_matplotlib.py @@ -18,12 +18,38 @@ from builtins import input import re +import itertools from projectq.cengines import LastEngineException, BasicEngine from projectq.ops import (SwapGate, FlushGate, Measure, Allocate, Deallocate) from projectq.meta import get_control_count from projectq.backends._circuits import to_draw +# ============================================================================== + + +def _format_gate_str(cmd): + param_str = '' + gate_name = str(cmd.gate) + if '(' in gate_name: + (gate_name, param_str) = re.search(r'(.+)\((.*)\)', gate_name).groups() + params = re.findall(r'([^,]+)', param_str) + params_str_list = [] + for param in params: + try: + params_str_list.append('{0:.2f}'.format(float(param))) + except ValueError: + if len(param) < 8: + params_str_list.append(param) + else: + params_str_list.append(param[:5] + '...') + + gate_name += '(' + ','.join(params_str_list) + ')' + return gate_name + + +# ============================================================================== + class CircuitDrawerMatplotlib(BasicEngine): """ @@ -46,7 +72,7 @@ def __init__(self, accept_input=False, default_measure=0): self._accept_input = accept_input self._default_measure = default_measure self._map = dict() - self._gates = [] + self._qubit_lines = {} def is_available(self, cmd): """ @@ -62,15 +88,82 @@ def is_available(self, cmd): the Command (if there is a next engine). """ try: - # General multi-target qubit gates are not supported yet - if (not isinstance(cmd.gate, SwapGate) - and len([qubit for qureg in cmd.qubits - for qubit in qureg]) > 1): - return False + # Multi-qubit gates may fail at drawing time if the target qubits + # are not right next to each other on the output graphic. return BasicEngine.is_available(self, cmd) except LastEngineException: return True + def _process(self, cmd): + """ + Process the command cmd and stores it in the internal storage + + Queries the user for measurement input if a measurement command + arrives if accept_input was set to True. Otherwise, it uses the + default_measure parameter to register the measurement outcome. + + Args: + cmd (Command): Command to add to the circuit diagram. + """ + if cmd.gate == Allocate: + qubit_id = cmd.qubits[0][0].id + if qubit_id not in self._map: + self._map[qubit_id] = qubit_id + self._qubit_lines[qubit_id] = [] + return + + if cmd.gate == Deallocate: + return + + if self.is_last_engine and cmd.gate == Measure: + assert get_control_count(cmd) == 0 + for qureg in cmd.qubits: + for qubit in qureg: + if self._accept_input: + measurement = None + while measurement not in ('0', '1', 1, 0): + prompt = ("Input measurement result (0 or 1) for " + "qubit " + str(qubit) + ": ") + measurement = input(prompt) + else: + measurement = self._default_measure + self.main_engine.set_measurement_result( + qubit, int(measurement)) + + targets = [qubit.id for qureg in cmd.qubits for qubit in qureg] + controls = [qubit.id for qubit in cmd.control_qubits] + + ref_qubit_id = targets[0] + gate_str = _format_gate_str(cmd) + + # First find out what is the maximum index that this command might + # have + max_depth = max( + len(self._qubit_lines[qubit_id]) + for qubit_id in itertools.chain(targets, controls)) + + # If we have a multi-qubit gate, make sure that all the qubit axes + # have the same depth. We do that by recalculating the maximum index + # over all the known qubit axes. + # This is to avoid the possibility of a multi-qubit gate overlapping + # with some other gates. This could potentially be improved by only + # considering the qubit axes that are between the topmost and + # bottommost qubit axes of the current command. + if len(targets) + len(controls) > 1: + max_depth = max( + len(self._qubit_lines[qubit_id]) + for qubit_id in self._qubit_lines) + + for qubit_id in itertools.chain(targets, controls): + depth = len(self._qubit_lines[qubit_id]) + self._qubit_lines[qubit_id] += [None] * (max_depth - depth) + + if qubit_id == ref_qubit_id: + self._qubit_lines[qubit_id].append( + (gate_str, targets, controls)) + else: + self._qubit_lines[qubit_id].append(None) + def receive(self, command_list): """ Receive a list of commands from the previous engine, print the @@ -80,67 +173,29 @@ def receive(self, command_list): command_list (list): List of Commands to print (and potentially send on to the next engine). """ - for cmd in command_list: - param_str = '' - gate_name = str(cmd.gate) - if '(' in gate_name: - (gate_name, param_str) = re.search(r'(.+)\((.*)\)', - gate_name).groups() - params = re.findall(r'([^,]+)', param_str) - params_str_list = [] - for param in params: - try: - params_str_list.append('{0:.2f}'.format(float(param))) - except ValueError: - if len(param) < 8: - params_str_list.append(param) - else: - params_str_list.append(param[:5] + '...') - - gate_name += '(' + ','.join(params_str_list) + ')' - - if (cmd.gate not in [Allocate, Deallocate] - and not isinstance(cmd.gate, FlushGate)): - targets = tuple(qubit.id for qureg in cmd.qubits - for qubit in qureg) - - if len(cmd.control_qubits) > 0: - self._gates.append( - (gate_name, targets, - tuple(qubit.id for qubit in cmd.control_qubits))) - else: - self._gates.append((gate_name, targets)) - - if cmd.gate == Allocate: - qubit_id = cmd.qubits[0][0].id - if qubit_id not in self._map: - self._map[qubit_id] = qubit_id - - elif self.is_last_engine and cmd.gate == Measure: - assert get_control_count(cmd) == 0 - for qureg in cmd.qubits: - for qubit in qureg: - if self._accept_input: - m = None - while m not in ('0', '1', 1, 0): - prompt = ('Input measurement result (0 or 1) ' - 'for qubit ' + str(qubit) + ': ') - m = input(prompt) - else: - m = self._default_measure - m = int(m) - self.main_engine.set_measurement_result(qubit, m) - - # (try to) send on + if not isinstance(cmd.gate, FlushGate): + self._process(cmd) + if not self.is_last_engine: self.send([cmd]) - def draw(self): + def draw(self, qubit_labels=None, drawing_order=None): """ Returns the plot of the quantum circuit - """ - qubits = [self._map[id] for id in self._map] - # extract all the allocated qubits from the circuit - return to_draw(self._gates, qubits) + Args: + drawing_order (dictionary): position of each qubit in the output + graphic. Keys: qubit IDs, Values: position of qubit on the qubit + line in the graphic. + """ + max_depth = max( + len(self._qubit_lines[qubit_id]) for qubit_id in self._qubit_lines) + for qubit_id in self._qubit_lines: + depth = len(self._qubit_lines[qubit_id]) + if depth < max_depth: + self._qubit_lines[qubit_id] += [None] * (max_depth - depth) + + return to_draw(self._qubit_lines, + qubit_labels=qubit_labels, + drawing_order=drawing_order) diff --git a/projectq/backends/_circuits/_drawer_matplotlib_test.py b/projectq/backends/_circuits/_drawer_matplotlib_test.py new file mode 100644 index 000000000..ad30ff250 --- /dev/null +++ b/projectq/backends/_circuits/_drawer_matplotlib_test.py @@ -0,0 +1,149 @@ +# Copyright 2020 ProjectQ-Framework (www.projectq.ch) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Tests for projectq.backends.circuits._drawer.py. +""" + +import pytest + +from projectq import MainEngine +from projectq.cengines import DummyEngine +from projectq.ops import (H, X, Rx, CNOT, Swap, Measure, Command, BasicGate) +from projectq.types import WeakQubitRef + +from . import _drawer_matplotlib as _drawer +from ._drawer_matplotlib import CircuitDrawerMatplotlib + + +def test_drawer_measurement(): + drawer = CircuitDrawerMatplotlib(default_measure=0) + eng = MainEngine(drawer, []) + qubit = eng.allocate_qubit() + Measure | qubit + assert int(qubit) == 0 + + drawer = CircuitDrawerMatplotlib(default_measure=1) + eng = MainEngine(drawer, []) + qubit = eng.allocate_qubit() + Measure | qubit + assert int(qubit) == 1 + + drawer = CircuitDrawerMatplotlib(accept_input=True) + eng = MainEngine(drawer, []) + qubit = eng.allocate_qubit() + + old_input = _drawer.input + + _drawer.input = lambda x: '1' + Measure | qubit + assert int(qubit) == 1 + _drawer.input = old_input + + +class MockEngine(object): + def is_available(self, cmd): + self.cmd = cmd + self.called = True + return False + + +def test_drawer_isavailable(): + drawer = CircuitDrawerMatplotlib() + drawer.is_last_engine = True + + qb0 = WeakQubitRef(None, 0) + qb1 = WeakQubitRef(None, 1) + qb2 = WeakQubitRef(None, 2) + qb3 = WeakQubitRef(None, 3) + + for gate in (X, Rx(1.0)): + for qubits in (([qb0], ), ([qb0, qb1], ), ([qb0, qb1, qb2], )): + print(qubits) + cmd = Command(None, gate, qubits) + assert drawer.is_available(cmd) + + cmd0 = Command(None, X, ([qb0], )) + cmd1 = Command(None, Swap, ([qb0], [qb1])) + cmd2 = Command(None, Swap, ([qb0], [qb1]), [qb2]) + cmd3 = Command(None, Swap, ([qb0], [qb1]), [qb2, qb3]) + + assert drawer.is_available(cmd1) + assert drawer.is_available(cmd2) + assert drawer.is_available(cmd3) + + mock_engine = MockEngine() + mock_engine.called = False + drawer.is_last_engine = False + drawer.next_engine = mock_engine + + assert not drawer.is_available(cmd0) + assert mock_engine.called + assert mock_engine.cmd is cmd0 + + assert not drawer.is_available(cmd1) + assert mock_engine.called + assert mock_engine.cmd is cmd1 + + +def _draw_subst(qubit_lines, qubit_labels=None, drawing_order=None, **kwargs): + return qubit_lines + + +class MyGate(BasicGate): + def __init__(self, *args): + BasicGate.__init__(self) + self.params = args + + def __str__(self): + param_str = '{}'.format(self.params[0]) + for param in self.params[1:]: + param_str += ',{}'.format(param) + return str(self.__class__.__name__) + "(" + param_str + ")" + + +def test_drawer_draw(): + old_draw = _drawer.to_draw + _drawer.to_draw = _draw_subst + + backend = DummyEngine() + + drawer = CircuitDrawerMatplotlib() + + eng = MainEngine(backend, [drawer]) + qureg = eng.allocate_qureg(3) + H | qureg[1] + H | qureg[0] + X | qureg[0] + Rx(1) | qureg[1] + CNOT | (qureg[0], qureg[1]) + Swap | (qureg[0], qureg[1]) + MyGate(1.2) | qureg[2] + MyGate(1.23456789) | qureg[2] + MyGate(1.23456789, 2.3456789) | qureg[2] + MyGate(1.23456789, 'aaaaaaaa', 'bbb', 2.34) | qureg[2] + X | qureg[0] + + qubit_lines = drawer.draw() + + assert qubit_lines == { + 0: [('H', [0], []), ('X', [0], []), None, ('Swap', [0, 1], []), + ('X', [0], [])], + 1: [('H', [1], []), ('Rx(1.00)', [1], []), ('X', [1], [0]), None, + None], + 2: [('MyGate(1.20)', [2], []), ('MyGate(1.23)', [2], []), + ('MyGate(1.23,2.35)', [2], []), + ('MyGate(1.23,aaaaa...,bbb,2.34)', [2], []), None] + } + + _drawer.to_draw = old_draw From 64733448933b4e5120674d6c481af36082d09561 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Thu, 30 Jan 2020 16:43:41 +0100 Subject: [PATCH 28/37] Better graphics and adapt plot functions to new internal format - Support for new internal format - Resulting quantum circuit figure whould work better with scaling - Large quantum circuits will now result in wider figure instead of squeezing everything into the default matplotlib size - Some support for multi-target qubit gates - General code cleanup - Dropped support for double lines when qubit is in classical state --- projectq/backends/_circuits/_plot.py | 866 ++++++++++++---------- projectq/backends/_circuits/_plot_test.py | 283 +++++-- 2 files changed, 674 insertions(+), 475 deletions(-) diff --git a/projectq/backends/_circuits/_plot.py b/projectq/backends/_circuits/_plot.py index ff57d30f7..69215ce81 100644 --- a/projectq/backends/_circuits/_plot.py +++ b/projectq/backends/_circuits/_plot.py @@ -12,518 +12,564 @@ # See the License for the specific language governing permissions and # limitations under the License. -import matplotlib.pyplot as plt +from copy import deepcopy import numpy as np +import matplotlib.pyplot as plt +from matplotlib.collections import PatchCollection, LineCollection from matplotlib.lines import Line2D -from matplotlib.patches import Circle -from matplotlib.patches import Arc - - -def to_draw(gates, labels=None, inits=None, plot_labels=True, **kwargs): - """ - Use Matplotlib to plot a quantum circuit. - Args: - gates (list): List of tuples for each gate in the quantum circuit. - (name,target,control1,control2...). Targets and controls initially - defined in terms of labels. - labels (list): Qubits' index in the quantum circuit - inits (dict): Initialization list of gates, optional - plot_labels (bool): If plot_labels is false, the qubits' label will not - be drawed. - **kwargs (dict): Can override plot_parameters - """ - - if inits is None: - inits = {label: 0 for label in labels} - - plot_params = dict(scale=1.0, - fontsize=14.0, - linewidth=1.0, - linebetween=0.06, - control_radius=0.05, - not_radius=0.15, - swap_delta=0.08, - label_buffer=0.0) - plot_params.update(kwargs) - scale = plot_params['scale'] - - n_labels = len(labels) - n_gates = len(gates) +from matplotlib.patches import Circle, Arc, Rectangle - # create grid for the plot - wire_grid = np.arange(0.0, n_labels * scale, scale, dtype=float) - gate_grid = np.arange(0.0, n_gates * scale, scale, dtype=float) - if len(gate_grid) == 0: - gate_grid = wire_grid - - fig, axes = setup_figure(n_labels, n_gates, gate_grid, wire_grid, - plot_params) - - draw_wires(axes, n_labels, gate_grid, wire_grid, plot_params) - - if plot_labels: - draw_labels(axes, labels, inits, gate_grid, wire_grid, plot_params) +# Important note on units for the plot parameters. +# The following entries are in inches: +# - column_spacing +# - labels_margin +# - wire_height +# +# The following entries are in data units (matplotlib) +# - control_radius +# - gate_offset +# - mgate_width +# - not_radius +# - swap_delta +# - x_offset +# +# The rest have misc. units (as defined by matplotlib) +_DEFAULT_PLOT_PARAMS = dict(fontsize=14.0, + column_spacing=.5, + control_radius=0.015, + labels_margin=1, + linewidth=1.0, + not_radius=0.03, + gate_offset=.05, + mgate_width=0.1, + swap_delta=0.02, + x_offset=.05, + wire_height=1) - draw_gates(axes, gates, labels, gate_grid, wire_grid, plot_params) - return fig, axes +# ============================================================================== +# Functions used to calculate the layout -def draw_gates(axes, gates, labels, gate_grid, wire_grid, plot_params): +def gate_width(axes, gate_str, plot_params): """ - matching the position of each gate to the figure and draw each gate - Args: - ax (AxesSubplot): axes object - gates (list): List of tuples for each gate in the quantum circuit. - labels (list): contains qubits' label - gate_grid (ndarray): grid for positioning gate - wire_grid (ndarray): grid for positioning wires - plot_params (dict): parameter for the figure - """ - - # initialize the position of gates as 0 for each qubit label - x_labels = {label: 0 for label in labels} - x_position = 0 - check_gate_length = False # keep track of the last gate length - - for gate in gates: - if len(gate) > 2: # case: multi-control or target gate - gate_name, qb_target, qb_control = gate - tar_max = max(qb_target) - tar_min = min(qb_target) - ctr_max = max(qb_control) - ctr_min = min(qb_control) - - # get the index of qubit between control and target qubit - begin = min(ctr_min, tar_min) - end = max(ctr_max, tar_max) - - # check the max position between control and target gate - max_position = max(x_labels[tar_max], x_labels[ctr_max]) - check_max = False - for qb_id in range(begin, end + 1): - if x_labels[qb_id] > max_position: - check_max = True - break - if check_max: - x_position = max(x_labels.values()) - else: - x_position = max_position - - draw_controls(axes, x_position, gate, labels, gate_grid, wire_grid, - plot_params) - - for qb_id in qb_target: - x_labels[qb_id] = x_position - - draw_target(axes, x_labels, gate, labels, gate_grid, wire_grid, - plot_params) - draw_lines(axes, x_labels, gate, labels, gate_grid, wire_grid, - plot_params) + Calculate the width of a gate based on its string representation. - # update x position between control and target qubit - distance = 2 if len(gate_name) > 4 else 1 - check_gate_length = (distance == 2) - for itr in x_labels: - if begin <= itr <= end: - x_labels[itr] = x_position + distance + Args: + axes (matplotlib.axes.Axes): axes object + gate_str (str): string representation of a gate + plot_params (dict): plot parameters - else: - # get target qubit (tuple) - _, target_qubits = gate - # if the last gate length > 4 - if check_gate_length: - for qb_id in target_qubits: - x_labels[qb_id] = x_labels[qb_id] - 1 - - draw_target(axes, x_labels, gate, labels, gate_grid, wire_grid, - plot_params) - draw_lines(axes, x_labels, gate, labels, gate_grid, wire_grid, - plot_params) + Returns: + The width of a gate on the figure (in inches) + """ + if gate_str == 'X': + return 2 * plot_params['not_radius'] / plot_params['units_per_inch'] + # if gate_str == 'Z': + # return ... + if gate_str == 'Swap': + return 2 * plot_params['swap_delta'] / plot_params['units_per_inch'] - if len(target_qubits) > 1: - begin = min(target_qubits) - end = max(target_qubits) - for itr in x_labels: - if begin <= itr <= end: - x_labels[itr] = x_labels[itr] + 1 - else: - qb_id = target_qubits[0] - x_labels[qb_id] = x_labels[qb_id] + 1 + if gate_str == 'Measure': + return plot_params['mgate_width'] - check_gate_length = False + obj = axes.text(0, + 0, + gate_str, + visible=True, + bbox=dict(ec='k', fc='w', fill=True, lw=1.0), + fontsize=14) + obj.figure.canvas.draw() + width = (obj.get_window_extent(obj.figure.canvas.get_renderer()).width + / axes.figure.dpi) + obj.remove() + return width + 2 * plot_params['gate_offset'] -def draw_lines(axes, x_labels, gate, labels, gate_grid, wire_grid, - plot_params): - """ - draw the wires of connection between gates and control qubits - Args: - ax (AxesSubplot): axes object - x_labels (dict): the x position of each qubit - gate (tuple): control qubit gate - labels (list): contains qubits' label - gate_grid (ndarray): grid for positioning gate - wire_grid (ndarray): grid for positioning wires - plot_params (dict): parameter for the figure +def calculate_gate_grid(axes, qubit_lines, plot_params): """ + Calculate an optimal grid spacing for a list of quantum gates. - if len(gate) == 3: - _, targets, controls = gate + Args: + axes (matplotlib.axes.Axes): axes object + qubit_lines (dict): list of gates for each qubit axis + plot_params (dict): plot parameters - tar_indices = get_flipped_indices(targets, labels) + Returns: + An array (np.ndarray) with the gate x positions. + """ + # NB: column_spacing is still in inch when this function is called + column_spacing = plot_params['column_spacing'] + data = list(qubit_lines.values()) + depth = len(data[0]) - # include multi-control gate - ctr_indices = get_flipped_indices(controls, labels) + width_list = [ + max( + gate_width(axes, line[idx][0], plot_params) if line[idx] else 0 + for line in data) for idx in range(depth) + ] - i = x_labels[targets[0]] - tar_max = max(tar_indices) - tar_min = min(tar_indices) - ctr_max = max(ctr_indices) - ctr_min = min(ctr_indices) + gate_grid = np.array([0] * (depth + 1), dtype=float) - min_wire = min(tar_min, ctr_min) - max_wire = max(tar_max, ctr_max) - line(axes, (gate_grid[i], gate_grid[i]), - (wire_grid[min_wire], wire_grid[max_wire]), plot_params) - else: - _, targets = gate + gate_grid[0] = plot_params['labels_margin'] + (width_list[0]) * 0.5 + for idx in range(1, depth): + gate_grid[idx] = gate_grid[idx - 1] + column_spacing + ( + width_list[idx] + width_list[idx - 1]) * 0.5 + gate_grid[-1] = gate_grid[-2] + column_spacing + width_list[-1] * 0.5 + return gate_grid - tar_indices = get_flipped_indices(targets, labels) - i = x_labels[targets[0]] # use the first target qubit position - tar_max = max(tar_indices) - tar_min = min(tar_indices) - line(axes, (gate_grid[i], gate_grid[i]), - (wire_grid[tar_min], wire_grid[tar_max]), plot_params) +# ============================================================================== +# Basic helper functions -def draw_controls(axes, i, gate, labels, gate_grid, wire_grid, plot_params): +def text(axes, gate_pos, wire_pos, textstr, plot_params): """ - draw the control qubit gate + Draws a text box on the figure. + Args: - ax (AxesSubplot): axes object - i (int): position of the control gate - gate (tuple): control qubit gate - labels (list): contains qubits' label - gate_grid (ndarray): grid for positioning gate - wire_grid (ndarray): grid for positioning wires - plot_params (dict): parameter for the figure + axes (matplotlib.axes.Axes): axes object + gate_pos (float): x coordinate of the gate [data units] + wire_pos (float): y coordinate of the qubit wire + textstr (str): text of the gate and box + plot_params (dict): plot parameters + box (bool): draw the rectangle box if box is True """ + return axes.text(gate_pos, + wire_pos, + textstr, + color='k', + ha='center', + va='center', + clip_on=True, + size=plot_params['fontsize']) - _, _, controls = gate - - # include multi-control gate - ctr_indices = get_flipped_indices(controls, labels) - for cidx in ctr_indices: - cdot(axes, gate_grid[i], wire_grid[cidx], plot_params) +# ============================================================================== -def draw_target(axes, x_labels, gate, labels, gate_grid, wire_grid, - plot_params): +def create_figure(plot_params): """ - draw the target gate in figure + Create a new figure as well as a new axes instance + Args: - ax (AxesSubplot): axes object - x_labels (dict): the x position of each qubit - gate (tuple): control qubit gate - labels (list): contains qubits' label - gate_grid (ndarray): grid for positioning gate - wire_grid (ndarray): grid for positioning wires - plot_params (dict): parameter for the figure - """ - # pylint: disable=invalid-name + plot_params (dict): plot parameters - if len(gate) == 3: - name, targets, _ = gate - else: - name, targets = gate - - for qb_id in targets: - i = x_labels[qb_id] - x = gate_grid[i] - - target_index = get_flipped_index(qb_id, labels) - y = wire_grid[target_index] - - if name in ('X', 'CNOT', 'TOFFOLI'): - oplus(axes, x, y, plot_params) - elif name == 'Swap': - swapx(axes, x, y, plot_params) - elif name == 'Measure': - draw_mwires(axes, x, y, gate_grid, wire_grid, plot_params) - measure(axes, x, y, plot_params) - else: - text(axes, x, y, name, plot_params, box=True) + Returns: + A tuple with (figure, axes) + """ + fig = plt.figure(facecolor='w', edgecolor='w') + axes = plt.axes() + axes.set_axis_off() + axes.set_aspect('equal') + plot_params['units_per_inch'] = fig.dpi / axes.get_window_extent().width + return fig, axes -def measure(axes, x, y, plot_params): +def resize_figure(fig, axes, width, height, plot_params): """ - drawing the measure gate + Resizes a figure and adjust the limits of the axes instance to make sure + that the distances in data coordinates on the screen stay constant. + Args: - ax (AxesSubplot): axes object - x (float): x coordinate - y (float): y coordinate - plot_params (dict): parameter for the figure - """ - # pylint: disable=invalid-name + fig (matplotlib.figure.Figure): figure object + axes (matplotlib.axes.Axes): axes object + width (float): new figure width + height (float): new figure height + plot_params (dict): plot parameters - height = 0.65 - width = 0.65 + Returns: + A tuple with (figure, axes) + """ + fig.set_size_inches(width, height) - # add box - text(axes, x, y, ' ', plot_params, box=True) - # add measure symbol - arc = Arc(xy=(x, y - 0.15 * height), - width=width * 0.60, - height=height * 0.7, - theta1=0, - theta2=180, - fill=False, - linewidth=1, - zorder=5) - axes.add_patch(arc) - axes.plot([x, x + 0.35 * width], [y - 0.15 * height, y + 0.20 * height], - color='k', - linewidth=1, - zorder=5) + new_limits = plot_params['units_per_inch'] * np.array([width, height]) + axes.set_xlim(0, new_limits[0]) + axes.set_ylim(0, new_limits[1]) -def line(axes, xdata, ydata, plot_params): +def to_draw(qubit_lines, qubit_labels=None, drawing_order=None, **kwargs): """ - draw line in the plot, begin at (x1, y1) and end at (x2, y2) + Draws a quantum circuit in a matplotlib figure. + Args: - ax (AxesSubplot): axes object - x1 (float): x_1 coordinate - x2 (float): x_2 coordinate - y1 (float): y_1 coordinate - y2 (float): y_2 coordinate - plot_params (dict): parameter for the figure + qubit_lines (dict): list of gates for each qubit axis + qubit_labels (dict): label to print in front of the qubit wire for + each qubit ID + drawing_order (dict): index of the wire for each qubit ID to be drawn + **kwargs (dict): additional parameters are used to update the default + plot parameters + + Returns: + A tuple with (figure, axes) """ - axes.add_line(Line2D(xdata, ydata, color='k', lw=plot_params['linewidth'])) + if drawing_order is None: + n_qubits = len(qubit_lines) + drawing_order = { + qubit_id: n_qubits - qubit_id - 1 + for qubit_id in list(qubit_lines) + } + plot_params = deepcopy(_DEFAULT_PLOT_PARAMS) + plot_params.update(kwargs) -def text(axes, x, y, textstr, plot_params, box=False): - """ - draw the name of gate or qubit and draw the rectangle box at (x, y) - Args: - ax (AxesSubplot): axes object - x (float): x coordinate - y (float): y coordinate - textstr (str): text of the gate and box - plot_params (dict): parameter for the text - box (bool): draw the rectangle box if box is True - """ - # pylint: disable=invalid-name + n_labels = len(list(qubit_lines)) - linewidth = plot_params['linewidth'] - fontsize = plot_params['fontsize'] + wire_height = plot_params['wire_height'] + # Grid in inches + wire_grid = np.arange(wire_height, (n_labels + 1) * wire_height, + wire_height, + dtype=float) - if box: - # draw gate box - bbox = dict(ec='k', fc='w', fill=True, lw=linewidth) - else: - # draw the qubit box - bbox = dict(ec='w', fc='w', fill=False, lw=linewidth) - # draw the text - axes.text(x, - y, - textstr, - color='k', - ha='center', - va='center', - bbox=bbox, - size=fontsize) - - -def oplus(axes, x, y, plot_params): - """ - Draw the Symbol for control gate - Args: - ax (AxesSubplot): axes object - x (float): x coordinate - y (float): y coordinate - plot_params (dict): parameter for the text - """ - # pylint: disable=invalid-name + fig, axes = create_figure(plot_params) - not_radius = plot_params['not_radius'] - linewidth = plot_params['linewidth'] + # Grid in inches + gate_grid = calculate_gate_grid(axes, qubit_lines, plot_params) - axes.add_patch( - Circle((x, y), not_radius, ec='k', fc='w', fill=False, lw=linewidth)) + width = gate_grid[-1] + plot_params['column_spacing'] + height = wire_grid[-1] + wire_height - line(axes, (x, x), (y - not_radius, y + not_radius), plot_params) + resize_figure(fig, axes, width, height, plot_params) + # Convert grids into data coordinates + units_per_inch = plot_params['units_per_inch'] -def cdot(axes, x, y, plot_params): - """ - draw the control dot for control gate - Args: - ax (AxesSubplot): axes object - x (float): x coordinate - y (float): y coordinate - plot_params (dict): parameter for the text - """ - # pylint: disable=invalid-name + gate_grid *= units_per_inch + gate_grid = gate_grid + plot_params['x_offset'] + wire_grid *= units_per_inch + plot_params['column_spacing'] *= units_per_inch + + draw_wires(axes, n_labels, gate_grid, wire_grid, plot_params) - control_radius = plot_params['control_radius'] - scale = plot_params['scale'] - linewidth = plot_params['linewidth'] + if qubit_labels is None: + qubit_labels = {qubit_id: r'$|0\rangle$' for qubit_id in qubit_lines} + draw_labels(axes, qubit_labels, drawing_order, wire_grid, plot_params) - axes.add_patch( - Circle((x, y), - control_radius * scale, - ec='k', - fc='k', - fill=True, - lw=linewidth)) + draw_gates(axes, qubit_lines, drawing_order, gate_grid, wire_grid, + plot_params) + return fig, axes -def swapx(axes, x, y, plot_params): +def draw_gates(axes, qubit_lines, drawing_order, gate_grid, wire_grid, + plot_params): """ - draw the SwapX symbol + Draws the gates. + Args: - ax (AxesSubplot): axes object - x (float): x coordinate - y (float): y coordinate - plot_params (dict): parameter for the text - """ - # pylint: disable=invalid-name + qubit_lines (dict): list of gates for each qubit axis + drawing_order (dict): index of the wire for each qubit ID to be drawn + gate_grid (np.ndarray): x positions of the gates + wire_grid (np.ndarray): y positions of the qubit wires + plot_params (dict): plot parameters - d = plot_params['swap_delta'] - line(axes, (x - d, x + d), (y - d, y + d), plot_params) - line(axes, (x - d, x + d), (y + d, y - d), plot_params) + Returns: + A tuple with (figure, axes) + """ + for qubit_line in qubit_lines.values(): + for idx, data in enumerate(qubit_line): + if data is not None: + (gate_str, targets, controls) = data + targets_order = [drawing_order[tgt] for tgt in targets] + draw_gate( + axes, gate_str, gate_grid[idx], + [wire_grid[tgt] for tgt in targets_order], targets_order, + [wire_grid[drawing_order[ctrl]] + for ctrl in controls], plot_params) -def setup_figure(n_labels, n_gates, gate_grid, wire_grid, plot_params): +def draw_gate(axes, gate_str, gate_pos, target_wires, targets_order, + control_wires, plot_params): """ - Create the figure and set up the parameter of figure + Draws a single gate at a given location. + Args: - n_labels (int): number of labels representing qubits - n_gates (int): number of gates to be drawed - gate_grid (ndarray): grid for positioning gates - wire_grid (ndarray): grid for positioning wires - plot_params (dict): parameter for the figure + axes (AxesSubplot): axes object + gate_str (str): string representation of a gate + gate_pos (float): x coordinate of the gate [data units] + target_wires (list): y coordinates of the target qubits + targets_order (list): index of the wires corresponding to the target + qubit IDs + control_wires (list): y coordinates of the control qubits + plot_params (dict): plot parameters + Returns: - return the Figure and AxesSubplot object - """ - scale = plot_params['scale'] - width = n_gates * scale - height = n_labels * scale - if width == 0: - width = height + A tuple with (figure, axes) + """ + # Special cases + if gate_str == 'Z' and len(control_wires) == 1: + draw_control_z_gate(axes, gate_pos, target_wires[0], control_wires[0], + plot_params) + elif gate_str == 'X': + draw_x_gate(axes, gate_pos, target_wires[0], plot_params) + elif gate_str == 'Swap': + draw_swap_gate(axes, gate_pos, target_wires[0], target_wires[1], + plot_params) + elif gate_str == 'Measure': + draw_measure_gate(axes, gate_pos, target_wires[0], plot_params) + else: + if len(target_wires) == 1: + draw_generic_gate(axes, gate_pos, target_wires[0], gate_str, + plot_params) + else: + if sorted(targets_order) != list( + range(min(targets_order), + max(targets_order) + 1)): + raise RuntimeError( + 'Multi-qubit gate with non-neighbouring qubits!\n' + + 'Gate: {} on wires {}'.format(gate_str, targets_order)) - fig = plt.figure(figsize=(width, height), facecolor='w', edgecolor='w') + multi_qubit_gate(axes, gate_str, gate_pos, min(target_wires), + max(target_wires), plot_params) - axes = plt.subplot() - axes.set_axis_off() - offset = scale + if not control_wires: + return - axes.set_xlim(gate_grid[0] - offset, gate_grid[-1] + offset) - axes.set_ylim(wire_grid[0] - offset, wire_grid[-1] + offset) - axes.set_aspect('equal') - return fig, axes + for control_wire in control_wires: + axes.add_patch( + Circle((gate_pos, control_wire), + plot_params['control_radius'], + ec='k', + fc='k', + fill=True, + lw=plot_params['linewidth'])) + all_wires = target_wires + control_wires + axes.add_line( + Line2D((gate_pos, gate_pos), (min(all_wires), max(all_wires)), + color='k', + lw=plot_params['linewidth'])) -def draw_wires(axes, n_labels, gate_grid, wire_grid, plot_params): + +def draw_generic_gate(axes, gate_pos, wire_pos, gate_str, plot_params): """ - draw the circuit wire + Draws a measurement gate. + Args: - ax (AxesSubplot): axes object - n_labels (int): number of qubit - gate_grid (ndarray): grid for positioning gates - wire_grid (ndarray): grid for positioning wires - plot_params (dict): parameter for the figure + axes (AxesSubplot): axes object + gate_pos (float): x coordinate of the gate [data units] + wire_pos (float): y coordinate of the qubit wire + gate_str (str) : string representation of a gate + plot_params (dict): plot parameters """ - # pylint: disable=invalid-name + obj = text(axes, gate_pos, wire_pos, gate_str, plot_params) + obj.set_zorder(7) - scale = plot_params['scale'] - x_pos = (gate_grid[0] - 0.5 * scale, gate_grid[-1] + 2 * scale) + factor = plot_params['units_per_inch'] / obj.figure.dpi + gate_offset = plot_params['gate_offset'] - for i in range(n_labels): - line(axes, (x_pos[0], x_pos[-1]), (wire_grid[i], wire_grid[i]), - plot_params) + width = obj.get_window_extent().width * factor + 2 * gate_offset + height = obj.get_window_extent().height * factor + 2 * gate_offset + + axes.add_patch( + Rectangle((gate_pos - width / 2, wire_pos - height / 2), + width, + height, + ec='k', + fc='w', + fill=True, + lw=plot_params['linewidth'], + zorder=6)) -def draw_mwires(axes, x, y, gate_grid, wire_grid, plot_params): +def draw_measure_gate(axes, gate_pos, wire_pos, plot_params): """ - Add the doubling for measured wires + Draws a measurement gate. + Args: - ax (AxesSubplot): axes object - x (float): x coordinate - y (float): y coordinate - gate_grid (ndarray): grid for positioning gate - wire_grid (ndarray): grid for positioning wires - plot_params (dict): parameter for the figure + axes (AxesSubplot): axes object + gate_pos (float): x coordinate of the gate [data units] + wire_pos (float): y coordinate of the qubit wire + plot_params (dict): plot parameters """ # pylint: disable=invalid-name - scale = plot_params['scale'] - dy = plot_params['linebetween'] + width = plot_params['mgate_width'] + height = 0.9 * width + y_ref = wire_pos - 0.3 * height - # gate_grid indicate x-axes - line(axes, (x, gate_grid[-1] + 2 * scale), (y + dy, y + dy), plot_params) + # Cannot use PatchCollection for the arc due to bug in matplotlib code... + arc = Arc((gate_pos, y_ref), + width * 0.7, + height * 0.8, + theta1=0, + theta2=180, + ec='k', + fc='w', + zorder=5) + axes.add_patch(arc) + patches = [ + Rectangle((gate_pos - width / 2, wire_pos - height / 2), + width, + height, + fill=True), + Line2D((gate_pos, gate_pos + width * 0.35), + (y_ref, wire_pos + height * 0.35), + color='k', + linewidth=1) + ] -def draw_labels(axes, labels, inits, gate_grid, wire_grid, plot_params): + gate = PatchCollection(patches, + ec='k', + fc='w', + linewidths=plot_params['linewidth'], + zorder=5) + gate.set_label('Measure') + axes.add_collection(gate) + + +def multi_qubit_gate(axes, gate_str, gate_pos, wire_pos_min, wire_pos_max, + plot_params): """ - draw the qubit label + Draws a multi-target qubit gate. + Args: - ax (AxesSubplot): axes object - labels (list): labels of the qubit to be drawed - inits (list): Initialization of qubits - gate_grid (ndarray): grid for positioning gate - wire_grid (ndarray): grid for positioning wires - plot_params (dict): parameter for the figure - """ - scale = plot_params['scale'] - label_buffer = plot_params['label_buffer'] - n_labels = len(labels) - xdata = (gate_grid[0] - scale, gate_grid[-1] + scale) - for i in range(n_labels): - j = get_flipped_index(labels[i], labels) - text(axes, xdata[0] - label_buffer, wire_grid[j], - render_label(labels[i], inits), plot_params) + axes (matplotlib.axes.Axes): axes object + gate_str (str): string representation of a gate + gate_pos (float): x coordinate of the gate [data units] + wire_pos_min (float): y coordinate of the lowest qubit wire + wire_pos_max (float): y coordinate of the highest qubit wire + plot_params (dict): plot parameters + """ + gate_offset = plot_params['gate_offset'] + y_center = (wire_pos_max - wire_pos_min) / 2 + wire_pos_min + obj = axes.text(gate_pos, + y_center, + gate_str, + color='k', + ha='center', + va='center', + size=plot_params['fontsize'], + zorder=7) + height = wire_pos_max - wire_pos_min + 2 * gate_offset + inv = axes.transData.inverted() + width = inv.transform_bbox(obj.get_window_extent()).width + return axes.add_patch( + Rectangle((gate_pos - width / 2, wire_pos_min - gate_offset), + width, + height, + ec='k', + fc='w', + fill=True, + lw=plot_params['linewidth'], + zorder=6)) + + +def draw_x_gate(axes, gate_pos, wire_pos, plot_params): + """ + Draws the symbol for a X/NOT gate. + + Args: + axes (matplotlib.axes.Axes): axes object + gate_pos (float): x coordinate of the gate [data units] + wire_pos (float): y coordinate of the qubit wire [data units] + plot_params (dict): plot parameters + """ + not_radius = plot_params['not_radius'] + + gate = PatchCollection([ + Circle((gate_pos, wire_pos), not_radius, fill=False), + Line2D((gate_pos, gate_pos), + (wire_pos - not_radius, wire_pos + not_radius)) + ], + ec='k', + fc='w', + linewidths=plot_params['linewidth']) + gate.set_label('NOT') + axes.add_collection(gate) -def get_flipped_index(target, labels): +def draw_control_z_gate(axes, gate_pos, wire_pos1, wire_pos2, plot_params): """ - flip the index of the target qubit in order to match the coordination + Draws the symbol for a controlled-Z gate. - >>> get_flipped_index('q0', ['q0', 'q1']) - 1 - >>> get_flipped_index('q1', ['q0', 'q1']) - 0 + Args: + axes (matplotlib.axes.Axes): axes object + wire_pos (float): x coordinate of the gate [data units] + y1 (float): y coordinate of the 1st qubit wire + y2 (float): y coordinate of the 2nd qubit wire + plot_params (dict): plot parameters + """ + gate = PatchCollection([ + Circle( + (gate_pos, wire_pos1), plot_params['control_radius'], fill=True), + Circle( + (gate_pos, wire_pos2), plot_params['control_radius'], fill=True), + Line2D((gate_pos, gate_pos), (wire_pos1, wire_pos2)) + ], + ec='k', + fc='k', + linewidths=plot_params['linewidth']) + gate.set_label('CZ') + axes.add_collection(gate) + + +def draw_swap_gate(axes, gate_pos, wire_pos1, wire_pos2, plot_params): + """ + Draws the symbol for a SWAP gate. Args: - target (str): target qubit - labels (list): contains all labels of qubits + axes (matplotlib.axes.Axes): axes object + x (float): x coordinate [data units] + y1 (float): y coordinate of the 1st qubit wire + y2 (float): y coordinate of the 2nd qubit wire + plot_params (dict): plot parameters """ + delta = plot_params['swap_delta'] - n_labels = len(labels) - i = labels.index(target) + lines = [] + for wire_pos in (wire_pos1, wire_pos2): + lines.append([(gate_pos - delta, wire_pos - delta), + (gate_pos + delta, wire_pos + delta)]) + lines.append([(gate_pos - delta, wire_pos + delta), + (gate_pos + delta, wire_pos - delta)]) + lines.append([(gate_pos, wire_pos1), (gate_pos, wire_pos2)]) - return n_labels - i - 1 + gate = LineCollection(lines, + colors='k', + linewidths=plot_params['linewidth']) + gate.set_label('SWAP') + axes.add_collection(gate) -def get_flipped_indices(targets, labels): +def draw_wires(axes, n_labels, gate_grid, wire_grid, plot_params): """ - flip the index of the target qubit for multi targets + Draws all the circuit qubit wires. + Args: - targets (tuple): target qubit - labels (list): contains all labels of qubits + axes (matplotlib.axes.Axes): axes object + n_labels (int): number of qubit + gate_grid (ndarray): array with the ref. x positions of the gates + wire_grid (ndarray): array with the ref. y positions of the qubit + wires + plot_params (dict): plot parameters """ - return [get_flipped_index(t, labels) for t in targets] + # pylint: disable=invalid-name + lines = [] + for i in range(n_labels): + lines.append(((gate_grid[0] - plot_params['column_spacing'], + wire_grid[i]), (gate_grid[-1], wire_grid[i]))) + all_lines = LineCollection(lines, + linewidths=plot_params['linewidth'], + ec='k') + all_lines.set_label('qubit_wires') + axes.add_collection(all_lines) -def render_label(label, inits): + +def draw_labels(axes, qubit_labels, drawing_order, wire_grid, plot_params): """ - render qubit label as |0> + Draws the labels at the start of each qubit wire + Args: - label: label of the qubit - inits (list): initial qubits - """ - if label in inits: - return r'$|{}\rangle$'.format(inits[label]) - return r'$|{}\rangle$'.format(label) + axes (matplotlib.axes.Axes): axes object + qubit_labels (list): labels of the qubit to be drawn + drawing_order (dict): Mapping between wire indices and qubit IDs + gate_grid (ndarray): array with the ref. x positions of the gates + wire_grid (ndarray): array with the ref. y positions of the qubit + wires + plot_params (dict): plot parameters + """ + for qubit_id in qubit_labels: + wire_idx = drawing_order[qubit_id] + text(axes, plot_params['x_offset'], wire_grid[wire_idx], + qubit_labels[qubit_id], plot_params) diff --git a/projectq/backends/_circuits/_plot_test.py b/projectq/backends/_circuits/_plot_test.py index bfb6099ac..348f44153 100644 --- a/projectq/backends/_circuits/_plot_test.py +++ b/projectq/backends/_circuits/_plot_test.py @@ -11,7 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - """ Tests for projectq.backends._circuits._plot.py. @@ -21,73 +20,227 @@ Then run the tests simply with '--mpl' """ import pytest +from copy import deepcopy +import platform + +if platform.system() == 'Darwin': + import matplotlib + matplotlib.use('Qt5Agg') + from projectq import MainEngine from projectq.ops import * from projectq.backends import CircuitDrawerMatplotlib import projectq.backends._circuits._plot as _plot -@pytest.mark.mpl_image_compare -def test_draw_single_gates(): - allocate_qubit = [0] - gates = [('H',(0,))] - fig, ax = _plot.to_draw(gates, allocate_qubit) - return fig - -@pytest.mark.mpl_image_compare -def test_draw_multi_gates(): - allocate_qubit = [0,1,2,3] - gates = [('H',(0,)), ('H',(0,))] - fig, ax = _plot.to_draw(gates, allocate_qubit) - return fig - -@pytest.mark.mpl_image_compare -def test_gates_position(): - allocate_qubit = [0,1,2,3] - gates = [('H',(3,))] - fig, ax = _plot.to_draw(gates, allocate_qubit) - return fig - -@pytest.mark.mpl_image_compare -def test_gates_position2(): - allocate_qubit = [0,1,2,3] - gates = [('H',(3,)), ('X',(1,),(0,)), ('H',(2,)), ('H',(2,)),('H',(2,)),('X',(3,),(0,))] - fig, ax = _plot.to_draw(gates, allocate_qubit) - return fig - -@pytest.mark.mpl_image_compare -def test_simple_CNOT(): - allocate_qubit = [0,1] - gates = [('X',(1,),(0,))] - fig, ax = _plot.to_draw(gates, allocate_qubit) - return fig - -@pytest.mark.mpl_image_compare -def test_complex_CNOT(): - allocate_qubit = [0,1,2,3] - gates = [('X',(3,),(0,))] - fig, ax = _plot.to_draw(gates, allocate_qubit) - return fig - -@pytest.mark.mpl_image_compare -def test_complex_CNOT2(): - allocate_qubit = [0,1,2,3] - gates = [('X',(0,),(3,))] - fig, ax = _plot.to_draw(gates, allocate_qubit) - return fig - -@pytest.mark.mpl_image_compare -def test_qubit_numbers(): - # set up qubit numbers without quantum gates - allocate_qureg = [0, 1, 2, 3, 4] - gates = [] - fig, ax = _plot.to_draw(gates, allocate_qureg) - return fig - -@pytest.mark.mpl_image_compare -def test_measure_gate(): - # set up qubit numbers without quantum gates - allocate_qureg = [0] - gates = [('Measure',(0,))] - fig, ax = _plot.to_draw(gates, allocate_qureg) - return fig \ No newline at end of file + +class PseudoCanvas(object): + def __init__(self): + pass + + def draw(self): + pass + + def get_renderer(self): + return + + +class PseudoFigure(object): + def __init__(self): + self.canvas = PseudoCanvas() + self.dpi = 1 + + +class PseudoBBox(object): + def __init__(self, width, height): + self.width = width + self.height = height + + +class PseudoText(object): + def __init__(self, text): + self.text = text + self.figure = PseudoFigure() + + def get_window_extent(self, *args): + return PseudoBBox(len(self.text), 1) + + def remove(self): + pass + + +class PseudoTransform(object): + def __init__(self): + pass + + def inverted(self): + return self + + def transform_bbox(self, bbox): + return bbox + + +class PseudoAxes(object): + def __init__(self): + self.figure = PseudoFigure() + self.transData = PseudoTransform() + + def add_patch(self, x): + return x + + def text(self, x, y, text, *args, **kwargse): + return PseudoText(text) + + +# ============================================================================== + + +@pytest.fixture(scope="module") +def plot_params(): + params = deepcopy(_plot._DEFAULT_PLOT_PARAMS) + params.update([('units_per_inch', 1)]) + return params + + +@pytest.fixture +def axes(): + return PseudoAxes() + + +# ============================================================================== + + +@pytest.mark.parametrize('gate_str', ['X', 'Swap', 'Measure', 'Y', 'Rz(1.00)']) +def test_gate_width(axes, gate_str, plot_params): + width = _plot.gate_width(axes, gate_str, plot_params) + if gate_str == 'X': + assert width == 2 * plot_params['not_radius'] / plot_params[ + 'units_per_inch'] + elif gate_str == 'Swap': + assert width == 2 * plot_params['swap_delta'] / plot_params[ + 'units_per_inch'] + elif gate_str == 'Measure': + assert width == plot_params['mgate_width'] + else: + assert width == len(gate_str) + 2 * plot_params['gate_offset'] + + +def test_calculate_gate_grid(axes, plot_params): + qubit_lines = { + 0: [('X', [0], []), ('X', [0], []), ('X', [0], []), ('X', [0], [])] + } + + gate_grid = _plot.calculate_gate_grid(axes, qubit_lines, plot_params) + assert len(gate_grid) == 5 + assert gate_grid[0] > plot_params['labels_margin'] + width = [gate_grid[i + 1] - gate_grid[i] for i in range(4)] + + # Column grid is given by: + # |---*---|---*---|---*---|---*---| + # |-- w --|-- w --|-- w --|.5w| + + column_spacing = plot_params['column_spacing'] + ref_width = _plot.gate_width(axes, 'X', plot_params) + + for w in width[:-1]: + assert ref_width + column_spacing == pytest.approx(w) + assert 0.5 * ref_width + column_spacing == pytest.approx(width[-1]) + + +def test_create_figure(plot_params): + fig, axes = _plot.create_figure(plot_params) + + +def test_draw_single_gate(axes, plot_params): + with pytest.raises(RuntimeError): + _plot.draw_gate(axes, 'MyGate', 2, [0, 0, 0], [0, 1, 3], [], + plot_params) + _plot.draw_gate(axes, 'MyGate', 2, [0, 0, 0], [0, 1, 2], [], plot_params) + + +def test_draw_simple(plot_params): + qubit_lines = { + 0: [('X', [0], []), ('Z', [0], []), ('Z', [0], [1]), + ('Swap', [0, 1], []), ('Measure', [0], [])], + 1: [None, None, None, None, None] + } + fig, axes = _plot.to_draw(qubit_lines) + + units_per_inch = plot_params['units_per_inch'] + not_radius = plot_params['not_radius'] + control_radius = plot_params['control_radius'] + swap_delta = plot_params['swap_delta'] + wire_height = plot_params['wire_height'] * units_per_inch + mgate_width = plot_params['mgate_width'] + + labels = [] + text_gates = [] + measure_gates = [] + for text in axes.texts: + if text.get_text() == '$|0\\rangle$': + labels.append(text) + elif text.get_text() == ' ': + measure_gates.append(text) + else: + text_gates.append(text) + + assert all( + label.get_position()[0] == pytest.approx(plot_params['x_offset']) + for label in labels) + assert (abs(labels[1].get_position()[1] + - labels[0].get_position()[1]) == pytest.approx(wire_height)) + + # X gate + x_gate = [obj for obj in axes.collections if obj.get_label() == 'NOT'][0] + # find the filled circles + assert (x_gate.get_paths()[0].get_extents().width == pytest.approx( + 2 * not_radius)) + assert (x_gate.get_paths()[0].get_extents().height == pytest.approx( + 2 * not_radius)) + # find the vertical bar + x_vertical = x_gate.get_paths()[1] + assert len(x_vertical) == 2 + assert x_vertical.get_extents().width == 0. + assert (x_vertical.get_extents().height == pytest.approx( + 2 * plot_params['not_radius'])) + + # Z gate + assert len(text_gates) == 1 + assert text_gates[0].get_text() == 'Z' + assert text_gates[0].get_position()[1] == pytest.approx(2 * wire_height) + + # CZ gate + cz_gate = [obj for obj in axes.collections if obj.get_label() == 'CZ'][0] + # find the filled circles + for control in cz_gate.get_paths()[:-1]: + assert control.get_extents().width == pytest.approx(2 * control_radius) + assert control.get_extents().height == pytest.approx(2 + * control_radius) + # find the vertical bar + cz_vertical = cz_gate.get_paths()[-1] + assert len(cz_vertical) == 2 + assert cz_vertical.get_extents().width == 0. + assert (cz_vertical.get_extents().height == pytest.approx(wire_height)) + + # Swap gate + swap_gate = [obj for obj in axes.collections + if obj.get_label() == 'SWAP'][0] + # find the filled circles + for qubit in swap_gate.get_paths()[:-1]: + assert qubit.get_extents().width == pytest.approx(2 * swap_delta) + assert qubit.get_extents().height == pytest.approx(2 * swap_delta) + # find the vertical bar + swap_vertical = swap_gate.get_paths()[-1] + assert len(swap_vertical) == 2 + assert swap_vertical.get_extents().width == 0. + assert (swap_vertical.get_extents().height == pytest.approx(wire_height)) + + # Measure gate + measure_gate = [ + obj for obj in axes.collections if obj.get_label() == 'Measure' + ][0] + + assert (measure_gate.get_paths()[0].get_extents().width == pytest.approx( + mgate_width)) + assert (measure_gate.get_paths()[0].get_extents().height == pytest.approx( + 0.9 * mgate_width)) From d105086b9495287933b416ee8bed9918ac8fb2f6 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Mon, 3 Feb 2020 09:49:21 +0100 Subject: [PATCH 29/37] Complete test coverage + add some checks for to_draw() inputs --- .../backends/_circuits/_drawer_matplotlib.py | 2 +- projectq/backends/_circuits/_plot.py | 158 +++++++++++------- projectq/backends/_circuits/_plot_test.py | 57 ++++++- 3 files changed, 148 insertions(+), 69 deletions(-) diff --git a/projectq/backends/_circuits/_drawer_matplotlib.py b/projectq/backends/_circuits/_drawer_matplotlib.py index 1bf000566..e2b29880d 100644 --- a/projectq/backends/_circuits/_drawer_matplotlib.py +++ b/projectq/backends/_circuits/_drawer_matplotlib.py @@ -21,7 +21,7 @@ import itertools from projectq.cengines import LastEngineException, BasicEngine -from projectq.ops import (SwapGate, FlushGate, Measure, Allocate, Deallocate) +from projectq.ops import (FlushGate, Measure, Allocate, Deallocate) from projectq.meta import get_control_count from projectq.backends._circuits import to_draw diff --git a/projectq/backends/_circuits/_plot.py b/projectq/backends/_circuits/_plot.py index 69215ce81..6d494b911 100644 --- a/projectq/backends/_circuits/_plot.py +++ b/projectq/backends/_circuits/_plot.py @@ -11,6 +11,17 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +""" +This module provides the basic functionality required to plot a quantum +circuit in a matplotlib figure. +It is mainly used by the CircuitDrawerMatplotlib compiler engine. + +Currently, it supports all single-qubit gates, including their controlled +versions to an arbitrary number of control qubits. It also supports +multi-target qubit gates under some restrictions. Namely that the target +qubits must be neighbours in the output figure (which cannot be determined +durinng compilation at this time). +""" from copy import deepcopy import numpy as np @@ -46,6 +57,89 @@ x_offset=.05, wire_height=1) +# ============================================================================== + + +def to_draw(qubit_lines, qubit_labels=None, drawing_order=None, **kwargs): + """ + Translates a given circuit to a matplotlib figure. + + Args: + qubit_lines (dict): list of gates for each qubit axis + qubit_labels (dict): label to print in front of the qubit wire for + each qubit ID + drawing_order (dict): index of the wire for each qubit ID to be drawn. + **kwargs (dict): additional parameters are used to update the default + plot parameters + + Returns: + A tuple with (figure, axes) + + Note: + Numbering of qubit wires starts at 0 at the bottom and increases + vertically. + """ + if qubit_labels is None: + qubit_labels = {qubit_id: r'$|0\rangle$' for qubit_id in qubit_lines} + else: + if list(qubit_labels) != list(qubit_lines): + raise RuntimeError('Qubit IDs in qubit_labels do not match ' + + 'qubit IDs in qubit_lines!') + + if drawing_order is None: + n_qubits = len(qubit_lines) + drawing_order = { + qubit_id: n_qubits - qubit_id - 1 + for qubit_id in list(qubit_lines) + } + else: + if list(drawing_order) != list(qubit_lines): + raise RuntimeError('Qubit IDs in drawing_order do not match ' + + 'qubit IDs in qubit_lines!') + if (list(sorted(drawing_order.values())) != list( + range(len(drawing_order)))): + raise RuntimeError( + 'Indices of qubit wires in drawing_order ' + + 'must be between 0 and {}!'.format(len(drawing_order))) + + plot_params = deepcopy(_DEFAULT_PLOT_PARAMS) + plot_params.update(kwargs) + + n_labels = len(list(qubit_lines)) + + wire_height = plot_params['wire_height'] + # Grid in inches + wire_grid = np.arange(wire_height, (n_labels + 1) * wire_height, + wire_height, + dtype=float) + + fig, axes = create_figure(plot_params) + + # Grid in inches + gate_grid = calculate_gate_grid(axes, qubit_lines, plot_params) + + width = gate_grid[-1] + plot_params['column_spacing'] + height = wire_grid[-1] + wire_height + + resize_figure(fig, axes, width, height, plot_params) + + # Convert grids into data coordinates + units_per_inch = plot_params['units_per_inch'] + + gate_grid *= units_per_inch + gate_grid = gate_grid + plot_params['x_offset'] + wire_grid *= units_per_inch + plot_params['column_spacing'] *= units_per_inch + + draw_wires(axes, n_labels, gate_grid, wire_grid, plot_params) + + draw_labels(axes, qubit_labels, drawing_order, wire_grid, plot_params) + + draw_gates(axes, qubit_lines, drawing_order, gate_grid, wire_grid, + plot_params) + return fig, axes + + # ============================================================================== # Functions used to calculate the layout @@ -64,8 +158,6 @@ def gate_width(axes, gate_str, plot_params): """ if gate_str == 'X': return 2 * plot_params['not_radius'] / plot_params['units_per_inch'] - # if gate_str == 'Z': - # return ... if gate_str == 'Swap': return 2 * plot_params['swap_delta'] / plot_params['units_per_inch'] @@ -187,68 +279,6 @@ def resize_figure(fig, axes, width, height, plot_params): axes.set_ylim(0, new_limits[1]) -def to_draw(qubit_lines, qubit_labels=None, drawing_order=None, **kwargs): - """ - Draws a quantum circuit in a matplotlib figure. - - Args: - qubit_lines (dict): list of gates for each qubit axis - qubit_labels (dict): label to print in front of the qubit wire for - each qubit ID - drawing_order (dict): index of the wire for each qubit ID to be drawn - **kwargs (dict): additional parameters are used to update the default - plot parameters - - Returns: - A tuple with (figure, axes) - """ - if drawing_order is None: - n_qubits = len(qubit_lines) - drawing_order = { - qubit_id: n_qubits - qubit_id - 1 - for qubit_id in list(qubit_lines) - } - - plot_params = deepcopy(_DEFAULT_PLOT_PARAMS) - plot_params.update(kwargs) - - n_labels = len(list(qubit_lines)) - - wire_height = plot_params['wire_height'] - # Grid in inches - wire_grid = np.arange(wire_height, (n_labels + 1) * wire_height, - wire_height, - dtype=float) - - fig, axes = create_figure(plot_params) - - # Grid in inches - gate_grid = calculate_gate_grid(axes, qubit_lines, plot_params) - - width = gate_grid[-1] + plot_params['column_spacing'] - height = wire_grid[-1] + wire_height - - resize_figure(fig, axes, width, height, plot_params) - - # Convert grids into data coordinates - units_per_inch = plot_params['units_per_inch'] - - gate_grid *= units_per_inch - gate_grid = gate_grid + plot_params['x_offset'] - wire_grid *= units_per_inch - plot_params['column_spacing'] *= units_per_inch - - draw_wires(axes, n_labels, gate_grid, wire_grid, plot_params) - - if qubit_labels is None: - qubit_labels = {qubit_id: r'$|0\rangle$' for qubit_id in qubit_lines} - draw_labels(axes, qubit_labels, drawing_order, wire_grid, plot_params) - - draw_gates(axes, qubit_lines, drawing_order, gate_grid, wire_grid, - plot_params) - return fig, axes - - def draw_gates(axes, qubit_lines, drawing_order, gate_grid, wire_grid, plot_params): """ diff --git a/projectq/backends/_circuits/_plot_test.py b/projectq/backends/_circuits/_plot_test.py index 348f44153..ef7319f8f 100644 --- a/projectq/backends/_circuits/_plot_test.py +++ b/projectq/backends/_circuits/_plot_test.py @@ -27,12 +27,10 @@ import matplotlib matplotlib.use('Qt5Agg') -from projectq import MainEngine -from projectq.ops import * -from projectq.backends import CircuitDrawerMatplotlib - import projectq.backends._circuits._plot as _plot +# ============================================================================== + class PseudoCanvas(object): def __init__(self): @@ -244,3 +242,54 @@ def test_draw_simple(plot_params): mgate_width)) assert (measure_gate.get_paths()[0].get_extents().height == pytest.approx( 0.9 * mgate_width)) + + +def test_draw_advanced(plot_params): + qubit_lines = {0: [('X', [0], []), ('Measure', [0], [])], 1: [None, None]} + + with pytest.raises(RuntimeError): + _plot.to_draw(qubit_lines, qubit_labels={1: 'qb1', 2: 'qb2'}) + + with pytest.raises(RuntimeError): + _plot.to_draw(qubit_lines, drawing_order={0: 0, 1: 2}) + + with pytest.raises(RuntimeError): + _plot.to_draw(qubit_lines, drawing_order={1: 1, 2: 0}) + + # -------------------------------------------------------------------------- + + _, axes = _plot.to_draw(qubit_lines) + for text in axes.texts: + assert text.get_text() == r'$|0\rangle$' + + # NB numbering of wire starts from bottom. + _, axes = _plot.to_draw(qubit_lines, + qubit_labels={ + 0: 'qb0', + 1: 'qb1' + }, + drawing_order={ + 0: 0, + 1: 1 + }) + assert ([axes.texts[qubit_id].get_text() + for qubit_id in range(2)] == ['qb0', 'qb1']) + + positions = [axes.texts[qubit_id].get_position() for qubit_id in range(2)] + assert positions[1][1] > positions[0][1] + + _, axes = _plot.to_draw(qubit_lines, + qubit_labels={ + 0: 'qb2', + 1: 'qb3' + }, + drawing_order={ + 0: 1, + 1: 0 + }) + + assert ([axes.texts[qubit_id].get_text() + for qubit_id in range(2)] == ['qb2', 'qb3']) + + positions = [axes.texts[qubit_id].get_position() for qubit_id in range(2)] + assert positions[1][1] < positions[0][1] From fa3a7bd1d2fe208c3d03bce43059503eba7588b8 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Mon, 3 Feb 2020 10:08:21 +0100 Subject: [PATCH 30/37] Compatibility with matplotlib 2.2.3 --- .../_circuits/_drawer_matplotlib_test.py | 1 - projectq/backends/_circuits/_plot.py | 28 ++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/projectq/backends/_circuits/_drawer_matplotlib_test.py b/projectq/backends/_circuits/_drawer_matplotlib_test.py index ad30ff250..a76fbc99b 100644 --- a/projectq/backends/_circuits/_drawer_matplotlib_test.py +++ b/projectq/backends/_circuits/_drawer_matplotlib_test.py @@ -16,7 +16,6 @@ """ import pytest - from projectq import MainEngine from projectq.cengines import DummyEngine from projectq.ops import (H, X, Rx, CNOT, Swap, Measure, Command, BasicGate) diff --git a/projectq/backends/_circuits/_plot.py b/projectq/backends/_circuits/_plot.py index 6d494b911..009b00ab7 100644 --- a/projectq/backends/_circuits/_plot.py +++ b/projectq/backends/_circuits/_plot.py @@ -168,7 +168,7 @@ def gate_width(axes, gate_str, plot_params): 0, gate_str, visible=True, - bbox=dict(ec='k', fc='w', fill=True, lw=1.0), + bbox=dict(edgecolor='k', facecolor='w', fill=True, lw=1.0), fontsize=14) obj.figure.canvas.draw() width = (obj.get_window_extent(obj.figure.canvas.get_renderer()).width @@ -386,8 +386,9 @@ def draw_generic_gate(axes, gate_pos, wire_pos, gate_str, plot_params): factor = plot_params['units_per_inch'] / obj.figure.dpi gate_offset = plot_params['gate_offset'] - width = obj.get_window_extent().width * factor + 2 * gate_offset - height = obj.get_window_extent().height * factor + 2 * gate_offset + renderer = obj.figure.canvas.get_renderer() + width = obj.get_window_extent(renderer).width * factor + 2 * gate_offset + height = obj.get_window_extent(renderer).height * factor + 2 * gate_offset axes.add_patch( Rectangle((gate_pos - width / 2, wire_pos - height / 2), @@ -439,8 +440,8 @@ def draw_measure_gate(axes, gate_pos, wire_pos, plot_params): ] gate = PatchCollection(patches, - ec='k', - fc='w', + edgecolors='k', + facecolors='w', linewidths=plot_params['linewidth'], zorder=5) gate.set_label('Measure') @@ -472,13 +473,14 @@ def multi_qubit_gate(axes, gate_str, gate_pos, wire_pos_min, wire_pos_max, zorder=7) height = wire_pos_max - wire_pos_min + 2 * gate_offset inv = axes.transData.inverted() - width = inv.transform_bbox(obj.get_window_extent()).width + width = inv.transform_bbox( + obj.get_window_extent(obj.figure.canvas.get_renderer())).width return axes.add_patch( Rectangle((gate_pos - width / 2, wire_pos_min - gate_offset), width, height, - ec='k', - fc='w', + edgecolor='k', + facecolor='w', fill=True, lw=plot_params['linewidth'], zorder=6)) @@ -501,8 +503,8 @@ def draw_x_gate(axes, gate_pos, wire_pos, plot_params): Line2D((gate_pos, gate_pos), (wire_pos - not_radius, wire_pos + not_radius)) ], - ec='k', - fc='w', + edgecolors='k', + facecolors='w', linewidths=plot_params['linewidth']) gate.set_label('NOT') axes.add_collection(gate) @@ -526,8 +528,8 @@ def draw_control_z_gate(axes, gate_pos, wire_pos1, wire_pos2, plot_params): (gate_pos, wire_pos2), plot_params['control_radius'], fill=True), Line2D((gate_pos, gate_pos), (wire_pos1, wire_pos2)) ], - ec='k', - fc='k', + edgecolors='k', + facecolors='k', linewidths=plot_params['linewidth']) gate.set_label('CZ') axes.add_collection(gate) @@ -581,7 +583,7 @@ def draw_wires(axes, n_labels, gate_grid, wire_grid, plot_params): wire_grid[i]), (gate_grid[-1], wire_grid[i]))) all_lines = LineCollection(lines, linewidths=plot_params['linewidth'], - ec='k') + edgecolor='k') all_lines.set_label('qubit_wires') axes.add_collection(all_lines) From f8d9d79939c7f944c3aae07dfb4871389f6afbf3 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Mon, 3 Feb 2020 10:08:39 +0100 Subject: [PATCH 31/37] Remove compatibility code for MacOSX. Use local matplotlibrc if necessary instead. --- projectq/backends/_circuits/_plot_test.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/projectq/backends/_circuits/_plot_test.py b/projectq/backends/_circuits/_plot_test.py index ef7319f8f..cd5d3ab0f 100644 --- a/projectq/backends/_circuits/_plot_test.py +++ b/projectq/backends/_circuits/_plot_test.py @@ -21,12 +21,6 @@ """ import pytest from copy import deepcopy -import platform - -if platform.system() == 'Darwin': - import matplotlib - matplotlib.use('Qt5Agg') - import projectq.backends._circuits._plot as _plot # ============================================================================== From bd44103fc34bce157071ffcb1111824b08055d14 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Mon, 3 Feb 2020 10:09:13 +0100 Subject: [PATCH 32/37] Add matplotlib dependency to requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 903d45bdc..60d6b013c 100755 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ pybind11>=2.2.3 requests scipy networkx +matplotlib>=2.2.3 From 2be6cb48bc96aa65515de2f475d076ef5ac598cf Mon Sep 17 00:00:00 2001 From: Nguyen Damien Date: Mon, 3 Feb 2020 16:59:20 +0100 Subject: [PATCH 33/37] Fix non-UTF8 character in file --- projectq/backends/_circuits/_drawer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projectq/backends/_circuits/_drawer.py b/projectq/backends/_circuits/_drawer.py index 35290e0d6..2562a07dd 100755 --- a/projectq/backends/_circuits/_drawer.py +++ b/projectq/backends/_circuits/_drawer.py @@ -59,7 +59,7 @@ class CircuitDrawer(BasicEngine): After initializing the CircuitDrawer, it can also be given the mapping from qubit IDs to wire location (via the :meth:`set_qubit_locations` function): -ยทยท + .. code-block:: python circuit_backend = CircuitDrawer() From 90f7654517dc5490b26b5d6a938b6f6fefdc1156 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Tue, 4 Feb 2020 09:34:36 +0100 Subject: [PATCH 34/37] Fix .travis.yml --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index fbe009436..47f727e2a 100755 --- a/.travis.yml +++ b/.travis.yml @@ -42,6 +42,9 @@ install: - if [ "${PYTHON:0:1}" = "3" ]; then pip$PY install dormouse; fi - pip$PY install -e . +before_script: + - "echo 'backend: Agg' > matplotlibrc" + # command to run tests script: export OMP_NUM_THREADS=1 && pytest projectq --cov projectq From 398741324a557ae515142d2ff469009cefe75e44 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Tue, 4 Feb 2020 09:52:57 +0100 Subject: [PATCH 35/37] Remove unnecessary PNG files --- .../_circuits/baseline/test_complex_CNOT.png | Bin 3096 -> 0 bytes .../_circuits/baseline/test_complex_CNOT2.png | Bin 2963 -> 0 bytes .../baseline/test_draw_multi_gates.png | Bin 4562 -> 0 bytes .../baseline/test_draw_single_gates.png | Bin 1413 -> 0 bytes .../_circuits/baseline/test_gates_position.png | Bin 2440 -> 0 bytes .../_circuits/baseline/test_gates_position2.png | Bin 8037 -> 0 bytes .../_circuits/baseline/test_measure_gate.png | Bin 1792 -> 0 bytes .../_circuits/baseline/test_qubit_numbers.png | Bin 6099 -> 0 bytes .../_circuits/baseline/test_simple_CNOT.png | Bin 2362 -> 0 bytes projectq/tests/baseline/test_drawer_mpl.png | Bin 24432 -> 0 bytes 10 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 projectq/backends/_circuits/baseline/test_complex_CNOT.png delete mode 100644 projectq/backends/_circuits/baseline/test_complex_CNOT2.png delete mode 100644 projectq/backends/_circuits/baseline/test_draw_multi_gates.png delete mode 100644 projectq/backends/_circuits/baseline/test_draw_single_gates.png delete mode 100644 projectq/backends/_circuits/baseline/test_gates_position.png delete mode 100644 projectq/backends/_circuits/baseline/test_gates_position2.png delete mode 100644 projectq/backends/_circuits/baseline/test_measure_gate.png delete mode 100644 projectq/backends/_circuits/baseline/test_qubit_numbers.png delete mode 100644 projectq/backends/_circuits/baseline/test_simple_CNOT.png delete mode 100644 projectq/tests/baseline/test_drawer_mpl.png diff --git a/projectq/backends/_circuits/baseline/test_complex_CNOT.png b/projectq/backends/_circuits/baseline/test_complex_CNOT.png deleted file mode 100644 index a723f242c7e507b10bba2016040fd4dca78f6bb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3096 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 Q_ZJA_s*x_?^6iIz1K14!%K!iX diff --git a/projectq/backends/_circuits/baseline/test_draw_single_gates.png b/projectq/backends/_circuits/baseline/test_draw_single_gates.png deleted file mode 100644 index baf8991121a82d03367a763e73e92b6ca9e707ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1413 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 diff --git a/projectq/backends/_circuits/baseline/test_qubit_numbers.png b/projectq/backends/_circuits/baseline/test_qubit_numbers.png deleted file mode 100644 index 7180a15c2803cf29b7411ce56a9b1d682906634f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6099 zcmeHLX;4$i7H&WkQA9yR1w=tbMK(b|5&H~=6a*laS}p`P}BRtj|67H(yGQwF-?Wxgdq&pSh|xP}8D ze|xh@Jhgn{2X&f8Si3|xV!R`;x5Ms$pxY54ftZLuKhL94cf!K`Fu{hp2D%1kkNQSL zgqZ2+{qe$-!nXK}h=7+C1PuihTKcJ^lB;o z`^_o-eZLNpeUR{kwP=Yd$;9ME=OSennTJ2V}4XOf%6f6_g2yVm! zOeV93!GMp|g@5R3p6Y#4*{t`&Z>)1+_$gT!LQAy z+0(!RzYgaze$ccTAA5Dr@kqH;qUDX10_QUi2}@cdnM$e6OVD0kgkQUMEmXj{p_eN$ zYIzJt&&$o#(C{4ktrYKH>6Xst^AGvJU@$hXVPg+(L}C<~vw-DnRC#yi9iMax!+6VV@q$QsKKI|s94@f zRgzN~e_gz48Zz3LMmE8Wq-AO$bzjFZDZxxLwxSSRCkZZP5C$9Y8(FM8kw|0%?cmit zc_hrx!s3W%j+rNHHe4*MskzV@bOapM6JHM9x9^Gl;}m&y3&gEkERvVGxq0aP=*vOq zYcC9GG};Z_9IuJChp8`4fDY!rtF16WLFrn^pPP#9a>k(_g%5f9)PdB{nSSCSpI}Dy zJ`s;~t4T+~V=fy(*VKsOji_ZDG0*L*5sSqYF^iKfkfD+oX|U*J%LpS4^p!cYe^1_( zj+O%6*XUX?;+4oW$kotksL*6VM{WT!j5}P(nV$9+90u2OvMyo#{O&7hJH0hGId@J$ zN=gbcPuW8oxR@rpWjnv`xC_J1HFf7(vX}VmbhI>>)_{2LVjF4>Jb(Kc(cl*B|8dw) zboZ|lOG{5{Xk_c;tu)yE4f#byMVe+X*axw-A*&vjT3A>ZBena#?XCk@ulX##qa;E=~`fI7g z*y2xPfV{lCUdE(E0sqYoa}Cdpb+KatW`xC3@AE&uN8JL>?O0lhW>B%1VdZYnn1532 zIQQisA3M^yh5W66|NjO2E#!a3G5^az{<ZbeayTstFqMi&L337K=49F+rO07&YOcD}t$0Iqj)6=fGNamNO?)PF}vX z9=AH)bYC%ad10bDK^%Lttj{!Rb}%d4BxrblvBPO}U35?4H3^SkD7m9jsb0{qP!7HD zR%_SK<|;TGjz2p@KK&uz5^quNUh~@{ZOKN=rXb*Q(}hCePWjr(%1UcHJ3DyvyhqzI zm&@f(_mwRPrg}ZgcY^S23l0=%`3n>6gU@fj`i`A!b*!IsOC8GQ@}uTDel+aq?Ij=F zT+&Wm0A;=i1fuk2?t*KDOByr;3B$gB{X_K9{8-k#88C{>vfw~+M`QiI=vzn!)F+6| zxbBXQj;iLdZ&SS?oqR=IP*PGtAh=WoPUl0a$}2uTzGbMZt7~bM*YJvp1xvlfYicVE zL|3wROLt34O9@SB0}rW^2ar@bsH&P)RaF(koPk_sU}R)OV(q*rj+$c(ag-*lR|-hq zP>p4it#-@HBbNgM1J4=t-aS-vqZPEJm)x%?aIq&QCEDKeY-eCjUx z%tO!C2WtE|CUs|PS)YF$B-Qq)n(DJ>JM&D~@Bhf$wSX28QIxKw@65qlZ5)(c3wbQd z3DG2!T5syH`LS2MU1=E^t+Y5%369?p?;9L!AD*`(vBhrVx;vt&)bYtUBn5f~21GM1 zNF-7f9<48&W5T#!V=$Pk_6e7ounCPI$Ptv|<#ZGZ#r`8xD|G5jarBD7FMfI9L`t(K zqc$?2x*X6hiTiv+Y@)_C^t9H z<`kvK?)0>@rpCsLka^OBvfzMyVfKX5g~idX$1wilhi+3y;1)ScB-U=`_TMjL!@$|| zpHanQ3-(h#c8i^IFZn5L?Y1uV%eaepvBq&I{o_WaAm-slkUXa_WvXv z2x8Vow(M8{1?W#?fXT^8BT5YLt^40PAIv|o-N=gja*+QR`=ua%t1A0nt1*omAk>2= V^YyQ_VNd}c*jn2o%B?(-{sWoi%KiWV diff --git a/projectq/backends/_circuits/baseline/test_simple_CNOT.png b/projectq/backends/_circuits/baseline/test_simple_CNOT.png deleted file mode 100644 index ef7440144438200af9b10691fb0909ca3fb79942..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2362 zcmdT`dpy(YAOBWzo5prrQd*;Qk%ndz8Z+07a!F@M&2hhN*;p=-+?CrQ!%=R(V@wHK z$;NM%X>_DqGIXK*XwLWi_51z#`}aJr*XQ}Xp3n3A@w{H|%jc7E!O3=y{62XA z0QT71S-F7O7mNW}X>doFS@nQPifU=^CJU}OS>JT;Rl3~Xp~SgEi}jn8RZv24I+o5Q3fajU1T7QMlsRT`|mx991);z?Ueiv%c9}Q>T zN$j_JReY=2J=N~theM4H>FCHiIy!O}XdW+z{D^1qr_gAgKwvzF>*(xkBu`__V%G`m zj8;}cbppxgHvo2#b_V!vH%e?hRn&RZrekCQK(>tfIP?hO8uFW;Hsmg0> zYpd>hH8`l5kdRPPT3RX`fAfX_)KpX?O2%KmwyUkJHJVI$w}i#(=;$aR5aDY!2Q)Mk zl6r2YDIOFq9Yi3q&zE8UoNOVNm{Yt`f^+loU~yaDVf&3j8-(>^3k~rMM-A zyu`{%t+0RvOSdE%oxThB8q+x(=I!nMUGih|&Nl}F!74KH@$g`ePr1r!Y~jVEqG5j^ zLGo$)!~6Fs6%`fgD|5@s*#!j!)}0sPzRttq;^G>D`8v<~`bgKWU*}~%#PE^J%gYH- z6t>9PS@}**P93!5{{8IH(NXK@XHhg7^2`}^^Z2csmB(soYkP{(0j2R?0UNgB)W;zL z*3_U+)n`;@uwpriVq(?l!ZdwMJwM{rUS`jI)#OB!#KipiA0;c!ukQzB8;JyW`gA+L2_ zERKBf1oG#6T}rSL2!?pF<=qfN&wWzGx<~Nn%C&i}CLc$h?#kkDIK=HMCy+>swa_q> zyuADccX#QxBWyN@qnwzS2tm;83EjswHhWilQy`qkkM4C4Mq8W7PK(IMNVNCsW0{sD z66xzlfnM07B7bKh(H^Mj>WX*Hsl&9Zr5-(h--=AuW3gD(%_=G?nnp&dd3ky0DQ0Em zP9Bf9y1L3;C867I-UI-2S+(9#mL3s!J2#gm6nc1d#i_5Ytrg6ZK+AJfx+(9GIAx@PIBv7(zz* z{1lKc$;ruqn14Bp>JDV>H_k0CuH{ouLptd_SD!BO>*mY`R#){)N=jziQ}#8)xa#TZ zmI{y{=lT}LnD2K%_Z6+_4#8}*HF!RIcWjhvnEt;WG0+s~Zzu;>Q%jqfnNexP{!%*# zR(y$u=o3D~ud}m;u_-O|rG&N3U$uWmtb(Thj#9FP*gG zAU4i|NK}tnnc4%SXJp`PZDs1ObZ3LI_Ueq`*HU@HCNI?+Up_d3IxtI+XhV`Jj@4jQ(OGAR^tr>Aoo8uaV#HebGc*)`$(bXSM~<>v0LrKRQL)Xyuas-s*S zX)-Z2l>^?-%}tidN$uX9S5{UgVy7f0OT}zUIz=~xLSgl*Z1?{hv^}}dG{5nnSG=@j z<>CUDhd^Km1orU{Q9Iqv!2ur>bleh;XEyK8vU%Xy0*#K2rkcERA&&a-5TJdS-`!^a z(hm)tY~BO13N3k-%H?uv4Lm(OxI$s30|Pa@aoVA8Ins_mQ1I@G3wr#pu@RP8iPY32 zJ>LXXUe(c|VN>PYPIRgoTLA4Ru|MZvRYe6ZvO6O~ZgzI|sX(Ci>C-2t;@hq@ooPz2 z8U;j2)g@iXV7P{d*Z=XT_zP&a=Rhs^>hGNgu`|SCosQ&Z z0J(#{mG|!>GIMf{(&=4r#d40W_#nNGp;6vEb#kbY;ymNVh zUT+INd|3M^pv^p!lUGnU3@F3lmM!5TE%ro5%@=NFH4YAk*R-}qS5QmTZueP&pVL(x z`c>n~i4xtQU4`*P4wE_8bSA&aw$K`VXsEK7Hyh9mHVINh1iU}{z?hE{$}t~lY5Ey= h=AYS8|7R3<+3j-L@9s%T)Za4^U~lbYRd0Fq)?WpzC+PqH diff --git a/projectq/tests/baseline/test_drawer_mpl.png b/projectq/tests/baseline/test_drawer_mpl.png deleted file mode 100644 index 69358c44eee29f385bf703d5db14f30c89b133f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24432 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 From 613357b36d69afd77a797fc956e41321fa9f4d98 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Tue, 4 Feb 2020 09:59:55 +0100 Subject: [PATCH 36/37] Add CircuitDrawerMatplotlib to documentation and minor code fix --- docs/projectq.backends.rst | 1 + projectq/backends/__init__.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/projectq.backends.rst b/docs/projectq.backends.rst index 621f7ce86..cc6531df4 100755 --- a/docs/projectq.backends.rst +++ b/docs/projectq.backends.rst @@ -5,6 +5,7 @@ backends projectq.backends.CommandPrinter projectq.backends.CircuitDrawer + projectq.backends.CircuitDrawerMatplotlib projectq.backends.Simulator projectq.backends.ClassicalSimulator projectq.backends.ResourceCounter diff --git a/projectq/backends/__init__.py b/projectq/backends/__init__.py index 9d57425e2..4813a52b4 100755 --- a/projectq/backends/__init__.py +++ b/projectq/backends/__init__.py @@ -26,8 +26,7 @@ * an interface to the IBM Quantum Experience chip (and simulator). """ from ._printer import CommandPrinter -from ._circuits import CircuitDrawer -from ._circuits import CircuitDrawerMatplotlib +from ._circuits import CircuitDrawer, CircuitDrawerMatplotlib from ._sim import Simulator, ClassicalSimulator from ._resource import ResourceCounter from ._ibm import IBMBackend From 9b2d06f23c88f8ec68bc5f03e0d61c72c5b6d119 Mon Sep 17 00:00:00 2001 From: Damien Nguyen Date: Tue, 4 Feb 2020 11:23:35 +0100 Subject: [PATCH 37/37] Fix docstring for CircuitDrawerMatplotlib --- projectq/backends/_circuits/_drawer_matplotlib.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/projectq/backends/_circuits/_drawer_matplotlib.py b/projectq/backends/_circuits/_drawer_matplotlib.py index e2b29880d..23a07c767 100644 --- a/projectq/backends/_circuits/_drawer_matplotlib.py +++ b/projectq/backends/_circuits/_drawer_matplotlib.py @@ -83,6 +83,7 @@ def is_available(self, cmd): Args: cmd (Command): Command for which to check availability (all Commands can be printed). + Returns: availability (bool): True, unless the next engine cannot handle the Command (if there is a next engine). @@ -182,12 +183,18 @@ def receive(self, command_list): def draw(self, qubit_labels=None, drawing_order=None): """ - Returns the plot of the quantum circuit + Generates and returns the plot of the quantum circuit stored so far Args: - drawing_order (dictionary): position of each qubit in the output - graphic. Keys: qubit IDs, Values: position of qubit on the qubit - line in the graphic. + qubit_labels (dict): label for each wire in the output figure. + Keys: qubit IDs, Values: string to print out as label for + that particular qubit wire. + drawing_order (dict): position of each qubit in the output + graphic. Keys: qubit IDs, Values: position of qubit on the + qubit line in the graphic. + + Returns: + A tuple containing the matplotlib figure and axes objects """ max_depth = max( len(self._qubit_lines[qubit_id]) for qubit_id in self._qubit_lines)