Skip to content

Commit

Permalink
Added support for pyQVM (#4)
Browse files Browse the repository at this point in the history
* added support for pyQVM

* removed 2.4 changes

* Updating compiler urls

* Replacing old lattice with new

* code review suggestions

* pyqvm working without the compiler
  • Loading branch information
josh146 authored and msohaibalam committed Apr 26, 2019
1 parent 768533e commit c55f251
Show file tree
Hide file tree
Showing 13 changed files with 713 additions and 23 deletions.
10 changes: 6 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ Contains the PennyLane Forest plugin. This plugin allows three Rigetti devices t
Features
========

* Provides three devices to be used with PennyLane: ``forest.wavefunction``, ``forest.qvm``, and ``forest.qpu``. These provide access to the Forest wavefunction simulator, quantum virtual machine (QVM), and quantum processing unit (QPU) respectively.
* Provides four devices to be used with PennyLane: ``forest.numpy_wavefunction``, ``forest.wavefunction``, ``forest.qvm``, and ``forest.qpu``. These provide access to the pyQVM Numpy wavefunction simulator, Forest wavefunction simulator, quantum virtual machine (QVM), and quantum processing unit (QPU) respectively.

* All provided devices support all core qubit PennyLane operations.

* In addition, ``forest.wavefunction`` supports all core qubit PennyLane expectation values. ``forest.qvm`` and ``forest.qpu`` only support ``PauliZ`` expectation values.
* All provided devices support all core qubit PennyLane operations and expectation values.


* Provides custom PennyLane operations to cover additional pyQuil operations: ``T``, ``S``, ``ISWAP``, ``CCNOT``, ``PSWAP``, and many more. Every custom operation supports analytic differentiation.

Expand All @@ -50,8 +50,10 @@ You can instantiate these devices for PennyLane as follows:
.. code-block:: python
import pennylane as qml
dev_numpy = qml.device('forest.numpy_wavefunction', wires=2)
dev_simulator = qml.device('forest.wavefunction', wires=2)
dev_qvm = qml.device('forest.qvm', device='2q-qvm', wires=2, shots=1000)
dev_pyqvm = qml.device('forest.qvm', device='2q-pyqvm', shots=1000)
dev_qvm = qml.device('forest.qvm', device='2q-qvm', shots=1000)
dev_qpu = qml.device('forest.qpu', device='Aspen-0-12Q-A', shots=1000)
These devices can then be used just like other devices for the definition and evaluation of QNodes within PennyLane. For more details, see the `plugin usage guide <https://pennylane-forest.readthedocs.io/en/latest/usage.html>`_ and refer to the PennyLane documentation.
Expand Down
4 changes: 4 additions & 0 deletions doc/code/numpy_wavefunction.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.. automodule:: pennylane_forest.numpy_wavefunction
:members:
:private-members:
:special-members:
8 changes: 3 additions & 5 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,10 @@ This PennyLane plugin allows the Rigetti Forest and pyQuil simulators to be used
Features
========

* Provides three devices to be used with PennyLane: ``forest.wavefunction``, ``forest.qvm``, and ``forest.qpu``. These provide access to the Forest wavefunction simulator, quantum virtual machine (QVM), and quantum processing unit (QPU) respectively.
* Provides four devices to be used with PennyLane: ``forest.numpy_wavefunction``, ``forest.wavefunction``, ``forest.qvm``, and ``forest.qpu``. These provide access to the pyQVM Numpy wavefunction simulator, Forest wavefunction simulator, quantum virtual machine (QVM), and quantum processing unit (QPU) respectively.


* All provided devices support all core qubit PennyLane operations.


* In addition, ``forest.wavefunction`` supports all core qubit PennyLane expectation values. ``forest.qvm`` and ``forest.qpu`` only support ``PauliZ`` expectation values.
* All provided devices support all core qubit PennyLane operations and expectation values.


* Provides custom PennyLane operations to cover additional pyQuil operations: ``T``, ``S``, ``ISWAP``, ``CCNOT``, ``PSWAP``, and many more. Every custom operation supports analytic differentiation.
Expand Down Expand Up @@ -58,6 +55,7 @@ Contents

code/ops
code/device
code/numpy_wavefunction
code/wavefunction
code/qvm
code/qpu
53 changes: 45 additions & 8 deletions doc/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
Plugin usage
############

PennyLane-Forest provides three Forest devices for PennyLane:
PennyLane-Forest provides four Forest devices for PennyLane:

* :class:`forest.wavefunction <~WavefunctionDevice>`: provides a PennyLane device for the PyQuil wavefunction simulator
* :class:`forest.numpy_wavefunction <~NumpyWavefunctionDevice>`: provides a PennyLane device for the pyQVM Numpy wavefunction simulator

* :class:`forest.qvm <~QVMDevice>`: provides a PennyLane device for the Forest QVM simulator
* :class:`forest.wavefunction <~WavefunctionDevice>`: provides a PennyLane device for the Forest wavefunction simulator

* :class:`forest.qvm <~QVMDevice>`: provides a PennyLane device for the Forest QVM and pyQuil pyQVM simulator

* :class:`forest.qpu <~QPUDevice>`: provides a PennyLane device for Forest QPU hardware devices

Expand All @@ -20,9 +22,12 @@ Once PyQuil and the PennyLane plugin are installed, the three Forest devices can
You can instantiate these devices in PennyLane as follows:

>>> import pennylane as qml
>>> dev_sim = qml.device('forest.wavefunction', wires=4)
>>> dev_qvm = qml.device('forest.qvm', device='complete', wires=2)
>>> dev_qpu = qml.device('forest.qpu', device='Aspen-1-16Q-A')
>>> dev_numpy = qml.device('forest.numpy_wavefunction', wires=2)
>>> dev_simulator = qml.device('forest.wavefunction', wires=2)
>>> dev_pyqvm = qml.device('forest.qvm', device='2q-pyqvm', shots=1000)
>>> dev_qvm = qml.device('forest.qvm', device='2q-qvm', shots=1000)
>>> dev_qpu = qml.device('forest.qpu', device='Aspen-0-12Q-A', shots=1000)



These devices can then be used just like other devices for the definition and evaluation of QNodes within PennyLane.
Expand Down Expand Up @@ -62,7 +67,7 @@ It is also easy to perform abstract calculations on a physical Forest QPU:
Note that:

1. We import NumPy from PennyLane. This is a requirement, so that PennyLane can perform backpropagation in hybrid quantum-classical models.
1. We import NumPy from PennyLane. This is a requirement, so that PennyLane can perform backpropagation in hybrid quantum-classical models. Alternatively, you may use the experimental PennyLane `PyTorch <https://pennylane.readthedocs.io/en/latest/code/interfaces/torch.html>`_ and `TensorFlow <https://pennylane.readthedocs.io/en/latest/code/interfaces/tfe.html>`_ interfaces.

2. Additional Quil gates not provided directly in PennyLane are importable from :mod:`~.ops`. In this case, we import the :class:`~.PSWAP` gate.

Expand Down Expand Up @@ -110,6 +115,31 @@ On initialization, the PennyLane-Forest devices accept additional keyword argume
not need to be passed in PennyLane.


The ``forest.numpy_wavefunction`` device
========================================

The ``forest.numpy_wavefunction`` device provides an interface between PennyLane and the pyQuil `NumPy wavefunction simulator <http://docs.rigetti.com/en/stable/wavefunction_simulator.html>`_. Because the NumPy wavefunction simulator allows access and manipulation of the underlying quantum state vector, ``forest.numpy_wavefunction`` is able to support the full suite of PennyLane and Quil quantum operations and expectation values.

In addition, it is generally faster than running equivalent simulations on the QVM, as the final state can be inspected and the expectation value calculated analytically, rather than by sampling measurements.


.. note::

Since the NumPy wavefunction simulator is written entirely in NumPy, no external
Quil compiler is required.


.. note::

By default, ``forest.numpy_wavefunction`` is initialized with ``shots=0``, indicating
that the exact analytic expectation value is to be returned.

If the number of trials or shots provided to the ``forest.numpy_wavefunction`` is
instead non-zero, a spectral decomposition is performed and a Bernoulli distribution
is constructed and sampled. This allows the ``forest.numpy_wavefunction`` device to
'approximate' the effect of sampling the expectation value.


The ``forest.wavefunction`` device
==================================

Expand All @@ -131,7 +161,7 @@ In addition, it is generally faster than running equivalent simulations on the Q
The ``forest.qvm`` device
=========================

The ``forest.qvm`` device provides an interface between PennyLane and the Forest SDK `quantum virtual machine <http://docs.rigetti.com/en/stable/qvm.html>`_. The QVM is used to simulate various quantum abstract machines, ranging from simulations of physical QPUs to completely connected lattices.
The ``forest.qvm`` device provides an interface between PennyLane and the Forest SDK `quantum virtual machine <http://docs.rigetti.com/en/stable/qvm.html>`_ or the pyQuil built-in pyQVM. The QVM is used to simulate various quantum abstract machines, ranging from simulations of physical QPUs to completely connected lattices.

Note that, unlike ``forest.wavefunction``, you do not pass the number of wires - this is inferred automatically from the requested quantum computer topology.

Expand All @@ -145,6 +175,11 @@ In addition, you may also request a QVM with noise models to better simulate a p

Note that only the `default noise models <http://docs.rigetti.com/en/stable/noise.html>`_ provided by pyQuil are currently supported.

To specify the pyQVM, simply append ``pyqvm`` to the end of the device name instead of ``qvm``:

>>> dev = qml.device('forest.qvm', device='4q-pyqvm')


Choosing the quantum computer
-----------------------------

Expand All @@ -155,6 +190,8 @@ When initializing the ``forest.qvm`` device, the following required keyword argu

* ``Nq-qvm``: for a fully connected/unrestricted N-qubit QVM
* ``9q-qvm-square``: a :math:`9\times 9` lattice.
* ``Nq-pyqvm`` or ``9q-pyqvm-square``, for the same as the above but run
via the built-in pyQuil pyQVM device.
* Any other supported Rigetti device architecture, for
example a QPU lattice such as ``'Aspen-1-16Q-A'``.
* Graph topology (as a ``networkx.Graph`` object) representing the device architecture.
Expand Down
1 change: 1 addition & 0 deletions pennylane_forest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
from .qpu import QPUDevice
from .qvm import QVMDevice
from .wavefunction import WavefunctionDevice
from .numpy_wavefunction import NumpyWavefunctionDevice
from ._version import __version__
2 changes: 1 addition & 1 deletion pennylane_forest/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ def apply(self, operation, wires, par):
self.active_wires = set(range(self.num_wires))

@abc.abstractmethod
def pre_expval(self):
def pre_expval(self): #pragma no cover
"""Run the QVM or QPU"""
raise NotImplementedError

Expand Down
114 changes: 114 additions & 0 deletions pennylane_forest/numpy_wavefunction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"""
NumpyWavefunction simulator device
==================================
**Module name:** :mod:`pennylane_forest.numpy_wavefunction`
.. currentmodule:: pennylane_forest.numpy_wavefunction
This module contains the :class:`~.NumpyWavefunctionDevice` class, a PennyLane device that allows
evaluation and differentiation of pyQuil's NumpyWavefunctionSimulator using PennyLane.
Classes
-------
.. autosummary::
NumpyWavefunctionDevice
Code details
~~~~~~~~~~~~
"""
import numpy as np

from pyquil.pyqvm import PyQVM
from pyquil.numpy_simulator import NumpyWavefunctionSimulator

from .device import ForestDevice
from .wavefunction import expectation_map, spectral_decomposition_qubit
from ._version import __version__


class NumpyWavefunctionDevice(ForestDevice):
r"""NumpyWavefunction simulator device for PennyLane.
Args:
wires (int): the number of qubits to initialize the device in
shots (int): Number of circuit evaluations/random samples used
to estimate expectation values of expectations.
"""
name = 'pyQVM NumpyWavefunction Simulator Device'
short_name = 'forest.numpy_wavefunction'

expectations = {'PauliX', 'PauliY', 'PauliZ', 'Hadamard', 'Hermitian', 'Identity'}

def __init__(self, wires, *, shots=0, **kwargs):
super().__init__(wires, shots, **kwargs)
self.qc = PyQVM(n_qubits=wires, quantum_simulator_type=NumpyWavefunctionSimulator)
self.state = None

def pre_apply(self):
self.reset()
self.qc.wf_simulator.reset()

def pre_expval(self):
# TODO: currently, the PyQVM considers qubit 0 as the leftmost bit and therefore
# returns amplitudes in the opposite of the Rigetti Lisp QVM (which considers qubit
# 0 as the rightmost bit). This may change in the future, so in the future this
# might need to get udpated to be similar to the pre_expval function of
#pennylane_forest/wavefunction.py
self.state = self.qc.execute(self.prog).wf_simulator.wf.flatten()

def expval(self, expectation, wires, par):
# measurement/expectation value <psi|A|psi>
if expectation == 'Hermitian':
A = par[0]
else:
A = expectation_map[expectation]

if self.shots == 0:
# exact expectation value
ev = self.ev(A, wires)
else:
# estimate the ev
# sample Bernoulli distribution n_eval times / binomial distribution once
a, P = spectral_decomposition_qubit(A)
p0 = self.ev(P[0], wires) # probability of measuring a[0]
n0 = np.random.binomial(self.shots, p0)
ev = (n0*a[0] +(self.shots-n0)*a[1]) / self.shots

return ev

def ev(self, A, wires):
r"""Evaluates a one-qubit expectation in the current state.
Args:
A (array): :math:`2\times 2` Hermitian matrix corresponding to the expectation
wires (Sequence[int]): target subsystem
Returns:
float: expectation value :math:`\left\langle{A}\right\rangle = \left\langle{\psi}\mid A\mid{\psi}\right\rangle`
"""
# Expand the Hermitian observable over the entire subsystem
A = self.expand_one(A, wires)
return np.vdot(self.state, A @ self.state).real

def expand_one(self, U, wires):
r"""Expand a one-qubit operator into a full system operator.
Args:
U (array): :math:`2\times 2` matrix
wires (Sequence[int]): target subsystem
Returns:
array: :math:`2^n\times 2^n` matrix
"""
if U.shape != (2, 2):
raise ValueError('2x2 matrix required.')
if len(wires) != 1:
raise ValueError('One target subsystem required.')
wires = wires[0]
before = 2**wires
after = 2**(self.num_wires-wires-1)
U = np.kron(np.kron(np.eye(before), U), np.eye(after))
return U
14 changes: 12 additions & 2 deletions pennylane_forest/qvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,17 @@
class QVMDevice(ForestDevice):
r"""Forest QVM device for PennyLane.
This device supports both the Rigetti Lisp QVM, as well as the built-in pyQuil pyQVM.
If using the pyQVM, the ``qvm_url`` QVM server url keyword argument does not need to
be set.
Args:
device (Union[str, nx.Graph]): the name or topology of the device to initialise.
* ``Nq-qvm``: for a fully connected/unrestricted N-qubit QVM
* ``9q-qvm-square``: a :math:`9\times 9` lattice.
* ``Nq-pyqvm`` or ``9q-pyqvm-square``, for the same as the above but run
via the built-in pyQuil pyQVM device.
* Any other supported Rigetti device architecture.
* Graph topology representing the device architecture.
Expand Down Expand Up @@ -152,9 +158,13 @@ def pre_expval(self):
self.prog.inst(MEASURE(q, ro[i]))

self.prog.wrap_in_numshots_loop(self.shots)
executable = self.qc.compile(self.prog)

bitstring_array = self.qc.run(executable=executable)
if "pyqvm" in self.qc.name:
bitstring_array = self.qc.run(self.prog)
else:
executable = self.qc.compile(self.prog)
bitstring_array = self.qc.run(executable=executable)

self.state = {}
for i, q in enumerate(qubits):
self.state[q] = bitstring_array[:, i]
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
'forest.qpu = pennylane_forest:QPUDevice',
'forest.qvm = pennylane_forest:QVMDevice',
'forest.wavefunction = pennylane_forest:WavefunctionDevice',
'forest.numpy_wavefunction = pennylane_forest:NumpyWavefunctionDevice'
],
},
'description': 'Rigetti backend for the PennyLane library',
Expand Down

0 comments on commit c55f251

Please sign in to comment.