Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

QuantumCircuit to PennyLane template conversion #55

Merged
merged 41 commits into from
Nov 11, 2019
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
91bfd3a
Initial creation of the converter file
antalszava Oct 28, 2019
0a20ca8
Initial outline of the QuantumCircuit converter
antalszava Oct 29, 2019
74f9562
Correcting test cases except for one (BasisState)
antalszava Oct 30, 2019
d119076
Added tests and polished basic version of the load() function
antalszava Oct 30, 2019
35adfe7
Adding qasm loading, fixing tests except for the one reading from file
antalszava Oct 30, 2019
0e524cc
Add file test with tmp_directory fixture
antalszava Oct 30, 2019
7d55e68
Finalizing docstrings and tests
antalszava Oct 30, 2019
088c542
Updating comments
antalszava Oct 30, 2019
035c094
Modifying changelog
antalszava Oct 31, 2019
7bc701d
Update CHANGELOG.md
antalszava Oct 31, 2019
22e5ef7
Update pennylane_qiskit/converter.py
antalszava Oct 31, 2019
ca97645
Update pennylane_qiskit/converter.py
antalszava Oct 31, 2019
cfb00f5
Update tests/test_converter.py
antalszava Oct 31, 2019
3fa3ec1
Update tests/test_converter.py
antalszava Oct 31, 2019
362a174
Update tests/test_converter.py
antalszava Oct 31, 2019
50e3503
Modifications based on comments
antalszava Oct 31, 2019
288da58
Modifications based on comments
antalszava Oct 31, 2019
a20f93b
Adding test for coverage
antalszava Oct 31, 2019
2fca287
Modifying wires argument for load, adding tests
antalszava Nov 1, 2019
80c306f
Remove custom operation recorder
josh146 Nov 3, 2019
ffbfd1f
fix failing 3.5 test
josh146 Nov 3, 2019
025ac51
Parameters can be passed through QNode, added integration tests, modi…
antalszava Nov 4, 2019
e73a494
Organizing qasm conversions into one function, refactoring load, furt…
antalszava Nov 4, 2019
0912585
Modifying docstrings
antalszava Nov 4, 2019
798d26c
Update pennylane_qiskit/converter.py
antalszava Nov 6, 2019
23cbb96
Update pennylane_qiskit/converter.py
antalszava Nov 6, 2019
a3f0d65
Update pennylane_qiskit/converter.py
antalszava Nov 6, 2019
36c8e4f
Update pennylane_qiskit/converter.py
antalszava Nov 6, 2019
87f62a9
Reverting QASM conversion into two parts, corrections in the docstring
antalszava Nov 6, 2019
644a520
Resolving merge conflict
antalszava Nov 6, 2019
328ae38
Update tests/conftest.py
antalszava Nov 11, 2019
3d9c6e9
Update pennylane_qiskit/converter.py
antalszava Nov 11, 2019
31eb089
Update tests/test_converter.py
antalszava Nov 11, 2019
9af6498
Update tests/test_converter.py
antalszava Nov 11, 2019
2e95f37
Change names of tests
antalszava Nov 11, 2019
a1fa5e7
Merge branch 'quanctum_circuit_to_template' of https://github.com/Xan…
antalszava Nov 11, 2019
2a6d991
Update tests/test_converter.py
antalszava Nov 11, 2019
28bc3b5
Adding tests for map_wires, updating tests for U2, U3 and Toffoli
antalszava Nov 11, 2019
4a81f59
Merge branch 'quanctum_circuit_to_template' of https://github.com/Xan…
antalszava Nov 11, 2019
ae5f4df
Adding test on wires provided in a non-standard order
antalszava Nov 11, 2019
4fbd76e
Modifying test on ordering
antalszava Nov 11, 2019
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

### New features since last release

* Added a `QuantumCircuit`/QASM to PennyLane template converter
antalszava marked this conversation as resolved.
Show resolved Hide resolved
that allows incorporating Qiskit circuits in a PennyLane quantum
circuit.
[#55](https://github.com/XanaduAI/pennylane-qiskit/pull/55)


### Breaking changes

### Improvements
Expand All @@ -14,6 +20,8 @@

This release contains contributions from (in alphabetical order):

Antal Száva

---

# Release 0.6.0
Expand Down
3 changes: 1 addition & 2 deletions pennylane_qiskit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@
"""Top level PennyLane-qiskit module"""

from ._version import __version__

from .aer import AerDevice
from .basic_aer import BasicAerDevice
from .converter import load
antalszava marked this conversation as resolved.
Show resolved Hide resolved
from .ibmq import IBMQDevice

from .ops import *
150 changes: 150 additions & 0 deletions pennylane_qiskit/converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Copyright 2019 Xanadu Quantum Technologies Inc.

# 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.
r"""
QuanctumCircuit converter module
================================

.. currentmodule:: pennylane_qiskit.load

This module contains functions for converting Qiskit QuantumCirducit objects
antalszava marked this conversation as resolved.
Show resolved Hide resolved
PennyLane templates.
antalszava marked this conversation as resolved.
Show resolved Hide resolved
"""
import warnings

import numpy as np
antalszava marked this conversation as resolved.
Show resolved Hide resolved

from qiskit import QuantumCircuit
from qiskit.circuit import Parameter
from qiskit.exceptions import QiskitError

import pennylane.ops.qubit as pennylane_ops
from pennylane_qiskit.qiskit_device import QISKIT_OPERATION_MAP

# pylint: disable=too-many-instance-attributes

inv_map = {v.__name__: k for k, v in QISKIT_OPERATION_MAP.items()}


def check_parameter_bound(param):
"""Utility function determining if a certain parameter in a QuantumCircuit has
been bound.

Args:
param: the parameter to be checked

Returns:
param: the parameter after the check
"""
if isinstance(param, Parameter):
raise ValueError("The parameter {} was not bound correctly.".format(param))
return param


def load(quantum_circuit: QuantumCircuit):
"""Returns a PennyLane template created based on the input QuantumCircuit or QASM string.
antalszava marked this conversation as resolved.
Show resolved Hide resolved
Warnings are created for each of the QuantumCircuit instructions that were
not incorporated in the PennyLane template.
antalszava marked this conversation as resolved.
Show resolved Hide resolved

Args:
quantum_circuit (qiskit.QuantumCircuit): the QuantumCircuit to be converted
antalszava marked this conversation as resolved.
Show resolved Hide resolved

Returns:
function: the new PennyLane template
antalszava marked this conversation as resolved.
Show resolved Hide resolved
"""

def _function(params: dict = None, wire_shift: int = 0):
"""Returns a PennyLane template created based on the input QuantumCircuit.
Warnings are created for each of the QuantumCircuit instructions that were
not incorporated in the PennyLane template.

Args:
params (dict): specifies the parameters that need to bound in the
QuantumCircuit
antalszava marked this conversation as resolved.
Show resolved Hide resolved

antalszava marked this conversation as resolved.
Show resolved Hide resolved
wire_shift (int): the shift to be made with respect to the wires
already specifiec in the QuantumCircuit
antalszava marked this conversation as resolved.
Show resolved Hide resolved
e.g. if the QuantumCircuit acts on wires [0, 1] and if wire_shift == 1,
then the returned Pennylane template will act on wires [1, 2]
antalszava marked this conversation as resolved.
Show resolved Hide resolved
antalszava marked this conversation as resolved.
Show resolved Hide resolved

Returns:
function: the new PennyLane template
"""

if not isinstance(quantum_circuit, QuantumCircuit):
raise ValueError("The circuit {} is not a valid Qiskit QuantumCircuit.".format(quantum_circuit))

qc = quantum_circuit.bind_parameters(params) if params is not None else quantum_circuit

# Processing the dictionary of parameters passed
for op in qc.data:

# Indexing a Qubit object to obtain a wire and shifting it with the start wire index
wires = [wire_shift + qubit.index for qubit in op[1]]
instruction_name = op[0].__class__.__name__

if instruction_name in inv_map and inv_map[instruction_name] in pennylane_ops.ops:

operation_name = inv_map[instruction_name]

operation = getattr(pennylane_ops, operation_name)

parameters = [check_parameter_bound(param) for param in op[0].params]

if not parameters:
antalszava marked this conversation as resolved.
Show resolved Hide resolved
operation(wires=wires)
elif operation_name == 'QubitStateVector':
operation(np.array(parameters), wires=wires)
elif operation_name == 'QubitUnitary':
operation(*parameters, wires=wires)
elif len(parameters) == 1:
operation(float(*parameters), wires=wires)
else:
float_params = [float(param) for param in parameters]
operation(*float_params, wires=wires)

else:
try:
operation_matrix = op[0].to_matrix()
pennylane_ops.QubitUnitary(operation_matrix, wires=wires)
except (AttributeError, QiskitError):
warnings.warn(__name__ + " The {} instruction is not supported by PennyLane,"
antalszava marked this conversation as resolved.
Show resolved Hide resolved
" and has not been added to the template.".
format(instruction_name),
UserWarning)

return _function


def load_qasm_from_file(file: str):
"""Returns a PennyLane template created based on the input qasm file.

Args:
file (str): the name of the file

Returns:
function: the new PennyLane template
"""
return load(QuantumCircuit.from_qasm_file(file))
antalszava marked this conversation as resolved.
Show resolved Hide resolved


def load_qasm(qasm_string: str):
"""Returns a PennyLane template created based on the input qasm string.

Args:
qasm_string (str): the name of the qasm string

Returns:
function: the new PennyLane template
"""
return load(QuantumCircuit.from_qasm_str(qasm_string))
12 changes: 12 additions & 0 deletions pennylane_qiskit/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@
# pylint: disable=too-few-public-methods
from pennylane.operation import Operation

_ops = {
antalszava marked this conversation as resolved.
Show resolved Hide resolved
"S",
"Sdg",
"T",
"Tdg",
"U1",
"U2",
"U3",
"CSWAP",
"Toffoli",
}


class S(Operation):
r"""S(wires)
Expand Down
14 changes: 10 additions & 4 deletions pennylane_qiskit/qiskit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,22 +80,28 @@ def pauli_eigs(n):
"RX": ex.RXGate,
"RY": ex.RYGate,
"RZ": ex.RZGate,
"S": ex.SGate,
"T": ex.TGate,
"CSWAP": ex.FredkinGate,
"CRZ": ex.CrzGate,
"PhaseShift": ex.U1Gate,
"QubitStateVector": ex.Initialize,

# TODO: add the CRY gate, once the U3Gate is a part of PennyLane
# "CRY": U3Gate,
# operations not natively implemented in Qiskit but provided in gates.py
"Rot": Rot,
"BasisState": BasisState,
"QubitUnitary": ex.UnitaryGate,
# additional operations not native to PennyLane but present in Qiskit
"S": ex.SGate,
# operations not natively implemented in Qiskit but provided in gates.py

# TODO: once inverse is added to PennyLane-Qiskit, test the following gates
"Sdg": ex.SdgGate,
"T": ex.TGate,
"Tdg": ex.TdgGate,
"U1": ex.U1Gate,

"U2": ex.U2Gate,
"U3": ex.U3Gate,
"CSWAP": ex.FredkinGate,
"Toffoli": ex.ToffoliGate,
}

Expand Down
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
'qiskit.basicaer = pennylane_qiskit:BasicAerDevice',
'qiskit.ibmq = pennylane_qiskit:IBMQDevice',
],
'pennylane.io': [
'qiskit = pennylane_qiskit:load',
],
},
'description': 'PennyLane plugin for qiskit-terra',
'long_description': open('README.rst').read(),
Expand Down
116 changes: 116 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,109 @@
import pytest
import numpy as np

import pennylane as qml
from pennylane_qiskit import AerDevice, BasicAerDevice

import contextlib
import io


# TODO: once in PennyLane, modify the usage of Recorder and OperationRecorder
class Recorder:
"""Recorder class used by the :class:`~.OperationRecorder`.

The Recorder class is a very minimal QNode, that simply
acts as a QNode context for operator queueing."""
# pylint: disable=too-few-public-methods
def __init__(self, old_context):
self.old_context = old_context
self.ops = []
self.queue = []
self.ev = []
self.num_wires = 1

def _append_op(self, op):
""":class:`~.Operator` objects call this method
and append themselves upon initialization."""
self.ops.append(op)

if isinstance(op, qml.operation.Observable):
if op.return_type is None:
self.queue.append(op)
else:
self.ev.append(op)
else:
self.queue.append(op)

# this ensure the recorder does not interfere with
# any QNode contexts
if self.old_context:
self.old_context._append_op(op)


class OperationRecorder:
"""A template and quantum function inspector,
allowing easy introspection of operators that have been
applied without requiring a QNode.

**Example**:

The OperationRecorder is a context manager. Executing templates
or quantum functions stores resulting applied operators in the
recorder, which can then be printed.

>>> weights = qml.init.strong_ent_layers_normal(n_layers=1, n_wires=2)
>>>
>>> with qml.utils.OperationRecorder() as rec:
>>> qml.templates.layers.StronglyEntanglingLayers(*weights, wires=[0, 1])
>>>
>>> print(rec)
Operations
==========
Rot(-0.10832656163640327, 0.14429091013664083, -0.010835826725765343, wires=[0])
Rot(-0.11254523669444501, 0.0947222564914006, -0.09139600968423377, wires=[1])
CNOT(wires=[0, 1])
CNOT(wires=[1, 0])

Alternatively, the :attr:`~.OperationRecorder.queue` attribute can be used
to directly accessed the applied :class:`~.Operation` and :class:`~.Observable`
objects.
"""
def __init__(self):
self.rec = None
"""~.Recorder: a very minimal QNode, that simply
acts as a QNode context for operator queueing"""

self.queue = None
"""List[~.Operators]: list of operations applied within
the OperatorRecorder context."""

self.old_context = None

def __enter__(self):
self.rec = Recorder(qml.QNode._current_context)

# store the old context to be returned later
self.old_context = qml.QNode._current_context

# set the recorder as the QNode context
qml.QNode._current_context = self.rec

self.queue = None

return self

def __exit__(self, *args, **kwargs):
self.queue = self.rec.ops
qml.QNode._current_context = self.old_context

def __str__(self):
with io.StringIO() as buf, contextlib.redirect_stdout(buf):
qml.QNode.print_applied(self.rec)
output = buf.getvalue()

return output


np.random.seed(42)

Expand Down Expand Up @@ -53,3 +154,18 @@ def _device(n):
return request.param(wires=n, backend=backend, shots=shots, analytic=analytic)

return _device


@pytest.fixture(scope="function")
def mock_device(monkeypatch):
"""A mock instance of the abstract Device class"""

with monkeypatch.context() as m:
dev = qml.Device
m.setattr(dev, '__abstractmethods__', frozenset())
yield qml.Device()


@pytest.fixture(scope="function")
def recorder():
return OperationRecorder()
Loading