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

Adding PasqalDevice #40

Merged
merged 31 commits into from
Oct 8, 2020
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ac41fb2
[WIP] Add pasqal device (#31)
co9olguy Jul 8, 2020
92872e2
Update branch (#33)
co9olguy Jul 10, 2020
3270b3a
change coords
co9olguy Aug 14, 2020
7962f6b
Merge branch 'master' into pasqal-holding
co9olguy Aug 14, 2020
a083d43
fix
co9olguy Aug 14, 2020
bf27941
proper attaching of pasqal device
co9olguy Aug 14, 2020
0638871
debugging
co9olguy Aug 14, 2020
bc4385f
cleanup and explanation
co9olguy Aug 14, 2020
db54ef3
updates for failing tests
co9olguy Aug 14, 2020
5e895f5
updating requirements to master branch of cirq
co9olguy Aug 14, 2020
b6aae21
ordering test upgrades
co9olguy Aug 15, 2020
493322c
fixing more edge cases
co9olguy Aug 15, 2020
3f91da1
Update pennylane_cirq/cirq_device.py
co9olguy Aug 15, 2020
83d52f0
removing print
co9olguy Aug 15, 2020
e3703c0
make identity gates local
co9olguy Aug 15, 2020
defe579
Merge branch 'master' into pasqal-holding
co9olguy Oct 8, 2020
779ae95
Update pennylane_cirq/cirq_device.py
co9olguy Oct 8, 2020
df0447f
imports
co9olguy Oct 8, 2020
b3692de
black
co9olguy Oct 8, 2020
63d5199
Update format.yml
josh146 Oct 8, 2020
f6b5f13
Update pennylane_cirq/cirq_device.py
co9olguy Oct 8, 2020
37a7afa
blacker
co9olguy Oct 8, 2020
104dada
fixes
co9olguy Oct 8, 2020
093b99f
docs
co9olguy Oct 8, 2020
aad2488
more dox
co9olguy Oct 8, 2020
a1d5432
the
co9olguy Oct 8, 2020
8a4547c
Update tests.yml
josh146 Oct 8, 2020
22fd570
Update doc/devices/pasqal.rst
co9olguy Oct 8, 2020
33c9a06
Update tests.yml
josh146 Oct 8, 2020
d28563c
Update tests.yml
josh146 Oct 8, 2020
c3973ff
Update tests.yml
josh146 Oct 8, 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
23 changes: 18 additions & 5 deletions .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,22 @@ on:
jobs:
black:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v1
- name: Black Code Formatter
uses: lgeiger/black-action@v1.0.1
with:
args: "-l 100 pennylane_cirq/ --check"
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.4.1
with:
access_token: ${{ github.token }}

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8

- name: Install dependencies
run: pip install black

- uses: actions/checkout@v2

- name: Run Black
run: black -l 100 pennylane_cirq/ --check
4 changes: 2 additions & 2 deletions doc/devices/mixed_simulator.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
The Mixed Simulator Device
==========================
Mixed Simulator Device
======================

You can instantiate the mixed-state simulator device in PennyLane as follows:

Expand Down
35 changes: 35 additions & 0 deletions doc/devices/pasqal.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Pasqal Device
=============

You can instantiate a simulator for Pasqal's neutral-atom devices in PennyLane as follows:

.. code-block:: python

import pennylane as qml

dev = qml.device("cirq.pasqal", wires=2, control_radius=1.5)


This device can then be used just like other devices for the definition and evaluation of QNodes within PennyLane.

The Pasqal device supports unique features of Pasqal's quantum computing hardware provided via Cirq, namely
the ``ThreeDQubit`` and the notion of a ``control_radius``.

.. code-block:: python

from cirq.pasqal import ThreeDQubit
qubits = [ThreeDQubit(x, y, z)
for x in range(2)
for y in range(2)
for z in range(2)]
dev = qml.device("cirq.pasqal", control_radius = 2., qubits=qubits, wires=len(qubits))

@qml.qnode(dev)
def circuit(x):
qml.RX(x, wires=[0])
qml.CNOT(wires=[0, 1])
return qml.expval(qml.PauliZ(wires=1))

circuit(0.3)

For more details about Pasqal devices, consult the `Cirq docs <https://cirq.readthedocs.io/en/stable/docs/pasqal/getting_started.html>`_.
co9olguy marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 2 additions & 2 deletions doc/devices/simulator.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
The Simulator device
====================
Simulator device
================

You can instantiate the device in PennyLane as follows:

Expand Down
8 changes: 7 additions & 1 deletion doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ Currently, PennyLane-Cirq provides two Cirq devices for PennyLane:
:name: 'cirq.mixedsimulator'
:description: Cirq's density matrix simulator backend.
:link: devices/mixed_simulator.html


.. devicegalleryitem::
:name: 'cirq.pasqal'
:description: Simulator for Pasqal's neutral atom devices.
:link: devices/pasqal.html

.. raw:: html

<div style='clear:both'></div>
Expand Down Expand Up @@ -65,6 +70,7 @@ and simply replace ``'default.qubit'`` with the ``'cirq.simulator'`` device:

devices/simulator
devices/mixed_simulator
devices/pasqal

.. toctree::
:maxdepth: 1
Expand Down
1 change: 1 addition & 0 deletions pennylane_cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
===============
"""
from .simulator_device import SimulatorDevice, MixedStateSimulatorDevice
from .pasqal_device import PasqalDevice

from .ops import BitFlip, PhaseFlip, PhaseDamp, AmplitudeDamp, Depolarize
from ._version import __version__
1 change: 0 additions & 1 deletion pennylane_cirq/cirq_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import abc
from collections.abc import Iterable # pylint: disable=no-name-in-module
from collections import OrderedDict

import cirq
import numpy as np
import pennylane as qml
Expand Down
2 changes: 1 addition & 1 deletion pennylane_cirq/cirq_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

class CirqOperation:
"""A helper class that wraps the native Cirq operations and provides an
interface for parametrization and application."""
interface for parametrization and application."""

def __init__(self, parametrization):
"""Initializes the CirqOperation
Expand Down
53 changes: 53 additions & 0 deletions pennylane_cirq/pasqal_device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Copyright 2018 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.
# pylint: disable=too-many-arguments
"""
This module provides the ``PasqalDevice`` from Cirq.
"""
from cirq import pasqal

from .simulator_device import SimulatorDevice


class PasqalDevice(SimulatorDevice):
r"""Cirq Pasqal device for PennyLane.

Args:
wires (int): the number of wires to initialize the device with
control_radius (float): The maximum distance between qubits for a controlled
gate. Distance is measured in units of the ``ThreeDGridQubit`` indices.
shots (int): Number of circuit evaluations/random samples used
to estimate expectation values of observables. Shots need
to be >= 1. In analytic mode, shots indicates the number of entries
that are returned by ``device.sample``.
analytic (bool): indicates whether expectation values and variances should
be calculated analytically
qubits (List[cirq.ThreeDGridQubit]): A list of Cirq ThreeDGridQubits that are used
as wires. If not specified, the ThreeDGridQubits are put in a linear
arrangement along the first coordinate axis, separated by a distance of
``control_radius / 2``.
i.e., ``(0,0,0), (control_radius/2,0,0), (control_radius,0,0)``, etc.
"""
name = "Cirq Pasqal device for PennyLane"
short_name = "cirq.pasqal"

def __init__(self, wires, control_radius, shots=1000, analytic=True, qubits=None):

if not qubits:
qubits = [pasqal.ThreeDQubit(wire * control_radius / 2, 0, 0) for wire in range(wires)]
self.control_radius = float(control_radius)
if self.control_radius < 0:
raise ValueError("The control_radius must be a non-negative real number.")
super().__init__(wires, shots, analytic, qubits)
self.cirq_device = pasqal.PasqalVirtualDevice(self.control_radius, qubits)
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pennylane>=0.11
cirq>=0.7
cirq>=0.9
co9olguy marked this conversation as resolved.
Show resolved Hide resolved
numpy~=1.16
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
# Requirements should be as minimal as possible.
# Avoid pinning, and use minimum version numbers
# only where required.
requirements = ["pennylane>=0.11", "cirq"]
requirements = ["pennylane>=0.11", "cirq>=0.9"]

info = {
"name": "PennyLane-Cirq",
Expand All @@ -35,7 +35,8 @@
"license": "Apache License 2.0",
"packages": ["pennylane_cirq"],
"entry_points": {"pennylane.plugins": ["cirq.simulator = pennylane_cirq:SimulatorDevice",
"cirq.mixedsimulator = pennylane_cirq:MixedStateSimulatorDevice"],},
"cirq.mixedsimulator = pennylane_cirq:MixedStateSimulatorDevice",
"cirq.pasqal = pennylane_cirq:PasqalDevice"],},
# Place a one line description here. This will be shown by pip
"description": "PennyLane plugin for Cirq",
"long_description": open("README.rst").read(),
Expand Down
14 changes: 9 additions & 5 deletions tests/test_cirq_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,14 @@ def test_outer_init_of_qubits_with_wire_label_strings(self):
]

user_labels = ["alice", "bob", "charlie", "david"]
sort_order = [2,0,1,3]
sort_order = [2, 0, 1, 3]

dev = qml.device("cirq.simulator", wires=user_labels, qubits=unordered_qubits)
assert len(dev.qubits) == 4
assert dev.qubits == sorted(unordered_qubits)
assert all(dev.map_wires(Wires(label)) == Wires(idx) for label, idx in zip(user_labels, sort_order))
assert all(
dev.map_wires(Wires(label)) == Wires(idx) for label, idx in zip(user_labels, sort_order)
)

def test_outer_init_of_qubits_with_wire_label_ints(self):
"""Tests that giving qubits as parameters to CirqDevice works when the user also provides custom integer wire labels."""
Expand All @@ -142,13 +144,15 @@ def test_outer_init_of_qubits_with_wire_label_ints(self):
cirq.GridQubit(1, 1),
]

user_labels = [-1,1,66,0]
sort_order = [2,0,1,3]
user_labels = [-1, 1, 66, 0]
sort_order = [2, 0, 1, 3]

dev = qml.device("cirq.simulator", wires=user_labels, qubits=unordered_qubits)
assert len(dev.qubits) == 4
assert dev.qubits == sorted(unordered_qubits)
assert all(dev.map_wires(Wires(label)) == Wires(idx) for label, idx in zip(user_labels, sort_order))
assert all(
dev.map_wires(Wires(label)) == Wires(idx) for label, idx in zip(user_labels, sort_order)
)


@pytest.fixture(scope="function")
Expand Down
99 changes: 99 additions & 0 deletions tests/test_pasqal_device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Copyright 2018 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.
"""
Unit tests for the PasqalDevice
"""
import pytest

import pennylane as qml
from pennylane_cirq import PasqalDevice, SimulatorDevice
from cirq.pasqal import ThreeDQubit, PasqalVirtualDevice


class TestDeviceIntegration:
"""Tests that the SimulatorDevice integrates well with PennyLane"""

def test_device_loading(self):
"""Tests that the cirq.pasqal device is properly loaded"""

control_radius = 1.0
dev = qml.device("cirq.pasqal", wires=2, control_radius=1.0)

assert dev.num_wires == 2
assert len(dev.qubits) == 2
assert dev.shots == 1000
assert dev.short_name == "cirq.pasqal"
assert dev.analytic == True
assert dev.control_radius == 1.0
assert dev.qubits == sorted([ThreeDQubit(0, 0, 0), ThreeDQubit(control_radius / 2, 0, 0)])
assert isinstance(dev, SimulatorDevice)


class TestDevice:
"""Unit tests for the PasqalDevice"""

@pytest.mark.parametrize("control_radius", [1.0, 2.0, 99.99])
def test_device_creation(self, control_radius):
"""Tests that the cirq.pasqal device is properly created"""

dev = PasqalDevice(wires=2, shots=123, control_radius=control_radius)

assert dev.num_wires == 2
assert len(dev.qubits) == 2
assert dev.shots == 123
assert dev.short_name == "cirq.pasqal"
assert dev.analytic == True
assert dev.control_radius == control_radius
assert dev.qubits == [ThreeDQubit(0, 0, 0), ThreeDQubit(control_radius / 2, 0, 0)]
assert isinstance(dev, SimulatorDevice)
assert isinstance(dev.cirq_device, PasqalVirtualDevice)

@pytest.mark.parametrize(
"coord_idxs",
[
[(0, 0, 0), (1, 0, 0), (2, 0, 0), (3, 0, 0)],
[(0, 0, 0), (0, 0, 1), (0, 1, 1), (1, 1, 1)],
[(-1, -1, -1000), (1, 2, 3), (3, 2, 3), (3, 3, 3)],
],
)
def test_device_creation_threeDqubits_ordered(self, coord_idxs):
"""Tests that a PasqalDevice can be properly instantiated with ThreeDQubits that are ordered following Cirq's convention."""

qubits = [ThreeDQubit(*idxs) for idxs in coord_idxs]
dev = PasqalDevice(wires=4, qubits=qubits, control_radius=3)

assert dev.qubits == qubits

@pytest.mark.parametrize(
"coord_idxs",
[
[(0, 0, 0), (1, 0, 0), (2, 0, 0), (3, 0, 0)],
[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1)],
[(1, 2, 3), (3, 2, 1), (0, 0, 0), (-1, -1, -10)],
],
)
def test_device_creation_threeDqubits_unordered(self, coord_idxs):
"""Tests that a PasqalDevice can be properly instantiated with ThreeDQubits that are not ordered following Cirq's convention."""

qubits = [ThreeDQubit(*idxs) for idxs in coord_idxs]
dev = PasqalDevice(wires=4, qubits=qubits, control_radius=3)

assert dev.qubits == sorted(qubits)

def test_control_radius_negative_exception(self):
"""Tests that an exception is raised when the supplied control_radius parameter
is a negative real number"""

with pytest.raises(ValueError, match="must be a non-negative real number"):
dev = PasqalDevice(wires=2, shots=123, control_radius=-5.0)