Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
47c9291
Adding tutorials directory
Sep 25, 2019
d23b239
test
Bombenchris Sep 25, 2019
c7356a4
BV Algorithm
Sep 30, 2019
2817329
add Matplotlib circuit drawer backend, this version works for H, CNOT…
Oct 8, 2019
6f3c775
Delete the added unnecessary attributes in Command object
Oct 8, 2019
e7bb098
Create the CircuitDrawerMatplotlib Class to handles drawing with matp…
Oct 8, 2019
d95a777
Deleted tutorials/.gitkeep
Takishima Oct 10, 2019
824f29a
update
Oct 10, 2019
41c66c2
Merge branch 'fix_drawer_mpl' of rnd-gitlab-eu.huawei.com:zurich-rnd/…
Oct 10, 2019
5e46dae
fix measurement gate
Oct 11, 2019
2082351
Delete unrelated files.
Oct 11, 2019
66e7613
fix Toffoli gate position issue and change the qubit position from 's…
Oct 14, 2019
f33dcdd
Pytest for drawer_mpl
Oct 22, 2019
9a00d16
Tests for _plot function
Oct 22, 2019
83065df
Fix the R(angle) gate drawing
Oct 29, 2019
6f11056
added test for is_available and QFT gate
Oct 29, 2019
9805b5e
fix drawing distance between gates when gate_length >2
Oct 29, 2019
56a8403
new test png for pytest mpl
Oct 29, 2019
f3eab27
added Swap gates and CSwap gate with multi-control and multi-target.
Nov 4, 2019
e7caeda
update test and comments
Nov 5, 2019
dc41815
Address comments in _drawer.py
Takishima Nov 5, 2019
4c090b3
Reindent and reformat parts of _drawer.py
Takishima Nov 5, 2019
3f86dc3
Address comments in _plot.py
Takishima Nov 5, 2019
97411b4
Reindent and reformat _plot.py
Takishima Nov 5, 2019
a01c44a
update tests
Nov 7, 2019
8847f51
Move matplotlib drawer into its own file + add test coverage
Takishima Jan 16, 2020
830fcde
Use regular expressions to rewrite and shorten gate names
Takishima Jan 16, 2020
9df3377
Change internal storage format for CircuitDrawerMatplotlib
Takishima Jan 27, 2020
6473344
Better graphics and adapt plot functions to new internal format
Takishima Jan 30, 2020
d105086
Complete test coverage + add some checks for to_draw() inputs
Takishima Feb 3, 2020
fa3a7bd
Compatibility with matplotlib 2.2.3
Takishima Feb 3, 2020
f8d9d79
Remove compatibility code for MacOSX.
Takishima Feb 3, 2020
bd44103
Add matplotlib dependency to requirements.txt
Takishima Feb 3, 2020
6b5c221
Merge branch 'develop' into fix_drawer_mpl
Takishima Feb 3, 2020
2be6cb4
Fix non-UTF8 character in file
Takishima Feb 3, 2020
90f7654
Fix .travis.yml
Takishima Feb 4, 2020
3987413
Remove unnecessary PNG files
Takishima Feb 4, 2020
613357b
Add CircuitDrawerMatplotlib to documentation and minor code fix
Takishima Feb 4, 2020
9b2d06f
Fix docstring for CircuitDrawerMatplotlib
Takishima Feb 4, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions docs/projectq.backends.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ backends

projectq.backends.CommandPrinter
projectq.backends.CircuitDrawer
projectq.backends.CircuitDrawerMatplotlib
projectq.backends.Simulator
projectq.backends.ClassicalSimulator
projectq.backends.ResourceCounter
Expand Down
2 changes: 1 addition & 1 deletion projectq/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
* an interface to the IBM Quantum Experience chip (and simulator).
"""
from ._printer import CommandPrinter
from ._circuits import CircuitDrawer
from ._circuits import CircuitDrawer, CircuitDrawerMatplotlib
from ._sim import Simulator, ClassicalSimulator
from ._resource import ResourceCounter
from ._ibm import IBMBackend
4 changes: 4 additions & 0 deletions projectq/backends/_circuits/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@
# limitations under the License.

from ._to_latex import to_latex
from ._plot import to_draw

from ._drawer import CircuitDrawer
from ._drawer_matplotlib import CircuitDrawerMatplotlib

7 changes: 3 additions & 4 deletions projectq/backends/_circuits/_drawer.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
Contains a compiler engine which generates TikZ Latex code describing the
circuit.
"""
import sys

from builtins import input

from projectq.cengines import LastEngineException, BasicEngine
Expand Down Expand Up @@ -223,12 +221,13 @@ 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)
Expand Down
208 changes: 208 additions & 0 deletions projectq/backends/_circuits/_drawer_matplotlib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
# 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
import re
import itertools

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_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):
"""
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._qubit_lines = {}

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:
# 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
commands, and then send them on to the next engine.

Args:
command_list (list<Command>): List of Commands to print (and
potentially send on to the next engine).
"""
for cmd in command_list:
if not isinstance(cmd.gate, FlushGate):
self._process(cmd)

if not self.is_last_engine:
self.send([cmd])

def draw(self, qubit_labels=None, drawing_order=None):
"""
Generates and returns the plot of the quantum circuit stored so far

Args:
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)
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)
Loading