From e55c5ed7dc79131f7d38ca10e2c64bfe5b4ed9d7 Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Mon, 7 Aug 2023 08:45:03 -0700 Subject: [PATCH 01/13] add to changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ef9426..7caf57b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ This release contains contributions from (in alphabetical order): +Spencer Churchill + --- # Release 0.28.0 From 8e3c0c289e9593fd877e0492274a01a745321ee1 Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Thu, 26 Oct 2023 08:58:31 -0700 Subject: [PATCH 02/13] add custom backends to qpu class --- README.rst | 2 +- pennylane_ionq/device.py | 43 +++++++++++++++++++++++++++++++--------- setup.py | 6 ++---- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/README.rst b/README.rst index c7e26aa..b4d92f4 100644 --- a/README.rst +++ b/README.rst @@ -108,7 +108,7 @@ You can instantiate the IonQ devices for PennyLane as follows: import pennylane as qml dev1 = qml.device('ionq.simulator', wires=2, shots=1000) - dev2 = qml.device('ionq.qpu', wires=2, shots=1000) + dev2 = qml.device('ionq.qpu', backend='harmony', wires=2, shots=1000) These devices can then be used just like other devices for the definition and evaluation of quantum circuits within PennyLane. For more details and ideas, see the diff --git a/pennylane_ionq/device.py b/pennylane_ionq/device.py index fed1378..b334c94 100644 --- a/pennylane_ionq/device.py +++ b/pennylane_ionq/device.py @@ -93,9 +93,13 @@ class IonQDevice(QubitDevice): # and therefore does not support the Hermitian observable. observables = {"PauliX", "PauliY", "PauliZ", "Hadamard", "Identity"} - def __init__(self, wires, *, target="simulator", gateset="qis", shots=1024, api_key=None): + def __init__( + self, wires, *, target="simulator", gateset="qis", shots=1024, api_key=None + ): if shots is None: - raise ValueError("The ionq device does not support analytic expectation values.") + raise ValueError( + "The ionq device does not support analytic expectation values." + ) super().__init__(wires=wires, shots=shots) self.target = target @@ -134,7 +138,9 @@ def apply(self, operations, **kwargs): rotations = kwargs.pop("rotations", []) if len(operations) == 0 and len(rotations) == 0: - warnings.warn("Circuit is empty. Empty circuits return failures. Submitting anyway.") + warnings.warn( + "Circuit is empty. Empty circuits return failures. Submitting anyway." + ) for i, operation in enumerate(operations): if i > 0 and operation.name in {"BasisState", "QubitStateVector"}: @@ -210,7 +216,8 @@ def prob(self): # Here, we rearrange the states to match the big-endian ordering # expected by PennyLane. basis_states = ( - int(bin(int(k))[2:].rjust(self.num_wires, "0")[::-1], 2) for k in self.histogram + int(bin(int(k))[2:].rjust(self.num_wires, "0")[::-1], 2) + for k in self.histogram ) idx = np.fromiter(basis_states, dtype=int) @@ -230,7 +237,9 @@ def probability(self, wires=None, shot_range=None, bin_size=None): if shot_range is None and bin_size is None: return self.marginal_prob(self.prob, wires) - return self.estimate_probability(wires=wires, shot_range=shot_range, bin_size=bin_size) + return self.estimate_probability( + wires=wires, shot_range=shot_range, bin_size=bin_size + ) class SimulatorDevice(IonQDevice): @@ -251,8 +260,12 @@ class SimulatorDevice(IonQDevice): name = "IonQ Simulator PennyLane plugin" short_name = "ionq.simulator" - def __init__(self, wires, *, target="simulator", gateset="qis", shots=1024, api_key=None): - super().__init__(wires=wires, target=target, gateset=gateset, shots=shots, api_key=api_key) + def __init__( + self, wires, *, target="simulator", gateset="qis", shots=1024, api_key=None + ): + super().__init__( + wires=wires, target=target, gateset=gateset, shots=shots, api_key=api_key + ) def generate_samples(self): """Generates samples by random sampling with the probabilities returned by the simulator.""" @@ -279,8 +292,20 @@ class QPUDevice(IonQDevice): name = "IonQ QPU PennyLane plugin" short_name = "ionq.qpu" - def __init__(self, wires, *, target="qpu", gateset="qis", shots=1024, api_key=None): - super().__init__(wires=wires, target=target, gateset=gateset, shots=shots, api_key=api_key) + def __init__( + self, + wires, + *, + target="qpu", + backend="", + gateset="qis", + shots=1024, + api_key=None, + ): + target += backend + super().__init__( + wires=wires, target=target, gateset=gateset, shots=shots, api_key=api_key + ) def generate_samples(self): """Generates samples from the qpu. diff --git a/setup.py b/setup.py index 41205e8..7f049fe 100644 --- a/setup.py +++ b/setup.py @@ -33,13 +33,11 @@ "maintainer_email": "software@xanadu.ai", "url": "http://xanadu.ai", "license": "Apache License 2.0", - "packages": [ - "pennylane_ionq" - ], + "packages": ["pennylane_ionq"], "entry_points": { "pennylane.plugins": [ "ionq.simulator = pennylane_ionq:SimulatorDevice", - "ionq.qpu = pennylane_ionq:QPUDevice" + "ionq.qpu = pennylane_ionq:QPUDevice", ] }, "description": "PennyLane plugin for IonQ", From 2982c05dbce4231871f569cab6d5531b7ff94c46 Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Thu, 26 Oct 2023 09:34:19 -0700 Subject: [PATCH 03/13] use any backend --- pennylane_ionq/device.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_ionq/device.py b/pennylane_ionq/device.py index b334c94..ae09236 100644 --- a/pennylane_ionq/device.py +++ b/pennylane_ionq/device.py @@ -302,7 +302,7 @@ def __init__( shots=1024, api_key=None, ): - target += backend + target += "." + backend super().__init__( wires=wires, target=target, gateset=gateset, shots=shots, api_key=api_key ) From 0ebaacdf3458bf24fc1dec32045ed0d64aefcc0d Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Thu, 26 Oct 2023 09:36:59 -0700 Subject: [PATCH 04/13] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7caf57b..a9db856 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ ### Improvements +* Use new `backend` field to specify `qpu`. + [(#81)](https://github.com/PennyLaneAI/PennyLane-IonQ/pull/81) + ### Documentation ### Bug fixes From f2c2fcd3e85530deb3cec37a7eaf6ac9e870fee0 Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Thu, 26 Oct 2023 10:00:25 -0700 Subject: [PATCH 05/13] add backend classes --- pennylane_ionq/device.py | 67 ++++++++++++++++++++++++++++++++++++---- setup.py | 4 +++ 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/pennylane_ionq/device.py b/pennylane_ionq/device.py index b368d3a..949f8d3 100644 --- a/pennylane_ionq/device.py +++ b/pennylane_ionq/device.py @@ -14,9 +14,7 @@ """ This module contains the device class for constructing IonQ devices for PennyLane. """ -import itertools -import functools -import warnings +import os, warnings from time import sleep import numpy as np @@ -143,7 +141,11 @@ def apply(self, operations, **kwargs): ) for i, operation in enumerate(operations): - if i > 0 and operation.name in {"BasisState", "QubitStateVector", "StatePrep"}: + if i > 0 and operation.name in { + "BasisState", + "QubitStateVector", + "StatePrep", + }: raise DeviceError( f"The operation {operation.name} is only supported at the beginning of a circuit." ) @@ -282,6 +284,7 @@ class QPUDevice(IonQDevice): or iterable that contains unique labels for the subsystems as numbers (i.e., ``[-1, 0, 2]``) or strings (``['ancilla', 'q1', 'q2']``). gateset (str): the target gateset, either ``"qis"`` or ``"native"``. + backend (str): Optional specifier for an IonQ backend. Can be ``"harmony"``, ``"aria-1"``, etc. shots (int, list[int]): Number of circuit evaluations/random samples used to estimate expectation values of observables. If ``None``, the device calculates probability, expectation values, and variances analytically. If an integer, it specifies the number of samples to estimate these quantities. @@ -297,12 +300,13 @@ def __init__( wires, *, target="qpu", - backend="", + backend=None, gateset="qis", shots=1024, api_key=None, ): - target += "." + backend + if backend is not None: + target += "." + backend super().__init__( wires=wires, target=target, gateset=gateset, shots=shots, api_key=api_key ) @@ -323,3 +327,54 @@ def generate_samples(self): samples = np.repeat(np.arange(number_of_states), counts) np.random.shuffle(samples) return QubitDevice.states_to_binary(samples, self.num_wires) + + +# Specific Backends + + +class HarmonyQPUDevice(QPUDevice): + def __init__(self, wires, *, gateset="qis", shots=1024, api_key=None): + super().__init__( + wires, + target="qpu", + backend="harmony", + gateset=gateset, + shots=shots, + api_key=api_key, + ) + + +class Aria1QPUDevice(QPUDevice): + def __init__(self, wires, *, gateset="qis", shots=1024, api_key=None): + super().__init__( + wires, + target="qpu", + backend="aria-1", + gateset=gateset, + shots=shots, + api_key=api_key, + ) + + +class Aria2QPUDevice(QPUDevice): + def __init__(self, wires, *, gateset="qis", shots=1024, api_key=None): + super().__init__( + wires, + target="qpu", + backend="aria-2", + gateset=gateset, + shots=shots, + api_key=api_key, + ) + + +class Forte1QPUDevice(QPUDevice): + def __init__(self, wires, *, gateset="qis", shots=1024, api_key=None): + super().__init__( + wires, + target="qpu", + backend="forte-1", + gateset=gateset, + shots=shots, + api_key=api_key, + ) diff --git a/setup.py b/setup.py index 77d1a75..e5bc841 100644 --- a/setup.py +++ b/setup.py @@ -38,6 +38,10 @@ "pennylane.plugins": [ "ionq.simulator = pennylane_ionq:SimulatorDevice", "ionq.qpu = pennylane_ionq:QPUDevice", + "ionq.qpu.harmony = pennylane_ionq:HarmonyQPUDevice", + "ionq.qpu.aria-1 = pennylane_ionq:Aria1QPUDevice", + "ionq.qpu.aria-2 = pennylane_ionq:Aria2QPUDevice", + "ionq.qpu.forte-1 = pennylane_ionq:Forte1QPUDevice", ] }, "description": "PennyLane plugin for IonQ", From df2b48bf8c6b28333a63ad0d2ba16d32c9ea399c Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Thu, 26 Oct 2023 10:05:54 -0700 Subject: [PATCH 06/13] make qpu classes visible --- pennylane_ionq/__init__.py | 9 ++++++++- tests/conftest.py | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/pennylane_ionq/__init__.py b/pennylane_ionq/__init__.py index 096bb30..a547f50 100644 --- a/pennylane_ionq/__init__.py +++ b/pennylane_ionq/__init__.py @@ -16,5 +16,12 @@ ======================= """ from .ops import GPI, GPI2, MS, XX, YY, ZZ -from .device import SimulatorDevice, QPUDevice +from .device import ( + SimulatorDevice, + QPUDevice, + HarmonyQPUDevice, + Aria1QPUDevice, + Aria2QPUDevice, + Forte1QPUDevice, +) from ._version import __version__ diff --git a/tests/conftest.py b/tests/conftest.py index 58b7bc6..452ff3c 100755 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,7 +15,14 @@ import numpy as np import pytest -from pennylane_ionq import SimulatorDevice, QPUDevice +from pennylane_ionq import ( + SimulatorDevice, + QPUDevice, + HarmonyQPUDevice, + Aria1QPUDevice, + Aria2QPUDevice, + Forte1QPUDevice, +) np.random.seed(42) From 498b3bc2481da3d6153d39afac97eaf1a1f26213 Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Thu, 26 Oct 2023 10:13:26 -0700 Subject: [PATCH 07/13] add docstrings and remove os import --- pennylane_ionq/device.py | 44 ++++++++++++++++++++++++++++++++++++++++ tests/conftest.py | 1 - 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/pennylane_ionq/device.py b/pennylane_ionq/device.py index 949f8d3..73dad0a 100644 --- a/pennylane_ionq/device.py +++ b/pennylane_ionq/device.py @@ -333,6 +333,17 @@ def generate_samples(self): class HarmonyQPUDevice(QPUDevice): + """ + PennyLane device for the IonQ Harmony QPU backend. + + Args: + wires (int or Iterable[Number, str]): Number of subsystems represented by the device, or iterable of unique labels for the subsystems. + gateset (str): Target gateset, either "qis" or "native". + shots (int): Number of circuit evaluations/random samples used to estimate expectation values and variances of observables. + api_key (str): The IonQ API key. If not provided, the environment + variable ``IONQ_API_KEY`` is used. + """ + def __init__(self, wires, *, gateset="qis", shots=1024, api_key=None): super().__init__( wires, @@ -345,6 +356,17 @@ def __init__(self, wires, *, gateset="qis", shots=1024, api_key=None): class Aria1QPUDevice(QPUDevice): + """ + PennyLane device for the IonQ Aria-1 QPU backend. + + Args: + wires (int or Iterable[Number, str]): Number of subsystems represented by the device, or iterable of unique labels for the subsystems. + gateset (str): Target gateset, either "qis" or "native". + shots (int): Number of circuit evaluations/random samples used to estimate expectation values and variances of observables. + api_key (str): The IonQ API key. If not provided, the environment + variable ``IONQ_API_KEY`` is used. + """ + def __init__(self, wires, *, gateset="qis", shots=1024, api_key=None): super().__init__( wires, @@ -357,6 +379,17 @@ def __init__(self, wires, *, gateset="qis", shots=1024, api_key=None): class Aria2QPUDevice(QPUDevice): + """ + PennyLane device for the IonQ Aria-2 QPU backend. + + Args: + wires (int or Iterable[Number, str]): Number of subsystems represented by the device, or iterable of unique labels for the subsystems. + gateset (str): Target gateset, either "qis" or "native". + shots (int): Number of circuit evaluations/random samples used to estimate expectation values and variances of observables. + api_key (str): The IonQ API key. If not provided, the environment + variable ``IONQ_API_KEY`` is used. + """ + def __init__(self, wires, *, gateset="qis", shots=1024, api_key=None): super().__init__( wires, @@ -369,6 +402,17 @@ def __init__(self, wires, *, gateset="qis", shots=1024, api_key=None): class Forte1QPUDevice(QPUDevice): + """ + PennyLane device for the IonQ Forte-1 QPU backend. + + Args: + wires (int or Iterable[Number, str]): Number of subsystems represented by the device, or iterable of unique labels for the subsystems. + gateset (str): Target gateset, either "qis" or "native". + shots (int): Number of circuit evaluations/random samples used to estimate expectation values and variances of observables. + api_key (str): The IonQ API key. If not provided, the environment + variable ``IONQ_API_KEY`` is used. + """ + def __init__(self, wires, *, gateset="qis", shots=1024, api_key=None): super().__init__( wires, diff --git a/tests/conftest.py b/tests/conftest.py index 452ff3c..9116338 100755 --- a/tests/conftest.py +++ b/tests/conftest.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. -import os import numpy as np import pytest From e9b1a78463d17f9e8340f6cf6ccc04ba97508fb2 Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Thu, 26 Oct 2023 10:16:38 -0700 Subject: [PATCH 08/13] include both backend methods in readme --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index b4d92f4..48bd8af 100644 --- a/README.rst +++ b/README.rst @@ -108,7 +108,8 @@ You can instantiate the IonQ devices for PennyLane as follows: import pennylane as qml dev1 = qml.device('ionq.simulator', wires=2, shots=1000) - dev2 = qml.device('ionq.qpu', backend='harmony', wires=2, shots=1000) + dev2 = qml.device('ionq.qpu.harmony', wires=2, shots=1000) + dev3 = qml.device('ionq.qpu', backend='aria-1', wires=2, shots=1000) These devices can then be used just like other devices for the definition and evaluation of quantum circuits within PennyLane. For more details and ideas, see the From dc1bc7f2db2e35ef04a79fcdc51b105271ac53df Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Mon, 30 Oct 2023 08:42:38 -0700 Subject: [PATCH 09/13] add backends to test --- tests/conftest.py | 9 ++++++++- tests/test_device.py | 6 +----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 9116338..a9a01cc 100755 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -57,7 +57,14 @@ # List of all devices that do *not* support analytic expectation # value computation. This generally includes hardware devices # and hardware simulators. -hw_devices = [SimulatorDevice] +hw_devices = [ + SimulatorDevice, + QPUDevice, + HarmonyQPUDevice, + Aria1QPUDevice, + Aria2QPUDevice, + Forte1QPUDevice, +] # List of all device shortnames shortnames = [d.short_name for d in analytic_devices + hw_devices] diff --git a/tests/test_device.py b/tests/test_device.py index df5f236..0b330d7 100755 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -96,7 +96,6 @@ def mock_submit_job(*args): dev.apply([]) def test_failedcircuit(self, monkeypatch): - monkeypatch.setattr( requests, "post", lambda url, timeout, data, headers: (url, data, headers) ) @@ -246,7 +245,7 @@ def mock_submit_job(*args): pass mocker.patch("pennylane_ionq.device.IonQDevice._submit_job", mock_submit_job) - dev = IonQDevice(wires=(0,1,2), gateset="native") + dev = IonQDevice(wires=(0, 1, 2), gateset="native") with qml.tape.QuantumTape() as tape: GPI(0.1, wires=[0]) @@ -275,6 +274,3 @@ def mock_submit_job(*args): "targets": [1, 2], "phases": [0.2, 0.3], } - - - From 1f665a47323795ebecdbb958d7364080515e4a90 Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Tue, 31 Oct 2023 08:40:53 -0700 Subject: [PATCH 10/13] remove byte 0xe2 --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 25449ed..08e051a 100644 --- a/README.rst +++ b/README.rst @@ -120,7 +120,7 @@ to the `PennyLane documentation `_. Contributing ============ -We welcome contributions—simply fork the PennyLane-IonQ repository, and then make a +We welcome contributions-simply fork the PennyLane-IonQ repository, and then make a `pull request `_ containing your contribution. All contributers to PennyLane-IonQ will be listed as contributors on the releases. From 077fc5a3de7ca92286fc6863c0ddcabc7310d96d Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Wed, 1 Nov 2023 11:01:32 -0700 Subject: [PATCH 11/13] only use backend field + update docs and tests --- README.rst | 3 +- doc/devices.rst | 7 +-- pennylane_ionq/__init__.py | 9 +--- pennylane_ionq/device.py | 100 ++----------------------------------- setup.py | 4 -- tests/conftest.py | 19 ++----- tests/test_device.py | 12 +++++ 7 files changed, 24 insertions(+), 130 deletions(-) diff --git a/README.rst b/README.rst index 08e051a..b002764 100644 --- a/README.rst +++ b/README.rst @@ -108,8 +108,7 @@ You can instantiate the IonQ devices for PennyLane as follows: import pennylane as qml dev1 = qml.device('ionq.simulator', wires=2, shots=1000) - dev2 = qml.device('ionq.qpu.harmony', wires=2, shots=1000) - dev3 = qml.device('ionq.qpu', backend='aria-1', wires=2, shots=1000) + dev2 = qml.device('ionq.qpu', backend='aria-1', wires=2, shots=1000) These devices can then be used just like other devices for the definition and evaluation of quantum circuits within PennyLane. For more details and ideas, see the diff --git a/doc/devices.rst b/doc/devices.rst index 5c8275b..0d3cb28 100644 --- a/doc/devices.rst +++ b/doc/devices.rst @@ -38,16 +38,17 @@ directly in PennyLane by specifying ``"ionq.simulator"``: Trapped-Ion QPU --------------- -This device provides access to IonQ's trapped-ion QPU. +This device provides access to IonQ's trapped-ion QPUs. Once the plugin has been installed, you can use this device -directly in PennyLane by specifying ``"ionq.qpu"``: +directly in PennyLane by specifying ``"ionq.qpu"`` with a +``"backend"`` from `available backends `_: .. code-block:: python import pennylane as qml from pennylane_ionq import ops - dev = qml.device("ionq.qpu", wires=2) + dev = qml.device("ionq.qpu", backend="harmony", wires=2) @qml.qnode(dev) def circuit(x, y): diff --git a/pennylane_ionq/__init__.py b/pennylane_ionq/__init__.py index a547f50..096bb30 100644 --- a/pennylane_ionq/__init__.py +++ b/pennylane_ionq/__init__.py @@ -16,12 +16,5 @@ ======================= """ from .ops import GPI, GPI2, MS, XX, YY, ZZ -from .device import ( - SimulatorDevice, - QPUDevice, - HarmonyQPUDevice, - Aria1QPUDevice, - Aria2QPUDevice, - Forte1QPUDevice, -) +from .device import SimulatorDevice, QPUDevice from ._version import __version__ diff --git a/pennylane_ionq/device.py b/pennylane_ionq/device.py index 73dad0a..fc67bd0 100644 --- a/pennylane_ionq/device.py +++ b/pennylane_ionq/device.py @@ -305,8 +305,9 @@ def __init__( shots=1024, api_key=None, ): - if backend is not None: - target += "." + backend + self.backend = backend + if self.backend is not None: + target += "." + self.backend super().__init__( wires=wires, target=target, gateset=gateset, shots=shots, api_key=api_key ) @@ -327,98 +328,3 @@ def generate_samples(self): samples = np.repeat(np.arange(number_of_states), counts) np.random.shuffle(samples) return QubitDevice.states_to_binary(samples, self.num_wires) - - -# Specific Backends - - -class HarmonyQPUDevice(QPUDevice): - """ - PennyLane device for the IonQ Harmony QPU backend. - - Args: - wires (int or Iterable[Number, str]): Number of subsystems represented by the device, or iterable of unique labels for the subsystems. - gateset (str): Target gateset, either "qis" or "native". - shots (int): Number of circuit evaluations/random samples used to estimate expectation values and variances of observables. - api_key (str): The IonQ API key. If not provided, the environment - variable ``IONQ_API_KEY`` is used. - """ - - def __init__(self, wires, *, gateset="qis", shots=1024, api_key=None): - super().__init__( - wires, - target="qpu", - backend="harmony", - gateset=gateset, - shots=shots, - api_key=api_key, - ) - - -class Aria1QPUDevice(QPUDevice): - """ - PennyLane device for the IonQ Aria-1 QPU backend. - - Args: - wires (int or Iterable[Number, str]): Number of subsystems represented by the device, or iterable of unique labels for the subsystems. - gateset (str): Target gateset, either "qis" or "native". - shots (int): Number of circuit evaluations/random samples used to estimate expectation values and variances of observables. - api_key (str): The IonQ API key. If not provided, the environment - variable ``IONQ_API_KEY`` is used. - """ - - def __init__(self, wires, *, gateset="qis", shots=1024, api_key=None): - super().__init__( - wires, - target="qpu", - backend="aria-1", - gateset=gateset, - shots=shots, - api_key=api_key, - ) - - -class Aria2QPUDevice(QPUDevice): - """ - PennyLane device for the IonQ Aria-2 QPU backend. - - Args: - wires (int or Iterable[Number, str]): Number of subsystems represented by the device, or iterable of unique labels for the subsystems. - gateset (str): Target gateset, either "qis" or "native". - shots (int): Number of circuit evaluations/random samples used to estimate expectation values and variances of observables. - api_key (str): The IonQ API key. If not provided, the environment - variable ``IONQ_API_KEY`` is used. - """ - - def __init__(self, wires, *, gateset="qis", shots=1024, api_key=None): - super().__init__( - wires, - target="qpu", - backend="aria-2", - gateset=gateset, - shots=shots, - api_key=api_key, - ) - - -class Forte1QPUDevice(QPUDevice): - """ - PennyLane device for the IonQ Forte-1 QPU backend. - - Args: - wires (int or Iterable[Number, str]): Number of subsystems represented by the device, or iterable of unique labels for the subsystems. - gateset (str): Target gateset, either "qis" or "native". - shots (int): Number of circuit evaluations/random samples used to estimate expectation values and variances of observables. - api_key (str): The IonQ API key. If not provided, the environment - variable ``IONQ_API_KEY`` is used. - """ - - def __init__(self, wires, *, gateset="qis", shots=1024, api_key=None): - super().__init__( - wires, - target="qpu", - backend="forte-1", - gateset=gateset, - shots=shots, - api_key=api_key, - ) diff --git a/setup.py b/setup.py index e5bc841..77d1a75 100644 --- a/setup.py +++ b/setup.py @@ -38,10 +38,6 @@ "pennylane.plugins": [ "ionq.simulator = pennylane_ionq:SimulatorDevice", "ionq.qpu = pennylane_ionq:QPUDevice", - "ionq.qpu.harmony = pennylane_ionq:HarmonyQPUDevice", - "ionq.qpu.aria-1 = pennylane_ionq:Aria1QPUDevice", - "ionq.qpu.aria-2 = pennylane_ionq:Aria2QPUDevice", - "ionq.qpu.forte-1 = pennylane_ionq:Forte1QPUDevice", ] }, "description": "PennyLane plugin for IonQ", diff --git a/tests/conftest.py b/tests/conftest.py index a9a01cc..b59ea8d 100755 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,17 +11,11 @@ # 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 os import numpy as np import pytest -from pennylane_ionq import ( - SimulatorDevice, - QPUDevice, - HarmonyQPUDevice, - Aria1QPUDevice, - Aria2QPUDevice, - Forte1QPUDevice, -) +from pennylane_ionq import SimulatorDevice, QPUDevice np.random.seed(42) @@ -57,14 +51,7 @@ # List of all devices that do *not* support analytic expectation # value computation. This generally includes hardware devices # and hardware simulators. -hw_devices = [ - SimulatorDevice, - QPUDevice, - HarmonyQPUDevice, - Aria1QPUDevice, - Aria2QPUDevice, - Forte1QPUDevice, -] +hw_devices = [SimulatorDevice, QPUDevice] # List of all device shortnames shortnames = [d.short_name for d in analytic_devices + hw_devices] diff --git a/tests/test_device.py b/tests/test_device.py index 0b330d7..b999d2e 100755 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -181,6 +181,18 @@ def test_prob_no_results(self, d): dev = qml.device(d, wires=1, shots=1) assert dev.prob is None + @pytest.mark.parametrize( + "backend", ["harmony", "aria-1", "aria-2", "forte-1", None] + ) + def test_backend_initialization(self, backend): + """Test that the device initializes with the correct backend.""" + if backend: + dev = qml.device("ionq.qpu", wires=2, shots=1000, backend=backend) + assert dev.backend == backend + else: + dev = qml.device("ionq.qpu", wires=2, shots=1000) + assert dev.backend == None + class TestJobAttribute: """Tests job creation with mocked submission.""" From 52471b67d8d8cb6b14f2c07b844f52c2c995b33f Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Wed, 1 Nov 2023 11:50:40 -0700 Subject: [PATCH 12/13] fix line length --- pennylane_ionq/device.py | 6 +++++- tests/test_device.py | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/pennylane_ionq/device.py b/pennylane_ionq/device.py index fc67bd0..6067681 100644 --- a/pennylane_ionq/device.py +++ b/pennylane_ionq/device.py @@ -309,7 +309,11 @@ def __init__( if self.backend is not None: target += "." + self.backend super().__init__( - wires=wires, target=target, gateset=gateset, shots=shots, api_key=api_key + wires=wires, + target=target, + gateset=gateset, + shots=shots, + api_key=api_key, ) def generate_samples(self): diff --git a/tests/test_device.py b/tests/test_device.py index b999d2e..e50ef4c 100755 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -187,7 +187,12 @@ def test_prob_no_results(self, d): def test_backend_initialization(self, backend): """Test that the device initializes with the correct backend.""" if backend: - dev = qml.device("ionq.qpu", wires=2, shots=1000, backend=backend) + dev = qml.device( + "ionq.qpu", + wires=2, + shots=1000, + backend=backend, + ) assert dev.backend == backend else: dev = qml.device("ionq.qpu", wires=2, shots=1000) From 2f491bd6202488112a219eee716e56812d38ad4c Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Wed, 1 Nov 2023 17:51:13 -0500 Subject: [PATCH 13/13] run black formatter --- pennylane_ionq/device.py | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/pennylane_ionq/device.py b/pennylane_ionq/device.py index 6067681..76e6f97 100644 --- a/pennylane_ionq/device.py +++ b/pennylane_ionq/device.py @@ -91,13 +91,9 @@ class IonQDevice(QubitDevice): # and therefore does not support the Hermitian observable. observables = {"PauliX", "PauliY", "PauliZ", "Hadamard", "Identity"} - def __init__( - self, wires, *, target="simulator", gateset="qis", shots=1024, api_key=None - ): + def __init__(self, wires, *, target="simulator", gateset="qis", shots=1024, api_key=None): if shots is None: - raise ValueError( - "The ionq device does not support analytic expectation values." - ) + raise ValueError("The ionq device does not support analytic expectation values.") super().__init__(wires=wires, shots=shots) self.target = target @@ -136,9 +132,7 @@ def apply(self, operations, **kwargs): rotations = kwargs.pop("rotations", []) if len(operations) == 0 and len(rotations) == 0: - warnings.warn( - "Circuit is empty. Empty circuits return failures. Submitting anyway." - ) + warnings.warn("Circuit is empty. Empty circuits return failures. Submitting anyway.") for i, operation in enumerate(operations): if i > 0 and operation.name in { @@ -218,8 +212,7 @@ def prob(self): # Here, we rearrange the states to match the big-endian ordering # expected by PennyLane. basis_states = ( - int(bin(int(k))[2:].rjust(self.num_wires, "0")[::-1], 2) - for k in self.histogram + int(bin(int(k))[2:].rjust(self.num_wires, "0")[::-1], 2) for k in self.histogram ) idx = np.fromiter(basis_states, dtype=int) @@ -239,9 +232,7 @@ def probability(self, wires=None, shot_range=None, bin_size=None): if shot_range is None and bin_size is None: return self.marginal_prob(self.prob, wires) - return self.estimate_probability( - wires=wires, shot_range=shot_range, bin_size=bin_size - ) + return self.estimate_probability(wires=wires, shot_range=shot_range, bin_size=bin_size) class SimulatorDevice(IonQDevice): @@ -262,12 +253,8 @@ class SimulatorDevice(IonQDevice): name = "IonQ Simulator PennyLane plugin" short_name = "ionq.simulator" - def __init__( - self, wires, *, target="simulator", gateset="qis", shots=1024, api_key=None - ): - super().__init__( - wires=wires, target=target, gateset=gateset, shots=shots, api_key=api_key - ) + def __init__(self, wires, *, target="simulator", gateset="qis", shots=1024, api_key=None): + super().__init__(wires=wires, target=target, gateset=gateset, shots=shots, api_key=api_key) def generate_samples(self): """Generates samples by random sampling with the probabilities returned by the simulator."""