From 003f9f4d4756fc3c7a118aedd3c2fc61aec0779f Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Tue, 16 Apr 2024 11:48:27 -0700 Subject: [PATCH 01/13] enable partially entangling ms gates --- pennylane_ionq/device.py | 22 ++++++++++++---- pennylane_ionq/ops.py | 32 ++++++++++++++++-------- tests/test_device.py | 54 +++++++++++++++++++++++++++++++--------- 3 files changed, 81 insertions(+), 27 deletions(-) diff --git a/pennylane_ionq/device.py b/pennylane_ionq/device.py index 5d19c08..f2903fe 100644 --- a/pennylane_ionq/device.py +++ b/pennylane_ionq/device.py @@ -85,6 +85,7 @@ class IonQDevice(QubitDevice): distribution has peaks. See `IonQ Debiasing and Sharpening `_ for details. """ + # pylint: disable=too-many-instance-attributes name = "IonQ PennyLane plugin" short_name = "ionq" @@ -114,7 +115,9 @@ def __init__( sharpen=False, ): 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 @@ -164,7 +167,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 { @@ -201,7 +206,9 @@ def _apply_operation(self, operation): if self.gateset == "native": if len(par) > 1: - gate["phases"] = [float(v) for v in par] + gate["phases"] = [float(v) for v in par[:2]] + if len(par) > 2: + gate["rotation"] = float(par[2]) else: gate["phase"] = float(par[0]) elif par: @@ -246,7 +253,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) @@ -266,7 +274,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): @@ -285,6 +295,7 @@ class SimulatorDevice(IonQDevice): api_key (str): The IonQ API key. If not provided, the environment variable ``IONQ_API_KEY`` is used. """ + name = "IonQ Simulator PennyLane plugin" short_name = "ionq.simulator" @@ -329,6 +340,7 @@ class QPUDevice(IonQDevice): your expected output distribution has peaks. See `IonQ Debiasing and Sharpening `_ for details. """ + name = "IonQ QPU PennyLane plugin" short_name = "ionq.qpu" diff --git a/pennylane_ionq/ops.py b/pennylane_ionq/ops.py index 8a11d63..3a18738 100644 --- a/pennylane_ionq/ops.py +++ b/pennylane_ionq/ops.py @@ -33,6 +33,7 @@ class GPI(Operation): # pylint: disable=too-few-public-methods phi (float): phase :math:`\phi` wires (Sequence[int]): the subsystems the operation acts on """ + num_params = 1 num_wires = 1 grad_method = None @@ -53,33 +54,41 @@ class GPI2(Operation): # pylint: disable=too-few-public-methods phi (float): phase :math:`\phi` wires (Sequence[int]): the subsystems the operation acts on """ + num_params = 1 num_wires = 1 grad_method = None class MS(Operation): # pylint: disable=too-few-public-methods - r"""MS(phi0, phi1, wires) - 2-qubit entanlging MS gate. + r"""MS(phi0, phi1, theta=0.25, wires) + 2-qubit entangling MS gate. .. math:: - MS(\phi_{0}, \phi_{1}) = + MS(\phi_{0}, \phi_{1}, \theta) = \frac{1}{\sqrt{2}}\begin{pmatrix} - 1 & 0 & 0 & -i e^{-2 \pi i(\phi_{0}+\phi_{1})} \\ - 0 & 1 & -i e^{-2 \pi i (\phi_{0}-\phi_{1})} & 0 \\ - 0 & -i e^{2 \pi i(\phi_{0}-\phi_{1})} & 1 & 0 \\ - -i e^{2 \pi i(\phi_{0}+\phi_{1})} & 0 & 0 & 1 + \cos(\theta / 2) & 0 & 0 & -i e^{-2 \pi i(\phi_{0}+\phi_{1})} \\ + 0 & \cos(\theta / 2) & -i e^{-2 \pi i (\phi_{0}-\phi_{1})} & 0 \\ + 0 & -i e^{2 \pi i(\phi_{0}-\phi_{1})} & \cos(\theta / 2) & 0 \\ + -i e^{2 \pi i(\phi_{0}+\phi_{1})} & 0 & 0 & \cos(\theta / 2) \end{pmatrix} + Args: - phi0 (float): phase of the first qubit :math:`\phi` - phi1 (float): phase of the second qubit :math:`\phi` + phi0 (float): phase of the first qubit :math:`\phi_0` + phi1 (float): phase of the second qubit :math:`\phi_1` + theta (float): entanglement ratio of the qubits :math:`\theta` [0, 0.25], defaults to 0.25 wires (Sequence[int]): the subsystems the operation acts on """ - num_params = 2 + + num_params = 3 num_wires = 2 + par_domain = "R" grad_method = None + def __init__(self, phi0, phi1, theta=0.25, wires=None): + super().__init__(phi0, phi1, theta, wires=wires) + # Custom operations for the QIS Gateset below @@ -101,6 +110,7 @@ class XX(Operation): phi (float): rotation angle :math:`\phi` wires (Sequence[int]): the subsystems the operation acts on """ + num_params = 1 num_wires = 2 grad_method = "A" @@ -123,6 +133,7 @@ class YY(Operation): phi (float): rotation angle :math:`\phi` wires (Sequence[int]): the subsystems the operation acts on """ + num_params = 1 num_wires = 2 grad_method = "A" @@ -145,6 +156,7 @@ class ZZ(Operation): phi (float): rotation angle :math:`\phi` wires (Sequence[int]): the subsystems the operation acts on """ + num_params = 1 num_wires = 2 grad_method = "A" diff --git a/tests/test_device.py b/tests/test_device.py index 6333fe6..05ebf88 100755 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -52,11 +52,15 @@ def test_generate_samples_qpu_device(self, wires, histogram): unique_outcomes1 = np.unique(sample1, axis=0) unique_outcomes2 = np.unique(sample2, axis=0) - assert np.all(unique_outcomes1 == unique_outcomes2) # possible outcomes are the same + assert np.all( + unique_outcomes1 == unique_outcomes2 + ) # possible outcomes are the same sorted_outcomes1 = np.sort(sample1, axis=0) sorted_outcomes2 = np.sort(sample2, axis=0) - assert np.all(sorted_outcomes1 == sorted_outcomes2) # set of outcomes is the same + assert np.all( + sorted_outcomes1 == sorted_outcomes2 + ) # set of outcomes is the same class TestDeviceIntegration: @@ -96,7 +100,9 @@ def test_failedcircuit(self, monkeypatch): monkeypatch.setattr( requests, "post", lambda url, timeout, data, headers: (url, data, headers) ) - monkeypatch.setattr(ResourceManager, "handle_response", lambda self, response: None) + monkeypatch.setattr( + ResourceManager, "handle_response", lambda self, response: None + ) monkeypatch.setattr(Job, "is_complete", False) monkeypatch.setattr(Job, "is_failed", True) @@ -111,13 +117,17 @@ def test_shots(self, shots, monkeypatch, mocker, tol): monkeypatch.setattr( requests, "post", lambda url, timeout, data, headers: (url, data, headers) ) - monkeypatch.setattr(ResourceManager, "handle_response", lambda self, response: None) + monkeypatch.setattr( + ResourceManager, "handle_response", lambda self, response: None + ) monkeypatch.setattr(Job, "is_complete", True) def fake_response(self, resource_id=None, params=None): """Return fake response data""" fake_json = {"0": 1} - setattr(self.resource, "data", type("data", tuple(), {"value": fake_json})()) + setattr( + self.resource, "data", type("data", tuple(), {"value": fake_json})() + ) monkeypatch.setattr(ResourceManager, "get", fake_response) @@ -133,20 +143,26 @@ def circuit(): circuit() assert json.loads(spy.call_args[1]["data"])["shots"] == shots - @pytest.mark.parametrize("error_mitigation", [None, {"debias": True}, {"debias": False}]) + @pytest.mark.parametrize( + "error_mitigation", [None, {"debias": True}, {"debias": False}] + ) def test_error_mitigation(self, error_mitigation, monkeypatch, mocker): """Test that shots are correctly specified when submitting a job to the API.""" monkeypatch.setattr( requests, "post", lambda url, timeout, data, headers: (url, data, headers) ) - monkeypatch.setattr(ResourceManager, "handle_response", lambda self, response: None) + monkeypatch.setattr( + ResourceManager, "handle_response", lambda self, response: None + ) monkeypatch.setattr(Job, "is_complete", True) def fake_response(self, resource_id=None, params=None): """Return fake response data""" fake_json = {"0": 1} - setattr(self.resource, "data", type("data", tuple(), {"value": fake_json})()) + setattr( + self.resource, "data", type("data", tuple(), {"value": fake_json})() + ) monkeypatch.setattr(ResourceManager, "get", fake_response) @@ -167,7 +183,10 @@ def circuit(): spy = mocker.spy(requests, "post") circuit() if error_mitigation is not None: - assert json.loads(spy.call_args[1]["data"])["error_mitigation"] == error_mitigation + assert ( + json.loads(spy.call_args[1]["data"])["error_mitigation"] + == error_mitigation + ) else: with pytest.raises(KeyError, match="error_mitigation"): json.loads(spy.call_args[1]["data"])["error_mitigation"] @@ -218,12 +237,15 @@ def test_probability(self): assert np.array_equal(dev.probability(shot_range=(0, 2)), [0, 0, 0, 1]) uniform_prob = [0.25] * 4 - with patch("pennylane_ionq.device.SimulatorDevice.prob", new_callable=PropertyMock) as mock_prob: + with patch( + "pennylane_ionq.device.SimulatorDevice.prob", new_callable=PropertyMock + ) as mock_prob: mock_prob.return_value = uniform_prob assert np.array_equal(dev.probability(), uniform_prob) - - @pytest.mark.parametrize("backend", ["harmony", "aria-1", "aria-2", "forte-1", 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.""" dev = qml.device( @@ -304,6 +326,7 @@ def mock_submit_job(*args): GPI(0.1, wires=[0]) GPI2(0.2, wires=[1]) MS(0.2, 0.3, wires=[1, 2]) + MS(0.4, 0.5, 0.1, wires=[1, 2]) dev.apply(tape.operations) @@ -326,4 +349,11 @@ def mock_submit_job(*args): "gate": "ms", "targets": [1, 2], "phases": [0.2, 0.3], + "rotation": 0.25, + } + assert dev.job["input"]["circuit"][3] == { + "gate": "ms", + "targets": [1, 2], + "phases": [0.4, 0.5], + "rotation": 0.1, } From 9e213bb586900ffd77c7e14b4dbff59b294f2f50 Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Tue, 16 Apr 2024 11:52:12 -0700 Subject: [PATCH 02/13] fix lint error --- pennylane_ionq/device.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/pennylane_ionq/device.py b/pennylane_ionq/device.py index f2903fe..8105749 100644 --- a/pennylane_ionq/device.py +++ b/pennylane_ionq/device.py @@ -115,9 +115,7 @@ def __init__( sharpen=False, ): 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 @@ -167,9 +165,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 { @@ -253,8 +249,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) @@ -274,9 +269,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): From d531c825210695254368494b04cf00ddf4da7547 Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Tue, 16 Apr 2024 11:55:12 -0700 Subject: [PATCH 03/13] update length of circuit for 2nd ms gate --- tests/test_device.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_device.py b/tests/test_device.py index 05ebf88..f1b6b1e 100755 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -334,7 +334,7 @@ def mock_submit_job(*args): assert dev.job["input"]["gateset"] == "native" assert dev.job["input"]["qubits"] == 3 - assert len(dev.job["input"]["circuit"]) == 3 + assert len(dev.job["input"]["circuit"]) == 4 assert dev.job["input"]["circuit"][0] == { "gate": "gpi", "target": 0, From f4d326c2e98e767b4db6590d91074516b6fde3b6 Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Tue, 16 Apr 2024 12:21:36 -0700 Subject: [PATCH 04/13] fix rotation -> angle --- 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 8105749..e07678e 100644 --- a/pennylane_ionq/device.py +++ b/pennylane_ionq/device.py @@ -204,7 +204,7 @@ def _apply_operation(self, operation): if len(par) > 1: gate["phases"] = [float(v) for v in par[:2]] if len(par) > 2: - gate["rotation"] = float(par[2]) + gate["angle"] = float(par[2]) else: gate["phase"] = float(par[0]) elif par: From 49c37b8a42cf6affc51b26b5a75c133525bee48e Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Tue, 16 Apr 2024 13:06:21 -0700 Subject: [PATCH 05/13] fix tests --- tests/test_device.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_device.py b/tests/test_device.py index f1b6b1e..8ca6ddd 100755 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -349,11 +349,11 @@ def mock_submit_job(*args): "gate": "ms", "targets": [1, 2], "phases": [0.2, 0.3], - "rotation": 0.25, + "angle": 0.25, } assert dev.job["input"]["circuit"][3] == { "gate": "ms", "targets": [1, 2], "phases": [0.4, 0.5], - "rotation": 0.1, + "angle": 0.1, } From 1becedd425fe7f815407df66eb819c978baabaf5 Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Wed, 17 Apr 2024 11:09:06 -0700 Subject: [PATCH 06/13] clarify entanglement range Co-authored-by: Thomas R. Bromley <49409390+trbromley@users.noreply.github.com> --- pennylane_ionq/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_ionq/ops.py b/pennylane_ionq/ops.py index 3a18738..c2e5d77 100644 --- a/pennylane_ionq/ops.py +++ b/pennylane_ionq/ops.py @@ -77,7 +77,7 @@ class MS(Operation): # pylint: disable=too-few-public-methods Args: phi0 (float): phase of the first qubit :math:`\phi_0` phi1 (float): phase of the second qubit :math:`\phi_1` - theta (float): entanglement ratio of the qubits :math:`\theta` [0, 0.25], defaults to 0.25 + theta (float): entanglement ratio of the qubits :math:`\theta \in [0, 0.25]`, defaults to 0.25 wires (Sequence[int]): the subsystems the operation acts on """ From eceb1599b64d55570d81e48f0c663d52396a18c6 Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Wed, 17 Apr 2024 13:57:46 -0700 Subject: [PATCH 07/13] remove outdated par_domain Co-authored-by: Christina Lee --- pennylane_ionq/ops.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane_ionq/ops.py b/pennylane_ionq/ops.py index c2e5d77..e4b101b 100644 --- a/pennylane_ionq/ops.py +++ b/pennylane_ionq/ops.py @@ -83,7 +83,6 @@ class MS(Operation): # pylint: disable=too-few-public-methods num_params = 3 num_wires = 2 - par_domain = "R" grad_method = None def __init__(self, phi0, phi1, theta=0.25, wires=None): From cf5732bd197e4e0559875b3b5906a2255a9e213d Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Wed, 17 Apr 2024 14:04:27 -0700 Subject: [PATCH 08/13] include partial ms gate feature --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ed88df..68758bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### New features since last release +* Added an optional `theta` parameter to the MS gate to enable partially entangling gates, providing more control over qubit entanglement. This feature allows for finer adjustments in quantum algorithms without affecting existing implementations. [#101](https://github.com/PennyLaneAI/PennyLane-IonQ/pull/101) + ### Improvements 🛠 ### Breaking changes 💔 @@ -10,12 +12,16 @@ ### Documentation 📝 +* Updated the docstring for the MS operation to include the new `theta` parameter, explaining its use and effects on the gate operation. + ### Bug fixes 🐛 ### Contributors ✍️ This release contains contributions from (in alphabetical order): +Spencer Churchill + --- # Release 0.34.0 From 4b24245b1fb910350ddc6cebcbb8c30bfbb2cf0b Mon Sep 17 00:00:00 2001 From: Alex Preciado Date: Thu, 18 Apr 2024 23:07:43 -0400 Subject: [PATCH 09/13] Trigger Build after adding Codecov Token From 51fe8e56539259e1c8f4251379764fd16088fd05 Mon Sep 17 00:00:00 2001 From: Astral Cai Date: Thu, 25 Apr 2024 11:35:38 -0400 Subject: [PATCH 10/13] Trigger CI From 3849f118e58aa4a3b93299785450086d5e2b03fc Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Mon, 29 Apr 2024 19:11:05 -0700 Subject: [PATCH 11/13] add test for theta paremeter + add matrix to ms --- pennylane_ionq/ops.py | 26 ++++++++++++++++++++++++++ tests/test_device.py | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/pennylane_ionq/ops.py b/pennylane_ionq/ops.py index e4b101b..b1faf0f 100644 --- a/pennylane_ionq/ops.py +++ b/pennylane_ionq/ops.py @@ -14,6 +14,7 @@ """ Custom operations """ +import numpy as np from pennylane.operation import Operation @@ -88,6 +89,31 @@ class MS(Operation): # pylint: disable=too-few-public-methods def __init__(self, phi0, phi1, theta=0.25, wires=None): super().__init__(phi0, phi1, theta, wires=wires) + @staticmethod + def compute_matrix(phi0, phi1, theta): # pylint: disable=arguments-differ + r"""Representation of the operator as a canonical matrix in the computational basis. + + Args: + phi0 (float): phase of the first qubit :math:`\phi_0` + phi1 (float): phase of the second qubit :math:`\phi_1` + theta (float): entanglement ratio :math:`\theta` + + Returns: + np.ndarray: canonical matrix + """ + cos = np.cos(theta / 2) + exp = np.exp + pi = np.pi + i = 1j + return ( + 1 / np.sqrt(2) * np.array([ + [cos, 0, 0, -i * exp(-2 * pi * i * (phi0 + phi1))], + [0, cos, -i * exp(-2 * pi * i * (phi0 - phi1)), 0], + [0, -i * exp(2 * pi * i * (phi0 - phi1)), cos, 0], + [-i * exp(2 * pi * i * (phi0 + phi1)), 0, 0, cos], + ]) + ) + # Custom operations for the QIS Gateset below diff --git a/tests/test_device.py b/tests/test_device.py index b904514..beddca8 100755 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -243,7 +243,9 @@ def test_probability(self): mock_prob.return_value = uniform_prob assert np.array_equal(dev.probability(), uniform_prob) - @pytest.mark.parametrize("backend", ["harmony", "aria-1", "aria-2", "forte-1", 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.""" dev = qml.device( @@ -355,3 +357,33 @@ def mock_submit_job(*args): "phases": [0.4, 0.5], "angle": 0.1, } + + @pytest.mark.parametrize( + "phi0, phi1, theta", + [ + (0.1, 0.2, 0.25), # Default fully entangling case + (0, 0.3, 0.1), # Partially entangling case + (1.5, 2.7, 0), # No entanglement case + ], + ) + def test_ms_gate_theta_variation(self, phi0, phi1, theta, tol=1e-6): + """Test MS gate with different theta values to ensure correct entanglement behavior.""" + ms_gate = MS(phi0, phi1, theta, wires=[0, 1]) + + # Compute the matrix representation of the gate + computed_matrix = ms_gate.compute_matrix(*ms_gate.data) + + # Expected matrix + expected_matrix = ( + 1 / np.sqrt(2) * np.array([ + [np.cos(theta / 2), 0, 0, -1j * np.exp(-2 * np.pi * 1j * (phi0 + phi1))], + [0, np.cos(theta / 2), -1j * np.exp(-2 * np.pi * 1j * (phi0 - phi1)), 0], + [0, -1j * np.exp(2 * np.pi * 1j * (phi0 - phi1)), np.cos(theta / 2), 0], + [-1j * np.exp(2 * np.pi * 1j * (phi0 + phi1)), 0, 0, np.cos(theta / 2)], + ]) + ) + + assert ms_gate.data == (phi0, phi1, theta) + assert np.allclose( + computed_matrix, expected_matrix, atol=tol + ), "Computed matrix does not match the expected matrix" From c224511fe1dbbc44bd269bd87548be17ab6adec0 Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Mon, 29 Apr 2024 19:15:20 -0700 Subject: [PATCH 12/13] pass linting --- pennylane_ionq/ops.py | 16 ++++++++++------ tests/test_device.py | 24 ++++++++++++++++-------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/pennylane_ionq/ops.py b/pennylane_ionq/ops.py index b1faf0f..057172a 100644 --- a/pennylane_ionq/ops.py +++ b/pennylane_ionq/ops.py @@ -106,12 +106,16 @@ def compute_matrix(phi0, phi1, theta): # pylint: disable=arguments-differ pi = np.pi i = 1j return ( - 1 / np.sqrt(2) * np.array([ - [cos, 0, 0, -i * exp(-2 * pi * i * (phi0 + phi1))], - [0, cos, -i * exp(-2 * pi * i * (phi0 - phi1)), 0], - [0, -i * exp(2 * pi * i * (phi0 - phi1)), cos, 0], - [-i * exp(2 * pi * i * (phi0 + phi1)), 0, 0, cos], - ]) + 1 + / np.sqrt(2) + * np.array( + [ + [cos, 0, 0, -i * exp(-2 * pi * i * (phi0 + phi1))], + [0, cos, -i * exp(-2 * pi * i * (phi0 - phi1)), 0], + [0, -i * exp(2 * pi * i * (phi0 - phi1)), cos, 0], + [-i * exp(2 * pi * i * (phi0 + phi1)), 0, 0, cos], + ] + ) ) diff --git a/tests/test_device.py b/tests/test_device.py index beddca8..80fab48 100755 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -362,8 +362,8 @@ def mock_submit_job(*args): "phi0, phi1, theta", [ (0.1, 0.2, 0.25), # Default fully entangling case - (0, 0.3, 0.1), # Partially entangling case - (1.5, 2.7, 0), # No entanglement case + (0, 0.3, 0.1), # Partially entangling case + (1.5, 2.7, 0), # No entanglement case ], ) def test_ms_gate_theta_variation(self, phi0, phi1, theta, tol=1e-6): @@ -374,13 +374,21 @@ def test_ms_gate_theta_variation(self, phi0, phi1, theta, tol=1e-6): computed_matrix = ms_gate.compute_matrix(*ms_gate.data) # Expected matrix + cos = np.cos(theta / 2) + exp = np.exp + pi = np.pi + i = 1j expected_matrix = ( - 1 / np.sqrt(2) * np.array([ - [np.cos(theta / 2), 0, 0, -1j * np.exp(-2 * np.pi * 1j * (phi0 + phi1))], - [0, np.cos(theta / 2), -1j * np.exp(-2 * np.pi * 1j * (phi0 - phi1)), 0], - [0, -1j * np.exp(2 * np.pi * 1j * (phi0 - phi1)), np.cos(theta / 2), 0], - [-1j * np.exp(2 * np.pi * 1j * (phi0 + phi1)), 0, 0, np.cos(theta / 2)], - ]) + 1 + / np.sqrt(2) + * np.array( + [ + [cos, 0, 0, -i * exp(-2 * pi * i * (phi0 + phi1))], + [0, cos, -i * exp(-2 * pi * i * (phi0 - phi1)), 0], + [0, -i * exp(2 * pi * i * (phi0 - phi1)), cos, 0], + [-i * exp(2 * pi * i * (phi0 + phi1)), 0, 0, cos], + ] + ) ) assert ms_gate.data == (phi0, phi1, theta) From 80946476aa9562e0eff0c605f3742934df1936ca Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Mon, 29 Apr 2024 19:20:09 -0700 Subject: [PATCH 13/13] future proof list type --- tests/test_device.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_device.py b/tests/test_device.py index 80fab48..c8842fd 100755 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -391,7 +391,7 @@ def test_ms_gate_theta_variation(self, phi0, phi1, theta, tol=1e-6): ) ) - assert ms_gate.data == (phi0, phi1, theta) + assert list(ms_gate.data) == [phi0, phi1, theta] assert np.allclose( computed_matrix, expected_matrix, atol=tol ), "Computed matrix does not match the expected matrix"