From c54dcf7c713773a0f9580c6878d819e16b0311e8 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Wed, 8 Nov 2023 21:03:03 -0800 Subject: [PATCH 01/89] Barebones of initialize state --- pennylane/devices/qutrit_mixed/__init__.py | 0 .../devices/qutrit_mixed/initialize_state.py | 69 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 pennylane/devices/qutrit_mixed/__init__.py create mode 100644 pennylane/devices/qutrit_mixed/initialize_state.py diff --git a/pennylane/devices/qutrit_mixed/__init__.py b/pennylane/devices/qutrit_mixed/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py new file mode 100644 index 00000000000..42ef254633f --- /dev/null +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -0,0 +1,69 @@ +from typing import Iterable, Union +from functools import singledispatch +import pennylane as qml + + +@singledispatch +def create_initial_state( + wires: Union[qml.wires.Wires, Iterable], + prep_operation: qml.QutritBasisState = None, + like: str = None, +): + r""" + Returns an initial state, defaulting to :math:`\ket{0}\bra{0}` if no state-prep operator is provided. + + Args: + wires (Union[Wires, Iterable]): The wires to be present in the initial state + prep_operation (Optional[StatePrepBase]): An operation to prepare the initial state + like (Optional[str]): The machine learning interface used to create the initial state. + Defaults to None + + Returns: + array: The initial state of a circuit + """ + if not prep_operation: + num_wires = len(wires) + rho = _create_basis_state(num_wires, index, dtype) + else: + rho = _apply_basis_state(prep_operation, wires) + return qml.math.asarray(rho, like=like) + + +def _apply_basis_state(state, wires, dtype): + """Initialize the device in a specified computational basis state. + + Args: + state (array[int]): computational basis state of shape ``(wires,)`` + consisting of 0s and 1s. + wires (Wires): wires that the provided computational state should be initialized on + """ + num_wires = 1 #TODO??? + # length of basis state parameter + n_basis_state = len(state) + + if not set(state).issubset({0, 1}): + raise ValueError("BasisState parameter must consist of 0 or 1 integers.") + + if n_basis_state != len(wires): + raise ValueError("BasisState parameter and wires must be of equal length.") + + # get computational basis state number + basis_states = 3 ** (num_wires - 1 - wires.toarray()) + num = int(qml.math.dot(state, basis_states)) + + return _create_basis_state(num_wires, num, dtype) + + +def _create_basis_state(num_wires, index, dtype): + """Return the density matrix representing a computational basis state over all wires. + + Args: + index (int): integer representing the computational basis state. + + Returns: + array[complex]: complex array of shape ``[3] * (2 * num_wires)`` + representing the density matrix of the basis state. + """ + rho = qml.math.zeros((3 ** num_wires, 3 ** num_wires), dtype=dtype) + rho[index, index] = 1 + return qml.math.reshape(rho, [3] * (2 * num_wires)) \ No newline at end of file From eb801dd48c6c7bec8ddcda0fb1905191371a6ca1 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 10 Nov 2023 14:14:52 -0800 Subject: [PATCH 02/89] ideas for abstracting for mixed --- pennylane/devices/mixed/__init__.py | 1 + .../abstract}/__init__.py | 0 .../mixed/abstract/initialize_state.py | 89 +++++++++++++++++++ pennylane/devices/mixed/qutrit/__init__.py | 0 .../qutrit}/initialize_state.py | 12 ++- 5 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 pennylane/devices/mixed/__init__.py rename pennylane/devices/{qutrit_mixed => mixed/abstract}/__init__.py (100%) create mode 100644 pennylane/devices/mixed/abstract/initialize_state.py create mode 100644 pennylane/devices/mixed/qutrit/__init__.py rename pennylane/devices/{qutrit_mixed => mixed/qutrit}/initialize_state.py (89%) diff --git a/pennylane/devices/mixed/__init__.py b/pennylane/devices/mixed/__init__.py new file mode 100644 index 00000000000..052e3e09c42 --- /dev/null +++ b/pennylane/devices/mixed/__init__.py @@ -0,0 +1 @@ +import qutrit as qutrit_mixed diff --git a/pennylane/devices/qutrit_mixed/__init__.py b/pennylane/devices/mixed/abstract/__init__.py similarity index 100% rename from pennylane/devices/qutrit_mixed/__init__.py rename to pennylane/devices/mixed/abstract/__init__.py diff --git a/pennylane/devices/mixed/abstract/initialize_state.py b/pennylane/devices/mixed/abstract/initialize_state.py new file mode 100644 index 00000000000..401c17912d1 --- /dev/null +++ b/pennylane/devices/mixed/abstract/initialize_state.py @@ -0,0 +1,89 @@ +from typing import Iterable, Union +import pennylane as qml +import numpy as np + + +def apply_basis_state(state, wires, qudit_d=2): + """Initialize the device in a specified computational basis state. + + Args: + state (array[int]): computational basis state of shape ``(wires,)`` + consisting of 0s and 1s. + wires (Wires): wires that the provided computational state should be initialized on + """ + num_wires = len(wires) + # length of basis state parameter + n_basis_state = len(state) + + if not set(state).issubset({0, 1}): + raise ValueError("BasisState parameter must consist of 0 or 1 integers.") + + if n_basis_state != len(wires): + raise ValueError("BasisState parameter and wires must be of equal length.") + + # get computational basis state number + basis_states = qudit_d ** (num_wires - 1 - wires.toarray()) + num = int(qml.math.dot(state, basis_states)) + + return _create_basis_state(num_wires, num) + + +def create_basis_state(wires, index=0, qudit_d=2): + """Return the density matrix representing a computational basis state over all wires. + + Args: + index (int): integer representing the computational basis state. + qudit_d (int): dimensions of qudit being used + + Returns: + array[complex]: complex array of shape ``[qudit_d] * (2 * num_wires)`` + representing the density matrix of the basis state. + """ + num_wires = len(wires) + rho = np.zeros((qudit_d ** num_wires, qudit_d ** num_wires)) + rho[index, index] = 1 + return qml.math.reshape(rho, [qudit_d] * (2 * num_wires)) + +class CreateStateHelper: + def __init__(self, qudit_d): + self.qudit_d = qudit_d + + def apply_basis_state(self, state, wires): + """Initialize the device in a specified computational basis state. + + Args: + state (array[int]): computational basis state of shape ``(wires,)`` + consisting of 0s and 1s. + wires (Wires): wires that the provided computational state should be initialized on + """ + num_wires = len(wires) + # length of basis state parameter + n_basis_state = len(state) + + if not set(state).issubset({0, 1}): + raise ValueError("BasisState parameter must consist of 0 or 1 integers.") + + if n_basis_state != len(wires): + raise ValueError("BasisState parameter and wires must be of equal length.") + + # get computational basis state number + basis_states = self.qudit_d ** (num_wires - 1 - wires.toarray()) + num = int(qml.math.dot(state, basis_states)) + + return self.create_basis_state(num_wires, num) + + def create_basis_state(self, wires, index=0): + """Return the density matrix representing a computational basis state over all wires. + + Args: + index (int): integer representing the computational basis state. + qudit_d (int): dimensions of qudit being used + + Returns: + array[complex]: complex array of shape ``[qudit_d] * (2 * num_wires)`` + representing the density matrix of the basis state. + """ + num_wires = len(wires) + rho = np.zeros((self.qudit_d ** num_wires, self.qudit_d ** num_wires)) + rho[index, index] = 1 + return qml.math.reshape(rho, [self.qudit_d] * (2 * num_wires)) \ No newline at end of file diff --git a/pennylane/devices/mixed/qutrit/__init__.py b/pennylane/devices/mixed/qutrit/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/mixed/qutrit/initialize_state.py similarity index 89% rename from pennylane/devices/qutrit_mixed/initialize_state.py rename to pennylane/devices/mixed/qutrit/initialize_state.py index 42ef254633f..77624f44722 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/mixed/qutrit/initialize_state.py @@ -1,10 +1,8 @@ from typing import Iterable, Union -from functools import singledispatch import pennylane as qml -@singledispatch -def create_initial_state( +def create_initial_rho( wires: Union[qml.wires.Wires, Iterable], prep_operation: qml.QutritBasisState = None, like: str = None, @@ -23,13 +21,13 @@ def create_initial_state( """ if not prep_operation: num_wires = len(wires) - rho = _create_basis_state(num_wires, index, dtype) + rho = _create_basis_state(num_wires, 0) else: rho = _apply_basis_state(prep_operation, wires) return qml.math.asarray(rho, like=like) -def _apply_basis_state(state, wires, dtype): +def _apply_basis_state(state, wires): """Initialize the device in a specified computational basis state. Args: @@ -37,7 +35,7 @@ def _apply_basis_state(state, wires, dtype): consisting of 0s and 1s. wires (Wires): wires that the provided computational state should be initialized on """ - num_wires = 1 #TODO??? + num_wires = len(wires) # length of basis state parameter n_basis_state = len(state) @@ -51,7 +49,7 @@ def _apply_basis_state(state, wires, dtype): basis_states = 3 ** (num_wires - 1 - wires.toarray()) num = int(qml.math.dot(state, basis_states)) - return _create_basis_state(num_wires, num, dtype) + return _create_basis_state(num_wires, num) def _create_basis_state(num_wires, index, dtype): From d35a33c4e2e6154d251576bae5a240bf2b58e9ee Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 10 Nov 2023 14:55:24 -0800 Subject: [PATCH 03/89] Finished qutrit mixed code --- pennylane/devices/mixed/__init__.py | 1 - pennylane/devices/mixed/abstract/__init__.py | 0 .../mixed/abstract/initialize_state.py | 89 ------------------- pennylane/devices/mixed/qutrit/__init__.py | 0 pennylane/devices/qutrit_mixed/__init__.py | 14 +++ .../initialize_state.py | 25 ++++-- 6 files changed, 31 insertions(+), 98 deletions(-) delete mode 100644 pennylane/devices/mixed/__init__.py delete mode 100644 pennylane/devices/mixed/abstract/__init__.py delete mode 100644 pennylane/devices/mixed/abstract/initialize_state.py delete mode 100644 pennylane/devices/mixed/qutrit/__init__.py create mode 100644 pennylane/devices/qutrit_mixed/__init__.py rename pennylane/devices/{mixed/qutrit => qutrit_mixed}/initialize_state.py (71%) diff --git a/pennylane/devices/mixed/__init__.py b/pennylane/devices/mixed/__init__.py deleted file mode 100644 index 052e3e09c42..00000000000 --- a/pennylane/devices/mixed/__init__.py +++ /dev/null @@ -1 +0,0 @@ -import qutrit as qutrit_mixed diff --git a/pennylane/devices/mixed/abstract/__init__.py b/pennylane/devices/mixed/abstract/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/pennylane/devices/mixed/abstract/initialize_state.py b/pennylane/devices/mixed/abstract/initialize_state.py deleted file mode 100644 index 401c17912d1..00000000000 --- a/pennylane/devices/mixed/abstract/initialize_state.py +++ /dev/null @@ -1,89 +0,0 @@ -from typing import Iterable, Union -import pennylane as qml -import numpy as np - - -def apply_basis_state(state, wires, qudit_d=2): - """Initialize the device in a specified computational basis state. - - Args: - state (array[int]): computational basis state of shape ``(wires,)`` - consisting of 0s and 1s. - wires (Wires): wires that the provided computational state should be initialized on - """ - num_wires = len(wires) - # length of basis state parameter - n_basis_state = len(state) - - if not set(state).issubset({0, 1}): - raise ValueError("BasisState parameter must consist of 0 or 1 integers.") - - if n_basis_state != len(wires): - raise ValueError("BasisState parameter and wires must be of equal length.") - - # get computational basis state number - basis_states = qudit_d ** (num_wires - 1 - wires.toarray()) - num = int(qml.math.dot(state, basis_states)) - - return _create_basis_state(num_wires, num) - - -def create_basis_state(wires, index=0, qudit_d=2): - """Return the density matrix representing a computational basis state over all wires. - - Args: - index (int): integer representing the computational basis state. - qudit_d (int): dimensions of qudit being used - - Returns: - array[complex]: complex array of shape ``[qudit_d] * (2 * num_wires)`` - representing the density matrix of the basis state. - """ - num_wires = len(wires) - rho = np.zeros((qudit_d ** num_wires, qudit_d ** num_wires)) - rho[index, index] = 1 - return qml.math.reshape(rho, [qudit_d] * (2 * num_wires)) - -class CreateStateHelper: - def __init__(self, qudit_d): - self.qudit_d = qudit_d - - def apply_basis_state(self, state, wires): - """Initialize the device in a specified computational basis state. - - Args: - state (array[int]): computational basis state of shape ``(wires,)`` - consisting of 0s and 1s. - wires (Wires): wires that the provided computational state should be initialized on - """ - num_wires = len(wires) - # length of basis state parameter - n_basis_state = len(state) - - if not set(state).issubset({0, 1}): - raise ValueError("BasisState parameter must consist of 0 or 1 integers.") - - if n_basis_state != len(wires): - raise ValueError("BasisState parameter and wires must be of equal length.") - - # get computational basis state number - basis_states = self.qudit_d ** (num_wires - 1 - wires.toarray()) - num = int(qml.math.dot(state, basis_states)) - - return self.create_basis_state(num_wires, num) - - def create_basis_state(self, wires, index=0): - """Return the density matrix representing a computational basis state over all wires. - - Args: - index (int): integer representing the computational basis state. - qudit_d (int): dimensions of qudit being used - - Returns: - array[complex]: complex array of shape ``[qudit_d] * (2 * num_wires)`` - representing the density matrix of the basis state. - """ - num_wires = len(wires) - rho = np.zeros((self.qudit_d ** num_wires, self.qudit_d ** num_wires)) - rho[index, index] = 1 - return qml.math.reshape(rho, [self.qudit_d] * (2 * num_wires)) \ No newline at end of file diff --git a/pennylane/devices/mixed/qutrit/__init__.py b/pennylane/devices/mixed/qutrit/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/pennylane/devices/qutrit_mixed/__init__.py b/pennylane/devices/qutrit_mixed/__init__.py new file mode 100644 index 00000000000..89a86ccf0ab --- /dev/null +++ b/pennylane/devices/qutrit_mixed/__init__.py @@ -0,0 +1,14 @@ +""" +Submodule for performing mixed-qutrit-based simulations of quantum circuits. + +This submodule is internal and subject to change without a deprecation cycle. Use +at your own discretion. + +.. currentmodule:: pennylane.devices.qutrit +.. autosummary:: TODO is this necessary? + :toctree: api + + create_initial_state +""" + +from .initialize_state import create_initial_state \ No newline at end of file diff --git a/pennylane/devices/mixed/qutrit/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py similarity index 71% rename from pennylane/devices/mixed/qutrit/initialize_state.py rename to pennylane/devices/qutrit_mixed/initialize_state.py index 77624f44722..f7165084259 100644 --- a/pennylane/devices/mixed/qutrit/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -1,10 +1,15 @@ from typing import Iterable, Union import pennylane as qml +from pennylane import ( + QutritBasisState, +) +qudit_dim = 3 # specifies qudit dimension -def create_initial_rho( + +def create_initial_state( wires: Union[qml.wires.Wires, Iterable], - prep_operation: qml.QutritBasisState = None, + prep_operation: qml.Operation = None, like: str = None, ): r""" @@ -22,18 +27,22 @@ def create_initial_rho( if not prep_operation: num_wires = len(wires) rho = _create_basis_state(num_wires, 0) + elif isinstance(prep_operation, QutritBasisState): + rho = _apply_basis_state(prep_operation.parameters[0], wires) else: - rho = _apply_basis_state(prep_operation, wires) + raise NotImplementedError(f"{prep_operation} has not been implemented for qutrit.mixed") + return qml.math.asarray(rho, like=like) -def _apply_basis_state(state, wires): - """Initialize the device in a specified computational basis state. +def _apply_basis_state(state, wires): # function is easy to abstract for qudit + """Returns initial state for a specified computational basis state. Args: state (array[int]): computational basis state of shape ``(wires,)`` consisting of 0s and 1s. wires (Wires): wires that the provided computational state should be initialized on + """ num_wires = len(wires) # length of basis state parameter @@ -46,13 +55,13 @@ def _apply_basis_state(state, wires): raise ValueError("BasisState parameter and wires must be of equal length.") # get computational basis state number - basis_states = 3 ** (num_wires - 1 - wires.toarray()) + basis_states = qudit_dim ** (num_wires - 1 - wires.toarray()) num = int(qml.math.dot(state, basis_states)) return _create_basis_state(num_wires, num) -def _create_basis_state(num_wires, index, dtype): +def _create_basis_state(num_wires, index, dtype): # function is easy to abstract for qudit """Return the density matrix representing a computational basis state over all wires. Args: @@ -64,4 +73,4 @@ def _create_basis_state(num_wires, index, dtype): """ rho = qml.math.zeros((3 ** num_wires, 3 ** num_wires), dtype=dtype) rho[index, index] = 1 - return qml.math.reshape(rho, [3] * (2 * num_wires)) \ No newline at end of file + return qml.math.reshape(rho, [qudit_dim] * (2 * num_wires)) From 4f044786819250eb55c031369ee71880fc3a763d Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 10 Nov 2023 14:56:38 -0800 Subject: [PATCH 04/89] Black on qutrit_mixed --- pennylane/devices/qutrit_mixed/__init__.py | 2 +- pennylane/devices/qutrit_mixed/initialize_state.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/__init__.py b/pennylane/devices/qutrit_mixed/__init__.py index 89a86ccf0ab..bf2a93a70cb 100644 --- a/pennylane/devices/qutrit_mixed/__init__.py +++ b/pennylane/devices/qutrit_mixed/__init__.py @@ -11,4 +11,4 @@ create_initial_state """ -from .initialize_state import create_initial_state \ No newline at end of file +from .initialize_state import create_initial_state diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index f7165084259..1c96825ce0e 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -71,6 +71,6 @@ def _create_basis_state(num_wires, index, dtype): # function is easy to abstrac array[complex]: complex array of shape ``[3] * (2 * num_wires)`` representing the density matrix of the basis state. """ - rho = qml.math.zeros((3 ** num_wires, 3 ** num_wires), dtype=dtype) + rho = qml.math.zeros((3**num_wires, 3**num_wires), dtype=dtype) rho[index, index] = 1 return qml.math.reshape(rho, [qudit_dim] * (2 * num_wires)) From 167c10b21ca02e5f6ee3de09070280f4b83f99b5 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 10 Nov 2023 17:12:21 -0800 Subject: [PATCH 05/89] Added input check --- pennylane/devices/qutrit_mixed/initialize_state.py | 11 ++++++++++- tests/devices/qutrit_mixed/test_initialize_state.py | 0 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/devices/qutrit_mixed/test_initialize_state.py diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index 1c96825ce0e..48c94c9788c 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -4,7 +4,9 @@ QutritBasisState, ) + qudit_dim = 3 # specifies qudit dimension +qubit_setup_operators = qml.ops.qubit.state_prep_ops def create_initial_state( @@ -27,10 +29,17 @@ def create_initial_state( if not prep_operation: num_wires = len(wires) rho = _create_basis_state(num_wires, 0) + elif isinstance(prep_operation, QutritBasisState): rho = _apply_basis_state(prep_operation.parameters[0], wires) + + elif prep_operation.name in qubit_setup_operators: + raise ValueError("Input qubit prep operation") + else: - raise NotImplementedError(f"{prep_operation} has not been implemented for qutrit.mixed") + raise NotImplementedError( + f"{prep_operation.name} has not been implemented for qutrit.mixed" + ) return qml.math.asarray(rho, like=like) diff --git a/tests/devices/qutrit_mixed/test_initialize_state.py b/tests/devices/qutrit_mixed/test_initialize_state.py new file mode 100644 index 00000000000..e69de29bb2d From 20e4e11e6cd9f49cb87cd1b87124986cc47fe7ec Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 10 Nov 2023 17:20:56 -0800 Subject: [PATCH 06/89] added some test stubs --- .../qutrit_mixed/test_initialize_state.py | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/devices/qutrit_mixed/test_initialize_state.py b/tests/devices/qutrit_mixed/test_initialize_state.py index e69de29bb2d..ef23e03efb5 100644 --- a/tests/devices/qutrit_mixed/test_initialize_state.py +++ b/tests/devices/qutrit_mixed/test_initialize_state.py @@ -0,0 +1,54 @@ +"""Unit tests for create_initial_state in devices/qubit.""" + +import pytest + +import pennylane as qml +from pennylane import numpy as np +from pennylane.devices.qutrit_mixed import create_initial_state +from pennylane import ( + QutritBasisState, +) + +class TestInitializeState: + """Test the functions in qutrit_mixed/initialize_state.py""" + + # TODO add pylint disable + + @pytest.mark.all_interfaces + @pytest.mark.parametrize("interface", ["numpy", "jax", "torch", "tensorflow"]) + def test_create_initial_state_no_state_prep(self, interface): + """Tests that create_initial_state works without a state-prep operation.""" + state = create_initial_state([0, 1], like=interface) + assert qml.math.allequal(state, None ) #TODO[[1, 0], [0, 0]]) + assert qml.math.get_interface(state) == interface + + def test_create_initial_state_with_BasisState(self): + """Tests that create_initial_state works with a real state-prep operator.""" + prep_op = QutritBasisState([0, 1, 0], wires=[0, 1, 2]) + state = create_initial_state([0, 1, 2], prep_operation=prep_op) + assert state[0, 1, 0] == 1 + state[0, 1, 0] = 0 # set to zero to make test below simple + assert qml.math.allequal(state, np.zeros((2, 2, 2))) + + def test_create_initial_state_with_BasisState_broadcasted(self): + """Tests that create_initial_state works with a broadcasted StatePrep + operator.""" + prep_op = QutritBasisState(np.array([[0, 1], [1, 0], [2, 1]]), wires=[0, 1]) + state = create_initial_state([0, 1], prep_operation=prep_op) + expected = None #TODO np.zeros((3, 2, 2)) + expected[0, 0, 1] = expected[1, 1, 1] = expected[2, 1, 0] = 1 + assert np.array_equal(state, expected) + + def test_create_initial_state_defaults_to_numpy(self): + """Tests that the default interface is vanilla numpy.""" + state = qml.devices.qubit.create_initial_state((0, 1)) + assert qml.math.get_interface(state) == "numpy" + + def test_create_initial_state_with_New_State_prep(self): + """""" + pass + + @pytest.mark.parametrize("", ["", "", "", ""]) + def test_create_initial_state_with_Qubit_State_prep(self): + """""" + pass From 9349713d8310fb1554d5732489c4637728af6369 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 17 Nov 2023 19:25:16 -0800 Subject: [PATCH 07/89] close --- .../devices/qutrit_mixed/initialize_state.py | 52 ++++++++------ .../qutrit_mixed/test_initialize_state.py | 68 +++++++++++++------ 2 files changed, 79 insertions(+), 41 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index 48c94c9788c..8654af3202a 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -1,17 +1,21 @@ from typing import Iterable, Union import pennylane as qml +import numpy as np from pennylane import ( QutritBasisState, ) - +from pennylane.operation import ( + StatePrepBase, + Operation, +) +from pennylane.wires import Wires qudit_dim = 3 # specifies qudit dimension -qubit_setup_operators = qml.ops.qubit.state_prep_ops def create_initial_state( wires: Union[qml.wires.Wires, Iterable], - prep_operation: qml.Operation = None, + prep_operation: Operation = None, like: str = None, ): r""" @@ -33,16 +37,27 @@ def create_initial_state( elif isinstance(prep_operation, QutritBasisState): rho = _apply_basis_state(prep_operation.parameters[0], wires) - elif prep_operation.name in qubit_setup_operators: - raise ValueError("Input qubit prep operation") + elif isinstance(prep_operation, StatePrepBase): + rho = _apply_state_vector(prep_operation.state_vector(wire_order=list(wires)), wires) - else: - raise NotImplementedError( - f"{prep_operation.name} has not been implemented for qutrit.mixed" - ) + # TODO: add instance for prep_operations as added return qml.math.asarray(rho, like=like) +def _apply_state_vector(state, wires): # function is easy to abstract for qudit + """Initialize the internal state in a specified pure state. + + Args: + state (array[complex]): normalized input state of length + ``3**len(wires)`` + device_wires (Wires): wires that get initialized in the state + """ + num_wires = len(wires) + + # Initialize the entire wires with the state + rho = qml.math.outer(state, qml.math.conj(state)) + return qml.math.reshape(rho, [3] * 2 * num_wires) + def _apply_basis_state(state, wires): # function is easy to abstract for qudit """Returns initial state for a specified computational basis state. @@ -54,23 +69,20 @@ def _apply_basis_state(state, wires): # function is easy to abstract for qudit """ num_wires = len(wires) - # length of basis state parameter - n_basis_state = len(state) - - if not set(state).issubset({0, 1}): - raise ValueError("BasisState parameter must consist of 0 or 1 integers.") - - if n_basis_state != len(wires): - raise ValueError("BasisState parameter and wires must be of equal length.") + if isinstance(wires, Wires): + wire_array = wires.toarray() + else: + wire_array = np.array(wires) # get computational basis state number - basis_states = qudit_dim ** (num_wires - 1 - wires.toarray()) + basis_states = qudit_dim ** (num_wires - 1 - wire_array) + print(qml.math.dot(state, basis_states)) num = int(qml.math.dot(state, basis_states)) return _create_basis_state(num_wires, num) -def _create_basis_state(num_wires, index, dtype): # function is easy to abstract for qudit +def _create_basis_state(num_wires, index): # function is easy to abstract for qudit """Return the density matrix representing a computational basis state over all wires. Args: @@ -80,6 +92,6 @@ def _create_basis_state(num_wires, index, dtype): # function is easy to abstrac array[complex]: complex array of shape ``[3] * (2 * num_wires)`` representing the density matrix of the basis state. """ - rho = qml.math.zeros((3**num_wires, 3**num_wires), dtype=dtype) + rho = np.zeros((3**num_wires, 3**num_wires)) rho[index, index] = 1 return qml.math.reshape(rho, [qudit_dim] * (2 * num_wires)) diff --git a/tests/devices/qutrit_mixed/test_initialize_state.py b/tests/devices/qutrit_mixed/test_initialize_state.py index ef23e03efb5..d2d947235fc 100644 --- a/tests/devices/qutrit_mixed/test_initialize_state.py +++ b/tests/devices/qutrit_mixed/test_initialize_state.py @@ -8,47 +8,73 @@ from pennylane import ( QutritBasisState, ) +from pennylane.operation import StatePrepBase class TestInitializeState: """Test the functions in qutrit_mixed/initialize_state.py""" # TODO add pylint disable + class DefaultPrep(StatePrepBase): + """A dummy class that assumes it was given a state vector.""" + + num_wires = qml.operation.AllWires + + def state_vector(self, wire_order=None): + return self.parameters[0] @pytest.mark.all_interfaces @pytest.mark.parametrize("interface", ["numpy", "jax", "torch", "tensorflow"]) def test_create_initial_state_no_state_prep(self, interface): """Tests that create_initial_state works without a state-prep operation.""" state = create_initial_state([0, 1], like=interface) - assert qml.math.allequal(state, None ) #TODO[[1, 0], [0, 0]]) + expected = np.zeros((3, 3, 3, 3)) + expected[0, 0, 0, 0] = 1 + assert qml.math.allequal(state, expected) + assert qml.math.get_interface(state) == interface + + @pytest.mark.all_interfaces + @pytest.mark.parametrize("interface", ["numpy", "jax", "torch", "tensorflow"]) + def test_create_initial_state_with_state_prep(self, interface): + """Tests that create_initial_state works with a state-prep operation.""" + prep_op = self.DefaultPrep(qml.math.array([1 / np.sqrt(9)] * 9, like=interface), wires=[0, 1]) + state = create_initial_state([0, 1], prep_operation=prep_op) + #assert qml.math.allequal(state, [1 / 2] * 4) TODO fix assert qml.math.get_interface(state) == interface def test_create_initial_state_with_BasisState(self): """Tests that create_initial_state works with a real state-prep operator.""" - prep_op = QutritBasisState([0, 1, 0], wires=[0, 1, 2]) + prep_op = QutritBasisState([1, 2, 0], wires=[0, 1, 2]) state = create_initial_state([0, 1, 2], prep_operation=prep_op) - assert state[0, 1, 0] == 1 - state[0, 1, 0] = 0 # set to zero to make test below simple - assert qml.math.allequal(state, np.zeros((2, 2, 2))) - - def test_create_initial_state_with_BasisState_broadcasted(self): - """Tests that create_initial_state works with a broadcasted StatePrep - operator.""" - prep_op = QutritBasisState(np.array([[0, 1], [1, 0], [2, 1]]), wires=[0, 1]) - state = create_initial_state([0, 1], prep_operation=prep_op) - expected = None #TODO np.zeros((3, 2, 2)) - expected[0, 0, 1] = expected[1, 1, 1] = expected[2, 1, 0] = 1 - assert np.array_equal(state, expected) + assert state[1, 2, 0, 1, 2, 0] == 1 + state[1, 2, 0, 1, 2, 0] = 0 # set to zero to make test below simple + assert qml.math.allequal(state, np.zeros(([3] * 6))) + + # def test_create_initial_state_with_StatePrep(self, prep_op_cls): + # """Tests that create_initial_state works with the StatePrep operator.""" + # prep_op = prep_op_cls(np.array([0, 1, 0, 0, 0, 0, 0, 1]) / np.sqrt(2), wires=[0, 1, 2]) + # state = create_initial_state([0, 1, 2], prep_operation=prep_op) + # expected = np.zeros((2, 2, 2)) + # expected[0, 0, 1] = expected[1, 1, 1] = 1 / np.sqrt(2) + # assert np.array_equal(state, expected) + + # def test_create_initial_state_with_StatePrep_broadcasted(self): + # """Tests that create_initial_state works with a broadcasted StatePrep + # operator.""" + # prep_op = QutritBasisState(np.array([[1, 0], [2, 1]]), wires=[0, 1]) + # state = create_initial_state([0, 1], prep_operation=prep_op) + # expected = np.zeros((3, 3, 3)) + # expected[0, 1, 0, 1, 0] = expected[1, 2, 1, 2, 1] = 1 + # assert np.array_equal(state, expected) def test_create_initial_state_defaults_to_numpy(self): """Tests that the default interface is vanilla numpy.""" state = qml.devices.qubit.create_initial_state((0, 1)) assert qml.math.get_interface(state) == "numpy" - def test_create_initial_state_with_New_State_prep(self): - """""" - pass - @pytest.mark.parametrize("", ["", "", "", ""]) - def test_create_initial_state_with_Qubit_State_prep(self): - """""" - pass + @pytest.mark.torch + def test_create_initial_state_casts_to_like_with_prep_op(self): + """Tests that the like argument is not ignored when a prep-op is provided.""" + prep_op = self.DefaultPrep([1 / np.sqrt(9)] * 9, wires=[0, 1]) + state = create_initial_state([0, 1], prep_operation=prep_op, like="torch") + assert qml.math.get_interface(state) == "torch" \ No newline at end of file From 84de122ab61e5cd0ec82f19a7c872d78643944cf Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Mon, 20 Nov 2023 19:05:04 -0800 Subject: [PATCH 08/89] Finalized tests --- .../qutrit_mixed/test_initialize_state.py | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/tests/devices/qutrit_mixed/test_initialize_state.py b/tests/devices/qutrit_mixed/test_initialize_state.py index d2d947235fc..00319ad54fa 100644 --- a/tests/devices/qutrit_mixed/test_initialize_state.py +++ b/tests/devices/qutrit_mixed/test_initialize_state.py @@ -1,4 +1,4 @@ -"""Unit tests for create_initial_state in devices/qubit.""" +"""Unit tests for create_initial_state in devices/qutrit_mixed.""" import pytest @@ -10,10 +10,10 @@ ) from pennylane.operation import StatePrepBase + class TestInitializeState: """Test the functions in qutrit_mixed/initialize_state.py""" - # TODO add pylint disable class DefaultPrep(StatePrepBase): """A dummy class that assumes it was given a state vector.""" @@ -36,9 +36,13 @@ def test_create_initial_state_no_state_prep(self, interface): @pytest.mark.parametrize("interface", ["numpy", "jax", "torch", "tensorflow"]) def test_create_initial_state_with_state_prep(self, interface): """Tests that create_initial_state works with a state-prep operation.""" - prep_op = self.DefaultPrep(qml.math.array([1 / np.sqrt(9)] * 9, like=interface), wires=[0, 1]) + prep_op = self.DefaultPrep( + qml.math.array([1 / np.sqrt(9)] * 9, like=interface), wires=[0, 1] + ) state = create_initial_state([0, 1], prep_operation=prep_op) - #assert qml.math.allequal(state, [1 / 2] * 4) TODO fix + expected = np.reshape([1 / 9] * 81, [3, 3, 3, 3]) + + assert qml.math.allequal(state, expected) assert qml.math.get_interface(state) == interface def test_create_initial_state_with_BasisState(self): @@ -49,29 +53,13 @@ def test_create_initial_state_with_BasisState(self): state[1, 2, 0, 1, 2, 0] = 0 # set to zero to make test below simple assert qml.math.allequal(state, np.zeros(([3] * 6))) - # def test_create_initial_state_with_StatePrep(self, prep_op_cls): - # """Tests that create_initial_state works with the StatePrep operator.""" - # prep_op = prep_op_cls(np.array([0, 1, 0, 0, 0, 0, 0, 1]) / np.sqrt(2), wires=[0, 1, 2]) - # state = create_initial_state([0, 1, 2], prep_operation=prep_op) - # expected = np.zeros((2, 2, 2)) - # expected[0, 0, 1] = expected[1, 1, 1] = 1 / np.sqrt(2) - # assert np.array_equal(state, expected) - - # def test_create_initial_state_with_StatePrep_broadcasted(self): - # """Tests that create_initial_state works with a broadcasted StatePrep - # operator.""" - # prep_op = QutritBasisState(np.array([[1, 0], [2, 1]]), wires=[0, 1]) - # state = create_initial_state([0, 1], prep_operation=prep_op) - # expected = np.zeros((3, 3, 3)) - # expected[0, 1, 0, 1, 0] = expected[1, 2, 1, 2, 1] = 1 - # assert np.array_equal(state, expected) + # TODO: Add tests for qutrit state prep def test_create_initial_state_defaults_to_numpy(self): """Tests that the default interface is vanilla numpy.""" state = qml.devices.qubit.create_initial_state((0, 1)) assert qml.math.get_interface(state) == "numpy" - @pytest.mark.torch def test_create_initial_state_casts_to_like_with_prep_op(self): """Tests that the like argument is not ignored when a prep-op is provided.""" From 6ee799371301605b1a879eae53048efb479f2ecf Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Mon, 20 Nov 2023 19:06:17 -0800 Subject: [PATCH 09/89] dded docstring --- pennylane/devices/qutrit_mixed/initialize_state.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index 8654af3202a..554cafd97ee 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -1,3 +1,5 @@ +"""Functions to prepare a qutrit mixed state.""" + from typing import Iterable, Union import pennylane as qml import numpy as np From 90101c9ebcdcf68460210f18d738406fddb40cb3 Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Mon, 20 Nov 2023 19:09:19 -0800 Subject: [PATCH 10/89] Reformatted file --- pennylane/devices/qutrit_mixed/initialize_state.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index 554cafd97ee..810af432b8c 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -46,6 +46,7 @@ def create_initial_state( return qml.math.asarray(rho, like=like) + def _apply_state_vector(state, wires): # function is easy to abstract for qudit """Initialize the internal state in a specified pure state. From d9473fda1819686f3162d19bbb7e786f2e9e78af Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Mon, 20 Nov 2023 19:10:40 -0800 Subject: [PATCH 11/89] Fixed import order --- pennylane/devices/qutrit_mixed/initialize_state.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index 810af432b8c..65fa3f8981e 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -1,8 +1,8 @@ """Functions to prepare a qutrit mixed state.""" from typing import Iterable, Union -import pennylane as qml import numpy as np +import pennylane as qml from pennylane import ( QutritBasisState, ) From acb88f03ca2891718e7a6fd85890ba5e9158c352 Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Mon, 20 Nov 2023 22:43:42 -0800 Subject: [PATCH 12/89] fixed pylint and reformatted --- tests/devices/qutrit_mixed/test_initialize_state.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/devices/qutrit_mixed/test_initialize_state.py b/tests/devices/qutrit_mixed/test_initialize_state.py index 00319ad54fa..8397cee5cf1 100644 --- a/tests/devices/qutrit_mixed/test_initialize_state.py +++ b/tests/devices/qutrit_mixed/test_initialize_state.py @@ -14,6 +14,7 @@ class TestInitializeState: """Test the functions in qutrit_mixed/initialize_state.py""" + # pylint:disable=unused-argument,too-few-public-methods class DefaultPrep(StatePrepBase): """A dummy class that assumes it was given a state vector.""" @@ -65,4 +66,4 @@ def test_create_initial_state_casts_to_like_with_prep_op(self): """Tests that the like argument is not ignored when a prep-op is provided.""" prep_op = self.DefaultPrep([1 / np.sqrt(9)] * 9, wires=[0, 1]) state = create_initial_state([0, 1], prep_operation=prep_op, like="torch") - assert qml.math.get_interface(state) == "torch" \ No newline at end of file + assert qml.math.get_interface(state) == "torch" From 19afcd2643f9189eca7d59b3412d032ce16729de Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Mon, 20 Nov 2023 22:46:25 -0800 Subject: [PATCH 13/89] Changed file name --- ..._initialize_state.py => test_qutrit_mixed_initialize_state.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/devices/qutrit_mixed/{test_initialize_state.py => test_qutrit_mixed_initialize_state.py} (100%) diff --git a/tests/devices/qutrit_mixed/test_initialize_state.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_initialize_state.py similarity index 100% rename from tests/devices/qutrit_mixed/test_initialize_state.py rename to tests/devices/qutrit_mixed/test_qutrit_mixed_initialize_state.py From 4deca53720991527c9aad5ddd357a6db6ce2755c Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 21 Nov 2023 13:20:45 -0800 Subject: [PATCH 14/89] added test for Wires --- .../qutrit_mixed/test_qutrit_mixed_initialize_state.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_initialize_state.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_initialize_state.py index 8397cee5cf1..d35b03a1d43 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_initialize_state.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_initialize_state.py @@ -54,6 +54,14 @@ def test_create_initial_state_with_BasisState(self): state[1, 2, 0, 1, 2, 0] = 0 # set to zero to make test below simple assert qml.math.allequal(state, np.zeros(([3] * 6))) + @pytest.mark.parametrize("wires", [(0, 1), qml.Wires([0, 1])]) + def test_create_initial_state_wires(self, wires): + """Tests that create_initial_state works with qml.Wires object and list.""" + state = create_initial_state(wires) + expected = np.zeros((3, 3, 3, 3)) + expected[0, 0, 0, 0] = 1 + assert qml.math.allequal(state, expected) + # TODO: Add tests for qutrit state prep def test_create_initial_state_defaults_to_numpy(self): From f0101139793aeb1d800245ce13217e6464af6a83 Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:13:29 -0800 Subject: [PATCH 15/89] fixed qml.Wires to qml.wires.Wires --- .../devices/qutrit_mixed/test_qutrit_mixed_initialize_state.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_initialize_state.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_initialize_state.py index d35b03a1d43..75deebbc639 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_initialize_state.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_initialize_state.py @@ -54,7 +54,7 @@ def test_create_initial_state_with_BasisState(self): state[1, 2, 0, 1, 2, 0] = 0 # set to zero to make test below simple assert qml.math.allequal(state, np.zeros(([3] * 6))) - @pytest.mark.parametrize("wires", [(0, 1), qml.Wires([0, 1])]) + @pytest.mark.parametrize("wires", [(0, 1), qml.wires.Wires([0, 1])]) def test_create_initial_state_wires(self, wires): """Tests that create_initial_state works with qml.Wires object and list.""" state = create_initial_state(wires) From 505ff1bd77a7c64b57b264b7e8979fea56bfda7e Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Thu, 23 Nov 2023 12:25:08 -0800 Subject: [PATCH 16/89] Fixed wires to convert to array --- .../devices/qutrit_mixed/initialize_state.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index 65fa3f8981e..e192fa3466d 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -32,30 +32,34 @@ def create_initial_state( Returns: array: The initial state of a circuit """ + if isinstance(wires, Wires): + wire_array = wires.toarray() + else: + wire_array = np.array(wires) + num_wires = len(wire_array) + if not prep_operation: - num_wires = len(wires) rho = _create_basis_state(num_wires, 0) elif isinstance(prep_operation, QutritBasisState): - rho = _apply_basis_state(prep_operation.parameters[0], wires) + rho = _apply_basis_state(prep_operation.parameters[0], wire_array) elif isinstance(prep_operation, StatePrepBase): - rho = _apply_state_vector(prep_operation.state_vector(wire_order=list(wires)), wires) + rho = _apply_state_vector(prep_operation.state_vector(wire_order=list(wire_array)), num_wires) # TODO: add instance for prep_operations as added return qml.math.asarray(rho, like=like) -def _apply_state_vector(state, wires): # function is easy to abstract for qudit +def _apply_state_vector(state, num_wires): # function is easy to abstract for qudit """Initialize the internal state in a specified pure state. Args: state (array[complex]): normalized input state of length ``3**len(wires)`` - device_wires (Wires): wires that get initialized in the state + num_wires (array[int]): wires that get initialized in the state """ - num_wires = len(wires) # Initialize the entire wires with the state rho = qml.math.outer(state, qml.math.conj(state)) @@ -68,17 +72,13 @@ def _apply_basis_state(state, wires): # function is easy to abstract for qudit Args: state (array[int]): computational basis state of shape ``(wires,)`` consisting of 0s and 1s. - wires (Wires): wires that the provided computational state should be initialized on + wires (array[int]): wires that the provided computational state should be initialized on """ num_wires = len(wires) - if isinstance(wires, Wires): - wire_array = wires.toarray() - else: - wire_array = np.array(wires) # get computational basis state number - basis_states = qudit_dim ** (num_wires - 1 - wire_array) + basis_states = qudit_dim ** (num_wires - 1 - wires) print(qml.math.dot(state, basis_states)) num = int(qml.math.dot(state, basis_states)) From be7c1f1b458d26d70f8ee51d565caa6e4d13c28d Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Thu, 23 Nov 2023 12:31:12 -0800 Subject: [PATCH 17/89] refomatted black --- pennylane/devices/qutrit_mixed/initialize_state.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index e192fa3466d..701a55200ff 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -45,7 +45,9 @@ def create_initial_state( rho = _apply_basis_state(prep_operation.parameters[0], wire_array) elif isinstance(prep_operation, StatePrepBase): - rho = _apply_state_vector(prep_operation.state_vector(wire_order=list(wire_array)), num_wires) + rho = _apply_state_vector( + prep_operation.state_vector(wire_order=list(wire_array)), num_wires + ) # TODO: add instance for prep_operations as added From e0d456c44de00e4628bebe3a45b446a751763a41 Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:45:33 -0800 Subject: [PATCH 18/89] Update pennylane/devices/qutrit_mixed/initialize_state.py Co-authored-by: Mudit Pandey --- pennylane/devices/qutrit_mixed/initialize_state.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index 701a55200ff..3d33ba002f0 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -1,3 +1,16 @@ +# Copyright 2018-2023 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. """Functions to prepare a qutrit mixed state.""" from typing import Iterable, Union From 644c173256fc3e38c22eeff7d19f1e0014cfc1e2 Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:46:07 -0800 Subject: [PATCH 19/89] Fix comments Co-authored-by: Mudit Pandey --- .../test_qutrit_mixed_initialize_state.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_initialize_state.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_initialize_state.py index 75deebbc639..d609c49596a 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_initialize_state.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_initialize_state.py @@ -1,3 +1,16 @@ +# Copyright 2018-2023 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 create_initial_state in devices/qutrit_mixed.""" import pytest From 8a73ccf4fe777c0d0aa9269813bdb15bf7c9e060 Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:49:28 -0800 Subject: [PATCH 20/89] removed print Co-authored-by: Mudit Pandey --- pennylane/devices/qutrit_mixed/initialize_state.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index 3d33ba002f0..7dccc3c3dc3 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -94,7 +94,6 @@ def _apply_basis_state(state, wires): # function is easy to abstract for qudit # get computational basis state number basis_states = qudit_dim ** (num_wires - 1 - wires) - print(qml.math.dot(state, basis_states)) num = int(qml.math.dot(state, basis_states)) return _create_basis_state(num_wires, num) From 58fb4f465fbac783cf9f13d56958682a5c8e411e Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:53:46 -0800 Subject: [PATCH 21/89] fixed basis state comment Co-authored-by: Mudit Pandey --- pennylane/devices/qutrit_mixed/initialize_state.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index 7dccc3c3dc3..5a693783916 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -86,7 +86,7 @@ def _apply_basis_state(state, wires): # function is easy to abstract for qudit Args: state (array[int]): computational basis state of shape ``(wires,)`` - consisting of 0s and 1s. + consisting of 0s, 1s and 2s. wires (array[int]): wires that the provided computational state should be initialized on """ From b20578d872cdca388ec5ecc610705ea910fb3754 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 28 Nov 2023 15:24:28 -0800 Subject: [PATCH 22/89] Fixed wires and doc-strings --- .../devices/qutrit_mixed/initialize_state.py | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index 5a693783916..680d21ad632 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -46,21 +46,18 @@ def create_initial_state( array: The initial state of a circuit """ if isinstance(wires, Wires): - wire_array = wires.toarray() - else: - wire_array = np.array(wires) - num_wires = len(wire_array) + wires = wires.tolist() + + num_wires = len(wires) if not prep_operation: rho = _create_basis_state(num_wires, 0) elif isinstance(prep_operation, QutritBasisState): - rho = _apply_basis_state(prep_operation.parameters[0], wire_array) + rho = _apply_basis_state(prep_operation.parameters[0], wires) elif isinstance(prep_operation, StatePrepBase): - rho = _apply_state_vector( - prep_operation.state_vector(wire_order=list(wire_array)), num_wires - ) + rho = _apply_state_vector(prep_operation.state_vector(wire_order=list(wires)), num_wires) # TODO: add instance for prep_operations as added @@ -73,7 +70,11 @@ def _apply_state_vector(state, num_wires): # function is easy to abstract for q Args: state (array[complex]): normalized input state of length ``3**len(wires)`` - num_wires (array[int]): wires that get initialized in the state + num_wires (int): number of wires that get initialized in the state + + Returns: + array[complex]: complex array of shape ``[3] * (2 * num_wires)`` + representing the density matrix of this state. """ # Initialize the entire wires with the state @@ -87,8 +88,11 @@ def _apply_basis_state(state, wires): # function is easy to abstract for qudit Args: state (array[int]): computational basis state of shape ``(wires,)`` consisting of 0s, 1s and 2s. - wires (array[int]): wires that the provided computational state should be initialized on + wires (Iterable[int]): wires that the provided computational state should be initialized on + Returns: + array[complex]: complex array of shape ``[3] * (2 * num_wires)`` + representing the density matrix of this basis state. """ num_wires = len(wires) @@ -103,6 +107,7 @@ def _create_basis_state(num_wires, index): # function is easy to abstract for q """Return the density matrix representing a computational basis state over all wires. Args: + num_wires (int): number of wires to initialize index (int): integer representing the computational basis state. Returns: From 2fcf17e149148e71187e28abea4ebacb3577a334 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 28 Nov 2023 15:56:55 -0800 Subject: [PATCH 23/89] Fixed basis states from change to list --- pennylane/devices/qutrit_mixed/initialize_state.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index 680d21ad632..093dde6a3cf 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -97,7 +97,7 @@ def _apply_basis_state(state, wires): # function is easy to abstract for qudit num_wires = len(wires) # get computational basis state number - basis_states = qudit_dim ** (num_wires - 1 - wires) + basis_states = qudit_dim ** (num_wires - 1 - np.array(wires)) num = int(qml.math.dot(state, basis_states)) return _create_basis_state(num_wires, num) From e3212ed806ab67c73d5dc3d37926b9bcd01fc7bd Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Mon, 4 Dec 2023 17:21:48 -0800 Subject: [PATCH 24/89] fixed num wires, working on operation batched --- .../devices/qutrit_mixed/apply_operation.py | 285 ++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 pennylane/devices/qutrit_mixed/apply_operation.py diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py new file mode 100644 index 00000000000..3caea233d56 --- /dev/null +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -0,0 +1,285 @@ +import functools +from functools import singledispatch +import pennylane as qml +from pennylane import math +from string import ascii_letters as alphabet +from pennylane import numpy as np +from pennylane.operation import Channel + +qutrit_diagonal_in_z_basis = qml.Attribute(["TClock", "TRZ"]) +alphabet_array = np.array(list(alphabet)) + +qudit_dim = 3 # specifies qudit dimension + +def apply_channel_einsum(op: qml.operation.Operator, state, is_state_batched: bool = False): + r"""Apply a quantum channel specified by a list of Kraus operators to subsystems of the + quantum state. For a unitary gate, there is a single Kraus operator. + + Args: + op (Operator): Operator to apply to the quantum state + state (array[complex]): Input quantum state + is_state_batched (bool): Boolean representing whether the state is batched or not + + Returns: + array[complex]: output_state + """ + num_ch_wires = len(op.wires) + num_wires = int((qml.math.shape(state) - is_state_batched)/2) + rho_dim = 2 * num_wires + + # Computes K^\dagger, needed for the transformation K \rho K^\dagger + kraus_dagger = [math.conj(math.transpose(k)) for k in kraus] + + kraus = math.stack(kraus) + kraus_dagger = math.stack(kraus_dagger) + + # Shape kraus operators + kraus_shape = [len(kraus)] + [qudit_dim] * num_ch_wires * 2 + kraus = math.cast(math.reshape(kraus, kraus_shape), complex) + kraus_dagger = math.cast(math.reshape(kraus_dagger, kraus_shape), complex) + + # Tensor indices of the state. For each qubit, need an index for rows *and* columns + state_indices = alphabet[:rho_dim] + + # row indices of the quantum state affected by this operation + row_wires_list = wires.tolist() + row_indices = "".join(alphabet_array[row_wires_list].tolist()) + + # column indices are shifted by the number of wires + col_wires_list = [w + num_wires for w in row_wires_list] + col_indices = "".join(alphabet_array[col_wires_list].tolist()) + + # indices in einsum must be replaced with new ones + new_row_indices = alphabet[rho_dim: rho_dim + num_ch_wires] + new_col_indices = alphabet[rho_dim + num_ch_wires: rho_dim + 2 * num_ch_wires] + + # index for summation over Kraus operators + kraus_index = alphabet[rho_dim + 2 * num_ch_wires: rho_dim + 2 * num_ch_wires + 1] + + # new state indices replace row and column indices with new ones + new_state_indices = functools.reduce( + lambda old_string, idx_pair: old_string.replace(idx_pair[0], idx_pair[1]), + zip(col_indices + row_indices, new_col_indices + new_row_indices), + state_indices, + ) + + # index mapping for einsum, e.g., 'iga,abcdef,idh->gbchef' + einsum_indices = ( + f"{kraus_index}{new_row_indices}{row_indices}, {state_indices}," + f"{kraus_index}{col_indices}{new_col_indices}->{new_state_indices}" + ) + + return math.einsum(einsum_indices, kraus, state, kraus_dagger) + + +def apply_channel_tensordot(op: qml.operation.Operator, state, is_state_batched: bool = False): + r"""Apply a quantum channel specified by a list of Kraus operators to subsystems of the + quantum state. For a unitary gate, there is a single Kraus operator. + + Args: + op (Operator): Operator to apply to the quantum state + state (array[complex]): Input quantum state + is_state_batched (bool): Boolean representing whether the state is batched or not # TODO + + Returns: + array[complex]: output_state + """ + num_ch_wires = len(op.wires) + num_wires = (qml.math.shape(state) - is_state_batched)/2 + + # Shape kraus operators and cast them to complex data type + kraus_shape = [qudit_dim] * (num_ch_wires * 2) + + #TODO deal with batching + dim = 2 + if is_mat_batched := batch_size is not None: + # Add broadcasting dimension to shape + new_mat_shape = [batch_size] + new_mat_shape + if op.batch_size is None: + op._batch_size = batch_size # pylint:disable=protected-access + + kraus = [math.cast(math.reshape(k, kraus_shape), complex) for k in kraus] + + # row indices of the quantum state affected by this operation + row_wires_list = op.wires.tolist() + # column indices are shifted by the number of wires + col_wires_list = [w + num_wires for w in row_wires_list] + + channel_col_ids = list(range(num_ch_wires, 2 * num_ch_wires)) + axes_left = [channel_col_ids, row_wires_list] + # Use column indices instead or rows to incorporate transposition of K^\dagger + axes_right = [col_wires_list, channel_col_ids] + + # Apply the Kraus operators, and sum over all Kraus operators afterwards + def _conjugate_state_with(k): + """Perform the double tensor product k @ self._state @ k.conj(). + The `axes_left` and `axes_right` arguments are taken from the ambient variable space + and `axes_right` is assumed to incorporate the tensor product and the transposition + of k.conj() simultaneously.""" + return math.tensordot(math.tensordot(k, state, axes_left), math.conj(k), axes_right) + + if len(kraus) == 1: + _state = _conjugate_state_with(kraus[0]) + else: + _state = math.sum(math.stack([_conjugate_state_with(k) for k in kraus]), axis=0) + + # Permute the affected axes to their destination places. + # The row indices of the kraus operators are moved from the beginning to the original + # target row locations, the column indices from the end to the target column locations + source_left = list(range(num_ch_wires)) + dest_left = row_wires_list + source_right = list(range(-num_ch_wires, 0)) + dest_right = col_wires_list + return math.moveaxis(_state, source_left + source_right, dest_left + dest_right) + + +@singledispatch +def apply_operation( + op: qml.operation.Operator, state, is_state_batched: bool = False, debugger=None +): + """Apply and operator to a given state. + + Args: + op (Operator): The operation to apply to ``state`` + state (TensorLike): The starting state. + is_state_batched (bool): Boolean representing whether the state is batched or not + debugger (_Debugger): The debugger to use + + Returns: + ndarray: output state + + .. warning:: + + ``apply_operation`` is an internal function, and thus subject to change without a deprecation cycle. + + .. warning:: + ``apply_operation`` applies no validation to its inputs. + + This function assumes that the wires of the operator correspond to indices + of the state. See :func:`~.map_wires` to convert operations to integer wire labels. + + The shape of state should be ``[qudit_dim]*(num_wires * 2)``. + + This is a ``functools.singledispatch`` function, so additional specialized kernels + for specific operations can be registered like: + + .. code-block:: python + + @apply_operation.register + def _(op: type_op, state): + # custom op application method here + + **Example:** + + >>> state = np.zeros((3,3)) + >>> state[0][0] = 1 + >>> state + tensor([[1., 0., 0.], + [0., 0., 0.], + [0., 0., 0.]], requires_grad=True) + >>> apply_operation(qml.TShift(0), state) + tensor([[0., 0., 0.], + [0., 1., 0], + [0., 0., 0.],], requires_grad=True) + + """ + return _apply_operation_default(op, state, is_state_batched, debugger) + + +def _apply_operation_default(operation, state, is_state_batched, debugger): + """Applies operations to the internal device state. + + Args: + operation (.Operation): operation to apply on the device + + Returns: + ndarray: output state + """ + wires = operation.wires + matrices = _get_kraus(operation) + + if operation in qutrit_diagonal_in_z_basis: + return _apply_diagonal_unitary(matrices, wires, state, is_state_batched) + else: + num_op_wires = len(wires) + interface = math.get_interface(state, *matrices) + # Use tensordot for Autograd and Numpy if there are more than 2 wires + # Use tensordot in any case for more than 7 wires, as einsum does not support this case + + if (num_op_wires > 2 and interface in {"autograd", "numpy"}) or num_op_wires > 7: + return apply_channel_tensordot(matrices, wires, state, is_state_batched) + else: + return apply_channel_einsum(matrices, wires, state, is_state_batched) + + +# pylint: disable=arguments-differ + + +def _apply_diagonal_unitary(eigvals, wires, state, is_state_batched): + r"""Apply a diagonal unitary gate specified by a list of eigenvalues. This method uses + the fact that the unitary is diagonal for a more efficient implementation. + + Args: + eigvals (array): eigenvalues (phases) of the diagonal unitary + wires (Wires): target wires + + Returns: + ndarray: output state + """ + num_wires = (qml.math.shape(state) - is_state_batched)/2 + + eigvals = math.stack(eigvals) + + # reshape vectors + eigvals = math.cast(math.reshape(eigvals, [qudit_dim] * len(wires)), complex) + + # Tensor indices of the state. For each qubit, need an index for rows *and* columns + state_indices = alphabet[: 2 * num_wires] + + # row indices of the quantum state affected by this operation + row_indices = "".join(alphabet_array[wires].tolist()) + + # column indices are shifted by the number of wires + col_wires_list = [w + num_wires for w in wires] + col_indices = "".join(alphabet_array[col_wires_list].tolist()) + + einsum_indices = f"{row_indices},{state_indices},{col_indices}->{state_indices}" + + return math.einsum(einsum_indices, eigvals, state, math.conj(eigvals)) + + +@apply_operation.register +def apply_identity(op: qml.Identity, state, is_state_batched: bool = False, debugger=None): + """Applies a :class:`~.Identity` operation by just returning the input state.""" + return state + + +@apply_operation.register +def apply_snapshot(op: qml.Snapshot, state, is_state_batched: bool = False, debugger=None): + if debugger and debugger.active: + dim = int(np.sqrt(state.size)) + density_matrix = math.reshape(state, (dim, dim)) + if op.tag: + debugger.snapshots[op.tag] = density_matrix + else: + debugger.snapshots[len(debugger.snapshots)] = density_matrix + return state + + +def _get_kraus(operation): # pylint: disable=no-self-use + """Return the Kraus operators representing the operation. + + Args: + operation (.Operation): a PennyLane operation + + Returns: + list[array[complex]]: Returns a list of 2D matrices representing the Kraus operators. If + the operation is unitary, returns a single Kraus operator. In the case of a diagonal + unitary, returns a 1D array representing the matrix diagonal. + """ + if operation in qutrit_diagonal_in_z_basis: + return operation.eigvals() + + if isinstance(operation, Channel): + return operation.kraus_matrices() + return [operation.matrix()] From c6577190ae5bfd66eb0fbe0183e127be6dd4445a Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Mon, 4 Dec 2023 17:48:15 -0800 Subject: [PATCH 25/89] Fixed selection in default --- .../devices/qutrit_mixed/apply_operation.py | 60 ++++++++----------- .../test_qutrit_mixed_apply_operation.py | 0 2 files changed, 25 insertions(+), 35 deletions(-) create mode 100644 tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 3caea233d56..cf7c06c6e72 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -6,12 +6,15 @@ from pennylane import numpy as np from pennylane.operation import Channel -qutrit_diagonal_in_z_basis = qml.Attribute(["TClock", "TRZ"]) alphabet_array = np.array(list(alphabet)) +qutrit_diagonal_in_z_basis = qml.Attribute(["TClock", "TRZ"]) + +EINSUM_OP_WIRECOUNT_PERF_THRESHOLD = 3 +EINSUM_STATE_WIRECOUNT_PERF_THRESHOLD = 13 qudit_dim = 3 # specifies qudit dimension -def apply_channel_einsum(op: qml.operation.Operator, state, is_state_batched: bool = False): +def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: bool = False): r"""Apply a quantum channel specified by a list of Kraus operators to subsystems of the quantum state. For a unitary gate, there is a single Kraus operator. @@ -42,7 +45,7 @@ def apply_channel_einsum(op: qml.operation.Operator, state, is_state_batched: bo state_indices = alphabet[:rho_dim] # row indices of the quantum state affected by this operation - row_wires_list = wires.tolist() + row_wires_list = op.wires.tolist() row_indices = "".join(alphabet_array[row_wires_list].tolist()) # column indices are shifted by the number of wires @@ -72,7 +75,7 @@ def apply_channel_einsum(op: qml.operation.Operator, state, is_state_batched: bo return math.einsum(einsum_indices, kraus, state, kraus_dagger) -def apply_channel_tensordot(op: qml.operation.Operator, state, is_state_batched: bool = False): +def apply_operation_tensordot(op: qml.operation.Operator, state, is_state_batched: bool = False): r"""Apply a quantum channel specified by a list of Kraus operators to subsystems of the quantum state. For a unitary gate, there is a single Kraus operator. @@ -186,46 +189,33 @@ def _(op: type_op, state): return _apply_operation_default(op, state, is_state_batched, debugger) -def _apply_operation_default(operation, state, is_state_batched, debugger): - """Applies operations to the internal device state. - - Args: - operation (.Operation): operation to apply on the device - - Returns: - ndarray: output state +def _apply_operation_default(op, state, is_state_batched, debugger): + """The default behaviour of apply_operation, accessed through the standard dispatch + of apply_operation, as well as conditionally in other dispatches. """ - wires = operation.wires - matrices = _get_kraus(operation) - - if operation in qutrit_diagonal_in_z_basis: - return _apply_diagonal_unitary(matrices, wires, state, is_state_batched) - else: - num_op_wires = len(wires) - interface = math.get_interface(state, *matrices) - # Use tensordot for Autograd and Numpy if there are more than 2 wires - # Use tensordot in any case for more than 7 wires, as einsum does not support this case - - if (num_op_wires > 2 and interface in {"autograd", "numpy"}) or num_op_wires > 7: - return apply_channel_tensordot(matrices, wires, state, is_state_batched) - else: - return apply_channel_einsum(matrices, wires, state, is_state_batched) - + if op in qutrit_diagonal_in_z_basis: + return _apply_diagonal_unitary(op, state, is_state_batched) + if ( + len(op.wires) < EINSUM_OP_WIRECOUNT_PERF_THRESHOLD + and math.ndim(state) < EINSUM_STATE_WIRECOUNT_PERF_THRESHOLD + ) or (op.batch_size and is_state_batched): + return apply_operation_einsum(op, state, is_state_batched=is_state_batched) + return apply_operation_tensordot(op, state, is_state_batched=is_state_batched) -# pylint: disable=arguments-differ - -def _apply_diagonal_unitary(eigvals, wires, state, is_state_batched): +def _apply_diagonal_unitary(op: qml.operation.Operator, state, is_state_batched): r"""Apply a diagonal unitary gate specified by a list of eigenvalues. This method uses the fact that the unitary is diagonal for a more efficient implementation. Args: - eigvals (array): eigenvalues (phases) of the diagonal unitary - wires (Wires): target wires + op (Operator): The operation to apply to ``state`` + state (TensorLike): The starting state. + is_state_batched (bool): Boolean representing whether the state is batched or not Returns: ndarray: output state """ + num_wires = (qml.math.shape(state) - is_state_batched)/2 eigvals = math.stack(eigvals) @@ -237,10 +227,10 @@ def _apply_diagonal_unitary(eigvals, wires, state, is_state_batched): state_indices = alphabet[: 2 * num_wires] # row indices of the quantum state affected by this operation - row_indices = "".join(alphabet_array[wires].tolist()) + row_indices = "".join(alphabet_array[op.wires].tolist()) # column indices are shifted by the number of wires - col_wires_list = [w + num_wires for w in wires] + col_wires_list = [w + num_wires for w in op.wires] col_indices = "".join(alphabet_array[col_wires_list].tolist()) einsum_indices = f"{row_indices},{state_indices},{col_indices}->{state_indices}" diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py new file mode 100644 index 00000000000..e69de29bb2d From bffd530f509ec9c1f28a640aadd905f8f1e4eec2 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Mon, 4 Dec 2023 18:06:44 -0800 Subject: [PATCH 26/89] Added stub for TAdd --- pennylane/devices/qutrit_mixed/apply_operation.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index cf7c06c6e72..1d6a11aa498 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -256,6 +256,12 @@ def apply_snapshot(op: qml.Snapshot, state, is_state_batched: bool = False, debu return state +@apply_operation.register +def apply_(op: qml.TAdd, state, is_state_batched: bool = False, debugger=None): + """Apply TAdd gate to state.""" + pass + + def _get_kraus(operation): # pylint: disable=no-self-use """Return the Kraus operators representing the operation. @@ -273,3 +279,5 @@ def _get_kraus(operation): # pylint: disable=no-self-use if isinstance(operation, Channel): return operation.kraus_matrices() return [operation.matrix()] + + From 08cbb58f38d5a1919833d872e6780b71ffaf145e Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Mon, 4 Dec 2023 18:07:20 -0800 Subject: [PATCH 27/89] Added stubs for tests to add --- .../test_qutrit_mixed_apply_operation.py | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index e69de29bb2d..bca068fbcef 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -0,0 +1,117 @@ +# Copyright 2018-2023 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 create_initial_state in devices/apply_operation.""" + +import pytest +import pennylane as qml + +ml_frameworks_list = [ + "numpy", + pytest.param("autograd", marks=pytest.mark.autograd), + pytest.param("jax", marks=pytest.mark.jax), + pytest.param("torch", marks=pytest.mark.torch), + pytest.param("tensorflow", marks=pytest.mark.tf), +] + + +methods = [apply_operation_einsum, apply_operation_tensordot, apply_operation] + + +def test_custom_operator_with_matrix(): + """Test that apply_operation works with any operation that defines a matrix.""" + pass + +@pytest.mark.parametrize("ml_framework", ml_frameworks_list) +@pytest.mark.parametrize("method", methods) +@pytest.mark.parametrize("wire", (0, 1)) +class TestTwoQubitStateSpecialCases: + """Test the special cases on a two qubit state. Also tests the special cases for einsum and tensor application methods + for additional testing of these generic matrix application methods.""" + + def test_identity(self, method, wire, ml_framework): + """Test the application of an Identity gate on a two qutrit state.""" + pass + + def test_TAdd(self, method, wire, ml_framework): + """Test the application of an TAdd gate on a two qutrit state.""" + pass + + def test_diagonal_in_z(self, method, wire, ml_framework): + """Test the application of an Identity gate on a two qutrit state.""" + pass +@pytest.mark.parametrize("ml_framework", ml_frameworks_list) +class TestSnapshot: + """Test that apply_operation works for Snapshot ops""" + pass + +@pytest.mark.parametrize("method", methods) +class TestTRXCalcGrad: + """Tests the application and differentiation of an TRX gate in the different interfaces.""" + state = None # TODO add init state + + def compare_expected_result(self, phi, state, new_state, g): + """Compare TODO""" + pass + + @pytest.mark.autograd + def test_rx_grad_autograd(self, method): + """Test that the application of an rx gate is differentiable with autograd.""" + pass + + @pytest.mark.jax + @pytest.mark.parametrize("use_jit", (True, False)) + def test_rx_grad_jax(self, method, use_jit): + """Test that the application of an rx gate is differentiable with jax.""" + + import jax + pass + + @pytest.mark.torch + def test_rx_grad_torch(self, method): + """Tests the application and differentiation of an rx gate with torch.""" + + import torch + pass + + @pytest.mark.tf + def test_rx_grad_tf(self, method): + """Tests the application and differentiation of an rx gate with tensorflow""" + import tensorflow as tf + pass + + +@pytest.mark.parametrize("ml_framework", ml_frameworks_list) +@pytest.mark.parametrize("method", methods) +class TestBroadcasting: # pylint: disable=too-few-public-methods + """Tests that broadcasted operations are applied correctly.""" + + @pytest.mark.parametrize("op", broadcasted_ops) + def test_broadcasted_op(self, op, method, ml_framework): + """Tests that batched operations are applied correctly to an unbatched state.""" + pass + + @pytest.mark.parametrize("op", unbroadcasted_ops) + def test_broadcasted_state(self, op, method, ml_framework): + """Tests that unbatched operations are applied correctly to a batched state.""" + pass + + @pytest.mark.parametrize("op", broadcasted_ops) + def test_broadcasted_op_broadcasted_state(self, op, method, ml_framework): + """Tests that batched operations are applied correctly to a batched state.""" + pass + + def test_batch_size_set_if_missing(self, method, ml_framework): + """Tests that the batch_size is set on an operator if it was missing before. + Mostly useful for TF-autograph since it may have batch size set to None.""" + pass \ No newline at end of file From e4b826ac698fc281ce0fb16e584d73906db0c09c Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Tue, 12 Dec 2023 02:32:41 -0800 Subject: [PATCH 28/89] Continued implementation of broadcasting --- .../devices/qutrit_mixed/apply_operation.py | 110 +++++++----------- 1 file changed, 45 insertions(+), 65 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 1d6a11aa498..af782c0229c 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -7,13 +7,13 @@ from pennylane.operation import Channel alphabet_array = np.array(list(alphabet)) -qutrit_diagonal_in_z_basis = qml.Attribute(["TClock", "TRZ"]) EINSUM_OP_WIRECOUNT_PERF_THRESHOLD = 3 EINSUM_STATE_WIRECOUNT_PERF_THRESHOLD = 13 qudit_dim = 3 # specifies qudit dimension + def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: bool = False): r"""Apply a quantum channel specified by a list of Kraus operators to subsystems of the quantum state. For a unitary gate, there is a single Kraus operator. @@ -27,9 +27,10 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: array[complex]: output_state """ num_ch_wires = len(op.wires) - num_wires = int((qml.math.shape(state) - is_state_batched)/2) + num_wires = int(len(qml.math.shape(state)) / 2 - is_state_batched) rho_dim = 2 * num_wires + kraus = _get_kraus(op) # Computes K^\dagger, needed for the transformation K \rho K^\dagger kraus_dagger = [math.conj(math.transpose(k)) for k in kraus] @@ -38,6 +39,18 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: # Shape kraus operators kraus_shape = [len(kraus)] + [qudit_dim] * num_ch_wires * 2 + + if not isinstance(op, Channel): # TODO Channels broadcasting is causing issues currently + # TODO need to talk to PennyLane team to find out more + mat = op.matrix() + dim = qudit_dim**num_ch_wires + batch_size = qml.math.get_batch_size(mat, (dim, dim), dim**2) + if batch_size is not None: + # Add broadcasting dimension to shape + kraus_shape = [batch_size] + kraus_shape + if op.batch_size is None: + op._batch_size = batch_size # pylint:disable=protected-access + kraus = math.cast(math.reshape(kraus, kraus_shape), complex) kraus_dagger = math.cast(math.reshape(kraus_dagger, kraus_shape), complex) @@ -53,11 +66,11 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: col_indices = "".join(alphabet_array[col_wires_list].tolist()) # indices in einsum must be replaced with new ones - new_row_indices = alphabet[rho_dim: rho_dim + num_ch_wires] - new_col_indices = alphabet[rho_dim + num_ch_wires: rho_dim + 2 * num_ch_wires] + new_row_indices = alphabet[rho_dim : rho_dim + num_ch_wires] + new_col_indices = alphabet[rho_dim + num_ch_wires : rho_dim + 2 * num_ch_wires] # index for summation over Kraus operators - kraus_index = alphabet[rho_dim + 2 * num_ch_wires: rho_dim + 2 * num_ch_wires + 1] + kraus_index = alphabet[rho_dim + 2 * num_ch_wires : rho_dim + 2 * num_ch_wires + 1] # new state indices replace row and column indices with new ones new_state_indices = functools.reduce( @@ -82,29 +95,30 @@ def apply_operation_tensordot(op: qml.operation.Operator, state, is_state_batche Args: op (Operator): Operator to apply to the quantum state state (array[complex]): Input quantum state - is_state_batched (bool): Boolean representing whether the state is batched or not # TODO + is_state_batched (bool): Boolean representing whether the state is batched or not Returns: array[complex]: output_state """ num_ch_wires = len(op.wires) - num_wires = (qml.math.shape(state) - is_state_batched)/2 + num_wires = int(len(qml.math.shape(state)) / 2 - is_state_batched) # Shape kraus operators and cast them to complex data type kraus_shape = [qudit_dim] * (num_ch_wires * 2) - #TODO deal with batching - dim = 2 - if is_mat_batched := batch_size is not None: - # Add broadcasting dimension to shape - new_mat_shape = [batch_size] + new_mat_shape - if op.batch_size is None: - op._batch_size = batch_size # pylint:disable=protected-access - - kraus = [math.cast(math.reshape(k, kraus_shape), complex) for k in kraus] + if not isinstance(op, Channel): # TODO Channels broadcasting is causing issues currently, + # TODO need to talk to PennyLane team to find out more + mat = op.matrix() + dim = qudit_dim**num_ch_wires + batch_size = qml.math.get_batch_size(mat, (dim, dim), dim**2) + if is_mat_batched := batch_size is not None: + # Add broadcasting dimension to shape + kraus_shape = [batch_size] + kraus_shape + if op.batch_size is None: + op._batch_size = batch_size # pylint:disable=protected-access # row indices of the quantum state affected by this operation - row_wires_list = op.wires.tolist() + row_wires_list = op.wires.toarray() + int(is_state_batched) # column indices are shifted by the number of wires col_wires_list = [w + num_wires for w in row_wires_list] @@ -119,12 +133,14 @@ def _conjugate_state_with(k): The `axes_left` and `axes_right` arguments are taken from the ambient variable space and `axes_right` is assumed to incorporate the tensor product and the transposition of k.conj() simultaneously.""" + k = math.cast(math.reshape(k, kraus_shape), complex) return math.tensordot(math.tensordot(k, state, axes_left), math.conj(k), axes_right) - if len(kraus) == 1: - _state = _conjugate_state_with(kraus[0]) - else: + if isinstance(op, Channel): + kraus = op.kraus_matrices() _state = math.sum(math.stack([_conjugate_state_with(k) for k in kraus]), axis=0) + else: + _state = _conjugate_state_with(op.matrix()) # Permute the affected axes to their destination places. # The row indices of the kraus operators are moved from the beginning to the original @@ -133,12 +149,18 @@ def _conjugate_state_with(k): dest_left = row_wires_list source_right = list(range(-num_ch_wires, 0)) dest_right = col_wires_list + + if is_mat_batched: + pass # TODO #perm = [0] + [i + 1 for i in perm] + if is_state_batched: + pass # TODO #perm.insert(num_indices, -1) + return math.moveaxis(_state, source_left + source_right, dest_left + dest_right) @singledispatch def apply_operation( - op: qml.operation.Operator, state, is_state_batched: bool = False, debugger=None + op: qml.operation.Operator, state, is_state_batched: bool = False, debugger=None ): """Apply and operator to a given state. @@ -193,8 +215,6 @@ def _apply_operation_default(op, state, is_state_batched, debugger): """The default behaviour of apply_operation, accessed through the standard dispatch of apply_operation, as well as conditionally in other dispatches. """ - if op in qutrit_diagonal_in_z_basis: - return _apply_diagonal_unitary(op, state, is_state_batched) if ( len(op.wires) < EINSUM_OP_WIRECOUNT_PERF_THRESHOLD and math.ndim(state) < EINSUM_STATE_WIRECOUNT_PERF_THRESHOLD @@ -203,39 +223,7 @@ def _apply_operation_default(op, state, is_state_batched, debugger): return apply_operation_tensordot(op, state, is_state_batched=is_state_batched) -def _apply_diagonal_unitary(op: qml.operation.Operator, state, is_state_batched): - r"""Apply a diagonal unitary gate specified by a list of eigenvalues. This method uses - the fact that the unitary is diagonal for a more efficient implementation. - - Args: - op (Operator): The operation to apply to ``state`` - state (TensorLike): The starting state. - is_state_batched (bool): Boolean representing whether the state is batched or not - - Returns: - ndarray: output state - """ - - num_wires = (qml.math.shape(state) - is_state_batched)/2 - - eigvals = math.stack(eigvals) - - # reshape vectors - eigvals = math.cast(math.reshape(eigvals, [qudit_dim] * len(wires)), complex) - - # Tensor indices of the state. For each qubit, need an index for rows *and* columns - state_indices = alphabet[: 2 * num_wires] - - # row indices of the quantum state affected by this operation - row_indices = "".join(alphabet_array[op.wires].tolist()) - - # column indices are shifted by the number of wires - col_wires_list = [w + num_wires for w in op.wires] - col_indices = "".join(alphabet_array[col_wires_list].tolist()) - - einsum_indices = f"{row_indices},{state_indices},{col_indices}->{state_indices}" - - return math.einsum(einsum_indices, eigvals, state, math.conj(eigvals)) +# TODO add diagonal for speed up. @apply_operation.register @@ -256,10 +244,7 @@ def apply_snapshot(op: qml.Snapshot, state, is_state_batched: bool = False, debu return state -@apply_operation.register -def apply_(op: qml.TAdd, state, is_state_batched: bool = False, debugger=None): - """Apply TAdd gate to state.""" - pass +# TODO add TAdd speedup def _get_kraus(operation): # pylint: disable=no-self-use @@ -273,11 +258,6 @@ def _get_kraus(operation): # pylint: disable=no-self-use the operation is unitary, returns a single Kraus operator. In the case of a diagonal unitary, returns a 1D array representing the matrix diagonal. """ - if operation in qutrit_diagonal_in_z_basis: - return operation.eigvals() - if isinstance(operation, Channel): return operation.kraus_matrices() return [operation.matrix()] - - From 5fe57dc4cf65158ea799e274c2051ce54127e78f Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Tue, 12 Dec 2023 02:33:13 -0800 Subject: [PATCH 29/89] Continued implementing tests --- .../test_qutrit_mixed_apply_operation.py | 165 ++++++++++++++++-- 1 file changed, 147 insertions(+), 18 deletions(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index bca068fbcef..31831dae168 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -15,6 +15,64 @@ import pytest import pennylane as qml +import numpy as np +from pennylane import math + +density_matrix = np.array( + [ + [ + [ + [0.09208281 + 0.0j, 0.03160535 + 0.0664417j, 0.03416066 + 0.0206764j], + [-0.01227047 + 0.06373319j, 0.01815704 - 0.03131422j, -0.03700634 + 0.01956628j], + [0.03128229 + 0.02937806j, 0.03555759 - 0.01100168j, -0.01982137 - 0.04605857j], + ], + [ + [0.03160535 - 0.0664417j, 0.10864802 + 0.0j, 0.01877872 + 0.01624366j], + [0.10580479 + 0.04077997j, -0.06409966 + 0.01593581j, 0.00204818 + 0.03765312j], + [-0.00255557 - 0.06777649j, -0.04497981 - 0.02030987j, -0.05656505 + 0.06693942j], + ], + [ + [0.03416066 - 0.0206764j, 0.01877872 - 0.01624366j, 0.08975985 + 0.0j], + [0.03077889 - 0.02115111j, 0.04876844 + 0.04583181j, -0.04781717 + 0.04220666j], + [-0.03402139 + 0.04582056j, 0.03198441 + 0.06076387j, 0.02845793 - 0.00996117j], + ], + ], + [ + [ + [-0.01227047 - 0.06373319j, 0.10580479 - 0.04077997j, 0.03077889 + 0.02115111j], + [0.14327529 + 0.0j, -0.07288875 + 0.07020128j, -0.000707 + 0.04396371j], + [-0.05027514 - 0.08649698j, -0.07202654 + 0.01724066j, -0.03980968 + 0.11268854j], + ], + [ + [0.01815704 + 0.03131422j, -0.06409966 - 0.01593581j, 0.04876844 - 0.04583181j], + [-0.07288875 - 0.07020128j, 0.12370217 + 0.0j, -0.00788202 + 0.02235794j], + [-0.0128203 + 0.11522974j, 0.09896394 + 0.04999461j, 0.08419826 - 0.06733029j], + ], + [ + [-0.03700634 - 0.01956628j, 0.00204818 - 0.03765312j, -0.04781717 - 0.04220666j], + [-0.000707 - 0.04396371j, -0.00788202 - 0.02235794j, 0.08931812 + 0.0j], + [0.00766162 - 0.01285426j, -0.0084444 - 0.042322j, 0.00769262 + 0.03245046j], + ], + ], + [ + [ + [0.03128229 - 0.02937806j, -0.00255557 + 0.06777649j, -0.03402139 - 0.04582056j], + [-0.05027514 + 0.08649698j, -0.0128203 - 0.11522974j, 0.00766162 + 0.01285426j], + [0.11637437 + 0.0j, 0.03960783 - 0.09361331j, -0.08419771 - 0.07692928j], + ], + [ + [0.03555759 + 0.01100168j, -0.04497981 + 0.02030987j, 0.03198441 - 0.06076387j], + [-0.07202654 - 0.01724066j, 0.09896394 - 0.04999461j, -0.0084444 + 0.042322j], + [0.03960783 + 0.09361331j, 0.10660842 + 0.0j, 0.02629697 - 0.08574598j], + ], + [ + [-0.01982137 + 0.04605857j, -0.05656505 - 0.06693942j, 0.02845793 + 0.00996117j], + [-0.03980968 - 0.11268854j, 0.08419826 + 0.06733029j, 0.00769262 - 0.03245046j], + [-0.08419771 + 0.07692928j, 0.02629697 + 0.08574598j, 0.13023096 + 0.0j], + ], + ], + ] +) ml_frameworks_list = [ "numpy", @@ -32,6 +90,7 @@ def test_custom_operator_with_matrix(): """Test that apply_operation works with any operation that defines a matrix.""" pass + @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @pytest.mark.parametrize("method", methods) @pytest.mark.parametrize("wire", (0, 1)) @@ -41,60 +100,130 @@ class TestTwoQubitStateSpecialCases: def test_identity(self, method, wire, ml_framework): """Test the application of an Identity gate on a two qutrit state.""" - pass + initial_state = qml.math.asarray(density_matrix, like=ml_framework) + new_state = method(qml.Identity(wire), initial_state) + assert qml.math.allclose(initial_state, new_state) - def test_TAdd(self, method, wire, ml_framework): - """Test the application of an TAdd gate on a two qutrit state.""" - pass + # TODO: Add more tests as Special cases are added + +#TODO add normal test - def test_diagonal_in_z(self, method, wire, ml_framework): - """Test the application of an Identity gate on a two qutrit state.""" - pass @pytest.mark.parametrize("ml_framework", ml_frameworks_list) class TestSnapshot: """Test that apply_operation works for Snapshot ops""" - pass -@pytest.mark.parametrize("method", methods) + pass # TODO add snapshot tests + + +@pytest.mark.parametrize("method", methods, "subspace") class TestTRXCalcGrad: """Tests the application and differentiation of an TRX gate in the different interfaces.""" - state = None # TODO add init state + + state = density_matrix def compare_expected_result(self, phi, state, new_state, g): """Compare TODO""" - pass + expected0 = np.cos(phi / 2) * state[0, :, :] + -1j * np.sin(phi / 2) * state[1, :, :] + expected1 = -1j * np.sin(phi / 2) * state[0, :, :] + np.cos(phi / 2) * state[1, :, :] + + assert qml.math.allclose(new_state[0, :, :], expected0) + assert qml.math.allclose(new_state[1, :, :], expected1) + + g_expected0 = ( + -0.5 * np.sin(phi / 2) * state[0, :, :] - 0.5j * np.cos(phi / 2) * state[1, :, :] + ) + g_expected1 = ( + -0.5j * np.cos(phi / 2) * state[0, :, :] - 0.5 * np.sin(phi / 2) * state[1, :, :] + ) + + assert qml.math.allclose(g[0], g_expected0) + assert qml.math.allclose(g[1], g_expected1) @pytest.mark.autograd def test_rx_grad_autograd(self, method): """Test that the application of an rx gate is differentiable with autograd.""" - pass + state = qml.numpy.array(self.state) + + def f(phi): + op = qml.TRX(phi, wires=0) + return method(op, state) + + phi = qml.numpy.array(0.325 + 0j, requires_grad=True) + + new_state = f(phi) + g = qml.jacobian(lambda x: qml.math.real(f(x)))(phi) + self.compare_expected_result(phi, state, new_state, g) @pytest.mark.jax @pytest.mark.parametrize("use_jit", (True, False)) def test_rx_grad_jax(self, method, use_jit): """Test that the application of an rx gate is differentiable with jax.""" - import jax - pass + + state = jax.numpy.array(self.state) + + def f(phi): + op = qml.RX(phi, wires=0) + return method(op, state) + + if use_jit: + f = jax.jit(f) + + phi = 0.325 + + new_state = f(phi) + g = jax.jacobian(f, holomorphic=True)(phi + 0j) + self.compare_expected_result(phi, state, new_state, g) @pytest.mark.torch def test_rx_grad_torch(self, method): """Tests the application and differentiation of an rx gate with torch.""" - import torch - pass + + state = torch.tensor(self.state) + + def f(phi): + op = qml.RX(phi, wires=0) + return method(op, state) + + phi = torch.tensor(0.325, requires_grad=True) + + new_state = f(phi) + # forward-mode needed with complex results. + # See bug: https://github.com/pytorch/pytorch/issues/94397 + g = torch.autograd.functional.jacobian(f, phi + 0j, strategy="forward-mode", vectorize=True) + + self.compare_expected_result( + phi.detach().numpy(), + state.detach().numpy(), + new_state.detach().numpy(), + g.detach().numpy(), + ) @pytest.mark.tf def test_rx_grad_tf(self, method): """Tests the application and differentiation of an rx gate with tensorflow""" import tensorflow as tf - pass + + state = tf.Variable(self.state) + phi = tf.Variable(0.8589 + 0j) + + with tf.GradientTape() as grad_tape: + op = qml.RX(phi, wires=0) + new_state = method(op, state) + + grads = grad_tape.jacobian(new_state, [phi]) + # tf takes gradient with respect to conj(z), so we need to conj the gradient + phi_grad = tf.math.conj(grads[0]) + + self.compare_expected_result(phi, state, new_state, phi_grad) @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @pytest.mark.parametrize("method", methods) class TestBroadcasting: # pylint: disable=too-few-public-methods """Tests that broadcasted operations are applied correctly.""" + broadcasted_ops = [] @pytest.mark.parametrize("op", broadcasted_ops) def test_broadcasted_op(self, op, method, ml_framework): @@ -114,4 +243,4 @@ def test_broadcasted_op_broadcasted_state(self, op, method, ml_framework): def test_batch_size_set_if_missing(self, method, ml_framework): """Tests that the batch_size is set on an operator if it was missing before. Mostly useful for TF-autograph since it may have batch size set to None.""" - pass \ No newline at end of file + pass From cb0bf3bcc0c7f10e806d87b3f5470ba528bcf835 Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Wed, 20 Dec 2023 15:14:52 -0800 Subject: [PATCH 30/89] Added support for operation batching, continued work on tests --- pennylane/devices/qutrit_mixed/__init__.py | 4 +- .../devices/qutrit_mixed/apply_operation.py | 21 ++- .../test_qutrit_mixed_apply_operation.py | 145 ++++++++++++++++-- 3 files changed, 144 insertions(+), 26 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/__init__.py b/pennylane/devices/qutrit_mixed/__init__.py index bf2a93a70cb..c58cd8dd758 100644 --- a/pennylane/devices/qutrit_mixed/__init__.py +++ b/pennylane/devices/qutrit_mixed/__init__.py @@ -5,10 +5,12 @@ at your own discretion. .. currentmodule:: pennylane.devices.qutrit -.. autosummary:: TODO is this necessary? +.. autosummary:: :toctree: api create_initial_state + apply_operation """ +from .apply_operation import apply_operation from .initialize_state import create_initial_state diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index af782c0229c..90386d57d45 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -40,7 +40,8 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: # Shape kraus operators kraus_shape = [len(kraus)] + [qudit_dim] * num_ch_wires * 2 - if not isinstance(op, Channel): # TODO Channels broadcasting is causing issues currently + if not isinstance(op, Channel): + # TODO Channels broadcasting is causing issues currently # TODO need to talk to PennyLane team to find out more mat = op.matrix() dim = qudit_dim**num_ch_wires @@ -106,7 +107,8 @@ def apply_operation_tensordot(op: qml.operation.Operator, state, is_state_batche # Shape kraus operators and cast them to complex data type kraus_shape = [qudit_dim] * (num_ch_wires * 2) - if not isinstance(op, Channel): # TODO Channels broadcasting is causing issues currently, + if not isinstance(op, Channel): + # TODO Channels broadcasting is causing issues currently, # TODO need to talk to PennyLane team to find out more mat = op.matrix() dim = qudit_dim**num_ch_wires @@ -151,9 +153,13 @@ def _conjugate_state_with(k): dest_right = col_wires_list if is_mat_batched: - pass # TODO #perm = [0] + [i + 1 for i in perm] + source_left = [0] + [i + 1 for i in source_left] + source_right = [i + 1 for i in source_right] + dest_left = [0] + [i + 1 for i in source_left] + dest_right = [i + 1 for i in source_right] if is_state_batched: - pass # TODO #perm.insert(num_indices, -1) + source_right += [-1] + dest_right += [-1] return math.moveaxis(_state, source_left + source_right, dest_left + dest_right) @@ -225,13 +231,6 @@ def _apply_operation_default(op, state, is_state_batched, debugger): # TODO add diagonal for speed up. - -@apply_operation.register -def apply_identity(op: qml.Identity, state, is_state_batched: bool = False, debugger=None): - """Applies a :class:`~.Identity` operation by just returning the input state.""" - return state - - @apply_operation.register def apply_snapshot(op: qml.Snapshot, state, is_state_batched: bool = False, debugger=None): if debugger and debugger.active: diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 31831dae168..ca7a61837ab 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -17,6 +17,11 @@ import pennylane as qml import numpy as np from pennylane import math +from pennylane.devices.qutrit_mixed.apply_operation import ( + apply_operation_einsum, + apply_operation_tensordot, + apply_operation, +) density_matrix = np.array( [ @@ -98,21 +103,121 @@ class TestTwoQubitStateSpecialCases: """Test the special cases on a two qubit state. Also tests the special cases for einsum and tensor application methods for additional testing of these generic matrix application methods.""" - def test_identity(self, method, wire, ml_framework): - """Test the application of an Identity gate on a two qutrit state.""" - initial_state = qml.math.asarray(density_matrix, like=ml_framework) - new_state = method(qml.Identity(wire), initial_state) - assert qml.math.allclose(initial_state, new_state) + def test_TAdd(self, method, wire, ml_framework): + """Test the application of a TAdd gate on a two qutrit state.""" + initial_state = math.asarray(density_matrix, like=ml_framework) + + control = wire + target = int(not control) + new_state = method(qml.TAdd((control, target)), initial_state) + print(new_state) + + initial0 = math.take(initial_state, 0, axis=control)#TODO Fix for mixed! + new0 = math.take(new_state, 0, axis=control) + assert math.allclose(initial0, new0) + + initial1 = math.take(initial_state, 1, axis=control) + new1 = math.take(new_state, 1, axis=control) + assert math.allclose(initial1[2], new1[0]) + assert math.allclose(initial1[0], new1[1]) + assert math.allclose(initial1[1], new1[2]) + + initial1 = math.take(initial_state, 2, axis=control) + new1 = math.take(new_state, 2, axis=control) + assert math.allclose(initial1[1], new1[0]) + assert math.allclose(initial1[2], new1[1]) + assert math.allclose(initial1[0], new1[2]) + + def test_TShift(self, method, wire, ml_framework): + """Test the application of a TShift gate on a two qutrit state.""" + initial_state = math.asarray(density_matrix, like=ml_framework) + new_state = method(qml.TShift(wire), initial_state) + + initial0dim = math.take(initial_state, 0, axis=wire)#TODO Fix for mixed! + new1dim = math.take(new_state, 1, axis=wire) + + assert math.allclose(initial0dim, new1dim) + + initial1dim = math.take(initial_state, 1, axis=wire) + new2dim = math.take(new_state, 2, axis=wire) + assert math.allclose(initial1dim, new2dim) + + initial2dim = math.take(initial_state, 2, axis=wire) + new0dim = math.take(new_state, 0, axis=wire) + assert math.allclose(initial2dim, new0dim) + + def test_TClock(self, method, wire, ml_framework): + """Test the application of a TClock gate on a two qutrit state.""" + initial_state = math.asarray(density_matrix, like=ml_framework) + new_state = method(qml.TShift(wire), initial_state) + w = math.exp(2j*math.pi/3) + + new0 = math.take(new_state, 0, axis=wire) + initial0 = math.take(initial_state, 0, axis=wire) + assert math.allclose(new0, initial0) + + initial1 = math.take(initial_state, 1, axis=wire) + new1 = math.take(new_state, 1, axis=wire) + assert math.allclose(w * initial1, new1) + + initial2 = math.take(initial_state, 2, axis=wire) + new2 = math.take(new_state, 2, axis=wire) + assert math.allclose(w**2 * initial2, new2) # TODO: Add more tests as Special cases are added -#TODO add normal test + +# TODO add normal test + @pytest.mark.parametrize("ml_framework", ml_frameworks_list) class TestSnapshot: """Test that apply_operation works for Snapshot ops""" - pass # TODO add snapshot tests + class Debugger: # pylint: disable=too-few-public-methods + """A dummy debugger class""" + + def __init__(self): + self.active = True + self.snapshots = {} + + def test_no_debugger(self, ml_framework): + """Test nothing happens when there is no debugger""" + initial_state = math.asarray(density_matrix, like=ml_framework) + new_state = apply_operation(qml.Snapshot(), initial_state) + + assert new_state.shape == initial_state.shape + assert math.allclose(new_state, initial_state) + + def test_empty_tag(self, ml_framework): + """Test a snapshot is recorded properly when there is no tag""" + initial_state = math.asarray(density_matrix, like=ml_framework) + + debugger = self.Debugger() + new_state = apply_operation(qml.Snapshot(), initial_state, debugger=debugger) + + assert new_state.shape == initial_state.shape + assert math.allclose(new_state, initial_state) + + assert list(debugger.snapshots.keys()) == [0] + assert debugger.snapshots[0].shape == (9, 9) + assert math.allclose(debugger.snapshots[0], math.reshape(initial_state, (9, 9))) + + def test_provided_tag(self, ml_framework): + """Test a snapshot is recorded property when provided a tag""" + initial_state = math.asarray(density_matrix, like=ml_framework) + + debugger = self.Debugger() + tag = "dense" + new_state = apply_operation(qml.Snapshot(tag), initial_state, debugger=debugger) + + assert new_state.shape == initial_state.shape + assert math.allclose(new_state, initial_state) + + assert list(debugger.snapshots.keys()) == [tag] + assert debugger.snapshots[tag].shape == (9, 9) + assert math.allclose(debugger.snapshots[tag], math.reshape(initial_state, (9, 9))) + @pytest.mark.parametrize("method", methods, "subspace") @@ -126,18 +231,18 @@ def compare_expected_result(self, phi, state, new_state, g): expected0 = np.cos(phi / 2) * state[0, :, :] + -1j * np.sin(phi / 2) * state[1, :, :] expected1 = -1j * np.sin(phi / 2) * state[0, :, :] + np.cos(phi / 2) * state[1, :, :] - assert qml.math.allclose(new_state[0, :, :], expected0) - assert qml.math.allclose(new_state[1, :, :], expected1) + assert math.allclose(new_state[0, :, :], expected0) + assert math.allclose(new_state[1, :, :], expected1) g_expected0 = ( - -0.5 * np.sin(phi / 2) * state[0, :, :] - 0.5j * np.cos(phi / 2) * state[1, :, :] + -0.5 * np.sin(phi / 2) * state[0, :, :] - 0.5j * np.cos(phi / 2) * state[1, :, :] ) g_expected1 = ( - -0.5j * np.cos(phi / 2) * state[0, :, :] - 0.5 * np.sin(phi / 2) * state[1, :, :] + -0.5j * np.cos(phi / 2) * state[0, :, :] - 0.5 * np.sin(phi / 2) * state[1, :, :] ) - assert qml.math.allclose(g[0], g_expected0) - assert qml.math.allclose(g[1], g_expected1) + assert math.allclose(g[0], g_expected0) + assert math.allclose(g[1], g_expected1) @pytest.mark.autograd def test_rx_grad_autograd(self, method): @@ -151,7 +256,7 @@ def f(phi): phi = qml.numpy.array(0.325 + 0j, requires_grad=True) new_state = f(phi) - g = qml.jacobian(lambda x: qml.math.real(f(x)))(phi) + g = qml.jacobian(lambda x: math.real(f(x)))(phi) self.compare_expected_result(phi, state, new_state, g) @pytest.mark.jax @@ -223,7 +328,9 @@ def test_rx_grad_tf(self, method): @pytest.mark.parametrize("method", methods) class TestBroadcasting: # pylint: disable=too-few-public-methods """Tests that broadcasted operations are applied correctly.""" + broadcasted_ops = [] + unbroadcasted_ops = [] @pytest.mark.parametrize("op", broadcasted_ops) def test_broadcasted_op(self, op, method, ml_framework): @@ -244,3 +351,13 @@ def test_batch_size_set_if_missing(self, method, ml_framework): """Tests that the batch_size is set on an operator if it was missing before. Mostly useful for TF-autograph since it may have batch size set to None.""" pass + + +@pytest.mark.parametrize("ml_framework", ml_frameworks_list) +@pytest.mark.parametrize("method", methods) +class TestBroadcasting: # pylint: disable=too-few-public-methods + """Tests that Arbritary Channel operation applied correctly.""" + + def test_channel(self, method, ml_framework): + """Tests that channels are applied correctly to an unbatched state.""" + pass From 15f83e0b971e722ecc9b9c1c2c5da2f83c854062 Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Fri, 5 Jan 2024 02:22:03 -0800 Subject: [PATCH 31/89] Added broadcasting, channel, and more tests. Fixed some bugs in apply_op --- .../devices/qutrit_mixed/apply_operation.py | 9 +- .../test_qutrit_mixed_apply_operation.py | 347 ++++++++++++++---- 2 files changed, 279 insertions(+), 77 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 90386d57d45..c191b798baf 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -40,9 +40,11 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: # Shape kraus operators kraus_shape = [len(kraus)] + [qudit_dim] * num_ch_wires * 2 - if not isinstance(op, Channel): + if isinstance(op, Channel): + is_mat_batched = False # TODO Channels broadcasting is causing issues currently # TODO need to talk to PennyLane team to find out more + else: mat = op.matrix() dim = qudit_dim**num_ch_wires batch_size = qml.math.get_batch_size(mat, (dim, dim), dim**2) @@ -107,9 +109,11 @@ def apply_operation_tensordot(op: qml.operation.Operator, state, is_state_batche # Shape kraus operators and cast them to complex data type kraus_shape = [qudit_dim] * (num_ch_wires * 2) - if not isinstance(op, Channel): + if isinstance(op, Channel): + is_mat_batched = False # TODO Channels broadcasting is causing issues currently, # TODO need to talk to PennyLane team to find out more + else: mat = op.matrix() dim = qudit_dim**num_ch_wires batch_size = qml.math.get_batch_size(mat, (dim, dim), dim**2) @@ -231,6 +235,7 @@ def _apply_operation_default(op, state, is_state_batched, debugger): # TODO add diagonal for speed up. + @apply_operation.register def apply_snapshot(op: qml.Snapshot, state, is_state_batched: bool = False, debugger=None): if debugger and debugger.active: diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index ca7a61837ab..7e3a8851057 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -17,6 +17,8 @@ import pennylane as qml import numpy as np from pennylane import math +from pennylane.operation import Channel +from scipy.stats import unitary_group from pennylane.devices.qutrit_mixed.apply_operation import ( apply_operation_einsum, apply_operation_tensordot, @@ -79,12 +81,24 @@ ] ) + +def get_random_mixed_state(num_qutrits): + dim = 3**num_qutrits + basis = unitary_group.rvs(dim) + Schmidt_weights = np.random.dirichlet(np.ones(dim), size=1).astype(complex)[0] + print(Schmidt_weights) + mixed_state = np.zeros((dim, dim)).astype(complex) + for i in range(dim): + mixed_state += Schmidt_weights[i] * np.outer(basis[i], basis[i]) + return mixed_state.reshape([3] * (2 * num_qutrits)) + + ml_frameworks_list = [ "numpy", - pytest.param("autograd", marks=pytest.mark.autograd), - pytest.param("jax", marks=pytest.mark.jax), - pytest.param("torch", marks=pytest.mark.torch), - pytest.param("tensorflow", marks=pytest.mark.tf), + # pytest.param("autograd", marks=pytest.mark.autograd), + # pytest.param("jax", marks=pytest.mark.jax), + # pytest.param("torch", marks=pytest.mark.torch), + # pytest.param("tensorflow", marks=pytest.mark.tf), ] @@ -93,7 +107,25 @@ def test_custom_operator_with_matrix(): """Test that apply_operation works with any operation that defines a matrix.""" - pass + mat = np.array( + [ + [-0.35546532 - 0.03636115j, -0.19051888 - 0.38049108j, 0.07943913 - 0.8276115j], + [-0.2766807 - 0.71617593j, -0.1227771 + 0.61271557j, -0.0872488 - 0.11150285j], + [-0.2312502 - 0.47894201j, -0.04564929 - 0.65295532j, -0.3629075 + 0.3962342j], + ] + ) + + # pylint: disable=too-few-public-methods + class CustomOp(qml.operation.Operation): + num_wires = 1 + + def matrix(self): + return mat + + state = np.array([0.26977852 + 0.15165342j, 0.59635993 + 0.26296103j, 0.39598925 + 0.56799616j]) + + new_state = apply_operation(CustomOp(0), state) + assert qml.math.allclose(new_state, mat @ state) @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @@ -110,66 +142,103 @@ def test_TAdd(self, method, wire, ml_framework): control = wire target = int(not control) new_state = method(qml.TAdd((control, target)), initial_state) - print(new_state) - initial0 = math.take(initial_state, 0, axis=control)#TODO Fix for mixed! + def check_TAdd_second_roll(initial_input, new_input): + initial_input0 = math.take(initial_input, 0, axis=control + 1) + new_input0 = math.take(new_input, 0, axis=control + 1) + assert math.allclose(initial_input0, new_input0) + + initial_input1 = math.take(initial_input, 1, axis=control + 1) + initial_input1_rolled = math.roll(initial_input1, 1, 1) + new_input1 = math.take(new_input, 1, axis=control + 1) + assert math.allclose(initial_input1_rolled, new_input1) + + initial_input2 = math.take(initial_input, 2, axis=control + 1) + initial_input2_rolled = math.roll(initial_input2, -1, 1) + new_input2 = math.take(new_input, 2, axis=control + 1) + assert math.allclose(initial_input2_rolled, new_input2) + + initial0 = math.take(initial_state, 0, axis=control) new0 = math.take(new_state, 0, axis=control) - assert math.allclose(initial0, new0) + check_TAdd_second_roll(initial0, new0) initial1 = math.take(initial_state, 1, axis=control) + initial1_rolled = np.roll(initial1, 1, 0) new1 = math.take(new_state, 1, axis=control) - assert math.allclose(initial1[2], new1[0]) - assert math.allclose(initial1[0], new1[1]) - assert math.allclose(initial1[1], new1[2]) + check_TAdd_second_roll(initial1_rolled, new1) - initial1 = math.take(initial_state, 2, axis=control) - new1 = math.take(new_state, 2, axis=control) - assert math.allclose(initial1[1], new1[0]) - assert math.allclose(initial1[2], new1[1]) - assert math.allclose(initial1[0], new1[2]) + initial2 = math.take(initial_state, 2, axis=control) + initial2_rolled = math.roll(initial2, -1, 0) + new2 = math.take(new_state, 2, axis=control) + check_TAdd_second_roll(initial2_rolled, new2) def test_TShift(self, method, wire, ml_framework): """Test the application of a TShift gate on a two qutrit state.""" initial_state = math.asarray(density_matrix, like=ml_framework) new_state = method(qml.TShift(wire), initial_state) - initial0dim = math.take(initial_state, 0, axis=wire)#TODO Fix for mixed! - new1dim = math.take(new_state, 1, axis=wire) + def check_second_roll(initial_input, new_input): + initial_input0 = math.take(initial_input, 0, axis=wire + 1) + new_input1 = math.take(new_input, 1, axis=wire + 1) + assert math.allclose(initial_input0, new_input1) - assert math.allclose(initial0dim, new1dim) + initial_input1 = math.take(initial_input, 1, axis=wire + 1) + new_input2 = math.take(new_input, 2, axis=wire + 1) + assert math.allclose(initial_input1, new_input2) - initial1dim = math.take(initial_state, 1, axis=wire) - new2dim = math.take(new_state, 2, axis=wire) - assert math.allclose(initial1dim, new2dim) + initial_input2 = math.take(initial_input, 2, axis=wire + 1) + new_input0 = math.take(new_input, 0, axis=wire + 1) + assert math.allclose(initial_input2, new_input0) - initial2dim = math.take(initial_state, 2, axis=wire) - new0dim = math.take(new_state, 0, axis=wire) - assert math.allclose(initial2dim, new0dim) + initial0 = math.take(initial_state, 0, axis=wire) + new1 = math.take(new_state, 1, axis=wire) + check_second_roll(initial0, new1) + + initial1 = math.take(initial_state, 1, axis=wire) + new2 = math.take(new_state, 2, axis=wire) + check_second_roll(initial1, new2) + + initial2 = math.take(initial_state, 2, axis=wire) + new0 = math.take(new_state, 0, axis=wire) + check_second_roll(initial2, new0) def test_TClock(self, method, wire, ml_framework): """Test the application of a TClock gate on a two qutrit state.""" initial_state = math.asarray(density_matrix, like=ml_framework) - new_state = method(qml.TShift(wire), initial_state) - w = math.exp(2j*math.pi/3) + new_state = method(qml.TClock(wire), initial_state) + w = math.exp(2j * np.pi / 3) + w2 = math.exp(4j * np.pi / 3) + + def check_second_roll(initial_input, new_input): + initial_input0 = math.take(initial_input, 0, axis=wire + 1) + new_input0 = math.take(new_input, 0, axis=wire + 1) + assert math.allclose(initial_input0, new_input0) + + initial_input1 = math.take(initial_input, 1, axis=wire + 1) + new_input1 = math.take(new_input, 1, axis=wire + 1) + print(initial_input1) + print(new_input1) + assert math.allclose(initial_input1 / w, new_input1) + + initial_input2 = math.take(initial_input, 2, axis=wire + 1) + new_input2 = math.take(new_input, 2, axis=wire + 1) + assert math.allclose(initial_input2 / w2, new_input2) - new0 = math.take(new_state, 0, axis=wire) initial0 = math.take(initial_state, 0, axis=wire) - assert math.allclose(new0, initial0) + new0 = math.take(new_state, 0, axis=wire) + check_second_roll(initial0, new0) initial1 = math.take(initial_state, 1, axis=wire) new1 = math.take(new_state, 1, axis=wire) - assert math.allclose(w * initial1, new1) + check_second_roll(w * initial1, new1) initial2 = math.take(initial_state, 2, axis=wire) new2 = math.take(new_state, 2, axis=wire) - assert math.allclose(w**2 * initial2, new2) + check_second_roll(w2 * initial2, new2) # TODO: Add more tests as Special cases are added -# TODO add normal test - - @pytest.mark.parametrize("ml_framework", ml_frameworks_list) class TestSnapshot: """Test that apply_operation works for Snapshot ops""" @@ -219,56 +288,61 @@ def test_provided_tag(self, ml_framework): assert math.allclose(debugger.snapshots[tag], math.reshape(initial_state, (9, 9))) +subspace_list = [(0, 1), (0, 2), (1, 2)] -@pytest.mark.parametrize("method", methods, "subspace") + +@pytest.mark.parametrize("method", methods, "subspace", subspace_list) # TODO class TestTRXCalcGrad: """Tests the application and differentiation of an TRX gate in the different interfaces.""" state = density_matrix - def compare_expected_result(self, phi, state, new_state, g): + def compare_expected_result(self, phi, state, new_state, g, subspace): """Compare TODO""" - expected0 = np.cos(phi / 2) * state[0, :, :] + -1j * np.sin(phi / 2) * state[1, :, :] - expected1 = -1j * np.sin(phi / 2) * state[0, :, :] + np.cos(phi / 2) * state[1, :, :] - assert math.allclose(new_state[0, :, :], expected0) - assert math.allclose(new_state[1, :, :], expected1) + matrix = qml.TRX.compute_matrix(phi, subspace=subspace) + expanded_mat = np.kron(matrix, np.eye(3)) + expanded_adjoint = np.conj(expanded_mat).T + flattened_state = state.reshape(9, 9) + res = expanded_mat @ flattened_state @ expanded_adjoint - g_expected0 = ( - -0.5 * np.sin(phi / 2) * state[0, :, :] - 0.5j * np.cos(phi / 2) * state[1, :, :] - ) - g_expected1 = ( - -0.5j * np.cos(phi / 2) * state[0, :, :] - 0.5 * np.sin(phi / 2) * state[1, :, :] - ) + assert math.allclose(new_state, res) - assert math.allclose(g[0], g_expected0) - assert math.allclose(g[1], g_expected1) + # g_expected0 = ( + # -0.5 * np.sin(phi / 2) * state[0, :, :] - 0.5j * np.cos(phi / 2) * state[1, :, :] + # ) + # g_expected1 = ( + # -0.5j * np.cos(phi / 2) * state[0, :, :] - 0.5 * np.sin(phi / 2) * state[1, :, :] + # ) + # + # assert math.allclose(g[0], g_expected0) + # assert math.allclose(g[1], g_expected1) @pytest.mark.autograd - def test_rx_grad_autograd(self, method): + def test_rx_grad_autograd(self, method, subspace): """Test that the application of an rx gate is differentiable with autograd.""" state = qml.numpy.array(self.state) def f(phi): - op = qml.TRX(phi, wires=0) + op = qml.TRX(phi, wires=0, subspace=subspace) return method(op, state) phi = qml.numpy.array(0.325 + 0j, requires_grad=True) new_state = f(phi) g = qml.jacobian(lambda x: math.real(f(x)))(phi) - self.compare_expected_result(phi, state, new_state, g) + self.compare_expected_result(phi, state, new_state, g, subspace) @pytest.mark.jax @pytest.mark.parametrize("use_jit", (True, False)) - def test_rx_grad_jax(self, method, use_jit): + def test_rx_grad_jax(self, method, use_jit, subspace): """Test that the application of an rx gate is differentiable with jax.""" import jax state = jax.numpy.array(self.state) def f(phi): - op = qml.RX(phi, wires=0) + op = qml.TRX(phi, wires=0, subspace=subspace) return method(op, state) if use_jit: @@ -278,17 +352,17 @@ def f(phi): new_state = f(phi) g = jax.jacobian(f, holomorphic=True)(phi + 0j) - self.compare_expected_result(phi, state, new_state, g) + self.compare_expected_result(phi, state, new_state, g, subspace) @pytest.mark.torch - def test_rx_grad_torch(self, method): + def test_rx_grad_torch(self, method, subspace): """Tests the application and differentiation of an rx gate with torch.""" import torch state = torch.tensor(self.state) def f(phi): - op = qml.RX(phi, wires=0) + op = qml.TRX(phi, wires=0, subspace=subspace) return method(op, state) phi = torch.tensor(0.325, requires_grad=True) @@ -303,10 +377,11 @@ def f(phi): state.detach().numpy(), new_state.detach().numpy(), g.detach().numpy(), + subspace, ) @pytest.mark.tf - def test_rx_grad_tf(self, method): + def test_rx_grad_tf(self, method, subspace): """Tests the application and differentiation of an rx gate with tensorflow""" import tensorflow as tf @@ -314,50 +389,172 @@ def test_rx_grad_tf(self, method): phi = tf.Variable(0.8589 + 0j) with tf.GradientTape() as grad_tape: - op = qml.RX(phi, wires=0) + op = qml.TRX(phi, wires=0, subspace=subspace) new_state = method(op, state) grads = grad_tape.jacobian(new_state, [phi]) # tf takes gradient with respect to conj(z), so we need to conj the gradient phi_grad = tf.math.conj(grads[0]) - self.compare_expected_result(phi, state, new_state, phi_grad) + self.compare_expected_result(phi, state, new_state, phi_grad, subspace) @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @pytest.mark.parametrize("method", methods) class TestBroadcasting: # pylint: disable=too-few-public-methods - """Tests that broadcasted operations are applied correctly.""" - - broadcasted_ops = [] - unbroadcasted_ops = [] + """Tests that broadcasted operations (not channels) are applied correctly.""" + + broadcasted_ops = [ + qml.TRX(np.array([np.pi, np.pi / 2, np.pi / 4]), wires=2, subspace=(0, 1)), + # qml.TRY(np.array([np.pi, np.pi / 2, np.pi / 4]), wires=2, subspace=(0,1)), + # qml.TRZ(np.array([np.pi, np.pi / 2, np.pi / 4]), wires=2, subspace=(1,2)), + # qml.QutritUnitary( + # np.array([unitary_group.rvs(27), unitary_group.rvs(27), unitary_group.rvs(27)]), + # wires=[0, 1, 2], + # ), + ] + unbroadcasted_ops = [ + qml.THadamard(wires=2), + # qml.TClock(wires=2), + # qml.TShift(wires=2), + # qml.TAdd(wires=[1, 2]), + # qml.TRX(np.pi / 3, wires=2, subspace=(0, 2)), + # qml.TRY(np.array([np.pi, np.pi / 2, np.pi / 4]), wires=2, subspace=(1, 2)), + # qml.TRZ(np.array([np.pi, np.pi / 2, np.pi / 4]), wires=2, subspace=(0, 1)), + # qml.QutritUnitary(unitary_group.rvs(27), wires=[0, 1, 2]), + ] @pytest.mark.parametrize("op", broadcasted_ops) def test_broadcasted_op(self, op, method, ml_framework): """Tests that batched operations are applied correctly to an unbatched state.""" - pass + state = get_random_mixed_state(3) + flattened_state = state.reshape(27, 27) + + res = method(op, qml.math.asarray(state, like=ml_framework)) + missing_wires = 3 - len(op.wires) + mat = op.matrix() + expanded_mats = [ + np.kron(np.eye(3**missing_wires), mat[i]) if missing_wires else mat[i] + for i in range(3) + ] + expected = [] + + for i in range(3): + expanded_mat = expanded_mats[i] + adjoint_mat = np.conj(expanded_mat).T + expected.append((expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * 6)) + + assert qml.math.get_interface(res) == ml_framework + assert qml.math.allclose(res, expected) @pytest.mark.parametrize("op", unbroadcasted_ops) def test_broadcasted_state(self, op, method, ml_framework): """Tests that unbatched operations are applied correctly to a batched state.""" - pass + if method is apply_operation_tensordot: + pytest.skip("Tensordot doesn't support batched operator and batched state.") + state = [get_random_mixed_state(3) for _ in range(3)] + + res = method(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) + missing_wires = 3 - len(op.wires) + mat = op.matrix() + expanded_mat = np.kron(np.eye(3**missing_wires), mat) if missing_wires else mat + adjoint_mat = np.conj(expanded_mat).T + expected = [] + + for i in range(3): + flattened_state = state[i].reshape(27, 27) + expected.append((expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * 6)) + + assert qml.math.get_interface(res) == ml_framework + assert qml.math.allclose(res, expected) @pytest.mark.parametrize("op", broadcasted_ops) def test_broadcasted_op_broadcasted_state(self, op, method, ml_framework): """Tests that batched operations are applied correctly to a batched state.""" - pass + state = [get_random_mixed_state(3) for _ in range(3)] + + res = method(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) + missing_wires = 3 - len(op.wires) + mat = op.matrix() + expanded_mats = [ + np.kron(np.eye(3**missing_wires), mat[i]) if missing_wires else mat[i] + for i in range(3) + ] + expected = [] + + for i in range(3): + expanded_mat = expanded_mats[i] + adjoint_mat = np.conj(expanded_mat).T + flattened_state = state[i].reshape(27, 27) + expected.append((expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * 6)) + assert qml.math.get_interface(res) == ml_framework + assert qml.math.allclose(res, expected) def test_batch_size_set_if_missing(self, method, ml_framework): - """Tests that the batch_size is set on an operator if it was missing before. - Mostly useful for TF-autograph since it may have batch size set to None.""" - pass + """Tests that the batch_size is set on an operator if it was missing before.""" + param = qml.math.asarray([0.1, 0.2, 0.3], like=ml_framework) + state = get_random_mixed_state(1) + op = qml.TRX(param, 0) + op._batch_size = None # pylint:disable=protected-access + state = method(op, state) + assert state.shape == (3, 2, 2) + assert op.batch_size == 3 @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @pytest.mark.parametrize("method", methods) -class TestBroadcasting: # pylint: disable=too-few-public-methods - """Tests that Arbritary Channel operation applied correctly.""" - - def test_channel(self, method, ml_framework): - """Tests that channels are applied correctly to an unbatched state.""" - pass +class TestChannels: # pylint: disable=too-few-public-methods + """Tests that Channel operations are applied correctly.""" + + class TestChannel(Channel): + num_params = 1 + num_wires = 1 + + def __init__(self, p, wires, id=None): + super().__init__(p, wires=wires, id=id) + + @staticmethod + def compute_kraus_matrices(p): + K0 = (np.sqrt(1 - p) * math.cast_like(np.eye(3), p)).astype(complex) + K1 = ( + np.sqrt(p) * math.cast_like(np.array([[0, 0, 1], [1, 0, 0], [0, 1, 0]]), p) + ).astype(complex) + return [K0, K1] + + def test_non_broadcasted_state(self, method, ml_framework): + """Tests that Channel operations are applied correctly to a state.""" + state = get_random_mixed_state(2) + test_channel = self.TestChannel(0.3, wires=1) + res = method(test_channel, state) + flattened_state = state.reshape(9, 9) + + mat = test_channel.kraus_matrices() + + expanded_mats = [np.kron(np.eye(3), mat[i]) for i in range(len(mat))] + expected = np.zeros((9, 9)).astype(complex) + for i in range(len(mat)): + expanded_mat = expanded_mats[i] + adjoint_mat = np.conj(expanded_mat).T + expected += expanded_mat @ flattened_state @ adjoint_mat + expected = expected.reshape([3] * 4) + assert qml.math.get_interface(res) == ml_framework + assert qml.math.allclose(res, expected) + + def test_broadcasted_state(self, method, ml_framework): + """Tests that Channel operations are applied correctly to a batched state.""" + state = [get_random_mixed_state(2) for _ in range(3)] + test_channel = self.TestChannel(0.3, wires=1) + res = method(test_channel, state) + + mat = test_channel.kraus_matrices() + expanded_mats = [np.kron(np.eye(3), mat[i]) for i in range(len(mat))] + expected = [np.zeros((9, 9)).astype(complex) for _ in range(3)] + for i in range(3): + flattened_state = state[i].reshape(9, 9) + for j in range(len(mat)): + expanded_mat = expanded_mats[j] + adjoint_mat = np.conj(expanded_mat).T + expected[i] += expanded_mat @ flattened_state @ adjoint_mat + expected[i] = expected[i].reshape([3] * 4) + assert qml.math.get_interface(res) == ml_framework + assert qml.math.allclose(res, expected) From 3ff3959c46defa1a5a07632a9e32c742ade84dd0 Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Fri, 5 Jan 2024 08:44:20 -0800 Subject: [PATCH 32/89] fixed tensordot method, work on tests --- .../devices/qutrit_mixed/apply_operation.py | 4 +- .../test_qutrit_mixed_apply_operation.py | 47 +++++++++++-------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index c191b798baf..c92f6c36820 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -124,7 +124,7 @@ def apply_operation_tensordot(op: qml.operation.Operator, state, is_state_batche op._batch_size = batch_size # pylint:disable=protected-access # row indices of the quantum state affected by this operation - row_wires_list = op.wires.toarray() + int(is_state_batched) + row_wires_list = list(op.wires.toarray() + int(is_state_batched)) # column indices are shifted by the number of wires col_wires_list = [w + num_wires for w in row_wires_list] @@ -165,6 +165,8 @@ def _conjugate_state_with(k): source_right += [-1] dest_right += [-1] + print(source_left + source_right) + print(dest_left + dest_right) return math.moveaxis(_state, source_left + source_right, dest_left + dest_right) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 7e3a8851057..25e648fefad 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -11,7 +11,7 @@ # 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 create_initial_state in devices/apply_operation.""" +"""Unit tests for create_initial_state in devices/qutrit_mixed/apply_operation.""" import pytest import pennylane as qml @@ -86,7 +86,6 @@ def get_random_mixed_state(num_qutrits): dim = 3**num_qutrits basis = unitary_group.rvs(dim) Schmidt_weights = np.random.dirichlet(np.ones(dim), size=1).astype(complex)[0] - print(Schmidt_weights) mixed_state = np.zeros((dim, dim)).astype(complex) for i in range(dim): mixed_state += Schmidt_weights[i] * np.outer(basis[i], basis[i]) @@ -95,10 +94,10 @@ def get_random_mixed_state(num_qutrits): ml_frameworks_list = [ "numpy", - # pytest.param("autograd", marks=pytest.mark.autograd), - # pytest.param("jax", marks=pytest.mark.jax), - # pytest.param("torch", marks=pytest.mark.torch), - # pytest.param("tensorflow", marks=pytest.mark.tf), + pytest.param("autograd", marks=pytest.mark.autograd), + pytest.param("jax", marks=pytest.mark.jax), + pytest.param("torch", marks=pytest.mark.torch), + pytest.param("tensorflow", marks=pytest.mark.tf), ] @@ -122,10 +121,16 @@ class CustomOp(qml.operation.Operation): def matrix(self): return mat - state = np.array([0.26977852 + 0.15165342j, 0.59635993 + 0.26296103j, 0.39598925 + 0.56799616j]) + state = np.array( + [ + [0.26159747 - 0.1906562j, -0.22592805 + 0.10450939j, 0.19576415 + 0.19025104j], + [-0.22592805 + 0.10450939j, 0.05110554 + 0.16562366j, -0.04140423 - 0.17584095j], + [0.19576415 + 0.19025104j, -0.04140423 - 0.17584095j, -0.18278332 - 0.04529327j], + ] + ) new_state = apply_operation(CustomOp(0), state) - assert qml.math.allclose(new_state, mat @ state) + assert qml.math.allclose(new_state, mat @ state @ np.conj(mat).T) @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @@ -288,10 +293,11 @@ def test_provided_tag(self, ml_framework): assert math.allclose(debugger.snapshots[tag], math.reshape(initial_state, (9, 9))) -subspace_list = [(0, 1), (0, 2), (1, 2)] +subspaces = [(0, 1), (0, 2), (1, 2)] -@pytest.mark.parametrize("method", methods, "subspace", subspace_list) # TODO +@pytest.mark.parametrize("method", methods) # TODO +@pytest.mark.parametrize("subspace", subspaces) class TestTRXCalcGrad: """Tests the application and differentiation of an TRX gate in the different interfaces.""" @@ -299,7 +305,6 @@ class TestTRXCalcGrad: def compare_expected_result(self, phi, state, new_state, g, subspace): """Compare TODO""" - matrix = qml.TRX.compute_matrix(phi, subspace=subspace) expanded_mat = np.kron(matrix, np.eye(3)) expanded_adjoint = np.conj(expanded_mat).T @@ -319,7 +324,7 @@ def compare_expected_result(self, phi, state, new_state, g, subspace): # assert math.allclose(g[1], g_expected1) @pytest.mark.autograd - def test_rx_grad_autograd(self, method, subspace): + def test_trx_grad_autograd(self, method, subspace): """Test that the application of an rx gate is differentiable with autograd.""" state = qml.numpy.array(self.state) @@ -335,7 +340,7 @@ def f(phi): @pytest.mark.jax @pytest.mark.parametrize("use_jit", (True, False)) - def test_rx_grad_jax(self, method, use_jit, subspace): + def test_trx_grad_jax(self, method, use_jit, subspace): """Test that the application of an rx gate is differentiable with jax.""" import jax @@ -355,7 +360,7 @@ def f(phi): self.compare_expected_result(phi, state, new_state, g, subspace) @pytest.mark.torch - def test_rx_grad_torch(self, method, subspace): + def test_trx_grad_torch(self, method, subspace): """Tests the application and differentiation of an rx gate with torch.""" import torch @@ -381,7 +386,7 @@ def f(phi): ) @pytest.mark.tf - def test_rx_grad_tf(self, method, subspace): + def test_trx_grad_tf(self, method, subspace): """Tests the application and differentiation of an rx gate with tensorflow""" import tensorflow as tf @@ -506,7 +511,7 @@ def test_batch_size_set_if_missing(self, method, ml_framework): class TestChannels: # pylint: disable=too-few-public-methods """Tests that Channel operations are applied correctly.""" - class TestChannel(Channel): + class CustomChannel(Channel): num_params = 1 num_wires = 1 @@ -524,7 +529,7 @@ def compute_kraus_matrices(p): def test_non_broadcasted_state(self, method, ml_framework): """Tests that Channel operations are applied correctly to a state.""" state = get_random_mixed_state(2) - test_channel = self.TestChannel(0.3, wires=1) + test_channel = self.CustomChannel(0.3, wires=1) res = method(test_channel, state) flattened_state = state.reshape(9, 9) @@ -537,13 +542,17 @@ def test_non_broadcasted_state(self, method, ml_framework): adjoint_mat = np.conj(expanded_mat).T expected += expanded_mat @ flattened_state @ adjoint_mat expected = expected.reshape([3] * 4) - assert qml.math.get_interface(res) == ml_framework + if ml_framework == "autograd": + assert qml.math.get_interface(res) == "numpy" + else: + assert qml.math.get_interface(res) == ml_framework + assert qml.math.allclose(res, expected) def test_broadcasted_state(self, method, ml_framework): """Tests that Channel operations are applied correctly to a batched state.""" state = [get_random_mixed_state(2) for _ in range(3)] - test_channel = self.TestChannel(0.3, wires=1) + test_channel = self.CustomChannel(0.3, wires=1) res = method(test_channel, state) mat = test_channel.kraus_matrices() From dc0ddb0c626555649abfe175f235a880a88b0d78 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 5 Jan 2024 13:39:11 -0800 Subject: [PATCH 33/89] Added THadamard test --- .../devices/qutrit_mixed/apply_operation.py | 2 +- .../test_qutrit_mixed_apply_operation.py | 30 ++++++++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index c92f6c36820..a32f76ac3c5 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -57,7 +57,7 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: kraus = math.cast(math.reshape(kraus, kraus_shape), complex) kraus_dagger = math.cast(math.reshape(kraus_dagger, kraus_shape), complex) - # Tensor indices of the state. For each qubit, need an index for rows *and* columns + # Tensor indices of the state. For each qutrit, need an index for rows *and* columns state_indices = alphabet[:rho_dim] # row indices of the quantum state affected by this operation diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 25e648fefad..acb7ad724de 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -102,6 +102,7 @@ def get_random_mixed_state(num_qutrits): methods = [apply_operation_einsum, apply_operation_tensordot, apply_operation] +subspaces = [(0, 1), (0, 2), (1, 2)] def test_custom_operator_with_matrix(): @@ -137,7 +138,7 @@ def matrix(self): @pytest.mark.parametrize("method", methods) @pytest.mark.parametrize("wire", (0, 1)) class TestTwoQubitStateSpecialCases: - """Test the special cases on a two qubit state. Also tests the special cases for einsum and tensor application methods + """Test the special cases on a two qutrit state. Also tests the special cases for einsum and tensor application methods for additional testing of these generic matrix application methods.""" def test_TAdd(self, method, wire, ml_framework): @@ -241,6 +242,25 @@ def check_second_roll(initial_input, new_input): new2 = math.take(new_state, 2, axis=wire) check_second_roll(w2 * initial2, new2) + @pytest.mark.parametrize("subspace", subspaces) + def test_THadamard(self, method, wire, ml_framework, subspace): + initial_state = math.asarray(density_matrix, like=ml_framework) + op = qml.THadamard(wire, subspace=subspace) + new_state = method(op, initial_state) + + flattened_state = initial_state.reshape(9,9) + sizes = [3, 3] + sizes[wire] = 1 + expanded_mat = np.kron(np.kron(np.eye(sizes[0]), op.matrix()), np.eye(sizes[1])) + adjoint_mat = np.conj(expanded_mat).T + expected = (expanded_mat @ flattened_state @ adjoint_mat).reshape([3]*4) + + assert math.allclose(expected, new_state) + + + + + # TODO: Add more tests as Special cases are added @@ -293,10 +313,7 @@ def test_provided_tag(self, ml_framework): assert math.allclose(debugger.snapshots[tag], math.reshape(initial_state, (9, 9))) -subspaces = [(0, 1), (0, 2), (1, 2)] - - -@pytest.mark.parametrize("method", methods) # TODO +@pytest.mark.parametrize("method", methods) @pytest.mark.parametrize("subspace", subspaces) class TestTRXCalcGrad: """Tests the application and differentiation of an TRX gate in the different interfaces.""" @@ -304,12 +321,11 @@ class TestTRXCalcGrad: state = density_matrix def compare_expected_result(self, phi, state, new_state, g, subspace): - """Compare TODO""" matrix = qml.TRX.compute_matrix(phi, subspace=subspace) expanded_mat = np.kron(matrix, np.eye(3)) expanded_adjoint = np.conj(expanded_mat).T flattened_state = state.reshape(9, 9) - res = expanded_mat @ flattened_state @ expanded_adjoint + res = (expanded_mat @ flattened_state @ expanded_adjoint).reshape([3]*4) assert math.allclose(new_state, res) From 9484c3517309ac57376f4b79245bf60e255ec22b Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 5 Jan 2024 15:06:08 -0800 Subject: [PATCH 34/89] fixed einsum throwing error on broadcast, still incorrect return --- .../devices/qutrit_mixed/apply_operation.py | 8 ++-- .../test_qutrit_mixed_apply_operation.py | 40 ++++++++++++++++++- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index a32f76ac3c5..2d643c40bd9 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -40,7 +40,7 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: # Shape kraus operators kraus_shape = [len(kraus)] + [qudit_dim] * num_ch_wires * 2 - if isinstance(op, Channel): + if isinstance(op, Channel): #TODO?? is_mat_batched = False # TODO Channels broadcasting is causing issues currently # TODO need to talk to PennyLane team to find out more @@ -82,10 +82,10 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: state_indices, ) - # index mapping for einsum, e.g., 'iga,abcdef,idh->gbchef' + # index mapping for einsum, e.g., '...iga,...abcdef,...idh->...gbchef' einsum_indices = ( - f"{kraus_index}{new_row_indices}{row_indices}, {state_indices}," - f"{kraus_index}{col_indices}{new_col_indices}->{new_state_indices}" + f"...{kraus_index}{new_row_indices}{row_indices},...{state_indices}," + f"...{kraus_index}{col_indices}{new_col_indices}->...{new_state_indices}" ) return math.einsum(einsum_indices, kraus, state, kraus_dagger) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index acb7ad724de..d9369a62cae 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -420,6 +420,44 @@ def test_trx_grad_tf(self, method, subspace): self.compare_expected_result(phi, state, new_state, phi_grad, subspace) +def test_broadcasted_op_simple(): # TODO========================================================= + """Tests that batched operations are applied correctly to an unbatched state. Simple TODO""" + num_qutrits = 3 + num_batched = 2 + state = get_random_mixed_state(num_qutrits) + flattened_state = state.reshape([3**num_qutrits]*2) + + method = apply_operation_einsum + ml_framework = "numpy" + op = qml.TRX(np.array([np.pi, np.pi / 2]), wires=0, subspace=(0, 1)) + mat = op.matrix() + missing_wires = num_qutrits - len(op.wires) + expanded_mats = [ + np.kron(mat[i], np.eye(3 ** missing_wires)) if missing_wires else mat[i] + for i in range(num_batched) + ] + + + res = method(op, qml.math.asarray(state, like=ml_framework)) + expected = [] + + for i in range(num_batched): + expanded_mat = expanded_mats[i] + adjoint_mat = np.conj(expanded_mat).T + expected.append((expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * (2*num_qutrits))) + + assert qml.math.get_interface(res) == ml_framework + print() + print(res) + print("----------------------------------------------------------") + print(expected) + print("----------------------------------------------------------") + print(res.shape) + print(expected[0].shape) + print(expected[1].shape) + + assert qml.math.allclose(res, expected) + @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @pytest.mark.parametrize("method", methods) class TestBroadcasting: # pylint: disable=too-few-public-methods @@ -492,7 +530,7 @@ def test_broadcasted_state(self, op, method, ml_framework): @pytest.mark.parametrize("op", broadcasted_ops) def test_broadcasted_op_broadcasted_state(self, op, method, ml_framework): """Tests that batched operations are applied correctly to a batched state.""" - state = [get_random_mixed_state(3) for _ in range(3)] + state = [get_random_mixed_state(3) for _ in range(3)] #TODO shouldn't be tested for tensordot res = method(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) missing_wires = 3 - len(op.wires) From 0aa07b202e7d805c605bf62c07e4c25d65b76431 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 9 Jan 2024 14:51:43 -0800 Subject: [PATCH 35/89] Fixed broadcasting for einsum, removed broadcasting from tensordot --- .../devices/qutrit_mixed/apply_operation.py | 102 +++++++++--------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 2d643c40bd9..e605e7f7a2a 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -27,36 +27,9 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: array[complex]: output_state """ num_ch_wires = len(op.wires) - num_wires = int(len(qml.math.shape(state)) / 2 - is_state_batched) + num_wires = int((len(qml.math.shape(state)) - is_state_batched) / 2) rho_dim = 2 * num_wires - kraus = _get_kraus(op) - # Computes K^\dagger, needed for the transformation K \rho K^\dagger - kraus_dagger = [math.conj(math.transpose(k)) for k in kraus] - - kraus = math.stack(kraus) - kraus_dagger = math.stack(kraus_dagger) - - # Shape kraus operators - kraus_shape = [len(kraus)] + [qudit_dim] * num_ch_wires * 2 - - if isinstance(op, Channel): #TODO?? - is_mat_batched = False - # TODO Channels broadcasting is causing issues currently - # TODO need to talk to PennyLane team to find out more - else: - mat = op.matrix() - dim = qudit_dim**num_ch_wires - batch_size = qml.math.get_batch_size(mat, (dim, dim), dim**2) - if batch_size is not None: - # Add broadcasting dimension to shape - kraus_shape = [batch_size] + kraus_shape - if op.batch_size is None: - op._batch_size = batch_size # pylint:disable=protected-access - - kraus = math.cast(math.reshape(kraus, kraus_shape), complex) - kraus_dagger = math.cast(math.reshape(kraus_dagger, kraus_shape), complex) - # Tensor indices of the state. For each qutrit, need an index for rows *and* columns state_indices = alphabet[:rho_dim] @@ -88,6 +61,35 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: f"...{kraus_index}{col_indices}{new_col_indices}->...{new_state_indices}" ) + kraus = _get_kraus(op) + + # Shape kraus operators + kraus_shape = [len(kraus)] + [qudit_dim] * num_ch_wires * 2 + kraus_dagger = [] + if not isinstance(op, Channel): + # TODO Channels broadcasting is causing issues currently + # TODO need to talk to PennyLane team to find out more + mat = op.matrix() + dim = qudit_dim**num_ch_wires + batch_size = math.get_batch_size(mat, (dim, dim), dim**2) + if batch_size is not None: + # Add broadcasting dimension to shape + kraus_shape = [batch_size] + kraus_shape + if op.batch_size is None: + op._batch_size = batch_size # pylint:disable=protected-access + # Computes K^\dagger, needed for the transformation K \rho K^\dagger + for op_mats in kraus: + kraus_dagger.append([math.conj(math.transpose(k)) for k in op_mats]) + if not kraus_dagger: + # Computes K^\dagger, needed for the transformation K \rho K^\dagger + kraus_dagger = [math.conj(math.transpose(k)) for k in kraus] + + kraus = math.stack(kraus) + kraus_dagger = math.stack(kraus_dagger) + + kraus = math.cast(math.reshape(kraus, kraus_shape), complex) + kraus_dagger = math.cast(math.reshape(kraus_dagger, kraus_shape), complex) + return math.einsum(einsum_indices, kraus, state, kraus_dagger) @@ -109,19 +111,19 @@ def apply_operation_tensordot(op: qml.operation.Operator, state, is_state_batche # Shape kraus operators and cast them to complex data type kraus_shape = [qudit_dim] * (num_ch_wires * 2) - if isinstance(op, Channel): - is_mat_batched = False - # TODO Channels broadcasting is causing issues currently, - # TODO need to talk to PennyLane team to find out more - else: - mat = op.matrix() - dim = qudit_dim**num_ch_wires - batch_size = qml.math.get_batch_size(mat, (dim, dim), dim**2) - if is_mat_batched := batch_size is not None: - # Add broadcasting dimension to shape - kraus_shape = [batch_size] + kraus_shape - if op.batch_size is None: - op._batch_size = batch_size # pylint:disable=protected-access + # if isinstance(op, Channel): + # is_mat_batched = False + # # TODO Channels broadcasting is causing issues currently, + # # TODO need to talk to PennyLane team to find out more + # else: + # mat = op.matrix() + # dim = qudit_dim**num_ch_wires + # batch_size = qml.math.get_batch_size(mat, (dim, dim), dim**2) + # if is_mat_batched := batch_size is not None: + # # Add broadcasting dimension to shape + # kraus_shape = [batch_size] + kraus_shape + # if op.batch_size is None: + # op._batch_size = batch_size # pylint:disable=protected-access # row indices of the quantum state affected by this operation row_wires_list = list(op.wires.toarray() + int(is_state_batched)) @@ -156,14 +158,14 @@ def _conjugate_state_with(k): source_right = list(range(-num_ch_wires, 0)) dest_right = col_wires_list - if is_mat_batched: - source_left = [0] + [i + 1 for i in source_left] - source_right = [i + 1 for i in source_right] - dest_left = [0] + [i + 1 for i in source_left] - dest_right = [i + 1 for i in source_right] - if is_state_batched: - source_right += [-1] - dest_right += [-1] + # if is_mat_batched: + # source_left = [0] + [i + 1 for i in source_left] + # source_right = [i + 1 for i in source_right] + # dest_left = [0] + [i + 1 for i in source_left] + # dest_right = [i + 1 for i in source_right] + # if is_state_batched: + # source_right += [-1] + # dest_right += [-1] print(source_left + source_right) print(dest_left + dest_right) @@ -230,7 +232,7 @@ def _apply_operation_default(op, state, is_state_batched, debugger): if ( len(op.wires) < EINSUM_OP_WIRECOUNT_PERF_THRESHOLD and math.ndim(state) < EINSUM_STATE_WIRECOUNT_PERF_THRESHOLD - ) or (op.batch_size and is_state_batched): + ) or (op.batch_size or is_state_batched): return apply_operation_einsum(op, state, is_state_batched=is_state_batched) return apply_operation_tensordot(op, state, is_state_batched=is_state_batched) From 90dc4eecccaf008fbfe78dcff32d9cc845ea47c9 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 9 Jan 2024 14:54:07 -0800 Subject: [PATCH 36/89] Tests for broadcasting fixed --- .../test_qutrit_mixed_apply_operation.py | 153 ++++++++---------- 1 file changed, 64 insertions(+), 89 deletions(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index d9369a62cae..0fff5584739 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -88,7 +88,8 @@ def get_random_mixed_state(num_qutrits): Schmidt_weights = np.random.dirichlet(np.ones(dim), size=1).astype(complex)[0] mixed_state = np.zeros((dim, dim)).astype(complex) for i in range(dim): - mixed_state += Schmidt_weights[i] * np.outer(basis[i], basis[i]) + mixed_state += Schmidt_weights[i] * np.outer(np.conj(basis[i]), basis[i]) + return mixed_state.reshape([3] * (2 * num_qutrits)) @@ -102,6 +103,7 @@ def get_random_mixed_state(num_qutrits): methods = [apply_operation_einsum, apply_operation_tensordot, apply_operation] +broadcasting_methods = [apply_operation_einsum, apply_operation] subspaces = [(0, 1), (0, 2), (1, 2)] @@ -248,19 +250,15 @@ def test_THadamard(self, method, wire, ml_framework, subspace): op = qml.THadamard(wire, subspace=subspace) new_state = method(op, initial_state) - flattened_state = initial_state.reshape(9,9) + flattened_state = initial_state.reshape(9, 9) sizes = [3, 3] sizes[wire] = 1 expanded_mat = np.kron(np.kron(np.eye(sizes[0]), op.matrix()), np.eye(sizes[1])) adjoint_mat = np.conj(expanded_mat).T - expected = (expanded_mat @ flattened_state @ adjoint_mat).reshape([3]*4) + expected = (expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * 4) assert math.allclose(expected, new_state) - - - - # TODO: Add more tests as Special cases are added @@ -325,7 +323,7 @@ def compare_expected_result(self, phi, state, new_state, g, subspace): expanded_mat = np.kron(matrix, np.eye(3)) expanded_adjoint = np.conj(expanded_mat).T flattened_state = state.reshape(9, 9) - res = (expanded_mat @ flattened_state @ expanded_adjoint).reshape([3]*4) + res = (expanded_mat @ flattened_state @ expanded_adjoint).reshape([3] * 4) assert math.allclose(new_state, res) @@ -341,7 +339,7 @@ def compare_expected_result(self, phi, state, new_state, g, subspace): @pytest.mark.autograd def test_trx_grad_autograd(self, method, subspace): - """Test that the application of an rx gate is differentiable with autograd.""" + """Test that the application of a trx gate is differentiable with autograd.""" state = qml.numpy.array(self.state) def f(phi): @@ -357,7 +355,7 @@ def f(phi): @pytest.mark.jax @pytest.mark.parametrize("use_jit", (True, False)) def test_trx_grad_jax(self, method, use_jit, subspace): - """Test that the application of an rx gate is differentiable with jax.""" + """Test that the application of a trx gate is differentiable with jax.""" import jax state = jax.numpy.array(self.state) @@ -377,7 +375,7 @@ def f(phi): @pytest.mark.torch def test_trx_grad_torch(self, method, subspace): - """Tests the application and differentiation of an rx gate with torch.""" + """Tests the application and differentiation of a trx gate with torch.""" import torch state = torch.tensor(self.state) @@ -403,7 +401,7 @@ def f(phi): @pytest.mark.tf def test_trx_grad_tf(self, method, subspace): - """Tests the application and differentiation of an rx gate with tensorflow""" + """Tests the application and differentiation of a trx gate with tensorflow""" import tensorflow as tf state = tf.Variable(self.state) @@ -420,88 +418,56 @@ def test_trx_grad_tf(self, method, subspace): self.compare_expected_result(phi, state, new_state, phi_grad, subspace) -def test_broadcasted_op_simple(): # TODO========================================================= - """Tests that batched operations are applied correctly to an unbatched state. Simple TODO""" - num_qutrits = 3 - num_batched = 2 - state = get_random_mixed_state(num_qutrits) - flattened_state = state.reshape([3**num_qutrits]*2) - - method = apply_operation_einsum - ml_framework = "numpy" - op = qml.TRX(np.array([np.pi, np.pi / 2]), wires=0, subspace=(0, 1)) - mat = op.matrix() - missing_wires = num_qutrits - len(op.wires) - expanded_mats = [ - np.kron(mat[i], np.eye(3 ** missing_wires)) if missing_wires else mat[i] - for i in range(num_batched) - ] - - - res = method(op, qml.math.asarray(state, like=ml_framework)) - expected = [] - - for i in range(num_batched): - expanded_mat = expanded_mats[i] - adjoint_mat = np.conj(expanded_mat).T - expected.append((expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * (2*num_qutrits))) - - assert qml.math.get_interface(res) == ml_framework - print() - print(res) - print("----------------------------------------------------------") - print(expected) - print("----------------------------------------------------------") - print(res.shape) - print(expected[0].shape) - print(expected[1].shape) - - assert qml.math.allclose(res, expected) - @pytest.mark.parametrize("ml_framework", ml_frameworks_list) -@pytest.mark.parametrize("method", methods) +@pytest.mark.parametrize("method", broadcasting_methods) class TestBroadcasting: # pylint: disable=too-few-public-methods """Tests that broadcasted operations (not channels) are applied correctly.""" broadcasted_ops = [ qml.TRX(np.array([np.pi, np.pi / 2, np.pi / 4]), wires=2, subspace=(0, 1)), - # qml.TRY(np.array([np.pi, np.pi / 2, np.pi / 4]), wires=2, subspace=(0,1)), - # qml.TRZ(np.array([np.pi, np.pi / 2, np.pi / 4]), wires=2, subspace=(1,2)), - # qml.QutritUnitary( - # np.array([unitary_group.rvs(27), unitary_group.rvs(27), unitary_group.rvs(27)]), - # wires=[0, 1, 2], - # ), + qml.TRY(np.array([np.pi, np.pi / 2, np.pi / 4]), wires=2, subspace=(0, 1)), + qml.TRZ(np.array([np.pi, np.pi / 2, np.pi / 4]), wires=2, subspace=(1, 2)), + qml.QutritUnitary( + np.array([unitary_group.rvs(27), unitary_group.rvs(27), unitary_group.rvs(27)]), + wires=[0, 1, 2], + ), ] unbroadcasted_ops = [ qml.THadamard(wires=2), - # qml.TClock(wires=2), - # qml.TShift(wires=2), - # qml.TAdd(wires=[1, 2]), - # qml.TRX(np.pi / 3, wires=2, subspace=(0, 2)), - # qml.TRY(np.array([np.pi, np.pi / 2, np.pi / 4]), wires=2, subspace=(1, 2)), - # qml.TRZ(np.array([np.pi, np.pi / 2, np.pi / 4]), wires=2, subspace=(0, 1)), - # qml.QutritUnitary(unitary_group.rvs(27), wires=[0, 1, 2]), + qml.TClock(wires=2), + qml.TShift(wires=2), + qml.TAdd(wires=[1, 2]), + qml.TRX(np.pi / 3, wires=2, subspace=(0, 2)), + qml.TRY(2 * np.pi / 3, wires=2, subspace=(1, 2)), + qml.TRZ(np.pi / 6, wires=2, subspace=(0, 1)), + qml.QutritUnitary(unitary_group.rvs(27), wires=[0, 1, 2]), ] + num_qutrits = 3 + num_batched = 3 + dim = (3**num_qutrits, 3**num_qutrits) @pytest.mark.parametrize("op", broadcasted_ops) def test_broadcasted_op(self, op, method, ml_framework): """Tests that batched operations are applied correctly to an unbatched state.""" - state = get_random_mixed_state(3) - flattened_state = state.reshape(27, 27) + + state = get_random_mixed_state(self.num_qutrits) + flattened_state = state.reshape(self.dim) res = method(op, qml.math.asarray(state, like=ml_framework)) missing_wires = 3 - len(op.wires) mat = op.matrix() expanded_mats = [ np.kron(np.eye(3**missing_wires), mat[i]) if missing_wires else mat[i] - for i in range(3) + for i in range(self.num_batched) ] expected = [] - for i in range(3): + for i in range(self.num_batched): expanded_mat = expanded_mats[i] adjoint_mat = np.conj(expanded_mat).T - expected.append((expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * 6)) + expected.append( + (expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * (self.num_qutrits * 2)) + ) assert qml.math.get_interface(res) == ml_framework assert qml.math.allclose(res, expected) @@ -509,20 +475,20 @@ def test_broadcasted_op(self, op, method, ml_framework): @pytest.mark.parametrize("op", unbroadcasted_ops) def test_broadcasted_state(self, op, method, ml_framework): """Tests that unbatched operations are applied correctly to a batched state.""" - if method is apply_operation_tensordot: - pytest.skip("Tensordot doesn't support batched operator and batched state.") - state = [get_random_mixed_state(3) for _ in range(3)] + state = [get_random_mixed_state(self.num_qutrits) for _ in range(self.num_batched)] res = method(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) - missing_wires = 3 - len(op.wires) + missing_wires = self.num_qutrits - len(op.wires) mat = op.matrix() expanded_mat = np.kron(np.eye(3**missing_wires), mat) if missing_wires else mat adjoint_mat = np.conj(expanded_mat).T expected = [] - for i in range(3): - flattened_state = state[i].reshape(27, 27) - expected.append((expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * 6)) + for i in range(self.num_batched): + flattened_state = state[i].reshape(self.dim) + expected.append( + (expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * (self.num_qutrits * 2)) + ) assert qml.math.get_interface(res) == ml_framework assert qml.math.allclose(res, expected) @@ -530,22 +496,24 @@ def test_broadcasted_state(self, op, method, ml_framework): @pytest.mark.parametrize("op", broadcasted_ops) def test_broadcasted_op_broadcasted_state(self, op, method, ml_framework): """Tests that batched operations are applied correctly to a batched state.""" - state = [get_random_mixed_state(3) for _ in range(3)] #TODO shouldn't be tested for tensordot + state = [get_random_mixed_state(self.num_qutrits) for _ in range(self.num_batched)] res = method(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) - missing_wires = 3 - len(op.wires) + missing_wires = self.num_qutrits - len(op.wires) mat = op.matrix() expanded_mats = [ np.kron(np.eye(3**missing_wires), mat[i]) if missing_wires else mat[i] - for i in range(3) + for i in range(self.num_batched) ] expected = [] - for i in range(3): + for i in range(self.num_batched): expanded_mat = expanded_mats[i] adjoint_mat = np.conj(expanded_mat).T - flattened_state = state[i].reshape(27, 27) - expected.append((expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * 6)) + flattened_state = state[i].reshape(self.dim) + expected.append( + (expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * (self.num_qutrits * 2)) + ) assert qml.math.get_interface(res) == ml_framework assert qml.math.allclose(res, expected) @@ -556,8 +524,15 @@ def test_batch_size_set_if_missing(self, method, ml_framework): op = qml.TRX(param, 0) op._batch_size = None # pylint:disable=protected-access state = method(op, state) - assert state.shape == (3, 2, 2) - assert op.batch_size == 3 + assert state.shape == (3, 3, 3) + assert op.batch_size == self.num_batched + + +def check_ml_framework(res, ml_framework): + if ml_framework == "autograd": + assert qml.math.get_interface(res) == "numpy" + else: + assert qml.math.get_interface(res) == ml_framework @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @@ -596,15 +571,14 @@ def test_non_broadcasted_state(self, method, ml_framework): adjoint_mat = np.conj(expanded_mat).T expected += expanded_mat @ flattened_state @ adjoint_mat expected = expected.reshape([3] * 4) - if ml_framework == "autograd": - assert qml.math.get_interface(res) == "numpy" - else: - assert qml.math.get_interface(res) == ml_framework + check_ml_framework(res, ml_framework) assert qml.math.allclose(res, expected) def test_broadcasted_state(self, method, ml_framework): """Tests that Channel operations are applied correctly to a batched state.""" + if method is apply_operation_tensordot: + pytest.skip("Tensordot doesn't support batched operations.") state = [get_random_mixed_state(2) for _ in range(3)] test_channel = self.CustomChannel(0.3, wires=1) res = method(test_channel, state) @@ -619,5 +593,6 @@ def test_broadcasted_state(self, method, ml_framework): adjoint_mat = np.conj(expanded_mat).T expected[i] += expanded_mat @ flattened_state @ adjoint_mat expected[i] = expected[i].reshape([3] * 4) - assert qml.math.get_interface(res) == ml_framework + + check_ml_framework(res, ml_framework) assert qml.math.allclose(res, expected) From c71996a420988aaf166df67727d5705b81d7359c Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 9 Jan 2024 16:14:02 -0800 Subject: [PATCH 37/89] Removed comments and unecessary tests --- .../devices/qutrit_mixed/apply_operation.py | 41 ++----- .../test_qutrit_mixed_apply_operation.py | 107 ------------------ 2 files changed, 9 insertions(+), 139 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index e605e7f7a2a..8e3de6b5ace 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -9,7 +9,7 @@ alphabet_array = np.array(list(alphabet)) EINSUM_OP_WIRECOUNT_PERF_THRESHOLD = 3 -EINSUM_STATE_WIRECOUNT_PERF_THRESHOLD = 13 +EINSUM_STATE_WIRECOUNT_PERF_THRESHOLD = 13 # TODO placeholder value, need to ask how to find these qudit_dim = 3 # specifies qudit dimension @@ -67,8 +67,7 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: kraus_shape = [len(kraus)] + [qudit_dim] * num_ch_wires * 2 kraus_dagger = [] if not isinstance(op, Channel): - # TODO Channels broadcasting is causing issues currently - # TODO need to talk to PennyLane team to find out more + # TODO Channels broadcasting doesn't seem to be implemented for qubits, should they be for qutrit? mat = op.matrix() dim = qudit_dim**num_ch_wires batch_size = math.get_batch_size(mat, (dim, dim), dim**2) @@ -93,40 +92,25 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: return math.einsum(einsum_indices, kraus, state, kraus_dagger) -def apply_operation_tensordot(op: qml.operation.Operator, state, is_state_batched: bool = False): +def apply_operation_tensordot(op: qml.operation.Operator, state): r"""Apply a quantum channel specified by a list of Kraus operators to subsystems of the quantum state. For a unitary gate, there is a single Kraus operator. Args: op (Operator): Operator to apply to the quantum state state (array[complex]): Input quantum state - is_state_batched (bool): Boolean representing whether the state is batched or not Returns: array[complex]: output_state """ num_ch_wires = len(op.wires) - num_wires = int(len(qml.math.shape(state)) / 2 - is_state_batched) + num_wires = int(len(qml.math.shape(state)) / 2) # Shape kraus operators and cast them to complex data type kraus_shape = [qudit_dim] * (num_ch_wires * 2) - # if isinstance(op, Channel): - # is_mat_batched = False - # # TODO Channels broadcasting is causing issues currently, - # # TODO need to talk to PennyLane team to find out more - # else: - # mat = op.matrix() - # dim = qudit_dim**num_ch_wires - # batch_size = qml.math.get_batch_size(mat, (dim, dim), dim**2) - # if is_mat_batched := batch_size is not None: - # # Add broadcasting dimension to shape - # kraus_shape = [batch_size] + kraus_shape - # if op.batch_size is None: - # op._batch_size = batch_size # pylint:disable=protected-access - # row indices of the quantum state affected by this operation - row_wires_list = list(op.wires.toarray() + int(is_state_batched)) + row_wires_list = list(op.wires.toarray()) # column indices are shifted by the number of wires col_wires_list = [w + num_wires for w in row_wires_list] @@ -158,15 +142,6 @@ def _conjugate_state_with(k): source_right = list(range(-num_ch_wires, 0)) dest_right = col_wires_list - # if is_mat_batched: - # source_left = [0] + [i + 1 for i in source_left] - # source_right = [i + 1 for i in source_right] - # dest_left = [0] + [i + 1 for i in source_left] - # dest_right = [i + 1 for i in source_right] - # if is_state_batched: - # source_right += [-1] - # dest_right += [-1] - print(source_left + source_right) print(dest_left + dest_right) return math.moveaxis(_state, source_left + source_right, dest_left + dest_right) @@ -197,7 +172,8 @@ def apply_operation( This function assumes that the wires of the operator correspond to indices of the state. See :func:`~.map_wires` to convert operations to integer wire labels. - The shape of state should be ``[qudit_dim]*(num_wires * 2)``. + The shape of state should be ``[qudit_dim]*(num_wires * 2)``, where ``qudit_dim`` is + the dimension of the system. This is a ``functools.singledispatch`` function, so additional specialized kernels for specific operations can be registered like: @@ -234,6 +210,7 @@ def _apply_operation_default(op, state, is_state_batched, debugger): and math.ndim(state) < EINSUM_STATE_WIRECOUNT_PERF_THRESHOLD ) or (op.batch_size or is_state_batched): return apply_operation_einsum(op, state, is_state_batched=is_state_batched) + # TODO fix state batching on tensordot return apply_operation_tensordot(op, state, is_state_batched=is_state_batched) @@ -252,7 +229,7 @@ def apply_snapshot(op: qml.Snapshot, state, is_state_batched: bool = False, debu return state -# TODO add TAdd speedup +# TODO add special case speedups def _get_kraus(operation): # pylint: disable=no-self-use diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 0fff5584739..de7080eb5f5 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -311,113 +311,6 @@ def test_provided_tag(self, ml_framework): assert math.allclose(debugger.snapshots[tag], math.reshape(initial_state, (9, 9))) -@pytest.mark.parametrize("method", methods) -@pytest.mark.parametrize("subspace", subspaces) -class TestTRXCalcGrad: - """Tests the application and differentiation of an TRX gate in the different interfaces.""" - - state = density_matrix - - def compare_expected_result(self, phi, state, new_state, g, subspace): - matrix = qml.TRX.compute_matrix(phi, subspace=subspace) - expanded_mat = np.kron(matrix, np.eye(3)) - expanded_adjoint = np.conj(expanded_mat).T - flattened_state = state.reshape(9, 9) - res = (expanded_mat @ flattened_state @ expanded_adjoint).reshape([3] * 4) - - assert math.allclose(new_state, res) - - # g_expected0 = ( - # -0.5 * np.sin(phi / 2) * state[0, :, :] - 0.5j * np.cos(phi / 2) * state[1, :, :] - # ) - # g_expected1 = ( - # -0.5j * np.cos(phi / 2) * state[0, :, :] - 0.5 * np.sin(phi / 2) * state[1, :, :] - # ) - # - # assert math.allclose(g[0], g_expected0) - # assert math.allclose(g[1], g_expected1) - - @pytest.mark.autograd - def test_trx_grad_autograd(self, method, subspace): - """Test that the application of a trx gate is differentiable with autograd.""" - state = qml.numpy.array(self.state) - - def f(phi): - op = qml.TRX(phi, wires=0, subspace=subspace) - return method(op, state) - - phi = qml.numpy.array(0.325 + 0j, requires_grad=True) - - new_state = f(phi) - g = qml.jacobian(lambda x: math.real(f(x)))(phi) - self.compare_expected_result(phi, state, new_state, g, subspace) - - @pytest.mark.jax - @pytest.mark.parametrize("use_jit", (True, False)) - def test_trx_grad_jax(self, method, use_jit, subspace): - """Test that the application of a trx gate is differentiable with jax.""" - import jax - - state = jax.numpy.array(self.state) - - def f(phi): - op = qml.TRX(phi, wires=0, subspace=subspace) - return method(op, state) - - if use_jit: - f = jax.jit(f) - - phi = 0.325 - - new_state = f(phi) - g = jax.jacobian(f, holomorphic=True)(phi + 0j) - self.compare_expected_result(phi, state, new_state, g, subspace) - - @pytest.mark.torch - def test_trx_grad_torch(self, method, subspace): - """Tests the application and differentiation of a trx gate with torch.""" - import torch - - state = torch.tensor(self.state) - - def f(phi): - op = qml.TRX(phi, wires=0, subspace=subspace) - return method(op, state) - - phi = torch.tensor(0.325, requires_grad=True) - - new_state = f(phi) - # forward-mode needed with complex results. - # See bug: https://github.com/pytorch/pytorch/issues/94397 - g = torch.autograd.functional.jacobian(f, phi + 0j, strategy="forward-mode", vectorize=True) - - self.compare_expected_result( - phi.detach().numpy(), - state.detach().numpy(), - new_state.detach().numpy(), - g.detach().numpy(), - subspace, - ) - - @pytest.mark.tf - def test_trx_grad_tf(self, method, subspace): - """Tests the application and differentiation of a trx gate with tensorflow""" - import tensorflow as tf - - state = tf.Variable(self.state) - phi = tf.Variable(0.8589 + 0j) - - with tf.GradientTape() as grad_tape: - op = qml.TRX(phi, wires=0, subspace=subspace) - new_state = method(op, state) - - grads = grad_tape.jacobian(new_state, [phi]) - # tf takes gradient with respect to conj(z), so we need to conj the gradient - phi_grad = tf.math.conj(grads[0]) - - self.compare_expected_result(phi, state, new_state, phi_grad, subspace) - - @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @pytest.mark.parametrize("method", broadcasting_methods) class TestBroadcasting: # pylint: disable=too-few-public-methods From 48e75ea2e0413932b8e78e7067cb3cdf9e2b2223 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 9 Jan 2024 17:52:11 -0800 Subject: [PATCH 38/89] Fixed ml_framework for Channel tests --- .../qutrit_mixed/test_qutrit_mixed_apply_operation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index de7080eb5f5..38aaab07a25 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -452,7 +452,7 @@ def test_non_broadcasted_state(self, method, ml_framework): """Tests that Channel operations are applied correctly to a state.""" state = get_random_mixed_state(2) test_channel = self.CustomChannel(0.3, wires=1) - res = method(test_channel, state) + res = method(test_channel, math.asarray(state, like=ml_framework)) flattened_state = state.reshape(9, 9) mat = test_channel.kraus_matrices() @@ -465,7 +465,7 @@ def test_non_broadcasted_state(self, method, ml_framework): expected += expanded_mat @ flattened_state @ adjoint_mat expected = expected.reshape([3] * 4) - check_ml_framework(res, ml_framework) + assert qml.math.get_interface(res) == ml_framework assert qml.math.allclose(res, expected) def test_broadcasted_state(self, method, ml_framework): @@ -474,7 +474,7 @@ def test_broadcasted_state(self, method, ml_framework): pytest.skip("Tensordot doesn't support batched operations.") state = [get_random_mixed_state(2) for _ in range(3)] test_channel = self.CustomChannel(0.3, wires=1) - res = method(test_channel, state) + res = method(test_channel, math.asarray(state, like=ml_framework)) mat = test_channel.kraus_matrices() expanded_mats = [np.kron(np.eye(3), mat[i]) for i in range(len(mat))] @@ -487,5 +487,5 @@ def test_broadcasted_state(self, method, ml_framework): expected[i] += expanded_mat @ flattened_state @ adjoint_mat expected[i] = expected[i].reshape([3] * 4) - check_ml_framework(res, ml_framework) + assert qml.math.get_interface(res) == ml_framework assert qml.math.allclose(res, expected) From 1ee2067519b1ee0a923b42e95f95b04063849450 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 9 Jan 2024 18:12:32 -0800 Subject: [PATCH 39/89] Fixed codefactor, updated apply_snapshot --- .../devices/qutrit_mixed/apply_operation.py | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 8e3de6b5ace..f924d3e1cc3 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -1,8 +1,23 @@ +# Copyright 2018-2024 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. +"""Functions to apply operations to a qutrit mixed state.""" + import functools from functools import singledispatch +from string import ascii_letters as alphabet import pennylane as qml from pennylane import math -from string import ascii_letters as alphabet from pennylane import numpy as np from pennylane.operation import Channel @@ -219,13 +234,24 @@ def _apply_operation_default(op, state, is_state_batched, debugger): @apply_operation.register def apply_snapshot(op: qml.Snapshot, state, is_state_batched: bool = False, debugger=None): + """Take a snapshot of the mixed state""" if debugger and debugger.active: - dim = int(np.sqrt(state.size)) - density_matrix = math.reshape(state, (dim, dim)) + measurement = op.hyperparameters["measurement"] + if measurement: + snapshot = qml.devices.qubit.measure(measurement, state) + else: + if is_state_batched: + dim = int(np.sqrt(math.size(state[0]))) + flat_shape = [math.shape(state)[0], dim, dim] + else: + dim = int(np.sqrt(math.size(state))) + flat_shape = [dim, dim] + + snapshot = math.reshape(state, flat_shape) if op.tag: - debugger.snapshots[op.tag] = density_matrix + debugger.snapshots[op.tag] = snapshot else: - debugger.snapshots[len(debugger.snapshots)] = density_matrix + debugger.snapshots[len(debugger.snapshots)] = snapshot return state From 901bd3ec3dca870688d3ee1983caf077db0d3312 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 9 Jan 2024 18:14:09 -0800 Subject: [PATCH 40/89] Fix interface issure in tests --- .../test_qutrit_mixed_apply_operation.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 38aaab07a25..2df437281dd 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -1,4 +1,4 @@ -# Copyright 2018-2023 Xanadu Quantum Technologies Inc. +# Copyright 2018-2024 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. @@ -166,16 +166,16 @@ def check_TAdd_second_roll(initial_input, new_input): new_input2 = math.take(new_input, 2, axis=control + 1) assert math.allclose(initial_input2_rolled, new_input2) - initial0 = math.take(initial_state, 0, axis=control) + initial0 = math.take(density_matrix, 0, axis=control) new0 = math.take(new_state, 0, axis=control) check_TAdd_second_roll(initial0, new0) - initial1 = math.take(initial_state, 1, axis=control) + initial1 = math.take(density_matrix, 1, axis=control) initial1_rolled = np.roll(initial1, 1, 0) new1 = math.take(new_state, 1, axis=control) check_TAdd_second_roll(initial1_rolled, new1) - initial2 = math.take(initial_state, 2, axis=control) + initial2 = math.take(density_matrix, 2, axis=control) initial2_rolled = math.roll(initial2, -1, 0) new2 = math.take(new_state, 2, axis=control) check_TAdd_second_roll(initial2_rolled, new2) @@ -198,15 +198,15 @@ def check_second_roll(initial_input, new_input): new_input0 = math.take(new_input, 0, axis=wire + 1) assert math.allclose(initial_input2, new_input0) - initial0 = math.take(initial_state, 0, axis=wire) + initial0 = math.take(density_matrix, 0, axis=wire) new1 = math.take(new_state, 1, axis=wire) check_second_roll(initial0, new1) - initial1 = math.take(initial_state, 1, axis=wire) + initial1 = math.take(density_matrix, 1, axis=wire) new2 = math.take(new_state, 2, axis=wire) check_second_roll(initial1, new2) - initial2 = math.take(initial_state, 2, axis=wire) + initial2 = math.take(density_matrix, 2, axis=wire) new0 = math.take(new_state, 0, axis=wire) check_second_roll(initial2, new0) @@ -232,15 +232,15 @@ def check_second_roll(initial_input, new_input): new_input2 = math.take(new_input, 2, axis=wire + 1) assert math.allclose(initial_input2 / w2, new_input2) - initial0 = math.take(initial_state, 0, axis=wire) + initial0 = math.take(density_matrix, 0, axis=wire) new0 = math.take(new_state, 0, axis=wire) check_second_roll(initial0, new0) - initial1 = math.take(initial_state, 1, axis=wire) + initial1 = math.take(density_matrix, 1, axis=wire) new1 = math.take(new_state, 1, axis=wire) check_second_roll(w * initial1, new1) - initial2 = math.take(initial_state, 2, axis=wire) + initial2 = math.take(density_matrix, 2, axis=wire) new2 = math.take(new_state, 2, axis=wire) check_second_roll(w2 * initial2, new2) @@ -250,7 +250,7 @@ def test_THadamard(self, method, wire, ml_framework, subspace): op = qml.THadamard(wire, subspace=subspace) new_state = method(op, initial_state) - flattened_state = initial_state.reshape(9, 9) + flattened_state = density_matrix.reshape(9, 9) sizes = [3, 3] sizes[wire] = 1 expanded_mat = np.kron(np.kron(np.eye(sizes[0]), op.matrix()), np.eye(sizes[1])) From c8ee90c01c865d04fc4193fcdaa1deb552353aa4 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 9 Jan 2024 18:22:08 -0800 Subject: [PATCH 41/89] Fixed pylint issues --- pennylane/devices/qutrit_mixed/apply_operation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index f924d3e1cc3..9aef1b2e18e 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Functions to apply operations to a qutrit mixed state.""" +# pylint: disable=unused-argument import functools from functools import singledispatch @@ -226,7 +227,7 @@ def _apply_operation_default(op, state, is_state_batched, debugger): ) or (op.batch_size or is_state_batched): return apply_operation_einsum(op, state, is_state_batched=is_state_batched) # TODO fix state batching on tensordot - return apply_operation_tensordot(op, state, is_state_batched=is_state_batched) + return apply_operation_tensordot(op, state) # TODO add diagonal for speed up. From 91698950b1278cfd2135bc1ce53eb39282be29af Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 9 Jan 2024 18:25:28 -0800 Subject: [PATCH 42/89] Removed unused function --- .../test_qutrit_mixed_apply_operation.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 2df437281dd..2dee2a9433a 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -421,13 +421,6 @@ def test_batch_size_set_if_missing(self, method, ml_framework): assert op.batch_size == self.num_batched -def check_ml_framework(res, ml_framework): - if ml_framework == "autograd": - assert qml.math.get_interface(res) == "numpy" - else: - assert qml.math.get_interface(res) == ml_framework - - @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @pytest.mark.parametrize("method", methods) class TestChannels: # pylint: disable=too-few-public-methods @@ -468,13 +461,14 @@ def test_non_broadcasted_state(self, method, ml_framework): assert qml.math.get_interface(res) == ml_framework assert qml.math.allclose(res, expected) - def test_broadcasted_state(self, method, ml_framework): + @pytest.mark.parametrize("broadcasting_method", broadcasting_methods) + def test_broadcasted_state(self, broadcasting_method, ml_framework): """Tests that Channel operations are applied correctly to a batched state.""" - if method is apply_operation_tensordot: + if broadcasting_method is apply_operation_tensordot: pytest.skip("Tensordot doesn't support batched operations.") state = [get_random_mixed_state(2) for _ in range(3)] test_channel = self.CustomChannel(0.3, wires=1) - res = method(test_channel, math.asarray(state, like=ml_framework)) + res = broadcasting_method(test_channel, math.asarray(state, like=ml_framework)) mat = test_channel.kraus_matrices() expanded_mats = [np.kron(np.eye(3), mat[i]) for i in range(len(mat))] From 05ee2978197c59dc7358f43f9829cbf1aa432b05 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 9 Jan 2024 18:27:31 -0800 Subject: [PATCH 43/89] Removed accadental addition --- .../qutrit_mixed/test_qutrit_mixed_apply_operation.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 2dee2a9433a..e3e14a785fa 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -461,14 +461,13 @@ def test_non_broadcasted_state(self, method, ml_framework): assert qml.math.get_interface(res) == ml_framework assert qml.math.allclose(res, expected) - @pytest.mark.parametrize("broadcasting_method", broadcasting_methods) - def test_broadcasted_state(self, broadcasting_method, ml_framework): + def test_broadcasted_state(self, method, ml_framework): """Tests that Channel operations are applied correctly to a batched state.""" - if broadcasting_method is apply_operation_tensordot: + if method is apply_operation_tensordot: pytest.skip("Tensordot doesn't support batched operations.") state = [get_random_mixed_state(2) for _ in range(3)] test_channel = self.CustomChannel(0.3, wires=1) - res = broadcasting_method(test_channel, math.asarray(state, like=ml_framework)) + res = method(test_channel, math.asarray(state, like=ml_framework)) mat = test_channel.kraus_matrices() expanded_mats = [np.kron(np.eye(3), mat[i]) for i in range(len(mat))] From 2a5cd34481f25dbcb91bd4cd970f674d553b12e2 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 9 Jan 2024 18:38:15 -0800 Subject: [PATCH 44/89] Fixed import order for pylint --- .../devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index e3e14a785fa..ae2032c3551 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -14,11 +14,11 @@ """Unit tests for create_initial_state in devices/qutrit_mixed/apply_operation.""" import pytest -import pennylane as qml import numpy as np +from scipy.stats import unitary_group +import pennylane as qml from pennylane import math from pennylane.operation import Channel -from scipy.stats import unitary_group from pennylane.devices.qutrit_mixed.apply_operation import ( apply_operation_einsum, apply_operation_tensordot, From 77af2f9f7e886a0171c5d1421b22ae10b50a3010 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 9 Jan 2024 19:18:56 -0800 Subject: [PATCH 45/89] Fixed conjugate for torch --- pennylane/devices/qutrit_mixed/apply_operation.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 9aef1b2e18e..ea2cbb4540f 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -81,7 +81,8 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: # Shape kraus operators kraus_shape = [len(kraus)] + [qudit_dim] * num_ch_wires * 2 - kraus_dagger = [] + # Compute K^T, will be list of lists if broadcasting + kraus_transpose = [] if not isinstance(op, Channel): # TODO Channels broadcasting doesn't seem to be implemented for qubits, should they be for qutrit? mat = op.matrix() @@ -92,15 +93,15 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: kraus_shape = [batch_size] + kraus_shape if op.batch_size is None: op._batch_size = batch_size # pylint:disable=protected-access - # Computes K^\dagger, needed for the transformation K \rho K^\dagger for op_mats in kraus: - kraus_dagger.append([math.conj(math.transpose(k)) for k in op_mats]) - if not kraus_dagger: - # Computes K^\dagger, needed for the transformation K \rho K^\dagger - kraus_dagger = [math.conj(math.transpose(k)) for k in kraus] + kraus_transpose.append([math.transpose(k) for k in op_mats]) + if not kraus_transpose: + kraus_transpose = [math.transpose(k) for k in kraus] kraus = math.stack(kraus) - kraus_dagger = math.stack(kraus_dagger) + kraus_transpose = math.stack(kraus_transpose) + # Torch throws error if math.conj is used before stack + kraus_dagger = math.conj(kraus_transpose) kraus = math.cast(math.reshape(kraus, kraus_shape), complex) kraus_dagger = math.cast(math.reshape(kraus_dagger, kraus_shape), complex) From f0780eeacf7a88fe797bac8a9f42a27ff4f54bb4 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 9 Jan 2024 19:33:58 -0800 Subject: [PATCH 46/89] updated changelog to include qutrit_mixed apply_operation --- doc/releases/changelog-dev.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index bbcfe201808..defb11807ab 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -13,6 +13,8 @@ * The transform `split_non_commuting` now accepts measurements of type `probs`, `sample` and `counts` which accept both wires and observables. [(#4972)](https://github.com/PennyLaneAI/pennylane/pull/4972) +* A function called `apply_operation` has been added to the new `qutrit_mixed` module found in `qml.devices` that applies operations to device-compatible states. + [(#5032)](https://github.com/PennyLaneAI/pennylane/pull/5032)

Breaking changes 💔

@@ -30,5 +32,6 @@ This release contains contributions from (in alphabetical order): Abhishek Abhishek, +Gabriel Bottrill Isaac De Vlugt, Matthew Silverman. From 17d06a5601a239926e74cd7bff10399222df1dc2 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 9 Jan 2024 20:39:00 -0800 Subject: [PATCH 47/89] Increased code coverage --- .../test_qutrit_mixed_apply_operation.py | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index ae2032c3551..fd2ec4028f5 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -262,7 +262,14 @@ def test_THadamard(self, method, wire, ml_framework, subspace): # TODO: Add more tests as Special cases are added +states_and_shapes = [ + (density_matrix, (9, 9)), + ([get_random_mixed_state(2), get_random_mixed_state(2)], (2, 9, 9)), +] + + @pytest.mark.parametrize("ml_framework", ml_frameworks_list) +@pytest.mark.parametrize("state,shape", states_and_shapes) class TestSnapshot: """Test that apply_operation works for Snapshot ops""" @@ -273,42 +280,46 @@ def __init__(self): self.active = True self.snapshots = {} - def test_no_debugger(self, ml_framework): + def test_no_debugger(self, ml_framework, state, shape): # pylint: disable=unused-argument """Test nothing happens when there is no debugger""" - initial_state = math.asarray(density_matrix, like=ml_framework) - new_state = apply_operation(qml.Snapshot(), initial_state) + initial_state = math.asarray(state, like=ml_framework) + new_state = apply_operation(qml.Snapshot(), initial_state, is_state_batched=len(shape) != 2) assert new_state.shape == initial_state.shape assert math.allclose(new_state, initial_state) - def test_empty_tag(self, ml_framework): + def test_empty_tag(self, ml_framework, state, shape): """Test a snapshot is recorded properly when there is no tag""" - initial_state = math.asarray(density_matrix, like=ml_framework) + initial_state = math.asarray(state, like=ml_framework) debugger = self.Debugger() - new_state = apply_operation(qml.Snapshot(), initial_state, debugger=debugger) + new_state = apply_operation( + qml.Snapshot(), initial_state, debugger=debugger, is_state_batched=len(shape) != 2 + ) assert new_state.shape == initial_state.shape assert math.allclose(new_state, initial_state) assert list(debugger.snapshots.keys()) == [0] - assert debugger.snapshots[0].shape == (9, 9) - assert math.allclose(debugger.snapshots[0], math.reshape(initial_state, (9, 9))) + assert debugger.snapshots[0].shape == shape + assert math.allclose(debugger.snapshots[0], math.reshape(initial_state, shape)) - def test_provided_tag(self, ml_framework): + def test_provided_tag(self, ml_framework, state, shape): """Test a snapshot is recorded property when provided a tag""" - initial_state = math.asarray(density_matrix, like=ml_framework) + initial_state = math.asarray(state, like=ml_framework) debugger = self.Debugger() tag = "dense" - new_state = apply_operation(qml.Snapshot(tag), initial_state, debugger=debugger) + new_state = apply_operation( + qml.Snapshot(tag), initial_state, debugger=debugger, is_state_batched=len(shape) != 2 + ) assert new_state.shape == initial_state.shape assert math.allclose(new_state, initial_state) assert list(debugger.snapshots.keys()) == [tag] - assert debugger.snapshots[tag].shape == (9, 9) - assert math.allclose(debugger.snapshots[tag], math.reshape(initial_state, (9, 9))) + assert debugger.snapshots[tag].shape == shape + assert math.allclose(debugger.snapshots[tag], math.reshape(initial_state, shape)) @pytest.mark.parametrize("ml_framework", ml_frameworks_list) From 8f3a4cb553ccae9ee906433eb2b9804dcb97e152 Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Wed, 10 Jan 2024 14:05:22 -0800 Subject: [PATCH 48/89] Update doc/releases/changelog-dev.md Co-authored-by: Thomas R. Bromley <49409390+trbromley@users.noreply.github.com> --- doc/releases/changelog-dev.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index defb11807ab..f946f309c5c 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -13,6 +13,7 @@ * The transform `split_non_commuting` now accepts measurements of type `probs`, `sample` and `counts` which accept both wires and observables. [(#4972)](https://github.com/PennyLaneAI/pennylane/pull/4972) + * A function called `apply_operation` has been added to the new `qutrit_mixed` module found in `qml.devices` that applies operations to device-compatible states. [(#5032)](https://github.com/PennyLaneAI/pennylane/pull/5032) From 707a4aa43f1a284183ff68a518d292e60ccf6a64 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Thu, 11 Jan 2024 14:14:50 -0800 Subject: [PATCH 49/89] Removed random value generation in tests --- .../test_qutrit_mixed_apply_operation.py | 170 +++++++----------- .../test_ref_files/1_qutrit_state.npy | Bin 0 -> 272 bytes .../test_ref_files/2_qutrits_states.npy | Bin 0 -> 1424 bytes .../test_ref_files/3_qutrits_states.npy | Bin 0 -> 11792 bytes 4 files changed, 61 insertions(+), 109 deletions(-) create mode 100644 tests/devices/qutrit_mixed/test_ref_files/1_qutrit_state.npy create mode 100644 tests/devices/qutrit_mixed/test_ref_files/2_qutrits_states.npy create mode 100644 tests/devices/qutrit_mixed/test_ref_files/3_qutrits_states.npy diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index fd2ec4028f5..50c33008358 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -13,6 +13,7 @@ # limitations under the License. """Unit tests for create_initial_state in devices/qutrit_mixed/apply_operation.""" +import os import pytest import numpy as np from scipy.stats import unitary_group @@ -25,72 +26,6 @@ apply_operation, ) -density_matrix = np.array( - [ - [ - [ - [0.09208281 + 0.0j, 0.03160535 + 0.0664417j, 0.03416066 + 0.0206764j], - [-0.01227047 + 0.06373319j, 0.01815704 - 0.03131422j, -0.03700634 + 0.01956628j], - [0.03128229 + 0.02937806j, 0.03555759 - 0.01100168j, -0.01982137 - 0.04605857j], - ], - [ - [0.03160535 - 0.0664417j, 0.10864802 + 0.0j, 0.01877872 + 0.01624366j], - [0.10580479 + 0.04077997j, -0.06409966 + 0.01593581j, 0.00204818 + 0.03765312j], - [-0.00255557 - 0.06777649j, -0.04497981 - 0.02030987j, -0.05656505 + 0.06693942j], - ], - [ - [0.03416066 - 0.0206764j, 0.01877872 - 0.01624366j, 0.08975985 + 0.0j], - [0.03077889 - 0.02115111j, 0.04876844 + 0.04583181j, -0.04781717 + 0.04220666j], - [-0.03402139 + 0.04582056j, 0.03198441 + 0.06076387j, 0.02845793 - 0.00996117j], - ], - ], - [ - [ - [-0.01227047 - 0.06373319j, 0.10580479 - 0.04077997j, 0.03077889 + 0.02115111j], - [0.14327529 + 0.0j, -0.07288875 + 0.07020128j, -0.000707 + 0.04396371j], - [-0.05027514 - 0.08649698j, -0.07202654 + 0.01724066j, -0.03980968 + 0.11268854j], - ], - [ - [0.01815704 + 0.03131422j, -0.06409966 - 0.01593581j, 0.04876844 - 0.04583181j], - [-0.07288875 - 0.07020128j, 0.12370217 + 0.0j, -0.00788202 + 0.02235794j], - [-0.0128203 + 0.11522974j, 0.09896394 + 0.04999461j, 0.08419826 - 0.06733029j], - ], - [ - [-0.03700634 - 0.01956628j, 0.00204818 - 0.03765312j, -0.04781717 - 0.04220666j], - [-0.000707 - 0.04396371j, -0.00788202 - 0.02235794j, 0.08931812 + 0.0j], - [0.00766162 - 0.01285426j, -0.0084444 - 0.042322j, 0.00769262 + 0.03245046j], - ], - ], - [ - [ - [0.03128229 - 0.02937806j, -0.00255557 + 0.06777649j, -0.03402139 - 0.04582056j], - [-0.05027514 + 0.08649698j, -0.0128203 - 0.11522974j, 0.00766162 + 0.01285426j], - [0.11637437 + 0.0j, 0.03960783 - 0.09361331j, -0.08419771 - 0.07692928j], - ], - [ - [0.03555759 + 0.01100168j, -0.04497981 + 0.02030987j, 0.03198441 - 0.06076387j], - [-0.07202654 - 0.01724066j, 0.09896394 - 0.04999461j, -0.0084444 + 0.042322j], - [0.03960783 + 0.09361331j, 0.10660842 + 0.0j, 0.02629697 - 0.08574598j], - ], - [ - [-0.01982137 + 0.04605857j, -0.05656505 - 0.06693942j, 0.02845793 + 0.00996117j], - [-0.03980968 - 0.11268854j, 0.08419826 + 0.06733029j, 0.00769262 - 0.03245046j], - [-0.08419771 + 0.07692928j, 0.02629697 + 0.08574598j, 0.13023096 + 0.0j], - ], - ], - ] -) - - -def get_random_mixed_state(num_qutrits): - dim = 3**num_qutrits - basis = unitary_group.rvs(dim) - Schmidt_weights = np.random.dirichlet(np.ones(dim), size=1).astype(complex)[0] - mixed_state = np.zeros((dim, dim)).astype(complex) - for i in range(dim): - mixed_state += Schmidt_weights[i] * np.outer(np.conj(basis[i]), basis[i]) - - return mixed_state.reshape([3] * (2 * num_qutrits)) ml_frameworks_list = [ @@ -106,8 +41,32 @@ def get_random_mixed_state(num_qutrits): broadcasting_methods = [apply_operation_einsum, apply_operation] subspaces = [(0, 1), (0, 2), (1, 2)] +@pytest.fixture +def one_qutrit_state(): + path = os.path.join(os.getcwd(), "test_ref_files", "one_qutrit_state") + return np.load(path).item() +@pytest.fixture +def two_qutrit_state(): + path = os.path.join(os.getcwd(), "test_ref_files", "two_qutrit_state") + return np.load(path).item()[0] -def test_custom_operator_with_matrix(): +@pytest.fixture +def two_qutrit_states(): + path = os.path.join(os.getcwd(), "test_ref_files", "two_qutrit_state") + return np.load(path).item() + +@pytest.fixture +def three_qutrit_state(): + path = os.path.join(os.getcwd(), "test_ref_files", "three_qutrit_state") + return np.load(path).item()[0] + +@pytest.fixture +def three_qutrit_states(): + path = os.path.join(os.getcwd(), "test_ref_files", "three_qutrit_state") + return np.load(path).item() + + +def test_custom_operator_with_matrix(one_qutrit_state): """Test that apply_operation works with any operation that defines a matrix.""" mat = np.array( [ @@ -124,18 +83,11 @@ class CustomOp(qml.operation.Operation): def matrix(self): return mat - state = np.array( - [ - [0.26159747 - 0.1906562j, -0.22592805 + 0.10450939j, 0.19576415 + 0.19025104j], - [-0.22592805 + 0.10450939j, 0.05110554 + 0.16562366j, -0.04140423 - 0.17584095j], - [0.19576415 + 0.19025104j, -0.04140423 - 0.17584095j, -0.18278332 - 0.04529327j], - ] - ) - - new_state = apply_operation(CustomOp(0), state) - assert qml.math.allclose(new_state, mat @ state @ np.conj(mat).T) + new_state = apply_operation(CustomOp(0), one_qutrit_state) + assert qml.math.allclose(new_state, mat @ one_qutrit_state @ np.conj(mat).T) +@pytest.mark.usefixtures("two_qutrit_state") @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @pytest.mark.parametrize("method", methods) @pytest.mark.parametrize("wire", (0, 1)) @@ -145,7 +97,7 @@ class TestTwoQubitStateSpecialCases: def test_TAdd(self, method, wire, ml_framework): """Test the application of a TAdd gate on a two qutrit state.""" - initial_state = math.asarray(density_matrix, like=ml_framework) + initial_state = math.asarray(two_qutrit_state, like=ml_framework) control = wire target = int(not control) @@ -166,23 +118,23 @@ def check_TAdd_second_roll(initial_input, new_input): new_input2 = math.take(new_input, 2, axis=control + 1) assert math.allclose(initial_input2_rolled, new_input2) - initial0 = math.take(density_matrix, 0, axis=control) + initial0 = math.take(two_qutrit_state, 0, axis=control) new0 = math.take(new_state, 0, axis=control) check_TAdd_second_roll(initial0, new0) - initial1 = math.take(density_matrix, 1, axis=control) + initial1 = math.take(two_qutrit_state, 1, axis=control) initial1_rolled = np.roll(initial1, 1, 0) new1 = math.take(new_state, 1, axis=control) check_TAdd_second_roll(initial1_rolled, new1) - initial2 = math.take(density_matrix, 2, axis=control) + initial2 = math.take(two_qutrit_state, 2, axis=control) initial2_rolled = math.roll(initial2, -1, 0) new2 = math.take(new_state, 2, axis=control) check_TAdd_second_roll(initial2_rolled, new2) def test_TShift(self, method, wire, ml_framework): """Test the application of a TShift gate on a two qutrit state.""" - initial_state = math.asarray(density_matrix, like=ml_framework) + initial_state = math.asarray(two_qutrit_state, like=ml_framework) new_state = method(qml.TShift(wire), initial_state) def check_second_roll(initial_input, new_input): @@ -198,21 +150,21 @@ def check_second_roll(initial_input, new_input): new_input0 = math.take(new_input, 0, axis=wire + 1) assert math.allclose(initial_input2, new_input0) - initial0 = math.take(density_matrix, 0, axis=wire) + initial0 = math.take(two_qutrit_state, 0, axis=wire) new1 = math.take(new_state, 1, axis=wire) check_second_roll(initial0, new1) - initial1 = math.take(density_matrix, 1, axis=wire) + initial1 = math.take(two_qutrit_state, 1, axis=wire) new2 = math.take(new_state, 2, axis=wire) check_second_roll(initial1, new2) - initial2 = math.take(density_matrix, 2, axis=wire) + initial2 = math.take(two_qutrit_state, 2, axis=wire) new0 = math.take(new_state, 0, axis=wire) check_second_roll(initial2, new0) def test_TClock(self, method, wire, ml_framework): """Test the application of a TClock gate on a two qutrit state.""" - initial_state = math.asarray(density_matrix, like=ml_framework) + initial_state = math.asarray(two_qutrit_state, like=ml_framework) new_state = method(qml.TClock(wire), initial_state) w = math.exp(2j * np.pi / 3) w2 = math.exp(4j * np.pi / 3) @@ -232,25 +184,25 @@ def check_second_roll(initial_input, new_input): new_input2 = math.take(new_input, 2, axis=wire + 1) assert math.allclose(initial_input2 / w2, new_input2) - initial0 = math.take(density_matrix, 0, axis=wire) + initial0 = math.take(two_qutrit_state, 0, axis=wire) new0 = math.take(new_state, 0, axis=wire) check_second_roll(initial0, new0) - initial1 = math.take(density_matrix, 1, axis=wire) + initial1 = math.take(two_qutrit_state, 1, axis=wire) new1 = math.take(new_state, 1, axis=wire) check_second_roll(w * initial1, new1) - initial2 = math.take(density_matrix, 2, axis=wire) + initial2 = math.take(two_qutrit_state, 2, axis=wire) new2 = math.take(new_state, 2, axis=wire) check_second_roll(w2 * initial2, new2) @pytest.mark.parametrize("subspace", subspaces) def test_THadamard(self, method, wire, ml_framework, subspace): - initial_state = math.asarray(density_matrix, like=ml_framework) + initial_state = math.asarray(two_qutrit_state, like=ml_framework) op = qml.THadamard(wire, subspace=subspace) new_state = method(op, initial_state) - flattened_state = density_matrix.reshape(9, 9) + flattened_state = two_qutrit_state.reshape(9, 9) sizes = [3, 3] sizes[wire] = 1 expanded_mat = np.kron(np.kron(np.eye(sizes[0]), op.matrix()), np.eye(sizes[1])) @@ -263,8 +215,8 @@ def test_THadamard(self, method, wire, ml_framework, subspace): states_and_shapes = [ - (density_matrix, (9, 9)), - ([get_random_mixed_state(2), get_random_mixed_state(2)], (2, 9, 9)), + (two_qutrit_state(), (9, 9)), + (two_qutrit_states(), (3, 9, 9)), ] @@ -348,14 +300,14 @@ class TestBroadcasting: # pylint: disable=too-few-public-methods ] num_qutrits = 3 num_batched = 3 - dim = (3**num_qutrits, 3**num_qutrits) + dims = (3**num_qutrits, 3**num_qutrits) @pytest.mark.parametrize("op", broadcasted_ops) - def test_broadcasted_op(self, op, method, ml_framework): + def test_broadcasted_op(self, op, method, ml_framework, three_qutrits_state): """Tests that batched operations are applied correctly to an unbatched state.""" - state = get_random_mixed_state(self.num_qutrits) - flattened_state = state.reshape(self.dim) + state = three_qutrits_state + flattened_state = state.reshape(self.dims) res = method(op, qml.math.asarray(state, like=ml_framework)) missing_wires = 3 - len(op.wires) @@ -377,9 +329,9 @@ def test_broadcasted_op(self, op, method, ml_framework): assert qml.math.allclose(res, expected) @pytest.mark.parametrize("op", unbroadcasted_ops) - def test_broadcasted_state(self, op, method, ml_framework): + def test_broadcasted_state(self, op, method, ml_framework, three_qutrits_states): """Tests that unbatched operations are applied correctly to a batched state.""" - state = [get_random_mixed_state(self.num_qutrits) for _ in range(self.num_batched)] + state = three_qutrits_states res = method(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) missing_wires = self.num_qutrits - len(op.wires) @@ -389,7 +341,7 @@ def test_broadcasted_state(self, op, method, ml_framework): expected = [] for i in range(self.num_batched): - flattened_state = state[i].reshape(self.dim) + flattened_state = state[i].reshape(self.dims) expected.append( (expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * (self.num_qutrits * 2)) ) @@ -398,9 +350,9 @@ def test_broadcasted_state(self, op, method, ml_framework): assert qml.math.allclose(res, expected) @pytest.mark.parametrize("op", broadcasted_ops) - def test_broadcasted_op_broadcasted_state(self, op, method, ml_framework): + def test_broadcasted_op_broadcasted_state(self, op, method, ml_framework, three_qutrits_states): """Tests that batched operations are applied correctly to a batched state.""" - state = [get_random_mixed_state(self.num_qutrits) for _ in range(self.num_batched)] + state = three_qutrits_states res = method(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) missing_wires = self.num_qutrits - len(op.wires) @@ -414,17 +366,17 @@ def test_broadcasted_op_broadcasted_state(self, op, method, ml_framework): for i in range(self.num_batched): expanded_mat = expanded_mats[i] adjoint_mat = np.conj(expanded_mat).T - flattened_state = state[i].reshape(self.dim) + flattened_state = state[i].reshape(self.dims) expected.append( (expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * (self.num_qutrits * 2)) ) assert qml.math.get_interface(res) == ml_framework assert qml.math.allclose(res, expected) - def test_batch_size_set_if_missing(self, method, ml_framework): + def test_batch_size_set_if_missing(self, method, ml_framework, one_qutrit_state): """Tests that the batch_size is set on an operator if it was missing before.""" param = qml.math.asarray([0.1, 0.2, 0.3], like=ml_framework) - state = get_random_mixed_state(1) + state = one_qutrit_state op = qml.TRX(param, 0) op._batch_size = None # pylint:disable=protected-access state = method(op, state) @@ -452,9 +404,9 @@ def compute_kraus_matrices(p): ).astype(complex) return [K0, K1] - def test_non_broadcasted_state(self, method, ml_framework): + def test_non_broadcasted_state(self, method, ml_framework, two_qutrits_state): """Tests that Channel operations are applied correctly to a state.""" - state = get_random_mixed_state(2) + state = two_qutrits_state test_channel = self.CustomChannel(0.3, wires=1) res = method(test_channel, math.asarray(state, like=ml_framework)) flattened_state = state.reshape(9, 9) @@ -472,11 +424,11 @@ def test_non_broadcasted_state(self, method, ml_framework): assert qml.math.get_interface(res) == ml_framework assert qml.math.allclose(res, expected) - def test_broadcasted_state(self, method, ml_framework): + def test_broadcasted_state(self, method, ml_framework, two_qutrits_states): """Tests that Channel operations are applied correctly to a batched state.""" if method is apply_operation_tensordot: pytest.skip("Tensordot doesn't support batched operations.") - state = [get_random_mixed_state(2) for _ in range(3)] + state = two_qutrits_states test_channel = self.CustomChannel(0.3, wires=1) res = method(test_channel, math.asarray(state, like=ml_framework)) diff --git a/tests/devices/qutrit_mixed/test_ref_files/1_qutrit_state.npy b/tests/devices/qutrit_mixed/test_ref_files/1_qutrit_state.npy new file mode 100644 index 0000000000000000000000000000000000000000..9688cac191b935e40aab5b46cf587e9be0219f6f GIT binary patch literal 272 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlWb_FuA`uymS0p-l$aNvUzCyxl5k7RDNY57 z7iT0EqyqUG#ySednmP)#3giN=z=s=kuOGQ<|8L%HOSVYIJsK;zfB)(^y#J0%mCd!> z^ZTb9zv`Khe`f#LD?2WA|65~^CT}mi!F)b*=M8&%J!8*6i&UHUOu}hSLTl{bPMUR^ h?a=|a{&Nuh`_bg>ZSU%&d8Hh-Z&MYTpC+ih2LP+1XcPbd literal 0 HcmV?d00001 diff --git a/tests/devices/qutrit_mixed/test_ref_files/2_qutrits_states.npy b/tests/devices/qutrit_mixed/test_ref_files/2_qutrits_states.npy new file mode 100644 index 0000000000000000000000000000000000000000..34603043af929686e85c18f6ed8a6cadf81b0b7e GIT binary patch literal 1424 zcmb7@e^8Tk9LMoWOz4N4ze2j)EE?ztL{7KZ@ew1G*-SjpNP@=3vO@L?1x}{|1M%8o z;uz|N%)t~ChQ#es+4{&abtsJOB#8?Za1u4RK&;cNFuJFQKc4@3?)m5WK7YLL{d&K? zC$^>Xwx`YW+wXUPAN1i=6{G;nIDQMg2 zEBaF)b$oTTp}!U0Eto$V6L$u`;dV)Ru11*b^=y46J`1^3iQJ;UjL<9^4qb0=L^3~; z$cLLL{te&pIw59a@x{Qlc$-_|*tp=n4yFt#QCiJ$C{s(k>=`4*Z@-nPxMaqLPSH;X z`z&bbmpX@jR^e|?yPY9JdTd^KkM1t0!3TpIt`yA3VRF)LUUmOG$a(@hKJ~A|taAxl zlwKnoDk=H;ts8ng|M+3nMCICXhk=^`T#bUNvCfK+Lt}<)^{5cY5rVu2j&NTf)3K{<6yY&^36dxQ1~O+e-qIkDEaUpt<=XW3j}{4`)?uoBPAc2iVVuOiR)lY{=%u| zZshbvYcd|bX26=;*8JNHBXHY`mF|5i@Md~d!$vKN)9G<`s|i$ZhgP5TYeZ+{^oVgs zBRt6)+vR%U6xF}E{Vl})bNqiG_*44>B_H46f5u|@U4=tq3+euvnRdP@yl)}RgeDtp z!W%9_vIk#R{0ZOopR3qD8hdr{hK2EL(2Yh{8teC zsr`Y>uO;#!*FqOQZ?{2g)!~*l&nBDgOU07pW-Z z)W*YLkuZG@mkKHOpL+kO{!{$}%Kkv|XA}I9(jO`Lz?1$yQIy&W>EE(f{~7rr=O5MG Bzr6qe literal 0 HcmV?d00001 diff --git a/tests/devices/qutrit_mixed/test_ref_files/3_qutrits_states.npy b/tests/devices/qutrit_mixed/test_ref_files/3_qutrits_states.npy new file mode 100644 index 0000000000000000000000000000000000000000..9df9ed8ed29de5ee869853e1bbbca020867ef56f GIT binary patch literal 11792 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 literal 0 HcmV?d00001 From f1dd22c6a95ec3112b874020704aba990e3098d0 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Thu, 11 Jan 2024 16:18:59 -0800 Subject: [PATCH 50/89] Fixed loading issues --- .../test_qutrit_mixed_apply_operation.py | 107 ++++++++---------- .../test_ref_files/1_qutrit_state.npy | Bin 272 -> 0 bytes .../test_ref_files/2_qutrits_states.npy | Bin 1424 -> 0 bytes .../test_ref_files/3_qutrits_states.npy | Bin 11792 -> 0 bytes .../test_ref_files/one_qutrit_states.npy | Bin 0 -> 560 bytes .../test_ref_files/three_qutrits_states.npy | Bin 0 -> 35120 bytes .../test_ref_files/two_qutrits_states.npy | Bin 0 -> 4016 bytes 7 files changed, 50 insertions(+), 57 deletions(-) delete mode 100644 tests/devices/qutrit_mixed/test_ref_files/1_qutrit_state.npy delete mode 100644 tests/devices/qutrit_mixed/test_ref_files/2_qutrits_states.npy delete mode 100644 tests/devices/qutrit_mixed/test_ref_files/3_qutrits_states.npy create mode 100644 tests/devices/qutrit_mixed/test_ref_files/one_qutrit_states.npy create mode 100644 tests/devices/qutrit_mixed/test_ref_files/three_qutrits_states.npy create mode 100644 tests/devices/qutrit_mixed/test_ref_files/two_qutrits_states.npy diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 50c33008358..0f8ef442d96 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -26,8 +26,6 @@ apply_operation, ) - - ml_frameworks_list = [ "numpy", pytest.param("autograd", marks=pytest.mark.autograd), @@ -36,34 +34,31 @@ pytest.param("tensorflow", marks=pytest.mark.tf), ] - methods = [apply_operation_einsum, apply_operation_tensordot, apply_operation] broadcasting_methods = [apply_operation_einsum, apply_operation] subspaces = [(0, 1), (0, 2), (1, 2)] -@pytest.fixture -def one_qutrit_state(): - path = os.path.join(os.getcwd(), "test_ref_files", "one_qutrit_state") - return np.load(path).item() -@pytest.fixture -def two_qutrit_state(): - path = os.path.join(os.getcwd(), "test_ref_files", "two_qutrit_state") - return np.load(path).item()[0] -@pytest.fixture -def two_qutrit_states(): - path = os.path.join(os.getcwd(), "test_ref_files", "two_qutrit_state") - return np.load(path).item() +def load_data(num_qutrits, batched): + file_name_start = f"{num_qutrits}_qutrit" + if num_qutrits != "one": + file_name_start += "s" + + file_name = f"{file_name_start}_states.npy" + path = os.path.join(os.getcwd(), + "test_ref_files", + file_name) + data = np.load(path, allow_pickle=True) + if batched: + return data + return data[0] -@pytest.fixture -def three_qutrit_state(): - path = os.path.join(os.getcwd(), "test_ref_files", "three_qutrit_state") - return np.load(path).item()[0] -@pytest.fixture -def three_qutrit_states(): - path = os.path.join(os.getcwd(), "test_ref_files", "three_qutrit_state") - return np.load(path).item() +one_qutrit_state = pytest.fixture(lambda: load_data("one", False), scope="module") +two_qutrits_state = pytest.fixture(lambda: load_data("two", False), scope="module") +two_qutrits_states = pytest.fixture(lambda: load_data("two", True), scope="module") +three_qutrits_state = pytest.fixture(lambda: load_data("three", False), scope="module") +three_qutrits_states = pytest.fixture(lambda: load_data("three", True), scope="module") def test_custom_operator_with_matrix(one_qutrit_state): @@ -87,7 +82,6 @@ def matrix(self): assert qml.math.allclose(new_state, mat @ one_qutrit_state @ np.conj(mat).T) -@pytest.mark.usefixtures("two_qutrit_state") @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @pytest.mark.parametrize("method", methods) @pytest.mark.parametrize("wire", (0, 1)) @@ -95,9 +89,9 @@ class TestTwoQubitStateSpecialCases: """Test the special cases on a two qutrit state. Also tests the special cases for einsum and tensor application methods for additional testing of these generic matrix application methods.""" - def test_TAdd(self, method, wire, ml_framework): + def test_TAdd(self, method, wire, ml_framework, two_qutrits_state): """Test the application of a TAdd gate on a two qutrit state.""" - initial_state = math.asarray(two_qutrit_state, like=ml_framework) + initial_state = math.asarray(two_qutrits_state, like=ml_framework) control = wire target = int(not control) @@ -118,23 +112,23 @@ def check_TAdd_second_roll(initial_input, new_input): new_input2 = math.take(new_input, 2, axis=control + 1) assert math.allclose(initial_input2_rolled, new_input2) - initial0 = math.take(two_qutrit_state, 0, axis=control) + initial0 = math.take(two_qutrits_state, 0, axis=control) new0 = math.take(new_state, 0, axis=control) check_TAdd_second_roll(initial0, new0) - initial1 = math.take(two_qutrit_state, 1, axis=control) + initial1 = math.take(two_qutrits_state, 1, axis=control) initial1_rolled = np.roll(initial1, 1, 0) new1 = math.take(new_state, 1, axis=control) check_TAdd_second_roll(initial1_rolled, new1) - initial2 = math.take(two_qutrit_state, 2, axis=control) + initial2 = math.take(two_qutrits_state, 2, axis=control) initial2_rolled = math.roll(initial2, -1, 0) new2 = math.take(new_state, 2, axis=control) check_TAdd_second_roll(initial2_rolled, new2) - def test_TShift(self, method, wire, ml_framework): + def test_TShift(self, method, wire, ml_framework, two_qutrits_state): """Test the application of a TShift gate on a two qutrit state.""" - initial_state = math.asarray(two_qutrit_state, like=ml_framework) + initial_state = math.asarray(two_qutrits_state, like=ml_framework) new_state = method(qml.TShift(wire), initial_state) def check_second_roll(initial_input, new_input): @@ -150,21 +144,21 @@ def check_second_roll(initial_input, new_input): new_input0 = math.take(new_input, 0, axis=wire + 1) assert math.allclose(initial_input2, new_input0) - initial0 = math.take(two_qutrit_state, 0, axis=wire) + initial0 = math.take(two_qutrits_state, 0, axis=wire) new1 = math.take(new_state, 1, axis=wire) check_second_roll(initial0, new1) - initial1 = math.take(two_qutrit_state, 1, axis=wire) + initial1 = math.take(two_qutrits_state, 1, axis=wire) new2 = math.take(new_state, 2, axis=wire) check_second_roll(initial1, new2) - initial2 = math.take(two_qutrit_state, 2, axis=wire) + initial2 = math.take(two_qutrits_state, 2, axis=wire) new0 = math.take(new_state, 0, axis=wire) check_second_roll(initial2, new0) - def test_TClock(self, method, wire, ml_framework): + def test_TClock(self, method, wire, ml_framework, two_qutrits_state): """Test the application of a TClock gate on a two qutrit state.""" - initial_state = math.asarray(two_qutrit_state, like=ml_framework) + initial_state = math.asarray(two_qutrits_state, like=ml_framework) new_state = method(qml.TClock(wire), initial_state) w = math.exp(2j * np.pi / 3) w2 = math.exp(4j * np.pi / 3) @@ -184,25 +178,25 @@ def check_second_roll(initial_input, new_input): new_input2 = math.take(new_input, 2, axis=wire + 1) assert math.allclose(initial_input2 / w2, new_input2) - initial0 = math.take(two_qutrit_state, 0, axis=wire) + initial0 = math.take(two_qutrits_state, 0, axis=wire) new0 = math.take(new_state, 0, axis=wire) check_second_roll(initial0, new0) - initial1 = math.take(two_qutrit_state, 1, axis=wire) + initial1 = math.take(two_qutrits_state, 1, axis=wire) new1 = math.take(new_state, 1, axis=wire) check_second_roll(w * initial1, new1) - initial2 = math.take(two_qutrit_state, 2, axis=wire) + initial2 = math.take(two_qutrits_state, 2, axis=wire) new2 = math.take(new_state, 2, axis=wire) check_second_roll(w2 * initial2, new2) @pytest.mark.parametrize("subspace", subspaces) - def test_THadamard(self, method, wire, ml_framework, subspace): - initial_state = math.asarray(two_qutrit_state, like=ml_framework) + def test_THadamard(self, method, wire, ml_framework, subspace, two_qutrits_state): + initial_state = math.asarray(two_qutrits_state, like=ml_framework) op = qml.THadamard(wire, subspace=subspace) new_state = method(op, initial_state) - flattened_state = two_qutrit_state.reshape(9, 9) + flattened_state = two_qutrits_state.reshape(9, 9) sizes = [3, 3] sizes[wire] = 1 expanded_mat = np.kron(np.kron(np.eye(sizes[0]), op.matrix()), np.eye(sizes[1])) @@ -214,14 +208,9 @@ def test_THadamard(self, method, wire, ml_framework, subspace): # TODO: Add more tests as Special cases are added -states_and_shapes = [ - (two_qutrit_state(), (9, 9)), - (two_qutrit_states(), (3, 9, 9)), -] - @pytest.mark.parametrize("ml_framework", ml_frameworks_list) -@pytest.mark.parametrize("state,shape", states_and_shapes) +@pytest.mark.parametrize("state,shape", [("two_qutrits_state", (9, 9)), ("two_qutrits_states", (3, 9, 9))]) class TestSnapshot: """Test that apply_operation works for Snapshot ops""" @@ -232,16 +221,19 @@ def __init__(self): self.active = True self.snapshots = {} - def test_no_debugger(self, ml_framework, state, shape): # pylint: disable=unused-argument + @pytest.mark.usefixtures("two_qutrits_state") + def test_no_debugger(self, ml_framework, state, shape, request): # pylint: disable=unused-argument """Test nothing happens when there is no debugger""" + state = request.getfixturevalue(state) initial_state = math.asarray(state, like=ml_framework) - new_state = apply_operation(qml.Snapshot(), initial_state, is_state_batched=len(shape) != 2) + new_state = apply_operation(qml.Snapshot(), initial_state, is_state_batched=len(shape) != 2) assert new_state.shape == initial_state.shape assert math.allclose(new_state, initial_state) - def test_empty_tag(self, ml_framework, state, shape): + def test_empty_tag(self, ml_framework, state, shape, request): """Test a snapshot is recorded properly when there is no tag""" + state = request.getfixturevalue(state) initial_state = math.asarray(state, like=ml_framework) debugger = self.Debugger() @@ -256,8 +248,9 @@ def test_empty_tag(self, ml_framework, state, shape): assert debugger.snapshots[0].shape == shape assert math.allclose(debugger.snapshots[0], math.reshape(initial_state, shape)) - def test_provided_tag(self, ml_framework, state, shape): + def test_provided_tag(self, ml_framework, state, shape, request): """Test a snapshot is recorded property when provided a tag""" + state = request.getfixturevalue(state) initial_state = math.asarray(state, like=ml_framework) debugger = self.Debugger() @@ -300,7 +293,7 @@ class TestBroadcasting: # pylint: disable=too-few-public-methods ] num_qutrits = 3 num_batched = 3 - dims = (3**num_qutrits, 3**num_qutrits) + dims = (3 ** num_qutrits, 3 ** num_qutrits) @pytest.mark.parametrize("op", broadcasted_ops) def test_broadcasted_op(self, op, method, ml_framework, three_qutrits_state): @@ -313,7 +306,7 @@ def test_broadcasted_op(self, op, method, ml_framework, three_qutrits_state): missing_wires = 3 - len(op.wires) mat = op.matrix() expanded_mats = [ - np.kron(np.eye(3**missing_wires), mat[i]) if missing_wires else mat[i] + np.kron(np.eye(3 ** missing_wires), mat[i]) if missing_wires else mat[i] for i in range(self.num_batched) ] expected = [] @@ -336,7 +329,7 @@ def test_broadcasted_state(self, op, method, ml_framework, three_qutrits_states) res = method(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) missing_wires = self.num_qutrits - len(op.wires) mat = op.matrix() - expanded_mat = np.kron(np.eye(3**missing_wires), mat) if missing_wires else mat + expanded_mat = np.kron(np.eye(3 ** missing_wires), mat) if missing_wires else mat adjoint_mat = np.conj(expanded_mat).T expected = [] @@ -358,7 +351,7 @@ def test_broadcasted_op_broadcasted_state(self, op, method, ml_framework, three_ missing_wires = self.num_qutrits - len(op.wires) mat = op.matrix() expanded_mats = [ - np.kron(np.eye(3**missing_wires), mat[i]) if missing_wires else mat[i] + np.kron(np.eye(3 ** missing_wires), mat[i]) if missing_wires else mat[i] for i in range(self.num_batched) ] expected = [] @@ -400,7 +393,7 @@ def __init__(self, p, wires, id=None): def compute_kraus_matrices(p): K0 = (np.sqrt(1 - p) * math.cast_like(np.eye(3), p)).astype(complex) K1 = ( - np.sqrt(p) * math.cast_like(np.array([[0, 0, 1], [1, 0, 0], [0, 1, 0]]), p) + np.sqrt(p) * math.cast_like(np.array([[0, 0, 1], [1, 0, 0], [0, 1, 0]]), p) ).astype(complex) return [K0, K1] diff --git a/tests/devices/qutrit_mixed/test_ref_files/1_qutrit_state.npy b/tests/devices/qutrit_mixed/test_ref_files/1_qutrit_state.npy deleted file mode 100644 index 9688cac191b935e40aab5b46cf587e9be0219f6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 272 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlWb_FuA`uymS0p-l$aNvUzCyxl5k7RDNY57 z7iT0EqyqUG#ySednmP)#3giN=z=s=kuOGQ<|8L%HOSVYIJsK;zfB)(^y#J0%mCd!> z^ZTb9zv`Khe`f#LD?2WA|65~^CT}mi!F)b*=M8&%J!8*6i&UHUOu}hSLTl{bPMUR^ h?a=|a{&Nuh`_bg>ZSU%&d8Hh-Z&MYTpC+ih2LP+1XcPbd diff --git a/tests/devices/qutrit_mixed/test_ref_files/2_qutrits_states.npy b/tests/devices/qutrit_mixed/test_ref_files/2_qutrits_states.npy deleted file mode 100644 index 34603043af929686e85c18f6ed8a6cadf81b0b7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1424 zcmb7@e^8Tk9LMoWOz4N4ze2j)EE?ztL{7KZ@ew1G*-SjpNP@=3vO@L?1x}{|1M%8o z;uz|N%)t~ChQ#es+4{&abtsJOB#8?Za1u4RK&;cNFuJFQKc4@3?)m5WK7YLL{d&K? zC$^>Xwx`YW+wXUPAN1i=6{G;nIDQMg2 zEBaF)b$oTTp}!U0Eto$V6L$u`;dV)Ru11*b^=y46J`1^3iQJ;UjL<9^4qb0=L^3~; z$cLLL{te&pIw59a@x{Qlc$-_|*tp=n4yFt#QCiJ$C{s(k>=`4*Z@-nPxMaqLPSH;X z`z&bbmpX@jR^e|?yPY9JdTd^KkM1t0!3TpIt`yA3VRF)LUUmOG$a(@hKJ~A|taAxl zlwKnoDk=H;ts8ng|M+3nMCICXhk=^`T#bUNvCfK+Lt}<)^{5cY5rVu2j&NTf)3K{<6yY&^36dxQ1~O+e-qIkDEaUpt<=XW3j}{4`)?uoBPAc2iVVuOiR)lY{=%u| zZshbvYcd|bX26=;*8JNHBXHY`mF|5i@Md~d!$vKN)9G<`s|i$ZhgP5TYeZ+{^oVgs zBRt6)+vR%U6xF}E{Vl})bNqiG_*44>B_H46f5u|@U4=tq3+euvnRdP@yl)}RgeDtp z!W%9_vIk#R{0ZOopR3qD8hdr{hK2EL(2Yh{8teC zsr`Y>uO;#!*FqOQZ?{2g)!~*l&nBDgOU07pW-Z z)W*YLkuZG@mkKHOpL+kO{!{$}%Kkv|XA}I9(jO`Lz?1$yQIy&W>EE(f{~7rr=O5MG Bzr6qe diff --git a/tests/devices/qutrit_mixed/test_ref_files/3_qutrits_states.npy b/tests/devices/qutrit_mixed/test_ref_files/3_qutrits_states.npy deleted file mode 100644 index 9df9ed8ed29de5ee869853e1bbbca020867ef56f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11792 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 diff --git a/tests/devices/qutrit_mixed/test_ref_files/one_qutrit_states.npy b/tests/devices/qutrit_mixed/test_ref_files/one_qutrit_states.npy new file mode 100644 index 0000000000000000000000000000000000000000..0834947cbaaac5d65c77ad2d1bea98f1264026f8 GIT binary patch literal 560 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 Z{z0lfurtC^V|{EsG`x4885RAr@_&87PoV$+ literal 0 HcmV?d00001 diff --git a/tests/devices/qutrit_mixed/test_ref_files/two_qutrits_states.npy b/tests/devices/qutrit_mixed/test_ref_files/two_qutrits_states.npy new file mode 100644 index 0000000000000000000000000000000000000000..3ef05a6fa2bc87a578df226401e2ba9ad6393b7c GIT binary patch literal 4016 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 literal 0 HcmV?d00001 From eeaadedc33744d0df2ba101255b1c380e6031477 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Thu, 11 Jan 2024 16:23:56 -0800 Subject: [PATCH 51/89] Removed tensordot from commit --- .../devices/qutrit_mixed/apply_operation.py | 67 +------------------ .../test_qutrit_mixed_apply_operation.py | 3 +- 2 files changed, 4 insertions(+), 66 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index ea2cbb4540f..eaa2a793993 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -24,9 +24,6 @@ alphabet_array = np.array(list(alphabet)) -EINSUM_OP_WIRECOUNT_PERF_THRESHOLD = 3 -EINSUM_STATE_WIRECOUNT_PERF_THRESHOLD = 13 # TODO placeholder value, need to ask how to find these - qudit_dim = 3 # specifies qudit dimension @@ -109,61 +106,6 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: return math.einsum(einsum_indices, kraus, state, kraus_dagger) -def apply_operation_tensordot(op: qml.operation.Operator, state): - r"""Apply a quantum channel specified by a list of Kraus operators to subsystems of the - quantum state. For a unitary gate, there is a single Kraus operator. - - Args: - op (Operator): Operator to apply to the quantum state - state (array[complex]): Input quantum state - - Returns: - array[complex]: output_state - """ - num_ch_wires = len(op.wires) - num_wires = int(len(qml.math.shape(state)) / 2) - - # Shape kraus operators and cast them to complex data type - kraus_shape = [qudit_dim] * (num_ch_wires * 2) - - # row indices of the quantum state affected by this operation - row_wires_list = list(op.wires.toarray()) - # column indices are shifted by the number of wires - col_wires_list = [w + num_wires for w in row_wires_list] - - channel_col_ids = list(range(num_ch_wires, 2 * num_ch_wires)) - axes_left = [channel_col_ids, row_wires_list] - # Use column indices instead or rows to incorporate transposition of K^\dagger - axes_right = [col_wires_list, channel_col_ids] - - # Apply the Kraus operators, and sum over all Kraus operators afterwards - def _conjugate_state_with(k): - """Perform the double tensor product k @ self._state @ k.conj(). - The `axes_left` and `axes_right` arguments are taken from the ambient variable space - and `axes_right` is assumed to incorporate the tensor product and the transposition - of k.conj() simultaneously.""" - k = math.cast(math.reshape(k, kraus_shape), complex) - return math.tensordot(math.tensordot(k, state, axes_left), math.conj(k), axes_right) - - if isinstance(op, Channel): - kraus = op.kraus_matrices() - _state = math.sum(math.stack([_conjugate_state_with(k) for k in kraus]), axis=0) - else: - _state = _conjugate_state_with(op.matrix()) - - # Permute the affected axes to their destination places. - # The row indices of the kraus operators are moved from the beginning to the original - # target row locations, the column indices from the end to the target column locations - source_left = list(range(num_ch_wires)) - dest_left = row_wires_list - source_right = list(range(-num_ch_wires, 0)) - dest_right = col_wires_list - - print(source_left + source_right) - print(dest_left + dest_right) - return math.moveaxis(_state, source_left + source_right, dest_left + dest_right) - - @singledispatch def apply_operation( op: qml.operation.Operator, state, is_state_batched: bool = False, debugger=None @@ -222,12 +164,9 @@ def _apply_operation_default(op, state, is_state_batched, debugger): """The default behaviour of apply_operation, accessed through the standard dispatch of apply_operation, as well as conditionally in other dispatches. """ - if ( - len(op.wires) < EINSUM_OP_WIRECOUNT_PERF_THRESHOLD - and math.ndim(state) < EINSUM_STATE_WIRECOUNT_PERF_THRESHOLD - ) or (op.batch_size or is_state_batched): - return apply_operation_einsum(op, state, is_state_batched=is_state_batched) - # TODO fix state batching on tensordot + + return apply_operation_einsum(op, state, is_state_batched=is_state_batched) + # TODO add tensordot and benchmark for performance return apply_operation_tensordot(op, state) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 0f8ef442d96..af2f98c4fa4 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -22,7 +22,6 @@ from pennylane.operation import Channel from pennylane.devices.qutrit_mixed.apply_operation import ( apply_operation_einsum, - apply_operation_tensordot, apply_operation, ) @@ -34,7 +33,7 @@ pytest.param("tensorflow", marks=pytest.mark.tf), ] -methods = [apply_operation_einsum, apply_operation_tensordot, apply_operation] +methods = [apply_operation_einsum, apply_operation] # TODO add tensordot back when added broadcasting_methods = [apply_operation_einsum, apply_operation] subspaces = [(0, 1), (0, 2), (1, 2)] From dd456b753ca96f06aa63b46b700f1d7d0b0c716a Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Thu, 11 Jan 2024 16:29:46 -0800 Subject: [PATCH 52/89] mend --- .../test_qutrit_mixed_apply_operation.py | 45 +++++++------------ tests/pauli/test_pauli_utils.py | 2 +- 2 files changed, 17 insertions(+), 30 deletions(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index af2f98c4fa4..8d9f08bd340 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -44,9 +44,7 @@ def load_data(num_qutrits, batched): file_name_start += "s" file_name = f"{file_name_start}_states.npy" - path = os.path.join(os.getcwd(), - "test_ref_files", - file_name) + path = os.path.join(os.getcwd(), "test_ref_files", file_name) data = np.load(path, allow_pickle=True) if batched: return data @@ -85,9 +83,12 @@ def matrix(self): @pytest.mark.parametrize("method", methods) @pytest.mark.parametrize("wire", (0, 1)) class TestTwoQubitStateSpecialCases: - """Test the special cases on a two qutrit state. Also tests the special cases for einsum and tensor application methods + """Test the special cases on a two qutrit state. Also tests the special cases for einsum application method for additional testing of these generic matrix application methods.""" + # Currently not added as special cases, but will be in future + # TODO add tensordot back after it has been added + def test_TAdd(self, method, wire, ml_framework, two_qutrits_state): """Test the application of a TAdd gate on a two qutrit state.""" initial_state = math.asarray(two_qutrits_state, like=ml_framework) @@ -189,27 +190,13 @@ def check_second_roll(initial_input, new_input): new2 = math.take(new_state, 2, axis=wire) check_second_roll(w2 * initial2, new2) - @pytest.mark.parametrize("subspace", subspaces) - def test_THadamard(self, method, wire, ml_framework, subspace, two_qutrits_state): - initial_state = math.asarray(two_qutrits_state, like=ml_framework) - op = qml.THadamard(wire, subspace=subspace) - new_state = method(op, initial_state) - - flattened_state = two_qutrits_state.reshape(9, 9) - sizes = [3, 3] - sizes[wire] = 1 - expanded_mat = np.kron(np.kron(np.eye(sizes[0]), op.matrix()), np.eye(sizes[1])) - adjoint_mat = np.conj(expanded_mat).T - expected = (expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * 4) - - assert math.allclose(expected, new_state) - # TODO: Add more tests as Special cases are added - @pytest.mark.parametrize("ml_framework", ml_frameworks_list) -@pytest.mark.parametrize("state,shape", [("two_qutrits_state", (9, 9)), ("two_qutrits_states", (3, 9, 9))]) +@pytest.mark.parametrize( + "state,shape", [("two_qutrits_state", (9, 9)), ("two_qutrits_states", (3, 9, 9))] +) class TestSnapshot: """Test that apply_operation works for Snapshot ops""" @@ -221,7 +208,9 @@ def __init__(self): self.snapshots = {} @pytest.mark.usefixtures("two_qutrits_state") - def test_no_debugger(self, ml_framework, state, shape, request): # pylint: disable=unused-argument + def test_no_debugger( + self, ml_framework, state, shape, request + ): # pylint: disable=unused-argument """Test nothing happens when there is no debugger""" state = request.getfixturevalue(state) initial_state = math.asarray(state, like=ml_framework) @@ -292,7 +281,7 @@ class TestBroadcasting: # pylint: disable=too-few-public-methods ] num_qutrits = 3 num_batched = 3 - dims = (3 ** num_qutrits, 3 ** num_qutrits) + dims = (3**num_qutrits, 3**num_qutrits) @pytest.mark.parametrize("op", broadcasted_ops) def test_broadcasted_op(self, op, method, ml_framework, three_qutrits_state): @@ -305,7 +294,7 @@ def test_broadcasted_op(self, op, method, ml_framework, three_qutrits_state): missing_wires = 3 - len(op.wires) mat = op.matrix() expanded_mats = [ - np.kron(np.eye(3 ** missing_wires), mat[i]) if missing_wires else mat[i] + np.kron(np.eye(3**missing_wires), mat[i]) if missing_wires else mat[i] for i in range(self.num_batched) ] expected = [] @@ -328,7 +317,7 @@ def test_broadcasted_state(self, op, method, ml_framework, three_qutrits_states) res = method(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) missing_wires = self.num_qutrits - len(op.wires) mat = op.matrix() - expanded_mat = np.kron(np.eye(3 ** missing_wires), mat) if missing_wires else mat + expanded_mat = np.kron(np.eye(3**missing_wires), mat) if missing_wires else mat adjoint_mat = np.conj(expanded_mat).T expected = [] @@ -350,7 +339,7 @@ def test_broadcasted_op_broadcasted_state(self, op, method, ml_framework, three_ missing_wires = self.num_qutrits - len(op.wires) mat = op.matrix() expanded_mats = [ - np.kron(np.eye(3 ** missing_wires), mat[i]) if missing_wires else mat[i] + np.kron(np.eye(3**missing_wires), mat[i]) if missing_wires else mat[i] for i in range(self.num_batched) ] expected = [] @@ -392,7 +381,7 @@ def __init__(self, p, wires, id=None): def compute_kraus_matrices(p): K0 = (np.sqrt(1 - p) * math.cast_like(np.eye(3), p)).astype(complex) K1 = ( - np.sqrt(p) * math.cast_like(np.array([[0, 0, 1], [1, 0, 0], [0, 1, 0]]), p) + np.sqrt(p) * math.cast_like(np.array([[0, 0, 1], [1, 0, 0], [0, 1, 0]]), p) ).astype(complex) return [K0, K1] @@ -418,8 +407,6 @@ def test_non_broadcasted_state(self, method, ml_framework, two_qutrits_state): def test_broadcasted_state(self, method, ml_framework, two_qutrits_states): """Tests that Channel operations are applied correctly to a batched state.""" - if method is apply_operation_tensordot: - pytest.skip("Tensordot doesn't support batched operations.") state = two_qutrits_states test_channel = self.CustomChannel(0.3, wires=1) res = method(test_channel, math.asarray(state, like=ml_framework)) diff --git a/tests/pauli/test_pauli_utils.py b/tests/pauli/test_pauli_utils.py index 68f58c9cfb3..076643b4fa8 100644 --- a/tests/pauli/test_pauli_utils.py +++ b/tests/pauli/test_pauli_utils.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """ -Unit tests for the :mod:`pauli` utility functions in ``pauli/utils.py``. +Unit tests for the :mod:`pauli` utility functions in ``pauli/.utils.py``. """ # pylint: disable=too-few-public-methods,too-many-public-methods import numpy as np From abb0efc99e8ad652f0c6a2ba6868e2e7bead626d Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Thu, 11 Jan 2024 16:48:43 -0800 Subject: [PATCH 53/89] Seperated einsum indices collection as it is useful for a later PR --- .../devices/qutrit_mixed/apply_operation.py | 38 ++--------- .../devices/qutrit_mixed/initialize_state.py | 2 - pennylane/devices/qutrit_mixed/utils.py | 67 +++++++++++++++++++ 3 files changed, 71 insertions(+), 36 deletions(-) create mode 100644 pennylane/devices/qutrit_mixed/utils.py diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index eaa2a793993..bf0da5d5ab6 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -21,12 +21,10 @@ from pennylane import math from pennylane import numpy as np from pennylane.operation import Channel +from .utils import qudit_dim, get_einsum_indices alphabet_array = np.array(list(alphabet)) -qudit_dim = 3 # specifies qudit dimension - - def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: bool = False): r"""Apply a quantum channel specified by a list of Kraus operators to subsystems of the quantum state. For a unitary gate, there is a single Kraus operator. @@ -39,41 +37,13 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: Returns: array[complex]: output_state """ - num_ch_wires = len(op.wires) - num_wires = int((len(qml.math.shape(state)) - is_state_batched) / 2) - rho_dim = 2 * num_wires - - # Tensor indices of the state. For each qutrit, need an index for rows *and* columns - state_indices = alphabet[:rho_dim] - - # row indices of the quantum state affected by this operation - row_wires_list = op.wires.tolist() - row_indices = "".join(alphabet_array[row_wires_list].tolist()) - - # column indices are shifted by the number of wires - col_wires_list = [w + num_wires for w in row_wires_list] - col_indices = "".join(alphabet_array[col_wires_list].tolist()) - - # indices in einsum must be replaced with new ones - new_row_indices = alphabet[rho_dim : rho_dim + num_ch_wires] - new_col_indices = alphabet[rho_dim + num_ch_wires : rho_dim + 2 * num_ch_wires] - - # index for summation over Kraus operators - kraus_index = alphabet[rho_dim + 2 * num_ch_wires : rho_dim + 2 * num_ch_wires + 1] - - # new state indices replace row and column indices with new ones - new_state_indices = functools.reduce( - lambda old_string, idx_pair: old_string.replace(idx_pair[0], idx_pair[1]), - zip(col_indices + row_indices, new_col_indices + new_row_indices), - state_indices, - ) - + indices = get_einsum_indices(op, state, is_state_batched) # index mapping for einsum, e.g., '...iga,...abcdef,...idh->...gbchef' einsum_indices = ( - f"...{kraus_index}{new_row_indices}{row_indices},...{state_indices}," - f"...{kraus_index}{col_indices}{new_col_indices}->...{new_state_indices}" + f"...{indices['op1']},...{indices['state']},...{indices['op2']}->...{indices['new_state']}" ) + num_ch_wires = len(op.wires) kraus = _get_kraus(op) # Shape kraus operators diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index 23c21b31890..e08f1d49c1c 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -17,8 +17,6 @@ import pennylane as qml from pennylane.operation import StatePrepBase -qudit_dim = 3 # specifies qudit dimension - def create_initial_state( wires: Union[qml.wires.Wires, Iterable], diff --git a/pennylane/devices/qutrit_mixed/utils.py b/pennylane/devices/qutrit_mixed/utils.py new file mode 100644 index 00000000000..8e0a14e7292 --- /dev/null +++ b/pennylane/devices/qutrit_mixed/utils.py @@ -0,0 +1,67 @@ +# Copyright 2018-2024 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. +"""Functions and variables to be utilized by qutrit mixed state simulator.""" +import functools +from string import ascii_letters as alphabet +import pennylane as qml +from pennylane import numpy as np + +alphabet_array = np.array(list(alphabet)) +qudit_dim = 3 # specifies qudit dimension + + +def get_einsum_indices(op: qml.operation.Operator, state, is_state_batched: bool = False): + r"""Finds the indices for einsum to multiply three matrices + + Args: + obs (Operator): Operator to apply to the quantum state + state (array[complex]): Input quantum state + is_state_batched (bool): Boolean representing whether the state is batched or not + + Returns: + dict: indices used by einsum to multiply 3 matrices + """ + num_ch_wires = len(op.wires) + num_wires = int((len(qml.math.shape(state)) - is_state_batched) / 2) + rho_dim = 2 * num_wires + + # Tensor indices of the state. For each qutrit, need an index for rows *and* columns + state_indices = alphabet[:rho_dim] + + # row indices of the quantum state affected by this operation + row_wires_list = op.wires.tolist() + row_indices = "".join(alphabet_array[row_wires_list].tolist()) + + # column indices are shifted by the number of wires + col_wires_list = [w + num_wires for w in row_wires_list] + col_indices = "".join(alphabet_array[col_wires_list].tolist()) + + # indices in einsum must be replaced with new ones + new_row_indices = alphabet[rho_dim: rho_dim + num_ch_wires] + new_col_indices = alphabet[rho_dim + num_ch_wires: rho_dim + 2 * num_ch_wires] + + # index for summation over Kraus operators + kraus_index = alphabet[rho_dim + 2 * num_ch_wires: rho_dim + 2 * num_ch_wires + 1] + + # new state indices replace row and column indices with new ones + new_state_indices = functools.reduce( + lambda old_string, idx_pair: old_string.replace(idx_pair[0], idx_pair[1]), + zip(col_indices + row_indices, new_col_indices + new_row_indices), + state_indices, + ) + + op_1_indices = f"{kraus_index}{new_row_indices}{row_indices}" + op_2_indices = f"{kraus_index}{col_indices}{new_col_indices}" + indices = {"op1": op_1_indices, "state": state_indices, "op2": op_2_indices, "new_state": new_state_indices} + return indices \ No newline at end of file From 24c6354e69ffd1bf027e77a962af09840f016b75 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Thu, 11 Jan 2024 17:08:57 -0800 Subject: [PATCH 54/89] fixed code issues --- pennylane/devices/qutrit_mixed/apply_operation.py | 2 +- .../devices/qutrit_mixed/initialize_state.py | 1 + pennylane/devices/qutrit_mixed/utils.py | 15 ++++++++++----- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index bf0da5d5ab6..422ec6307f1 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -25,6 +25,7 @@ alphabet_array = np.array(list(alphabet)) + def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: bool = False): r"""Apply a quantum channel specified by a list of Kraus operators to subsystems of the quantum state. For a unitary gate, there is a single Kraus operator. @@ -137,7 +138,6 @@ def _apply_operation_default(op, state, is_state_batched, debugger): return apply_operation_einsum(op, state, is_state_batched=is_state_batched) # TODO add tensordot and benchmark for performance - return apply_operation_tensordot(op, state) # TODO add diagonal for speed up. diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index e08f1d49c1c..fdac17376cc 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -16,6 +16,7 @@ from typing import Iterable, Union import pennylane as qml from pennylane.operation import StatePrepBase +from .utils import qudit_dim def create_initial_state( diff --git a/pennylane/devices/qutrit_mixed/utils.py b/pennylane/devices/qutrit_mixed/utils.py index 8e0a14e7292..edf42a4eb5d 100644 --- a/pennylane/devices/qutrit_mixed/utils.py +++ b/pennylane/devices/qutrit_mixed/utils.py @@ -48,11 +48,11 @@ def get_einsum_indices(op: qml.operation.Operator, state, is_state_batched: bool col_indices = "".join(alphabet_array[col_wires_list].tolist()) # indices in einsum must be replaced with new ones - new_row_indices = alphabet[rho_dim: rho_dim + num_ch_wires] - new_col_indices = alphabet[rho_dim + num_ch_wires: rho_dim + 2 * num_ch_wires] + new_row_indices = alphabet[rho_dim : rho_dim + num_ch_wires] + new_col_indices = alphabet[rho_dim + num_ch_wires : rho_dim + 2 * num_ch_wires] # index for summation over Kraus operators - kraus_index = alphabet[rho_dim + 2 * num_ch_wires: rho_dim + 2 * num_ch_wires + 1] + kraus_index = alphabet[rho_dim + 2 * num_ch_wires : rho_dim + 2 * num_ch_wires + 1] # new state indices replace row and column indices with new ones new_state_indices = functools.reduce( @@ -63,5 +63,10 @@ def get_einsum_indices(op: qml.operation.Operator, state, is_state_batched: bool op_1_indices = f"{kraus_index}{new_row_indices}{row_indices}" op_2_indices = f"{kraus_index}{col_indices}{new_col_indices}" - indices = {"op1": op_1_indices, "state": state_indices, "op2": op_2_indices, "new_state": new_state_indices} - return indices \ No newline at end of file + indices = { + "op1": op_1_indices, + "state": state_indices, + "op2": op_2_indices, + "new_state": new_state_indices, + } + return indices From dd189987d73304d8c2f449fda1ce736106b4ccba Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Thu, 11 Jan 2024 17:43:15 -0800 Subject: [PATCH 55/89] remove unused import --- pennylane/devices/qutrit_mixed/apply_operation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 422ec6307f1..3f71823eacc 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -14,7 +14,6 @@ """Functions to apply operations to a qutrit mixed state.""" # pylint: disable=unused-argument -import functools from functools import singledispatch from string import ascii_letters as alphabet import pennylane as qml From fb919e023af1eb37a6adf8f0f0d9688013d498e3 Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Thu, 11 Jan 2024 18:00:17 -0800 Subject: [PATCH 56/89] Fix naming mistake --- pennylane/devices/qutrit_mixed/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/devices/qutrit_mixed/utils.py b/pennylane/devices/qutrit_mixed/utils.py index edf42a4eb5d..e959fed6d1b 100644 --- a/pennylane/devices/qutrit_mixed/utils.py +++ b/pennylane/devices/qutrit_mixed/utils.py @@ -25,7 +25,7 @@ def get_einsum_indices(op: qml.operation.Operator, state, is_state_batched: bool r"""Finds the indices for einsum to multiply three matrices Args: - obs (Operator): Operator to apply to the quantum state + op (Operator): Operator to apply to the quantum state state (array[complex]): Input quantum state is_state_batched (bool): Boolean representing whether the state is batched or not From 6d5302082bf07d428a4f4c18126099715be9eb9e Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Fri, 12 Jan 2024 13:57:54 -0800 Subject: [PATCH 57/89] Remove accedental refactor Co-authored-by: Mudit Pandey --- tests/pauli/test_pauli_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pauli/test_pauli_utils.py b/tests/pauli/test_pauli_utils.py index 076643b4fa8..68f58c9cfb3 100644 --- a/tests/pauli/test_pauli_utils.py +++ b/tests/pauli/test_pauli_utils.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """ -Unit tests for the :mod:`pauli` utility functions in ``pauli/.utils.py``. +Unit tests for the :mod:`pauli` utility functions in ``pauli/utils.py``. """ # pylint: disable=too-few-public-methods,too-many-public-methods import numpy as np From 3884e7ff49c7f16e86d8cf5df8b48acd7b1952ea Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 12 Jan 2024 14:19:12 -0800 Subject: [PATCH 58/89] replaced qudit_dim with QUDIT_DIM --- .../devices/qutrit_mixed/apply_operation.py | 8 ++++---- .../devices/qutrit_mixed/initialize_state.py | 18 +++++++++--------- pennylane/devices/qutrit_mixed/utils.py | 3 ++- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 3f71823eacc..958cf9604f2 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -20,7 +20,7 @@ from pennylane import math from pennylane import numpy as np from pennylane.operation import Channel -from .utils import qudit_dim, get_einsum_indices +from .utils import QUDIT_DIM, get_einsum_indices alphabet_array = np.array(list(alphabet)) @@ -47,13 +47,13 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: kraus = _get_kraus(op) # Shape kraus operators - kraus_shape = [len(kraus)] + [qudit_dim] * num_ch_wires * 2 + kraus_shape = [len(kraus)] + [QUDIT_DIM] * num_ch_wires * 2 # Compute K^T, will be list of lists if broadcasting kraus_transpose = [] if not isinstance(op, Channel): # TODO Channels broadcasting doesn't seem to be implemented for qubits, should they be for qutrit? mat = op.matrix() - dim = qudit_dim**num_ch_wires + dim = QUDIT_DIM**num_ch_wires batch_size = math.get_batch_size(mat, (dim, dim), dim**2) if batch_size is not None: # Add broadcasting dimension to shape @@ -101,7 +101,7 @@ def apply_operation( This function assumes that the wires of the operator correspond to indices of the state. See :func:`~.map_wires` to convert operations to integer wire labels. - The shape of state should be ``[qudit_dim]*(num_wires * 2)``, where ``qudit_dim`` is + The shape of state should be ``[QUDIT_DIM]*(num_wires * 2)``, where ``QUDIT_DIM`` is the dimension of the system. This is a ``functools.singledispatch`` function, so additional specialized kernels diff --git a/pennylane/devices/qutrit_mixed/initialize_state.py b/pennylane/devices/qutrit_mixed/initialize_state.py index fdac17376cc..d247a5f9567 100644 --- a/pennylane/devices/qutrit_mixed/initialize_state.py +++ b/pennylane/devices/qutrit_mixed/initialize_state.py @@ -16,7 +16,7 @@ from typing import Iterable, Union import pennylane as qml from pennylane.operation import StatePrepBase -from .utils import qudit_dim +from .utils import QUDIT_DIM def create_initial_state( @@ -54,18 +54,18 @@ def _apply_state_vector(state, num_wires): # function is easy to abstract for q Args: state (array[complex]): normalized input state of length - ``qudit_dim**num_wires``, where ``qudit_dim`` is the dimension of the system. + ``QUDIT_DIM**num_wires``, where ``QUDIT_DIM`` is the dimension of the system. num_wires (int): number of wires that get initialized in the state Returns: - array[complex]: complex array of shape ``[qudit_dim] * (2 * num_wires)`` - representing the density matrix of this state, where ``qudit_dim`` is + array[complex]: complex array of shape ``[QUDIT_DIM] * (2 * num_wires)`` + representing the density matrix of this state, where ``QUDIT_DIM`` is the dimension of the system. """ # Initialize the entire set of wires with the state rho = qml.math.outer(state, qml.math.conj(state)) - return qml.math.reshape(rho, [qudit_dim] * 2 * num_wires) + return qml.math.reshape(rho, [QUDIT_DIM] * 2 * num_wires) def _create_basis_state(num_wires, index): # function is easy to abstract for qudit @@ -76,10 +76,10 @@ def _create_basis_state(num_wires, index): # function is easy to abstract for q index (int): integer representing the computational basis state. Returns: - array[complex]: complex array of shape ``[qudit_dim] * (2 * num_wires)`` - representing the density matrix of the basis state, where ``qudit_dim`` is + array[complex]: complex array of shape ``[QUDIT_DIM] * (2 * num_wires)`` + representing the density matrix of the basis state, where ``QUDIT_DIM`` is the dimension of the system. """ - rho = qml.math.zeros((qudit_dim**num_wires, qudit_dim**num_wires)) + rho = qml.math.zeros((QUDIT_DIM**num_wires, QUDIT_DIM**num_wires)) rho[index, index] = 1 - return qml.math.reshape(rho, [qudit_dim] * (2 * num_wires)) + return qml.math.reshape(rho, [QUDIT_DIM] * (2 * num_wires)) diff --git a/pennylane/devices/qutrit_mixed/utils.py b/pennylane/devices/qutrit_mixed/utils.py index e959fed6d1b..94912f24e78 100644 --- a/pennylane/devices/qutrit_mixed/utils.py +++ b/pennylane/devices/qutrit_mixed/utils.py @@ -18,7 +18,7 @@ from pennylane import numpy as np alphabet_array = np.array(list(alphabet)) -qudit_dim = 3 # specifies qudit dimension +QUDIT_DIM = 3 # specifies qudit dimension def get_einsum_indices(op: qml.operation.Operator, state, is_state_batched: bool = False): @@ -70,3 +70,4 @@ def get_einsum_indices(op: qml.operation.Operator, state, is_state_batched: bool "new_state": new_state_indices, } return indices + From 3f1acd6a0299b203fed8595951bd499defac252a Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 12 Jan 2024 14:32:08 -0800 Subject: [PATCH 59/89] Fixed docstrings and added a NotImplementedError --- pennylane/devices/qutrit_mixed/apply_operation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 958cf9604f2..e3000405e9c 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -148,7 +148,8 @@ def apply_snapshot(op: qml.Snapshot, state, is_state_batched: bool = False, debu if debugger and debugger.active: measurement = op.hyperparameters["measurement"] if measurement: - snapshot = qml.devices.qubit.measure(measurement, state) + # TODO replace with: measure once added + raise NotImplementedError # TODO else: if is_state_batched: dim = int(np.sqrt(math.size(state[0]))) @@ -176,8 +177,7 @@ def _get_kraus(operation): # pylint: disable=no-self-use Returns: list[array[complex]]: Returns a list of 2D matrices representing the Kraus operators. If - the operation is unitary, returns a single Kraus operator. In the case of a diagonal - unitary, returns a 1D array representing the matrix diagonal. + the operation is unitary, returns a single Kraus operator. """ if isinstance(operation, Channel): return operation.kraus_matrices() From b9c5f8b200bfa083a3e92e087e1ecaf914362790 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 12 Jan 2024 17:02:43 -0800 Subject: [PATCH 60/89] Moved state prep to conftest file to be fixtures --- tests/devices/qutrit_mixed/conftest.py | 49 +++++++++ .../test_qutrit_mixed_apply_operation.py | 97 ++++++++---------- .../test_ref_files/one_qutrit_states.npy | Bin 560 -> 0 bytes .../test_ref_files/three_qutrits_states.npy | Bin 35120 -> 0 bytes .../test_ref_files/two_qutrits_states.npy | Bin 4016 -> 0 bytes 5 files changed, 90 insertions(+), 56 deletions(-) create mode 100644 tests/devices/qutrit_mixed/conftest.py delete mode 100644 tests/devices/qutrit_mixed/test_ref_files/one_qutrit_states.npy delete mode 100644 tests/devices/qutrit_mixed/test_ref_files/three_qutrits_states.npy delete mode 100644 tests/devices/qutrit_mixed/test_ref_files/two_qutrits_states.npy diff --git a/tests/devices/qutrit_mixed/conftest.py b/tests/devices/qutrit_mixed/conftest.py new file mode 100644 index 00000000000..e940b9e273e --- /dev/null +++ b/tests/devices/qutrit_mixed/conftest.py @@ -0,0 +1,49 @@ +import numpy as np +from scipy.stats import unitary_group +import pytest + +SEED = 4774 +def get_random_mixed_state(num_qutrits): + dim = 3**num_qutrits + + np.random.seed(seed=SEED) + basis = unitary_group.rvs(dim) + Schmidt_weights = np.random.dirichlet(np.ones(dim), size=1).astype(complex)[0] + mixed_state = np.zeros((dim, dim)).astype(complex) + for i in range(dim): + mixed_state += Schmidt_weights[i] * np.outer(np.conj(basis[i]), basis[i]) + + return mixed_state.reshape([3] * (2 * num_qutrits)) + + +# 1 qutrit states +@pytest.fixture(scope="package") +def one_qutrit_state(): + return get_random_mixed_state(1) + + +@pytest.fixture(scope="package") +def one_qutrit_batched_state(): + return np.array([get_random_mixed_state(1) for _ in range(2)]) + + +# 2 qutrit states +@pytest.fixture(scope="package") +def two_qutrit_state(): + return get_random_mixed_state(2) + + +@pytest.fixture(scope="package") +def two_qutrit_batched_state(): + return np.array([get_random_mixed_state(2) for _ in range(2)]) + + +# 3 qutrit states +@pytest.fixture(scope="package") +def three_qutrit_state(): + return get_random_mixed_state(3) + + +@pytest.fixture(scope="package") +def three_qutrit_batched_state(): + return np.array([get_random_mixed_state(3) for _ in range(2)]) \ No newline at end of file diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 8d9f08bd340..3d11ef7ab38 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -38,26 +38,6 @@ subspaces = [(0, 1), (0, 2), (1, 2)] -def load_data(num_qutrits, batched): - file_name_start = f"{num_qutrits}_qutrit" - if num_qutrits != "one": - file_name_start += "s" - - file_name = f"{file_name_start}_states.npy" - path = os.path.join(os.getcwd(), "test_ref_files", file_name) - data = np.load(path, allow_pickle=True) - if batched: - return data - return data[0] - - -one_qutrit_state = pytest.fixture(lambda: load_data("one", False), scope="module") -two_qutrits_state = pytest.fixture(lambda: load_data("two", False), scope="module") -two_qutrits_states = pytest.fixture(lambda: load_data("two", True), scope="module") -three_qutrits_state = pytest.fixture(lambda: load_data("three", False), scope="module") -three_qutrits_states = pytest.fixture(lambda: load_data("three", True), scope="module") - - def test_custom_operator_with_matrix(one_qutrit_state): """Test that apply_operation works with any operation that defines a matrix.""" mat = np.array( @@ -89,9 +69,9 @@ class TestTwoQubitStateSpecialCases: # Currently not added as special cases, but will be in future # TODO add tensordot back after it has been added - def test_TAdd(self, method, wire, ml_framework, two_qutrits_state): + def test_TAdd(self, method, wire, ml_framework, two_qutrit_state): """Test the application of a TAdd gate on a two qutrit state.""" - initial_state = math.asarray(two_qutrits_state, like=ml_framework) + initial_state = math.asarray(two_qutrit_state, like=ml_framework) control = wire target = int(not control) @@ -112,23 +92,23 @@ def check_TAdd_second_roll(initial_input, new_input): new_input2 = math.take(new_input, 2, axis=control + 1) assert math.allclose(initial_input2_rolled, new_input2) - initial0 = math.take(two_qutrits_state, 0, axis=control) + initial0 = math.take(two_qutrit_state, 0, axis=control) new0 = math.take(new_state, 0, axis=control) check_TAdd_second_roll(initial0, new0) - initial1 = math.take(two_qutrits_state, 1, axis=control) + initial1 = math.take(two_qutrit_state, 1, axis=control) initial1_rolled = np.roll(initial1, 1, 0) new1 = math.take(new_state, 1, axis=control) check_TAdd_second_roll(initial1_rolled, new1) - initial2 = math.take(two_qutrits_state, 2, axis=control) + initial2 = math.take(two_qutrit_state, 2, axis=control) initial2_rolled = math.roll(initial2, -1, 0) new2 = math.take(new_state, 2, axis=control) check_TAdd_second_roll(initial2_rolled, new2) - def test_TShift(self, method, wire, ml_framework, two_qutrits_state): + def test_TShift(self, method, wire, ml_framework, two_qutrit_state): """Test the application of a TShift gate on a two qutrit state.""" - initial_state = math.asarray(two_qutrits_state, like=ml_framework) + initial_state = math.asarray(two_qutrit_state, like=ml_framework) new_state = method(qml.TShift(wire), initial_state) def check_second_roll(initial_input, new_input): @@ -144,21 +124,21 @@ def check_second_roll(initial_input, new_input): new_input0 = math.take(new_input, 0, axis=wire + 1) assert math.allclose(initial_input2, new_input0) - initial0 = math.take(two_qutrits_state, 0, axis=wire) + initial0 = math.take(two_qutrit_state, 0, axis=wire) new1 = math.take(new_state, 1, axis=wire) check_second_roll(initial0, new1) - initial1 = math.take(two_qutrits_state, 1, axis=wire) + initial1 = math.take(two_qutrit_state, 1, axis=wire) new2 = math.take(new_state, 2, axis=wire) check_second_roll(initial1, new2) - initial2 = math.take(two_qutrits_state, 2, axis=wire) + initial2 = math.take(two_qutrit_state, 2, axis=wire) new0 = math.take(new_state, 0, axis=wire) check_second_roll(initial2, new0) - def test_TClock(self, method, wire, ml_framework, two_qutrits_state): + def test_TClock(self, method, wire, ml_framework, two_qutrit_state): """Test the application of a TClock gate on a two qutrit state.""" - initial_state = math.asarray(two_qutrits_state, like=ml_framework) + initial_state = math.asarray(two_qutrit_state, like=ml_framework) new_state = method(qml.TClock(wire), initial_state) w = math.exp(2j * np.pi / 3) w2 = math.exp(4j * np.pi / 3) @@ -178,15 +158,15 @@ def check_second_roll(initial_input, new_input): new_input2 = math.take(new_input, 2, axis=wire + 1) assert math.allclose(initial_input2 / w2, new_input2) - initial0 = math.take(two_qutrits_state, 0, axis=wire) + initial0 = math.take(two_qutrit_state, 0, axis=wire) new0 = math.take(new_state, 0, axis=wire) check_second_roll(initial0, new0) - initial1 = math.take(two_qutrits_state, 1, axis=wire) + initial1 = math.take(two_qutrit_state, 1, axis=wire) new1 = math.take(new_state, 1, axis=wire) check_second_roll(w * initial1, new1) - initial2 = math.take(two_qutrits_state, 2, axis=wire) + initial2 = math.take(two_qutrit_state, 2, axis=wire) new2 = math.take(new_state, 2, axis=wire) check_second_roll(w2 * initial2, new2) @@ -195,7 +175,7 @@ def check_second_roll(initial_input, new_input): @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @pytest.mark.parametrize( - "state,shape", [("two_qutrits_state", (9, 9)), ("two_qutrits_states", (3, 9, 9))] + "state,shape", [("two_qutrit_state", (9, 9)), ("two_qutrit_batched_state", (2, 9, 9))] ) class TestSnapshot: """Test that apply_operation works for Snapshot ops""" @@ -207,7 +187,7 @@ def __init__(self): self.active = True self.snapshots = {} - @pytest.mark.usefixtures("two_qutrits_state") + @pytest.mark.usefixtures("two_qutrit_state") def test_no_debugger( self, ml_framework, state, shape, request ): # pylint: disable=unused-argument @@ -261,11 +241,11 @@ class TestBroadcasting: # pylint: disable=too-few-public-methods """Tests that broadcasted operations (not channels) are applied correctly.""" broadcasted_ops = [ - qml.TRX(np.array([np.pi, np.pi / 2, np.pi / 4]), wires=2, subspace=(0, 1)), - qml.TRY(np.array([np.pi, np.pi / 2, np.pi / 4]), wires=2, subspace=(0, 1)), - qml.TRZ(np.array([np.pi, np.pi / 2, np.pi / 4]), wires=2, subspace=(1, 2)), + qml.TRX(np.array([np.pi, np.pi / 2]), wires=2, subspace=(0, 1)), + qml.TRY(np.array([np.pi, np.pi / 2]), wires=2, subspace=(0, 1)), + qml.TRZ(np.array([np.pi, np.pi / 2]), wires=2, subspace=(1, 2)), qml.QutritUnitary( - np.array([unitary_group.rvs(27), unitary_group.rvs(27), unitary_group.rvs(27)]), + np.array([unitary_group.rvs(27), unitary_group.rvs(27)]), wires=[0, 1, 2], ), ] @@ -280,14 +260,14 @@ class TestBroadcasting: # pylint: disable=too-few-public-methods qml.QutritUnitary(unitary_group.rvs(27), wires=[0, 1, 2]), ] num_qutrits = 3 - num_batched = 3 + num_batched = 2 dims = (3**num_qutrits, 3**num_qutrits) @pytest.mark.parametrize("op", broadcasted_ops) - def test_broadcasted_op(self, op, method, ml_framework, three_qutrits_state): + def test_broadcasted_op(self, op, method, ml_framework, three_qutrit_state): """Tests that batched operations are applied correctly to an unbatched state.""" - state = three_qutrits_state + state = three_qutrit_state flattened_state = state.reshape(self.dims) res = method(op, qml.math.asarray(state, like=ml_framework)) @@ -310,9 +290,9 @@ def test_broadcasted_op(self, op, method, ml_framework, three_qutrits_state): assert qml.math.allclose(res, expected) @pytest.mark.parametrize("op", unbroadcasted_ops) - def test_broadcasted_state(self, op, method, ml_framework, three_qutrits_states): + def test_broadcasted_state(self, op, method, ml_framework, three_qutrit_batched_state): """Tests that unbatched operations are applied correctly to a batched state.""" - state = three_qutrits_states + state = three_qutrit_batched_state res = method(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) missing_wires = self.num_qutrits - len(op.wires) @@ -331,9 +311,9 @@ def test_broadcasted_state(self, op, method, ml_framework, three_qutrits_states) assert qml.math.allclose(res, expected) @pytest.mark.parametrize("op", broadcasted_ops) - def test_broadcasted_op_broadcasted_state(self, op, method, ml_framework, three_qutrits_states): + def test_broadcasted_op_broadcasted_state(self, op, method, ml_framework, three_qutrit_batched_state): """Tests that batched operations are applied correctly to a batched state.""" - state = three_qutrits_states + state = three_qutrit_batched_state res = method(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) missing_wires = self.num_qutrits - len(op.wires) @@ -356,12 +336,12 @@ def test_broadcasted_op_broadcasted_state(self, op, method, ml_framework, three_ def test_batch_size_set_if_missing(self, method, ml_framework, one_qutrit_state): """Tests that the batch_size is set on an operator if it was missing before.""" - param = qml.math.asarray([0.1, 0.2, 0.3], like=ml_framework) + param = qml.math.asarray([0.1, 0.2], like=ml_framework) state = one_qutrit_state op = qml.TRX(param, 0) op._batch_size = None # pylint:disable=protected-access state = method(op, state) - assert state.shape == (3, 3, 3) + assert state.shape == (self.num_batched, 3, 3) assert op.batch_size == self.num_batched @@ -385,9 +365,9 @@ def compute_kraus_matrices(p): ).astype(complex) return [K0, K1] - def test_non_broadcasted_state(self, method, ml_framework, two_qutrits_state): + def test_non_broadcasted_state(self, method, ml_framework, two_qutrit_state): """Tests that Channel operations are applied correctly to a state.""" - state = two_qutrits_state + state = two_qutrit_state test_channel = self.CustomChannel(0.3, wires=1) res = method(test_channel, math.asarray(state, like=ml_framework)) flattened_state = state.reshape(9, 9) @@ -403,24 +383,29 @@ def test_non_broadcasted_state(self, method, ml_framework, two_qutrits_state): expected = expected.reshape([3] * 4) assert qml.math.get_interface(res) == ml_framework + assert res.shape == expected.shape assert qml.math.allclose(res, expected) - def test_broadcasted_state(self, method, ml_framework, two_qutrits_states): + def test_broadcasted_state(self, method, ml_framework, two_qutrit_batched_state): """Tests that Channel operations are applied correctly to a batched state.""" - state = two_qutrits_states + state = two_qutrit_batched_state + num_batched = two_qutrit_batched_state.shape[0] + test_channel = self.CustomChannel(0.3, wires=1) res = method(test_channel, math.asarray(state, like=ml_framework)) mat = test_channel.kraus_matrices() expanded_mats = [np.kron(np.eye(3), mat[i]) for i in range(len(mat))] - expected = [np.zeros((9, 9)).astype(complex) for _ in range(3)] - for i in range(3): + expected = [np.zeros((9, 9)).astype(complex) for _ in range(num_batched)] + for i in range(num_batched): flattened_state = state[i].reshape(9, 9) for j in range(len(mat)): expanded_mat = expanded_mats[j] adjoint_mat = np.conj(expanded_mat).T expected[i] += expanded_mat @ flattened_state @ adjoint_mat expected[i] = expected[i].reshape([3] * 4) + expected = np.array(expected) assert qml.math.get_interface(res) == ml_framework + assert res.shape == expected.shape assert qml.math.allclose(res, expected) diff --git a/tests/devices/qutrit_mixed/test_ref_files/one_qutrit_states.npy b/tests/devices/qutrit_mixed/test_ref_files/one_qutrit_states.npy deleted file mode 100644 index 0834947cbaaac5d65c77ad2d1bea98f1264026f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 560 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlWb_FuA`uymS0p-l$aNvUzCyxl5k7RDNY57 z7iT0EqyqUG#ySchq^YA&t3VduD)3xgEuV79KH=;sMlpL18;z6IlF?HZ?AOT^vSsM`%k4L7&2QP4yfE5WB5h<- z=d$IKa7Ekx?L6gz1;5tWAF;iwdZ1+K{@RI#9cH&T+QZ~`Lge?C-OM<=BL9MYmEEQ# zdxDJiwD@fg61AOeADo=i!=BU)*Iy6OzaLG0zultU{wpJ{+TUuI44I#(wCAz3!I`5c zZrC4BYT6L*o@-;dBw$C=&HeVlJHLF(?^?A#bi1p8R`QnpeTOyFLQbvPk0x)w+WMNt z3XjY79;{D}_jp9^X^2*XEc87`|T zA*<%8pe!pdA?p(w&J1@CaSaXk^8Ww+g`Rs*gg5KYBmCSM-mKrR+#oMO|3^+#zd}{SL6CiRBZt0TF(t$GLk^U}+f^yjs){j0;YDQu*bD z0r;Z0Y&smL3bw7fR=xrC#-wh=aH^ry!pxm7iacSM8d8)r!!~`{j0W!Z<`Ch3^PZGtTY4pi7`7d(GY02`h8d0)D{YLx~{#i za04yXb1Hu4q9FEla?AX?(vTxvQeNWe1JVofdET8-#T!|CpN2j&(QWjefmy&7R6Z=y ztefYFRDEPfeX#sl)URUJVbhO*{QUeKU}4aFwK_T)UMA>%tGABAn^I?0raiaA z?nv#TjYc{kf4jUG+z9WMj$G1qH(6DIoC6-`l}I5Gq`s^ru|2gBF|qS9%Lm(V}LPX=1213hHd# zw=`A<&HbOb-z)XQw$|oDvs7cT`KQIZ(sdS)^xbL4p-E?0(AxWCt6>@h1zQ;|yORmG zcJH2TZE6AoD1OX1(-k`$(i`Ge>mkp75W#KXgP}W9-v!$-!SM3$AQOH!h*kKko#YY$ zt`F)wJ=SqF!|ibpcKz_a8;7Th3Q;#K4&(JbSCxmPU`}pOrz3wnswi;ut#$}y_s=8J zKR~MwHW!*STogY9dRa>rCacZs`1DL=``~CGgm5Zi!p+)??nO5Ufl;oq@oUfY zK%-Jby7yHeXhbub(#~Z-@BNj+LuUZ0F8x|=$0Y~n_ipp39d*J_V%cipcalMB>)x~T z+pN$c=Dub|S^%2$5)`y$!t@}QU>4DtSf(vo0Bk)@EhdPr#W?29Add=;P{_tnS z$KZ=-IJQf#$ocLV1NWbPRXh~zhA$cnUUT!uvgg|@GT+$!BS`uOY4rgHXKZ0?aSb?Z ziruH)D$vm&dp$D-BVk{7RgG2z1N^uXv-Zb_!!xH1I~OJgfJcnK*oeG8N(+|u_6{@9 zMrfkl%+M7CHpTzu?6Jo0^8?02R%F0}H>ze0eSxf>vrEsu{)-3EgVyT4&!VvZN|Wz0 z*#Kk|3!b=QxEVQm&RVF1M?iV;{1^7GqfjR6r#Sy?Kh!=E993uH14(POiVFLJajd3% zv;K5C44zRe^wtZ-gHtJMgoP8)r_H_TMq?OQ7b`e+tVxEX<)fEB8b`wM@{&h_u@R^n zc*(5pY6QgAeVbSJJPa>B%u`4dTmk|Uiz?R!*}zW;*Yx9h49p3*_)hGqAI7CV=$P0a z2(#PPC%bO9!F_{M_qrV8FrRU2LUt$u7{Sdq!;Cdy*1-3H$^CxNp0-jy^qL!(^(reB zR!f6Thx}881V>z%vpQnrKM4#s-FZ!Nn=h_1{nq`{B?2v$oT;sH^aQ7)2@A8AI{~$S z^G)`?fEv>X%JwXa{7W00e`p*MjnYVOE z>sVmd@>!Dx>1H@}W438ykq;)_opqyW%nc0tJSVT2$3p)6?yu{uQxMFICkM?v@QnZL z_4ztps3n%A?at7F_wg5YdU#l1^WJY(ruM;DbE{jb-`p2OHtN(Y9@Yfe00#S9b=C+$2sI}oMRn}&L|m{9xew&9}%!Eoo$DX$l9w$Scw;c-ba z37>2b8d}~R1OB{eFP>Ng;Eh-ri+~YlEam2RsP;~SsDDeejxSCJ0dMQAIULdO;jG$i z-3?I?+OWoY^j0{&Pw-eE*pv+4(spHCzRScsD>GFQiwIEi)-~C-G7OkD-@bQUj)B0h zPkJ2Z2Z37UMtteH6*GmtKQdY8fqGLd3v1DvRsY{Vmv#r>$}3{JYK`8ouyuCaoAW7f z*W|RT)h=naKG;t50aE+NitHbx%{O}gAgw;|-}gg(f1GQfK*aiK$G|eRqX$=8#)UiL z$B~Qsl~;tJ)Jzfa6+Iy+V{d=4@o)rQ8Swm=sBQw+mlqsYJra-4);0RgOA7@?9~@EI@aBp!=Jemtp4T1) z*ZvDtxyj`R(?Pd=Fg^}#&vGw4wlEQQD};x&Up0cEqg>`MGIBs|b<`2jNK4R4mDL+L6~Pghis#FeE8m?BG%zxq@ zbi6wnq&R1YPi=_8%o#;1u9l@>)EiUlQ&kq|-LNLds*r)zhd1oColHcglFxdTV-eul z^nB3~=@hnp^C$X^t`F$@2Wj&SX#E3Jea1+Az`y8Tlu=MU^w0guTs?iI-Cg+dQ^k|< zxL_^MzhEa@W(@q`Kx++!!f3p;nvj8fX-iuyvvqp;;@2q zm*k0bc(sgw!PoHR5dK6orFmrp9#0l+kDqYI{0XPX>GLs|lEG7*wQxDsefO*O_%jdQ zSvLGzuqKRE*GEse-NIn8@09JL)?~=bb>*lz=7uMVN((e=tik9&&GedNbvV)zJYzsH z0%Us+%!$#7!XL9A8!!7Ai*as)-HDDCI2N?;P<_1_^v^N;aQK!LhPy}JYQ4D)f(z<* zS#9%$kU9L~8^^tn@m*DMRlWko)(TB^muaH(ya!^lpIG7J88aehIHuF}HC?~a^#ObT zEGGL0sQH#i<{Q0#fU3`YQXlxT2;~dHYrtQ2g5%~)QME|Fn~w~4#eiJLONl(MGA;tap&>bzhjaNP-nS0?`xM#c-(WNX0d5DWH|e&u7w;R$(aIvF7u#1q$$?GbmuHBpP?Mm@eOPjipy-yg44d zjUO*g$c$>NT!wEH_{(1W@BrcNg%Sw?VQ|M8DqmdmfE(w<_xSO};L9(UE-XmphKqMj zolf*jKvBKrQNh-(usouHH|u9K^zAm*-mxVaxqPZWtT`MH+Slz$+W;)E8VJNfX zxj)?6AF5OB&BO}NRPg}u0HFCFp!D^7qOXyr-+?IxLTK=n@_=^vog z2i3#w^h)N|fZYBmkMPsdYR9_GL$42bfKBkD+~=d3I8}Z^b-_mkJfpev(XHbeu-sPo z%~7ok`13R9q41McxGVnPe#aaSSTo)))hwcihqa}**Mk(!>~HJ0wD8AQB_?0CHD+VS z8P4A93MV{0G}%4V%MZrec1rXg4}y}*g4=#rnPYfsmQ(V}AV{dT^NtwXioea*8(zvx zgemK+hh^te(B;GQqHf_>2(7D?9gqvh<)1kxmE2~a}FD*VWz5C}< zmqIrfn7M7ywo_r4SB76(4>$tAz`)lzYjE$hinQtbC~#pOn^#@Q0Nc9I1s`QCaDIou zhSa^=;n(_WbN^{4!XaOiZ>A&RD5Ab(dXbGeW((JCHC*8fG=GYe|5-`=4_jX^Bl;RB z{kDwgH=y*vDWVU6wtv|3O@z!hr25C6^bgSL1NI!tia11SU}F8EtXiSh?LQBEd?dlz z-y09bZx^37qVME@w-Vri|>AJgwHINGVUpzNk4I5~LqHJ-4Cw5j(Ce^!R!)MR67 z-tjoNbC&t*P=gsZx=8H|@nhoZFMsc4JhMS{y{HJEB!6sO_Tos!EGGD5mmRJO3&Xzq zd9BqoYWUz!(Da89*A!X>2b!DJx$B& zor2-g41@MZUGdoKg$ov$#KP>G_jt@Nd!w{U`jQDRdk{Dy>O0ps0JHWk5xU`>&gKW_ z2tPo|pBfT>3Y7oxApQp^eH}yeHBkEPHPLT$eZby7Z^-@u+I*w;57O#`X96x?e#v(T zW;An}ncFW?8#kJ%_)1b6b9r(Dh9pveM|aJngMlB^T-(PfK4J$eH8L9nP0X?T-&FK? zy&nkT`r$hnjxZG|WYk_64By1Hjr$ktpu!VmzT;$q#KwzyxTOe*4k@K2>LuQUnA5U zOt*Mz-ct94B$auE+;5ri>G1cHk$Zg6k6Fz8&r}zy&L42P(ZGTE9kK7NWI~bRJI#de z0F588{pn2NPl4utfYR58h`y%lH=yYQr0pN1%{O-cl#%`cT7B@eT;1IfjYANsV;!=` zQAEvVrB<)NGGAQ2__n~;!fhB7efsJ-g)qF7rIxxuA_ZR;-My`0<%g0obprptYK426JFTbS!46*#EE`H3_o4d;zFRLFv^XsVx z{Qj)!s>Pyey~>hr-kpp>r6V7QzW%ov>U^8^pWjM_s|OryM z7)w{=ihdJ2CKLuG<@+`*mUV>IpzB!QjhE)HQk^VudKJuhK@Uq--ft*Yg zY@TuOfvBmFnx9FLF~evP*fo~4rt-#Mg(PGBtM8$Z?Vf}^yf(-^PiOXCLk6DtxA@a} z$rxlldA}q5j1#Q={=jUzF%xh8r!h6E5P|iUcLYXNBjMkH*>8B;JmJVs?A>@I5MK!! z8cfdez>%!ndAu`1@b7$HCPOq57H?kmST4mB%a4!v9aC_F&21lBecTPeXrFeZoRtX# zzSZ`~uXP8T)4X?9Y)r*jOZ0eh)x4oR`me?2SLSf`+OcCEYSHjlLO9yzKX(|8=}=U5 zP=l4@md6?#Qt*`fnH51Z-ROLj&EIAb{>J7zhX~(6iXVI;`~WF`>O}l0+yDGW{0~t2 z`YO@aKX@qf zmg}|J7yNr~2!7fcjmozM`3_D-!mFrZC8@}GtoVLi%fB)luiY}!`Exu3*6|K&-tgAN zCe^(;N-vG!q~po>eMh~)$#Jbh%tSb7&6S(+-oOGJwb~?9PkLZ%o3_S0JvV3@(4I^X z)`#e|S^=h0Ol0QkES?aG#KhmzO|M;IfVa8XZf>w2Or$EQ^mExjRnnP}ZM$VaBmVC4 zIeM$$vlkNhw;q};7y!qXZXev z->u)OC~WM2M>myPH$3wPjSo^9FIO|M_JmfFFNY&86yBV~wImvzx1QJ-xX=qSLr*Px z-r)<+yMw|+CM_}T`@!4EFU(Lnx@XIM(=aSZY1teIreJfjW%~7`JJu+Dlg76ZF!g2V z@vB`1z=-h^k^9dY3SIH`ekSfM))_k;2p<#X$}csh(zo*yrW_s1B+c_JImxPV-K_xVUb6Q)e#Z4tz#zq)$zKRq2H~+t?c|~isU~?e z&PUn&t)K8WI^SXQgB-#SfaXt;@;?mXf9U#}uHV@DK!xZ7_WoH<_770=?JAjX^!`Cw zeXv<$;znPJ$1LzkYcN(8extIjxj*@DH+{dPvo2$VTk zc&p^E5h(wO8vCJUjd5?*jOF=7fwhgA+l{&ibnw-@FclvJVoK%4pL5RrK34FVD_TO^{nJj;(+cR-mj5e#zyOzC>i%$cAPoM@6~40c7!#$Z84R;C8J*!*P>I$4WRqTsUIot7_d+? z`nZ8_0$$VTfa3B{l?Y@&O{&3 z_YZr%4U_qXRR5Th{sF2!DWpE|dQIt()c+_ZoMIJZU2*Nf{AkFsw~ye?Oo3lwn&z|jSfWMq`RAW_ z-OBg>yM?mN{uS!!Zs$i8tEO_-4+SP<@{TepYb}mAD&?Lb+4I zd1)C)%eU$IDbn&er1GB~B>!RO3u7c-K#E`M5`K*oA8jOj6e<2DN%$L5d`E`x9X3Ds zP51%bpR)arBJn?LeLYO{HC?{}O&=g_{{U^i0j+<4s!t-R5Ae(Bt18!3!NO=U}l6}vGGlZw0R#~zHdOeDdHDBKX-P0+` z;hN?kzhL9RMc>nr$N5Qh|F%%f)(C$!?PCKfZyc}Q@^b^hFLGo085VGf|D|1|nkQ@t z8~S+rY6Q~m59s+eQu%2c$xngG=jM@oj-LM@Eni^s>*Iu9BgID_5J7z*9qTY z^MfeD50LVw*N8u5`yXE7e}Ja1kZ%FGOq}2yg#KnT%Kdpi5 zhjF1uAGcb}?V}Ts^E1)U6&YXeIiiM{O4T!E23GOn;jEbZ7Z^43mX1vsz@AkmOsN(v znkSU)uIB&bQ^!9-R~fEA2NsBhXC#V0BQL)Qu%2( z$xo5W=S)aGN6&wdmM;Lsua^;i4HO?WC43Yp{`QdYH#XndL--C-{2+kv1G+z@`yaZ# zMw)&Dnm$0<{$bCzM`XSst$%>3PY|gO#K|{)Hr}-lRtj`pt9roSQG4DeLq3Opp&6aP&qPGGe`U{I=pl?$QGOkLjLRQ$C_CxiSu(=APyBVck=?X*JA(Zz)LK zS80&@D*F8nQuiUN$bAU={=kjgA0RE?X6L8UBtNC+bL{-bo8&+2e8H9E3v_;s6dw&B zd=x4E=12G&QhaAN;X6p<2T1d$Nco>6;(vhB*HuJcv-O)B(Qj;hU`+G@eg6P$z5%U& z)T#PNlKNoTkY{thd<~TELrrIog&mF$ZS+^&X5i?>@Tbjt#5X!$nK@>8Vp zxf+ts(eodq@&z}NF95}_FB5)^6d(OW_$ZyfA&u|Q`2pLXt|0!D?SE8>|6%Lv*F;~l z_1i+C-`M(KlIR2a{-Mt|r26Lv=^vo#bDh)&#ynPQ52#haU6<#2e2m#@7TGyBt_B9- z$1n}v?Xpau-H!rwpQuXi6Op=qdrt1(fOcPn)cwu@a=$~r524>5u=DLQl5f-VQ>5~_ zOp?#h^B;P?fHZ#1=A#V4M}gvRLWI8o#dmHJzQg7RdkH@P%AfiWe@gd1bbZa%ZvjNV zAx$69_YctK8@+#ks?P^fAH1BNd1lf2V{mv+tZ=67+>UMjkJr!KTMf=~6ABS8Mz?%R zToYWv>jBMkyF13L(%}99|2tRq1maL^Ic|7k11*9M;%dm!r5iL&lFu;l=;TISk8TJ2 zwx#;Py_b>DyUl2cO64X@x_xFx|3Po`GQ6r466}a|H@BrOp2l z{pG@zPz>x`6@`hMx_sR|e$X?Vn6mI}Fa#y;YrNZI4YS@f>N{(jLc^|z%pyfi-0Qne z)%rM#mv<&ySzWRo?uvZi{C&p+BM&$nc`KFQ1XyypGUEdRnBMdW@Z`09uy7uP7SC+n5b8>ZP#c_L>6tOvV+J7?!yzW`5 zTVe=~#TGNgP6R;?v&)S8Pz2<6bQNi2Tf-9#zpaxCV&L3{!z)XNePH*_hUFtq85p|h zXHn1$A83%BndP-L1=c$TmbQ6E;l)jw&(cK%@Zq~6|5pRSNUIN;&v|UIqTmqRtWlp8 zx9d-PUXaY`D0c>G@X0H5I~GEr)nM#0pPkrrDbCgDZY+2u^prmtPXdOIxS`zR6lhz# zf9cZJ3cibHfLQ51IA0^?=wc9Xkvjsf;*3zf_ z#Ty>_E*f2{z75VEH+<0`8V^-^KX9ejy9Yq{4KS-+&p3$EF;7VK*$j=zbXPz8tzcrSn zR&N^(S9aAkZ>&#*x?dG1hR%CY^BY;JWt>{Ssj0LUN~x>dnY@U84LrZ6ZxKR zV$jbd=dZ*4T;%M0%rpHq9DZ%get$A79cPzau;!VW%JTaIN?!{6@y6lWCZCu2=vH-q z>A_|ROnr^JMn0tCmTx(a#423ziA#MW8acp+vecQ!=Iy~Y4dbUe|Bgky5?P-d#|&AzhVO zx(*#}REC*12nbI_F8x#1UEH3dh6;JgHN}8UL>U%j~;iqPv-_D;}@-5 z{@dDdNX<8OGT-R^gS7hKw&zpazgcgy=O#vh&O4&FQQ%bml! z`$t1zXV8;5v$DN##^R(00-YvsFXiqA*)%KE<@kK^<}4j3kGppVsX~0l?RPM4>}J1#RB6w#m3f z;7N{so5h8r@o3Jx!QnIU__5QAb6a9IXsplrdVOIWUNAfO{)na)yz%?JZ+S5THx%1f z&H0dnhL!)#n5@!)hJ<9zE+==qZ|pDR%PO8OYshiAFeG)^K)bUJ&SgHUO#@{8aS&i27j~( z$7i~VUsu@Jq1Wy+m(%}-z~}o{d-N*pFz6sNTlBCql%H)~*!0pH>_!cq`;P0v{nIN$ zDmfXrpBcpU(AE{W4?Qc~)DVLK30sZkeqjK$eJ-463K{+xs&VKj;C{fwCw>c1#=_RX=~|ict(ff~uU>J9fi3ecU#@-N zkJ^^qdMz66a8Wwo%m>>XT(LIz;QYsSm|xAGe&5X(D(5V1x0+>*LRH&lKKg2h+Iu~? z{L{Tbtl=Do&^jCVXP@$M+qy&?Yd_X9mbwPEt1RwXx+V^3`hdQF*z-+*%r~I=XBX)o zpz1S9>I1=}zedyp%HiCx_giJ`_|#-0T>kOPgaLA;=bkCE1f%kfMQ9tUT8v@@K3Wx>;&&Z8fG1mgp# zxl3iU4PjrJ+I~M_BNT1zk}S(%V2AMoCrJ~QJ~@~5AT_4-t9Ww$7N_1*m1^V|ryvfe2XjO|f8%5{$W*GMpqn)XX^l!GbRig9&! zFI*J2LWrk67Ug9F#cq7)Vw?V_#I|t$) zF!7F|ddkw-Ht;1st6-I~7yi5HdsjRr6{Jf7K}ypT+IDFqEt`%&K`HCa?GHmiQ1oQY z*%!fhYez|sY0x zV)6CtH}4iEfqcI~{p}1dn1Gtnf{`%rxF;tRWXGDbkvAQmz6ik|e`ZRTh3dk=7b{xT z1maLsZa|CktP_-rNJM;XOvO^+hTw1RHgtVW*KbJE2lV{|wE2d#{sCHj;JaObxxacX ze9xFCrt7n!qbE%1kdjysmKu%*eUS*m8D%xqH!8(&=-0xVgCAqC*P&=d!sQIipI`NK zm3B0wUeq_%+F}W1&09RjS@&frg>!2Yj`$$g=6nO4SsSr$@A0bE&(SFU_`ITsM<6(c zu6y(200Sk)ygSago1opI_>>cQS=jw-|3ZW66znWEnK|oSG8*hM>zf@FiG@5d@it0P z*j^~vAG*v7qRdXH9URRD(><#v_MBs2*UmZt(Nks^5%%=gIYZX{Z#OnH2xa24Wvj9h z)T1GA-qJr!Avr94?`r$t*Ba#3;)+lg3k1c4xpsb)>5%Y1KlJP?XUuAsS&&}885Gjj zwG}i)VrcDfyoQJku1q+7*TpXiFVrs@%hhJ0YAau`Vt5Lg8h;xSofF9RKS{*@Af>NY z5q*u6ew#z|8_@Itd;jE-{llJb`^bC)TK@o5pJGxUDEJzAZuRp@IKI`QOY<#%$H{^6 z(-#3y<<$>$tv6m!c~E{!B99*W42kDG)wRS-y`ewv14DtSC0Vw&kM%w4njZ&sb5mgW zp1JhT_GENzPg>NvD;M!7>@au``YjG#Dm7CkHLRBy)k@CQ;I0BE!-Y@nZRUy zPxtC%dwsrA0LI8%d_d$`5K6U1&9g6L!b&y0mkZ<)@J?nyNJe`wzUP|B=eVB%UTu?!#qHVXGyB4- zCzaeCG77g;dS;w}2$a!+_U7~ji15QcW2 zhW$liGEuBKDr4=+RP0;#qP;=P2aK;gJ|gcIfuEb*Y@fC;QC{5DsOK&dmVeY+E#YYl zx^8bR?raT!_WoUb2WMx)^?jX!!wr#W?eXx)sCO93Tu9Wo6dVH_r_ZbHco7DywJ&+oMpzv|eTKqc)o#)DnIGG=Ivv@B1JBgOt7wA^I9A{bo<}8(SX)5Pd-3 zKlJ&ARR4I8{sF2!exyDue`~M4o2Lp~RAqlWKhD>Y)%W67g|P?Fkv&3E1&$OPA~O6!<=v z*RoSN79uWrZhFG{ZnEoNSNty3B>3-l=c6of8(KS1g0Q$$|_rQaSA{l?Y@ z21Fmw_YY9>Et1SPp!E+>^%)`c!H-v>Qjc$}fx7%nrhl9zI*w>pc8YC{fw9-SxtBbbS5ctUD zo1Fk>?$_>qI^~SkoIFR>SDL`Bul#|A@7!^1{W`f)MQ0X)4H9=4p zR1j#tHyVa(gLYgKaz{1MCAX{;^@w zN}tRzNSHhL&N>-z`1>+rR&NqsRX!Tywr@R-?~_ccn->m#i}-r)jYeWX2ZzN47Uvl@ z)q610${9~>KP!P7d~wdEa~JlGx!}FI%8aM2EFT!BIK;&nixeNVAbb>Q{Eg0cfZ_*0 z_yOCW?j!ybY5s?;uO*1Srt3GlJ^*U}$dmm;pKnO@PY3BAr0Qcu>Vwl?&%RC_I1I1k z0dZ z@F8eqr;mIXI@V;BFXOj|u@d!%zfUHj{d2Qe9!Un+ZE%Td9P)&*1N%&;x-8+5k8Wto z)p)2W(>eR=cp~PXwqp)>#^MY-Dprmun0Z$R`L(DVV)_78o&A+3Lq zs!sx`55!~?w%EJWK+Lzr@7Ei1s4Yytu`EzN06)EZT2yJ32-jbEZBjoS2Tp6{m&e^> z@eMy&%@_!VMRHb33WFKImD8(Gv@{JbA1`3~9dJhh#kTuFjw~N;-0q-NF}LH{)rKC9ALT_=3etHpyHP0nwwm>R zfYbTrby=H0*|>bKg_a{+lq%>Kyf7PPS$YgJccnr6jh(537o6Dn&y?o>vp;2-O|7J(EYo`s4GxNq`)_2nsJ)NfH$sFqDcQ^Orr269^xr^EW!* z0UAF*%AdX^{*>;2kkZ$oL|+4?->wk-#?rh0qYv2o$ByhD`g~*e&s@?!K&ubBd@}C4 z5LyQ}Ri(Z(4E<|Q@!w!5nH`S(_f6*CH&cX^)$5e94;sM%E8D}$-vVK9`Ji8RmNRl& zE?sZNdOs)pa;pyaggdN!?RntWhahyh>vY~OI~j_l+_ul^bwStIO`cEW1M$|GOR-k} zTro)Yw&0HITEOwKqWYtW4yN7BDlL_ALn|v0kz+iup!2rmbH-E@9M~Q{V(s9MwEPsP zd`^wzb4bg7kjfWaNWQ@4*J*@bv-#*X!bgF|-`IR-JK;M(@q_h>G>Se@*jG>z~H`*MWQ{NGIs~6aZML*8 zd!KYPQt{4n5gg!_Tg0)32yFXyT6g9ACm7UJAjXq~1uG)==ORRxA{KYl$20 z&9)yYiUjKZV1nEq04?8U=cfWBKV|20Ye+sv&wqf*7pzFWz~>u`g6D0EuX#In<`k)bGe%-h8 z)v(lMuKeTqoNDgR`sN6*zNgh4x;Vr2a406!>ij;s(*wjH~W?nQ9i= z)ljVZf4&&w z#$*2Uj15-UF5j`EfQc<a4t))9aD!ga>G(##VD0yJ5$@g7{qq@sQM*vsm7Qfy$4;bnO9E zr0%QElKU$9{SN&;gnfVDL+%gg`8Lq5i{Naa6iB>!RO3;ZNsVDsy1gkJ;2M?DB1 zrSms7-|;7Wht3ax@~2hAp90PQAf>NeiM|F(zx5FPM%M>O?H?|(f7tV_jLbKB{{U5= zouob>wL^2sab_+2+BD-=oHX{*8WL1+@De`h5uf{s5_bdp^mxk(Qq#mCrFqJ_l6( zGjrMh3(HBqfHZ!MG(O7aZ=;020gdml`GFqc2W)?uK>R7)|FHEnFVWX*{dS1x zH@ZFm+Ww)>H=z2bhx8B9>Vpqfq^#nOs(^(`!QTJb%uw6cW>xiF)c`FsEo8%9`Cw3t zVB3zCZQ!YWD4^-5C;dJVsQWiZa{mUj`zrSR&Ovg&gS7h)`uzb?`L;O8x7qn=C&^EN zmd_!T|KyPThn+9%Ao&8)_%%>`l!Ne5I)4L-?{pEq!{!GmgdZT~Px**H1)Be1>+4lS zU$gbwYogzfrVrTrr@_I$fQ<{MJ|vyk);Q1vk<^?`}%mqx!ts^D^#VetKgC2DPL z?!pSvc2IDUNB7OJ7troUfp(t=)cxC6a{tD@ukt7NRrLEE`h5t}?hk;{wJIG zAGW?$BKjIB{gy`b8(kl;_m4W+KdkrN{%5`c)j#f}e~?xmG&{U?A)jI`7z~sNKD#cY z_VS`^)T+rtP`E2-PCvA7DOmPb?C4u2)_Hzzp2Y}9DKT@6YOWAmb1rt|JBv$x`!{1} zf`Bb}Iw?E9za55;1I4FICL@>c7y-{0Z(sIZbvxwz z$@3Q;%Rz>iE0mn}(F{8lZ>RC&U z1@5R&KWy+u$ppTLAJP4urj1dG3zk;MB*9t5uX}7*?_z5DS`ArLGoT_(XiMB~TjS@y#Aq=r4BUJl!?cFh(v{V+>sGwAt2;1t~Tpg zBs2 z0wY;rOLHzjzuq^eXR*G|;p=rOWF56b%f9^6x~#lcCZ&7z!gPD!72}*$VHF4?sgF(@ z?P0xFTb?S}u^3C`55S9B z4r}Uqndsjouq$bsH&iydGTYyUV(GNhhfld?us!>4-0T@aFxRU@DF0L{x@rgU=t_jZ z-a)~>ph!(T5?VakKF!LNqrNFFVZD#rJ9zw=o?0fT8vow)eN!Ham!EtfGn&TkpS7fa zkgCr~QXkkMQ!k}8u^%k{N-j_pncZ?^>w_^J9BL@Y7T^W8{<>@cy^I-s4g*-Z+ut;ceoHC7%iem`82l z@>tbKqjwV6TuJ%2$jkyXH=H`@78n5oJRjd*D~-!zkUCj#fcCeUUTw-janJ0Ma9lwaqx)uOg=AI+HH8V^P?qJJGl)Gn6moi zP`>#U*7p+GYqEQ@eo5k$*rf_nto-wQ%Uv^}pW5IvytHDhE&!%J+qswM$AKv0$fn5M zVbDCo*e$=)3*svFzn;U&+x*u4&9>1G!qdw+S2pvhA~oOK$$SH+WY&dEMCO*HJL zFGsJrwc8@sQde>3)J!m{=@s9}6NM|c+?;c(tM`K7j7>=a^-OpcxW&lVA{?!@ba*Os zIN{2R9;f=AxC6C+(#ifo+I*w;4^s6hAoan>Cqb&_i)+AhTC7w|?pOP&^}TDk3=;8{ z$3oLDn_c0q{ARb4F0A(%zl%<6D^9~HzosmqaVGHd_`l*`n~LK{yPRGZdP1Dzp_bii zVxYNaNz*T@bujzxBOQZZo-h*MZ&u##R;+8W<6j({UuANm>t_YN}b2f%{(VDObJZ28Y`GzyDQC z$60@an%DFPqTYnlRA#Fc&TULMSu&ao$N&2_bbnDeit5`w{Pvag&W+OM@y|AP;Jaz} zC8fY9ko8O-%qa_l;H;R3yL%bnc;ekmyRWXWPpemhuiYDbR=7RqYV-p|@uBGNA6ft3 ziY1n}FU*X@bra#ih#Z;4qQ;F|0QRl&8 zuVtD(c=6uR^i67s*tOJ3dnt)!C#@1EwaE&i$lN7k4>-+U?uR#dg~YpI$* zOwl&I_EbldFc=V>AK`}*3Yke}-HA}}^82i^k50hj@WC=)+>Uim?0;2B))51Ped@(u zX@Rhe;+YWE_YBE>0{`0I1tRMLTSUW24>(oB#_u=!;>EHJ&iaS)aMpjn%T3bLV76jJ zn7%_4lzbmK*Rw1ELPQu#gf?jc*AU;ihc{wS?V4f)V?zLVFFCR1dwnFX44p3F(6zy( zqKu(amxE#9<6~0J$(dMJ^y|u)U_4y-&rf-wL;wmdnU()KBNopkUrK#8KOMBbgsiyk zoC|S});c`ajewC9zefTybCIUsfTj=V`v<7`wwla0r1cNb>VupN=5AwMUgQQi6hey4vryYN>W}a8dNeQX+SDTk=^hr(ex_PD9R8TG)ao@tnT$Z zYkl{>aQ51JU)S&2&wVcbHy9vzLSt|_v zv$g8lJuh%pHP`*q=7u5{^3IB%ip0Bv_T6EA$EP%TM# zJuExh+^KWH5|tJ14CTsiMQ)FRaLUK+s3~dtCuCG8Q2H7v{RWgip!W|w-{}4Usy=u| zwXw&}pa6RAg=e166RV0Xc>K&jCI}+OjCCvsvIi;YQpxiX!KhRah?&9mjn|&_$O!KHz_}WA` zv({Lox`PkPmJbJ(r1SB}oEpcCDGPCHgPHd%fqtDQKVs317|$o#4az zpU1@iu=={4=xbKL%^~`Y)(7nVi6;Apo^N#jAXA^+q&_&QYQjSY$9&)%%FDXXkudrr zdsunu-FCtKgn6l$(0LWXyYyH7bX410qx?G86=8z=+dZYZmoDddAlK^KgZJO9!OJ>* z@7t^`@M)*W4)sf6pt)Q{e*fV(%$Gb}`OMA(uk02vep=*)l{$w4i^8`-lY|Z!JEdaN z&VdUsQW?YY3cSs90lEjnOr$F8A%2;zsl;&`c&uwAthCe}%l8DYERl9cFt&q$czbxW z)a&bkv2i#%*oZh(YPlC;A-M?gCC&SwYtCLFUT)f@0ta*fj zABrRzoo?J_jgz=oxNbx=?N4d{!|LnzL|-GN-;mM=^!{Pz+g38)km(;~(mz1e2hMkk z8fdBK!tWSG>(?k;HExSp*|47m?v=H@THE%0>A|8qJx@F4vt5WcP}j$@U>oKknP*I4yUaG)2!!#+QpH%oP%jJ@f`^#S0BK;p;G=uKdvC z-XK(&cV9_dVd-GYhpi8xFXNOJ%NXTMh~L9A^(i3*M0*S0g(h?S;0w zoEa?_xHvWU+|5c47g)IP2PbRsQfyXwsFIMtgD<)rg?b~LKxxmj-|N5ez-x_(W6UQX zEF9W?_<~zFR?ok;xz5%E>ryW5%y1Wc_qS29rKfN$?y5*1S`iS4+xkmIZX^iaFYediH8T3ml;}5L^uZyb50KhFK+QLx`UjZ$ zIFR~OU-x~9HBU6*`V0~+Tq^6bN z{n6+zu2(l=;OhCAj-lSLr#543#Z$pMy;}z4?+WhZM-Q~Q=4~-XQY-=lxH z7<*GcU8p!g@VlRjj(r{k{m)E__RR``?AyO(I?6rKN-=4LYMMLD+Sb0}P-qy=)N-;M z*%^uZkDf1iea8(bzQghZU&0S)e~OI%IYj&qQ2H7v{l@Bp0iq9p**`L5|FHAzBAIVM z^$$?>fxyron(9?2;2Os&PCrh?$kVswhH1Uvj!-zzMf+ba+Minede@5Q=FByDkkQR1$gAl7?bWK(4`AbDK61>0B^@2@znoZ(dNmR7vg4oG|nE?@lRU> z;P&*Y|GZXS7eC_y5v^Oktq`?FH|O%&laHOi>U^(U!LKD~ zn=vDG)fmA$z3uZQn~NiG=+T**hi`kK!xVYWwYV4b1t)XLBMAJ5u4A{peDyDZzLya#B!SO$cVL={D?I9D_>=H4;@aBG9CUbIsd@3x6JB zzauwJ!0Yn+T$e|ISZMmJ!|@YfnzXs4c1HrP85-VO{?!Z?-4n|47~YK;JMM7J#QeeF zmeIkVX96H3u;y9kl0a0SnzKw=E(Tsr>oz;l$3cdV&L(`6=5NUG9b3Y8Xnw%@({kcZ zY5zm(YozoWs}G8ZJ^*U}05#v({%Irq15AA)Nqr#2>}}7hmR$I_l53$kOSmdI%NxIT zdBQ)ULFGUE*5K(xuDRm#2=E#$v`^YR2-}W(?NkwV!X^iW-)~ZFp}*zBD^a&Z>{Y%R z)G95wuRr=g>SRs;IE&P-A3qRc)EkMg#JWI0|-t)VG?J6#m+BI63T7w@axB>2C_ zmd?nD=C-Tg4sWdP(Q-a`4heBA1m7{eh|4*k-QP)pA7JXUm(&OPmpwP@_s;>xS3C7nbzT@Y zMNfUR)?zjY&)+fR+G`FI+v?jAb9r#_>79c29qu^2-R!58;Qvx87Y}cmv{CT?97W1^ zw+C(lvCA_Dem?TUYVBd!@Vn~Z^Hm|}xPgF6Y;1p`xG)&B7VGm1d?T9To`_nA^46P59_sk z^M*3Hl-w}caGZQVp|XAJF>;nE572<{L2m<4O7lnED8j`rs|KR|V~t z^I^@|_xrl^zZh0s)b22d;|bmoa(eo>#tj6Z2&iZ*Hl$bv%pFE^VJH+c%yN2quutoJHW$u7N@Tz1dR^H$)_Gt1%J-GU-Dx@ zVC@l&tsW{K@NRZe25&tNh9-?nSuHXTT6gre7dvf4S?ebmTTDX0zr}IN9VH%0?U|%! zBlx~-z2pwQUZ&u?Lx~^{bo<}8>=y|u+Q-vh_bhkD|7G2>BYb3L$nmQ8;9 zp&)n~II!B}MJV=mYmU8>VgyqS!WEJ}?}Ai&rTeQBw_)~CiE%>tJ~;B`@SpVQfv6x; zrCH|6$FQy?Zy%{|hlyY2W@f90qWG^TAJw+=!QXFfYF?NLJeZ;LxFU!z$bTiq2G1Ff z+r#~r{0v9CiMYT)8NLOH^)Iw@yO#m{8l+Vd-Xc8 z25Z#J-M>Y7kOy@s9vd}sxBwq6KHoG|aNi#E%PA^RA6Bc1PD$^Ng3{cHX@RcmG2?*M zLmPh&xcGVPtnt^~@b-;*@1qHBDDxz=CSPzL+^5sr|6@ECd%RcVUK@^qp8SaA%E~;* z`@$KFs<*;Vmqf=s`ndrM6PG5u@ePFEJsO=oCj{T8|9^f8R6d7H{-a0oA2wf*Ciw!* zuYuyDG=Bqz?}QV+L-PaHpYA07l=VNN#Q)Ix8Y%ro>jPl+&q%U=fSGR>$b4h_=Pl_U zVCthq>VvP76_ol$6ha>N#68Ql7Q=zT#wq4kJn_2T{i<=Yua`a_#T#DK>MzI{1B%@< z;^F+N!beTBgTQlxmh+)(PbiUYns)XWvuq(2 zR=bLwuwDuQ={(cMGAme+VeozVR8LHkiW=UV9|O;S`=2%w*#I~9O|44H>COpnfgd5|E&*HOc-aos5%Gs zwT8^nDH&Upt*SY83HsnlJaxGKlLz)`aD3K8t;8mY&G+9--Uc7nzpPa0iUg7R^gYw0 zVo{}ZwT^0N1eo;321nnvLkGvn|0=BNuTF5+7BMm5f!X8BDzEGf;nNcRpw1q5_C7>t z-rxHWpzaTV%D0iqPl3tjTuDAh=Ra(|z#;hp&99N+qn8LDW%-*5;cv+B9eu)gfZ+#z zgdec}^f&RRtpBMe{)g7r!05LCqTi6w2Wdnfu={5p*+0n4H$O7p=>9>bKBlBT&^dmb zm&>U_2%W}X;D24-=1N@<c9Hr(c(K9MxsV05c53rQy6;sceHd-stPzN&>XNzr zbL=3)-XZyxSr~o@Y+KtfbjZGUK#3OTG zwSe4LA$7mQ-iL6>eF!r52jb-Z0GWJy6Un!c$xjPNe#+)^-$_2l=0Dd-{sTLfF_*2^d zu==`}=xe0(8&diJsQtsvw9>bKB=TW`0A9EvA5hQ*w}Hx;^ks3qo7Znesv>M z5fALo)Kv2am5Nc<8bA9$&ky+lbDubZ+$Xa4Zx6}+8#4D*QRKb~nfskr} z4+$anA;8=ptRVLXNafpfe#+)^H6)({D*s{g1vQc{0K>1{2){;(kFxx&i|{v^@6h}J z8Gm}8_)}#3&u8L)SbeQX^ffU0?G@2)v_4?>PZZfd$jrA(WWKTe(@OdWnEF(a`rym) z4H5BCC&0?%?!jQ)&y_tdvz1g6c<6gzhT`vqa_s%+Q*u9w%zffCa-T@wzX5Y!rAh9q z*!!Jka=(MreF!l32O8x5fX=ts{8WzQr^w`Ub|jx;^Phbr|3M~SFd+E?Q2ZJgKDwXq zQI@}X6aI!2-(mSdJmCkdKUM7g>rZL_!|LlgqOV!~)<^UkQu=`1Kex#K0cO7aAoC5W Z{z0lfurtC^V|{EsG`x4885RAr@_&87PoV$+ diff --git a/tests/devices/qutrit_mixed/test_ref_files/two_qutrits_states.npy b/tests/devices/qutrit_mixed/test_ref_files/two_qutrits_states.npy deleted file mode 100644 index 3ef05a6fa2bc87a578df226401e2ba9ad6393b7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4016 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 From 78418a60e629c7d23d6fa8ed0af6d3e6ff7b6d87 Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Fri, 12 Jan 2024 17:23:38 -0800 Subject: [PATCH 61/89] Fix name of class qubit->qutrit Co-authored-by: Mudit Pandey --- tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 3d11ef7ab38..48f1654d20a 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -62,7 +62,7 @@ def matrix(self): @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @pytest.mark.parametrize("method", methods) @pytest.mark.parametrize("wire", (0, 1)) -class TestTwoQubitStateSpecialCases: +class TestTwoQutritStateSpecialCases: """Test the special cases on a two qutrit state. Also tests the special cases for einsum application method for additional testing of these generic matrix application methods.""" From 0e287d1663a1830fbaa6bab20a27a32d9d59b77e Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 12 Jan 2024 18:18:50 -0800 Subject: [PATCH 62/89] removed special cases from tests --- tests/devices/qutrit_mixed/conftest.py | 4 +- .../test_qutrit_mixed_apply_operation.py | 138 ++---------------- 2 files changed, 18 insertions(+), 124 deletions(-) diff --git a/tests/devices/qutrit_mixed/conftest.py b/tests/devices/qutrit_mixed/conftest.py index e940b9e273e..148c77d7e7e 100644 --- a/tests/devices/qutrit_mixed/conftest.py +++ b/tests/devices/qutrit_mixed/conftest.py @@ -3,6 +3,8 @@ import pytest SEED = 4774 + + def get_random_mixed_state(num_qutrits): dim = 3**num_qutrits @@ -46,4 +48,4 @@ def three_qutrit_state(): @pytest.fixture(scope="package") def three_qutrit_batched_state(): - return np.array([get_random_mixed_state(3) for _ in range(2)]) \ No newline at end of file + return np.array([get_random_mixed_state(3) for _ in range(2)]) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 3d11ef7ab38..c302b4a65e5 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -33,8 +33,6 @@ pytest.param("tensorflow", marks=pytest.mark.tf), ] -methods = [apply_operation_einsum, apply_operation] # TODO add tensordot back when added -broadcasting_methods = [apply_operation_einsum, apply_operation] subspaces = [(0, 1), (0, 2), (1, 2)] @@ -59,118 +57,14 @@ def matrix(self): assert qml.math.allclose(new_state, mat @ one_qutrit_state @ np.conj(mat).T) -@pytest.mark.parametrize("ml_framework", ml_frameworks_list) -@pytest.mark.parametrize("method", methods) -@pytest.mark.parametrize("wire", (0, 1)) class TestTwoQubitStateSpecialCases: """Test the special cases on a two qutrit state. Also tests the special cases for einsum application method for additional testing of these generic matrix application methods.""" + # pylint: disable=too-few-public-methods # Currently not added as special cases, but will be in future - # TODO add tensordot back after it has been added - - def test_TAdd(self, method, wire, ml_framework, two_qutrit_state): - """Test the application of a TAdd gate on a two qutrit state.""" - initial_state = math.asarray(two_qutrit_state, like=ml_framework) - - control = wire - target = int(not control) - new_state = method(qml.TAdd((control, target)), initial_state) - - def check_TAdd_second_roll(initial_input, new_input): - initial_input0 = math.take(initial_input, 0, axis=control + 1) - new_input0 = math.take(new_input, 0, axis=control + 1) - assert math.allclose(initial_input0, new_input0) - - initial_input1 = math.take(initial_input, 1, axis=control + 1) - initial_input1_rolled = math.roll(initial_input1, 1, 1) - new_input1 = math.take(new_input, 1, axis=control + 1) - assert math.allclose(initial_input1_rolled, new_input1) - - initial_input2 = math.take(initial_input, 2, axis=control + 1) - initial_input2_rolled = math.roll(initial_input2, -1, 1) - new_input2 = math.take(new_input, 2, axis=control + 1) - assert math.allclose(initial_input2_rolled, new_input2) - - initial0 = math.take(two_qutrit_state, 0, axis=control) - new0 = math.take(new_state, 0, axis=control) - check_TAdd_second_roll(initial0, new0) - - initial1 = math.take(two_qutrit_state, 1, axis=control) - initial1_rolled = np.roll(initial1, 1, 0) - new1 = math.take(new_state, 1, axis=control) - check_TAdd_second_roll(initial1_rolled, new1) - - initial2 = math.take(two_qutrit_state, 2, axis=control) - initial2_rolled = math.roll(initial2, -1, 0) - new2 = math.take(new_state, 2, axis=control) - check_TAdd_second_roll(initial2_rolled, new2) - - def test_TShift(self, method, wire, ml_framework, two_qutrit_state): - """Test the application of a TShift gate on a two qutrit state.""" - initial_state = math.asarray(two_qutrit_state, like=ml_framework) - new_state = method(qml.TShift(wire), initial_state) - - def check_second_roll(initial_input, new_input): - initial_input0 = math.take(initial_input, 0, axis=wire + 1) - new_input1 = math.take(new_input, 1, axis=wire + 1) - assert math.allclose(initial_input0, new_input1) - - initial_input1 = math.take(initial_input, 1, axis=wire + 1) - new_input2 = math.take(new_input, 2, axis=wire + 1) - assert math.allclose(initial_input1, new_input2) - - initial_input2 = math.take(initial_input, 2, axis=wire + 1) - new_input0 = math.take(new_input, 0, axis=wire + 1) - assert math.allclose(initial_input2, new_input0) - - initial0 = math.take(two_qutrit_state, 0, axis=wire) - new1 = math.take(new_state, 1, axis=wire) - check_second_roll(initial0, new1) - - initial1 = math.take(two_qutrit_state, 1, axis=wire) - new2 = math.take(new_state, 2, axis=wire) - check_second_roll(initial1, new2) - - initial2 = math.take(two_qutrit_state, 2, axis=wire) - new0 = math.take(new_state, 0, axis=wire) - check_second_roll(initial2, new0) - - def test_TClock(self, method, wire, ml_framework, two_qutrit_state): - """Test the application of a TClock gate on a two qutrit state.""" - initial_state = math.asarray(two_qutrit_state, like=ml_framework) - new_state = method(qml.TClock(wire), initial_state) - w = math.exp(2j * np.pi / 3) - w2 = math.exp(4j * np.pi / 3) - - def check_second_roll(initial_input, new_input): - initial_input0 = math.take(initial_input, 0, axis=wire + 1) - new_input0 = math.take(new_input, 0, axis=wire + 1) - assert math.allclose(initial_input0, new_input0) - - initial_input1 = math.take(initial_input, 1, axis=wire + 1) - new_input1 = math.take(new_input, 1, axis=wire + 1) - print(initial_input1) - print(new_input1) - assert math.allclose(initial_input1 / w, new_input1) - - initial_input2 = math.take(initial_input, 2, axis=wire + 1) - new_input2 = math.take(new_input, 2, axis=wire + 1) - assert math.allclose(initial_input2 / w2, new_input2) - - initial0 = math.take(two_qutrit_state, 0, axis=wire) - new0 = math.take(new_state, 0, axis=wire) - check_second_roll(initial0, new0) - - initial1 = math.take(two_qutrit_state, 1, axis=wire) - new1 = math.take(new_state, 1, axis=wire) - check_second_roll(w * initial1, new1) - - initial2 = math.take(two_qutrit_state, 2, axis=wire) - new2 = math.take(new_state, 2, axis=wire) - check_second_roll(w2 * initial2, new2) - - # TODO: Add more tests as Special cases are added + # TODO add special cases as they are added + pass @pytest.mark.parametrize("ml_framework", ml_frameworks_list) @@ -236,7 +130,6 @@ def test_provided_tag(self, ml_framework, state, shape, request): @pytest.mark.parametrize("ml_framework", ml_frameworks_list) -@pytest.mark.parametrize("method", broadcasting_methods) class TestBroadcasting: # pylint: disable=too-few-public-methods """Tests that broadcasted operations (not channels) are applied correctly.""" @@ -264,13 +157,13 @@ class TestBroadcasting: # pylint: disable=too-few-public-methods dims = (3**num_qutrits, 3**num_qutrits) @pytest.mark.parametrize("op", broadcasted_ops) - def test_broadcasted_op(self, op, method, ml_framework, three_qutrit_state): + def test_broadcasted_op(self, op, ml_framework, three_qutrit_state): """Tests that batched operations are applied correctly to an unbatched state.""" state = three_qutrit_state flattened_state = state.reshape(self.dims) - res = method(op, qml.math.asarray(state, like=ml_framework)) + res = apply_operation(op, qml.math.asarray(state, like=ml_framework)) missing_wires = 3 - len(op.wires) mat = op.matrix() expanded_mats = [ @@ -290,11 +183,11 @@ def test_broadcasted_op(self, op, method, ml_framework, three_qutrit_state): assert qml.math.allclose(res, expected) @pytest.mark.parametrize("op", unbroadcasted_ops) - def test_broadcasted_state(self, op, method, ml_framework, three_qutrit_batched_state): + def test_broadcasted_state(self, op, ml_framework, three_qutrit_batched_state): """Tests that unbatched operations are applied correctly to a batched state.""" state = three_qutrit_batched_state - res = method(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) + res = apply_operation(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) missing_wires = self.num_qutrits - len(op.wires) mat = op.matrix() expanded_mat = np.kron(np.eye(3**missing_wires), mat) if missing_wires else mat @@ -311,11 +204,11 @@ def test_broadcasted_state(self, op, method, ml_framework, three_qutrit_batched_ assert qml.math.allclose(res, expected) @pytest.mark.parametrize("op", broadcasted_ops) - def test_broadcasted_op_broadcasted_state(self, op, method, ml_framework, three_qutrit_batched_state): + def test_broadcasted_op_broadcasted_state(self, op, ml_framework, three_qutrit_batched_state): """Tests that batched operations are applied correctly to a batched state.""" state = three_qutrit_batched_state - res = method(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) + res = apply_operation(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) missing_wires = self.num_qutrits - len(op.wires) mat = op.matrix() expanded_mats = [ @@ -334,19 +227,18 @@ def test_broadcasted_op_broadcasted_state(self, op, method, ml_framework, three_ assert qml.math.get_interface(res) == ml_framework assert qml.math.allclose(res, expected) - def test_batch_size_set_if_missing(self, method, ml_framework, one_qutrit_state): + def test_batch_size_set_if_missing(self, ml_framework, one_qutrit_state): """Tests that the batch_size is set on an operator if it was missing before.""" param = qml.math.asarray([0.1, 0.2], like=ml_framework) state = one_qutrit_state op = qml.TRX(param, 0) op._batch_size = None # pylint:disable=protected-access - state = method(op, state) + state = apply_operation(op, state) assert state.shape == (self.num_batched, 3, 3) assert op.batch_size == self.num_batched @pytest.mark.parametrize("ml_framework", ml_frameworks_list) -@pytest.mark.parametrize("method", methods) class TestChannels: # pylint: disable=too-few-public-methods """Tests that Channel operations are applied correctly.""" @@ -365,11 +257,11 @@ def compute_kraus_matrices(p): ).astype(complex) return [K0, K1] - def test_non_broadcasted_state(self, method, ml_framework, two_qutrit_state): + def test_non_broadcasted_state(self, ml_framework, two_qutrit_state): """Tests that Channel operations are applied correctly to a state.""" state = two_qutrit_state test_channel = self.CustomChannel(0.3, wires=1) - res = method(test_channel, math.asarray(state, like=ml_framework)) + res = apply_operation(test_channel, math.asarray(state, like=ml_framework)) flattened_state = state.reshape(9, 9) mat = test_channel.kraus_matrices() @@ -386,13 +278,13 @@ def test_non_broadcasted_state(self, method, ml_framework, two_qutrit_state): assert res.shape == expected.shape assert qml.math.allclose(res, expected) - def test_broadcasted_state(self, method, ml_framework, two_qutrit_batched_state): + def test_broadcasted_state(self, ml_framework, two_qutrit_batched_state): """Tests that Channel operations are applied correctly to a batched state.""" state = two_qutrit_batched_state num_batched = two_qutrit_batched_state.shape[0] test_channel = self.CustomChannel(0.3, wires=1) - res = method(test_channel, math.asarray(state, like=ml_framework)) + res = apply_operation(test_channel, math.asarray(state, like=ml_framework)) mat = test_channel.kraus_matrices() expanded_mats = [np.kron(np.eye(3), mat[i]) for i in range(len(mat))] From 72eb40ebad4b1df43b458a9e7bd327910548ea9d Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 12 Jan 2024 18:23:18 -0800 Subject: [PATCH 63/89] Changed einsum indices abstraction to be more abstract for future use --- .../devices/qutrit_mixed/apply_operation.py | 25 +++++++++--- pennylane/devices/qutrit_mixed/utils.py | 40 +++++++++++-------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index e3000405e9c..6040e828694 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -20,7 +20,8 @@ from pennylane import math from pennylane import numpy as np from pennylane.operation import Channel -from .utils import QUDIT_DIM, get_einsum_indices +from .utils import QUDIT_DIM, get_einsum_mapping, get_new_state_einsum_indices + alphabet_array = np.array(list(alphabet)) @@ -37,11 +38,23 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: Returns: array[complex]: output_state """ - indices = get_einsum_indices(op, state, is_state_batched) - # index mapping for einsum, e.g., '...iga,...abcdef,...idh->...gbchef' - einsum_indices = ( - f"...{indices['op1']},...{indices['state']},...{indices['op2']}->...{indices['new_state']}" - ) + + def map_indices( + state_indices, kraus_index, row_indices, new_row_indices, col_indices, new_col_indices + ): + """""" + op_1_indices = f"{kraus_index}{new_row_indices}{row_indices}" + op_2_indices = f"{kraus_index}{col_indices}{new_col_indices}" + + new_state_indices = get_new_state_einsum_indices( + old_indices=col_indices + row_indices, + new_indices=new_col_indices + new_row_indices, + state_indices=state_indices, + ) + # index mapping for einsum, e.g., '...iga,...abcdef,...idh->...gbchef' + return f"...{op_1_indices},...{state_indices},...{op_2_indices}->...{new_state_indices}" + + einsum_indices = get_einsum_mapping(op, state, map_indices, is_state_batched) num_ch_wires = len(op.wires) kraus = _get_kraus(op) diff --git a/pennylane/devices/qutrit_mixed/utils.py b/pennylane/devices/qutrit_mixed/utils.py index 94912f24e78..7111056817f 100644 --- a/pennylane/devices/qutrit_mixed/utils.py +++ b/pennylane/devices/qutrit_mixed/utils.py @@ -21,16 +21,19 @@ QUDIT_DIM = 3 # specifies qudit dimension -def get_einsum_indices(op: qml.operation.Operator, state, is_state_batched: bool = False): - r"""Finds the indices for einsum to multiply three matrices +def get_einsum_mapping( + op: qml.operation.Operator, state, map_indices, is_state_batched: bool = False +): + r"""Finds the indices for einsum to apply kraus operators to a mixed state Args: op (Operator): Operator to apply to the quantum state state (array[complex]): Input quantum state is_state_batched (bool): Boolean representing whether the state is batched or not + map_indices (function): Maps the calculated indices to an einsum indices string Returns: - dict: indices used by einsum to multiply 3 matrices + dict: indices used by einsum to apply kraus operators to a mixed state """ num_ch_wires = len(op.wires) num_wires = int((len(qml.math.shape(state)) - is_state_batched) / 2) @@ -54,20 +57,23 @@ def get_einsum_indices(op: qml.operation.Operator, state, is_state_batched: bool # index for summation over Kraus operators kraus_index = alphabet[rho_dim + 2 * num_ch_wires : rho_dim + 2 * num_ch_wires + 1] - # new state indices replace row and column indices with new ones - new_state_indices = functools.reduce( - lambda old_string, idx_pair: old_string.replace(idx_pair[0], idx_pair[1]), - zip(col_indices + row_indices, new_col_indices + new_row_indices), - state_indices, + # apply mapping function + return map_indices( + state_indices=state_indices, + kraus_index=kraus_index, + row_indices=row_indices, + new_row_indices=new_row_indices, + col_indices=col_indices, + new_col_indices=new_col_indices, ) - op_1_indices = f"{kraus_index}{new_row_indices}{row_indices}" - op_2_indices = f"{kraus_index}{col_indices}{new_col_indices}" - indices = { - "op1": op_1_indices, - "state": state_indices, - "op2": op_2_indices, - "new_state": new_state_indices, - } - return indices +def get_new_state_einsum_indices(old_indices, new_indices, state_indices): + """ + TODO + """ + return functools.reduce( + lambda old_string, idx_pair: old_string.replace(idx_pair[0], idx_pair[1]), + zip(old_indices, new_indices), + state_indices, + ) From cfddb5991bbf74c43cfa04e4d855ec4bbee50d3f Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 12 Jan 2024 18:42:48 -0800 Subject: [PATCH 64/89] Fixed pylint issues --- .../devices/qutrit_mixed/apply_operation.py | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 6040e828694..c6f22f3bf72 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -39,20 +39,25 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: array[complex]: output_state """ - def map_indices( - state_indices, kraus_index, row_indices, new_row_indices, col_indices, new_col_indices - ): - """""" - op_1_indices = f"{kraus_index}{new_row_indices}{row_indices}" - op_2_indices = f"{kraus_index}{col_indices}{new_col_indices}" + def map_indices(**kwargs): + """ map indices to wires + Args: + **kwargs (dict): Stores indices calculated in `get_einsum_mapping` + + Returns: + String of einsum indices to complete einsum calculations + + """ + op_1_indices = f"{kwargs['kraus_index']}{kwargs['new_row_indices']}{kwargs['row_indices']}" + op_2_indices = f"{kwargs['kraus_index']}{kwargs['col_indices']}{kwargs['new_col_indices']}" new_state_indices = get_new_state_einsum_indices( - old_indices=col_indices + row_indices, - new_indices=new_col_indices + new_row_indices, - state_indices=state_indices, + old_indices=kwargs['col_indices'] + kwargs['row_indices'], + new_indices=kwargs['new_col_indices'] + kwargs['new_row_indices'], + state_indices=kwargs['state_indices'], ) # index mapping for einsum, e.g., '...iga,...abcdef,...idh->...gbchef' - return f"...{op_1_indices},...{state_indices},...{op_2_indices}->...{new_state_indices}" + return f"...{op_1_indices},...{kwargs['state_indices']},...{op_2_indices}->...{new_state_indices}" einsum_indices = get_einsum_mapping(op, state, map_indices, is_state_batched) @@ -163,13 +168,12 @@ def apply_snapshot(op: qml.Snapshot, state, is_state_batched: bool = False, debu if measurement: # TODO replace with: measure once added raise NotImplementedError # TODO + if is_state_batched: + dim = int(np.sqrt(math.size(state[0]))) + flat_shape = [math.shape(state)[0], dim, dim] else: - if is_state_batched: - dim = int(np.sqrt(math.size(state[0]))) - flat_shape = [math.shape(state)[0], dim, dim] - else: - dim = int(np.sqrt(math.size(state))) - flat_shape = [dim, dim] + dim = int(np.sqrt(math.size(state))) + flat_shape = [dim, dim] snapshot = math.reshape(state, flat_shape) if op.tag: From 505c6d1f13a0a62ecaa66adc4a18f8701111eaad Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 12 Jan 2024 18:44:19 -0800 Subject: [PATCH 65/89] Refactor --- pennylane/devices/qutrit_mixed/apply_operation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index c6f22f3bf72..7f47e622ab0 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -40,7 +40,7 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: """ def map_indices(**kwargs): - """ map indices to wires + """map indices to wires Args: **kwargs (dict): Stores indices calculated in `get_einsum_mapping` @@ -52,9 +52,9 @@ def map_indices(**kwargs): op_2_indices = f"{kwargs['kraus_index']}{kwargs['col_indices']}{kwargs['new_col_indices']}" new_state_indices = get_new_state_einsum_indices( - old_indices=kwargs['col_indices'] + kwargs['row_indices'], - new_indices=kwargs['new_col_indices'] + kwargs['new_row_indices'], - state_indices=kwargs['state_indices'], + old_indices=kwargs["col_indices"] + kwargs["row_indices"], + new_indices=kwargs["new_col_indices"] + kwargs["new_row_indices"], + state_indices=kwargs["state_indices"], ) # index mapping for einsum, e.g., '...iga,...abcdef,...idh->...gbchef' return f"...{op_1_indices},...{kwargs['state_indices']},...{op_2_indices}->...{new_state_indices}" From 60434dabce14a051a3cd0c26e18b3be9e572f8d9 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 12 Jan 2024 18:58:51 -0800 Subject: [PATCH 66/89] Fixed docstrings of helper functions --- pennylane/devices/qutrit_mixed/utils.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/utils.py b/pennylane/devices/qutrit_mixed/utils.py index 7111056817f..71f92b4b5d9 100644 --- a/pennylane/devices/qutrit_mixed/utils.py +++ b/pennylane/devices/qutrit_mixed/utils.py @@ -33,7 +33,7 @@ def get_einsum_mapping( map_indices (function): Maps the calculated indices to an einsum indices string Returns: - dict: indices used by einsum to apply kraus operators to a mixed state + str: indices mapping that defines the einsum """ num_ch_wires = len(op.wires) num_wires = int((len(qml.math.shape(state)) - is_state_batched) / 2) @@ -69,8 +69,15 @@ def get_einsum_mapping( def get_new_state_einsum_indices(old_indices, new_indices, state_indices): - """ - TODO + """Retrieves the einsum indices string for the new state + + Args: + old_indices (str): indices that are summed + new_indices (str): indices that must be replaced with sums + state_indices (str): indices of the original state + + Returns: + str: the einsum indices of the new state """ return functools.reduce( lambda old_string, idx_pair: old_string.replace(idx_pair[0], idx_pair[1]), From 10c84083845ff7d2be4326119d3f5fffbdfe4024 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 12 Jan 2024 19:54:16 -0800 Subject: [PATCH 67/89] Fixed pylint issues --- tests/devices/qutrit_mixed/conftest.py | 14 ++++++++++++++ .../test_qutrit_mixed_apply_operation.py | 16 +++------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/tests/devices/qutrit_mixed/conftest.py b/tests/devices/qutrit_mixed/conftest.py index 148c77d7e7e..fc4f251389a 100644 --- a/tests/devices/qutrit_mixed/conftest.py +++ b/tests/devices/qutrit_mixed/conftest.py @@ -1,3 +1,17 @@ +# Copyright 2018-2024 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. +"""Pytest configuration file for PennyLane qutrit mixed state test suite.""" import numpy as np from scipy.stats import unitary_group import pytest diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 6e06ac5e230..4b513c31338 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -13,17 +13,14 @@ # limitations under the License. """Unit tests for create_initial_state in devices/qutrit_mixed/apply_operation.""" -import os import pytest import numpy as np from scipy.stats import unitary_group import pennylane as qml from pennylane import math from pennylane.operation import Channel -from pennylane.devices.qutrit_mixed.apply_operation import ( - apply_operation_einsum, - apply_operation, -) +from pennylane.devices.qutrit_mixed import apply_operation + ml_frameworks_list = [ "numpy", @@ -57,14 +54,7 @@ def matrix(self): assert qml.math.allclose(new_state, mat @ one_qutrit_state @ np.conj(mat).T) -class TestTwoQutritStateSpecialCases: - """Test the special cases on a two qutrit state. Also tests the special cases for einsum application method - for additional testing of these generic matrix application methods.""" - - # pylint: disable=too-few-public-methods - # Currently not added as special cases, but will be in future - # TODO add special cases as they are added - pass +# TODO add tests for special cases as they are added @pytest.mark.parametrize("ml_framework", ml_frameworks_list) From cfb1bf6ca8634adb9e87d40fddbbe0792cf7b250 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Fri, 12 Jan 2024 19:57:41 -0800 Subject: [PATCH 68/89] Fixed indent that caused tests to fail --- pennylane/devices/qutrit_mixed/apply_operation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 7f47e622ab0..dfd3dbb859c 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -175,7 +175,7 @@ def apply_snapshot(op: qml.Snapshot, state, is_state_batched: bool = False, debu dim = int(np.sqrt(math.size(state))) flat_shape = [dim, dim] - snapshot = math.reshape(state, flat_shape) + snapshot = math.reshape(state, flat_shape) if op.tag: debugger.snapshots[op.tag] = snapshot else: From 72e0973af09a47541e3f1ebee3e772ea3bbbdb20 Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Sun, 14 Jan 2024 19:01:04 -0800 Subject: [PATCH 69/89] Added comma to fix broken changelog --- doc/releases/changelog-dev.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 0180fe70c71..6d8b103b4c2 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -87,7 +87,7 @@ This release contains contributions from (in alphabetical order): Abhishek Abhishek, -Gabriel Bottrill +Gabriel Bottrill, Pablo Antonio Moreno Casares, Christina Lee, Isaac De Vlugt, From 7645f3730647583ca2a9c15b17eacc458584a8e1 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Mon, 15 Jan 2024 14:00:31 -0800 Subject: [PATCH 70/89] Added test for snapshot with measurement --- .../test_qutrit_mixed_apply_operation.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 4b513c31338..9e04ba018ef 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -118,6 +118,20 @@ def test_provided_tag(self, ml_framework, state, shape, request): assert debugger.snapshots[tag].shape == shape assert math.allclose(debugger.snapshots[tag], math.reshape(initial_state, shape)) + def test_snapshot_with_operator(self, ml_framework, state, shape, request): + """Test a snapshot with operator throws NotImplementedError""" + state = request.getfixturevalue(state) + initial_state = math.asarray(state, like=ml_framework) + + debugger = self.Debugger() + with pytest.raises(NotImplementedError): + _ = apply_operation( + qml.Snapshot(measurement=qml.expval(qml.GellMann(0, 1))), + initial_state, + debugger=debugger, + is_state_batched=len(shape) != 2, + ) + @pytest.mark.parametrize("ml_framework", ml_frameworks_list) class TestBroadcasting: # pylint: disable=too-few-public-methods From be2ac14c22659525c080b3f3f093f8db7e4baa53 Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Mon, 15 Jan 2024 20:35:20 -0800 Subject: [PATCH 71/89] Moved map indices to private function in apply_operation --- .../devices/qutrit_mixed/apply_operation.py | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index dfd3dbb859c..65129f67c4b 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -22,7 +22,6 @@ from pennylane.operation import Channel from .utils import QUDIT_DIM, get_einsum_mapping, get_new_state_einsum_indices - alphabet_array = np.array(list(alphabet)) @@ -38,28 +37,7 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: Returns: array[complex]: output_state """ - - def map_indices(**kwargs): - """map indices to wires - Args: - **kwargs (dict): Stores indices calculated in `get_einsum_mapping` - - Returns: - String of einsum indices to complete einsum calculations - - """ - op_1_indices = f"{kwargs['kraus_index']}{kwargs['new_row_indices']}{kwargs['row_indices']}" - op_2_indices = f"{kwargs['kraus_index']}{kwargs['col_indices']}{kwargs['new_col_indices']}" - - new_state_indices = get_new_state_einsum_indices( - old_indices=kwargs["col_indices"] + kwargs["row_indices"], - new_indices=kwargs["new_col_indices"] + kwargs["new_row_indices"], - state_indices=kwargs["state_indices"], - ) - # index mapping for einsum, e.g., '...iga,...abcdef,...idh->...gbchef' - return f"...{op_1_indices},...{kwargs['state_indices']},...{op_2_indices}->...{new_state_indices}" - - einsum_indices = get_einsum_mapping(op, state, map_indices, is_state_batched) + einsum_indices = get_einsum_mapping(op, state, _map_indices, is_state_batched) num_ch_wires = len(op.wires) kraus = _get_kraus(op) @@ -94,6 +72,28 @@ def map_indices(**kwargs): return math.einsum(einsum_indices, kraus, state, kraus_dagger) +def _map_indices(**kwargs): + """map indices to einsum string + Args: + **kwargs (dict): Stores indices calculated in `get_einsum_mapping` + + Returns: + String of einsum indices to complete einsum calculations + """ + op_1_indices = f"{kwargs['kraus_index']}{kwargs['new_row_indices']}{kwargs['row_indices']}" + op_2_indices = f"{kwargs['kraus_index']}{kwargs['col_indices']}{kwargs['new_col_indices']}" + + new_state_indices = get_new_state_einsum_indices( + old_indices=kwargs["col_indices"] + kwargs["row_indices"], + new_indices=kwargs["new_col_indices"] + kwargs["new_row_indices"], + state_indices=kwargs["state_indices"], + ) + # index mapping for einsum, e.g., '...iga,...abcdef,...idh->...gbchef' + return ( + f"...{op_1_indices},...{kwargs['state_indices']},...{op_2_indices}->...{new_state_indices}" + ) + + @singledispatch def apply_operation( op: qml.operation.Operator, state, is_state_batched: bool = False, debugger=None From d2d4aa2b32f64aff2b1ff03ce1f9fb4df0d15a4f Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Tue, 16 Jan 2024 12:53:57 -0800 Subject: [PATCH 72/89] changed test name Co-authored-by: Mudit Pandey --- .../devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 9e04ba018ef..9c6cf3c9386 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -118,8 +118,8 @@ def test_provided_tag(self, ml_framework, state, shape, request): assert debugger.snapshots[tag].shape == shape assert math.allclose(debugger.snapshots[tag], math.reshape(initial_state, shape)) - def test_snapshot_with_operator(self, ml_framework, state, shape, request): - """Test a snapshot with operator throws NotImplementedError""" + def test_snapshot_with_measurement(self, ml_framework, state, shape, request): + """Test a snapshot with measurement throws NotImplementedError""" state = request.getfixturevalue(state) initial_state = math.asarray(state, like=ml_framework) From 64c4fe117405262ad7c502617ece930e5138e646 Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:52:23 -0800 Subject: [PATCH 73/89] fixed docstring Co-authored-by: Christina Lee --- pennylane/devices/qutrit_mixed/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/devices/qutrit_mixed/utils.py b/pennylane/devices/qutrit_mixed/utils.py index 71f92b4b5d9..c80ef8dc19e 100644 --- a/pennylane/devices/qutrit_mixed/utils.py +++ b/pennylane/devices/qutrit_mixed/utils.py @@ -29,8 +29,8 @@ def get_einsum_mapping( Args: op (Operator): Operator to apply to the quantum state state (array[complex]): Input quantum state - is_state_batched (bool): Boolean representing whether the state is batched or not map_indices (function): Maps the calculated indices to an einsum indices string + is_state_batched (bool): Boolean representing whether the state is batched or not Returns: str: indices mapping that defines the einsum From 8c4b220471b2c9dae60b647196ae6230b737a59e Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:55:14 -0800 Subject: [PATCH 74/89] changed to local random number generator Co-authored-by: Christina Lee --- tests/devices/qutrit_mixed/conftest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/devices/qutrit_mixed/conftest.py b/tests/devices/qutrit_mixed/conftest.py index fc4f251389a..7e26d6b6418 100644 --- a/tests/devices/qutrit_mixed/conftest.py +++ b/tests/devices/qutrit_mixed/conftest.py @@ -22,9 +22,9 @@ def get_random_mixed_state(num_qutrits): dim = 3**num_qutrits - np.random.seed(seed=SEED) - basis = unitary_group.rvs(dim) - Schmidt_weights = np.random.dirichlet(np.ones(dim), size=1).astype(complex)[0] + rng = np.random.default(seed=4774) + basis = unitary_group.rvs(dim, seed=584545) + Schmidt_weights = rng.dirichlet(np.ones(dim), size=1).astype(complex)[0] mixed_state = np.zeros((dim, dim)).astype(complex) for i in range(dim): mixed_state += Schmidt_weights[i] * np.outer(np.conj(basis[i]), basis[i]) From e4cf3114fd20a4678557f7ea87c72a7cf77d75fd Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Tue, 16 Jan 2024 16:06:23 -0800 Subject: [PATCH 75/89] Fixed docstring Co-authored-by: Olivia Di Matteo <2068515+glassnotes@users.noreply.github.com> --- pennylane/devices/qutrit_mixed/apply_operation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 65129f67c4b..aa3d4f1073a 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -73,7 +73,7 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: def _map_indices(**kwargs): - """map indices to einsum string + """Map indices to einsum string Args: **kwargs (dict): Stores indices calculated in `get_einsum_mapping` From 736cfa845dfac5c28f62e1303a9c6c69f88401b1 Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Tue, 16 Jan 2024 16:13:07 -0800 Subject: [PATCH 76/89] swapped np to math Co-authored-by: Olivia Di Matteo <2068515+glassnotes@users.noreply.github.com> --- pennylane/devices/qutrit_mixed/apply_operation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index aa3d4f1073a..c18d4de077e 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -169,7 +169,7 @@ def apply_snapshot(op: qml.Snapshot, state, is_state_batched: bool = False, debu # TODO replace with: measure once added raise NotImplementedError # TODO if is_state_batched: - dim = int(np.sqrt(math.size(state[0]))) + dim = int(math.sqrt(math.size(state[0]))) flat_shape = [math.shape(state)[0], dim, dim] else: dim = int(np.sqrt(math.size(state))) From 9d62090070a52096709fa19bfb57ec313450e67b Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Tue, 16 Jan 2024 16:13:45 -0800 Subject: [PATCH 77/89] changed np to math Co-authored-by: Olivia Di Matteo <2068515+glassnotes@users.noreply.github.com> --- pennylane/devices/qutrit_mixed/apply_operation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index c18d4de077e..831e49609dc 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -172,7 +172,7 @@ def apply_snapshot(op: qml.Snapshot, state, is_state_batched: bool = False, debu dim = int(math.sqrt(math.size(state[0]))) flat_shape = [math.shape(state)[0], dim, dim] else: - dim = int(np.sqrt(math.size(state))) + dim = int(math.sqrt(math.size(state))) flat_shape = [dim, dim] snapshot = math.reshape(state, flat_shape) From 98e9af51baca492302b0ba401498c3e1f8c2ccc2 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 16 Jan 2024 17:29:39 -0800 Subject: [PATCH 78/89] Simplified getting Kraus transforms --- pennylane/devices/qutrit_mixed/apply_operation.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 831e49609dc..84efb917661 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -44,8 +44,6 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: # Shape kraus operators kraus_shape = [len(kraus)] + [QUDIT_DIM] * num_ch_wires * 2 - # Compute K^T, will be list of lists if broadcasting - kraus_transpose = [] if not isinstance(op, Channel): # TODO Channels broadcasting doesn't seem to be implemented for qubits, should they be for qutrit? mat = op.matrix() @@ -56,13 +54,9 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: kraus_shape = [batch_size] + kraus_shape if op.batch_size is None: op._batch_size = batch_size # pylint:disable=protected-access - for op_mats in kraus: - kraus_transpose.append([math.transpose(k) for k in op_mats]) - if not kraus_transpose: - kraus_transpose = [math.transpose(k) for k in kraus] kraus = math.stack(kraus) - kraus_transpose = math.stack(kraus_transpose) + kraus_transpose = math.stack(math.moveaxis(kraus, source=-1, destination=-2)) # Torch throws error if math.conj is used before stack kraus_dagger = math.conj(kraus_transpose) From 6df023ae675a856a72c59563c330898400227bdc Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 16 Jan 2024 17:45:21 -0800 Subject: [PATCH 79/89] Fixed failing tests from incorrect call to unitary_group.rvs --- tests/devices/qutrit_mixed/conftest.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/devices/qutrit_mixed/conftest.py b/tests/devices/qutrit_mixed/conftest.py index 7e26d6b6418..1ab06d40fab 100644 --- a/tests/devices/qutrit_mixed/conftest.py +++ b/tests/devices/qutrit_mixed/conftest.py @@ -16,14 +16,12 @@ from scipy.stats import unitary_group import pytest -SEED = 4774 - def get_random_mixed_state(num_qutrits): dim = 3**num_qutrits - rng = np.random.default(seed=4774) - basis = unitary_group.rvs(dim, seed=584545) + rng = np.random.default_rng(seed=4774) + basis = unitary_group(dim=dim, seed=584545).rvs() Schmidt_weights = rng.dirichlet(np.ones(dim), size=1).astype(complex)[0] mixed_state = np.zeros((dim, dim)).astype(complex) for i in range(dim): From ebd930fdcf976869314950d65bcebbed422bcc0f Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Wed, 17 Jan 2024 15:27:51 -0800 Subject: [PATCH 80/89] restructured helper functions --- .../devices/qutrit_mixed/apply_operation.py | 68 ++++++++----------- 1 file changed, 29 insertions(+), 39 deletions(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 84efb917661..602647b0005 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -25,6 +25,28 @@ alphabet_array = np.array(list(alphabet)) +def _map_indices_apply_channel(**kwargs): + """Map indices to einsum string + Args: + **kwargs (dict): Stores indices calculated in `get_einsum_mapping` + + Returns: + String of einsum indices to complete einsum calculations + """ + op_1_indices = f"{kwargs['kraus_index']}{kwargs['new_row_indices']}{kwargs['row_indices']}" + op_2_indices = f"{kwargs['kraus_index']}{kwargs['col_indices']}{kwargs['new_col_indices']}" + + new_state_indices = get_new_state_einsum_indices( + old_indices=kwargs["col_indices"] + kwargs["row_indices"], + new_indices=kwargs["new_col_indices"] + kwargs["new_row_indices"], + state_indices=kwargs["state_indices"], + ) + # index mapping for einsum, e.g., '...iga,...abcdef,...idh->...gbchef' + return ( + f"...{op_1_indices},...{kwargs['state_indices']},...{op_2_indices}->...{new_state_indices}" + ) + + def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: bool = False): r"""Apply a quantum channel specified by a list of Kraus operators to subsystems of the quantum state. For a unitary gate, there is a single Kraus operator. @@ -37,10 +59,15 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: Returns: array[complex]: output_state """ - einsum_indices = get_einsum_mapping(op, state, _map_indices, is_state_batched) + einsum_indices = get_einsum_mapping(op, state, _map_indices_apply_channel, is_state_batched) num_ch_wires = len(op.wires) - kraus = _get_kraus(op) + + # This could be pulled into separate function if tensordot is added + if isinstance(op, Channel): + kraus = op.kraus_matrices() + else: + kraus = [op.matrix()] # Shape kraus operators kraus_shape = [len(kraus)] + [QUDIT_DIM] * num_ch_wires * 2 @@ -66,28 +93,6 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: return math.einsum(einsum_indices, kraus, state, kraus_dagger) -def _map_indices(**kwargs): - """Map indices to einsum string - Args: - **kwargs (dict): Stores indices calculated in `get_einsum_mapping` - - Returns: - String of einsum indices to complete einsum calculations - """ - op_1_indices = f"{kwargs['kraus_index']}{kwargs['new_row_indices']}{kwargs['row_indices']}" - op_2_indices = f"{kwargs['kraus_index']}{kwargs['col_indices']}{kwargs['new_col_indices']}" - - new_state_indices = get_new_state_einsum_indices( - old_indices=kwargs["col_indices"] + kwargs["row_indices"], - new_indices=kwargs["new_col_indices"] + kwargs["new_row_indices"], - state_indices=kwargs["state_indices"], - ) - # index mapping for einsum, e.g., '...iga,...abcdef,...idh->...gbchef' - return ( - f"...{op_1_indices},...{kwargs['state_indices']},...{op_2_indices}->...{new_state_indices}" - ) - - @singledispatch def apply_operation( op: qml.operation.Operator, state, is_state_batched: bool = False, debugger=None @@ -178,18 +183,3 @@ def apply_snapshot(op: qml.Snapshot, state, is_state_batched: bool = False, debu # TODO add special case speedups - - -def _get_kraus(operation): # pylint: disable=no-self-use - """Return the Kraus operators representing the operation. - - Args: - operation (.Operation): a PennyLane operation - - Returns: - list[array[complex]]: Returns a list of 2D matrices representing the Kraus operators. If - the operation is unitary, returns a single Kraus operator. - """ - if isinstance(operation, Channel): - return operation.kraus_matrices() - return [operation.matrix()] From fa258e0813646fca5bcb82c698965fc8d5182d19 Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Wed, 17 Jan 2024 19:37:42 -0800 Subject: [PATCH 81/89] Added tests for different wires, refactored to abstract test functionality --- .../test_qutrit_mixed_apply_operation.py | 174 ++++++++++-------- 1 file changed, 94 insertions(+), 80 deletions(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index 9c6cf3c9386..bdeb626ce80 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -14,6 +14,7 @@ """Unit tests for create_initial_state in devices/qutrit_mixed/apply_operation.""" import pytest +from functools import reduce import numpy as np from scipy.stats import unitary_group import pennylane as qml @@ -21,7 +22,6 @@ from pennylane.operation import Channel from pennylane.devices.qutrit_mixed import apply_operation - ml_frameworks_list = [ "numpy", pytest.param("autograd", marks=pytest.mark.autograd), @@ -101,7 +101,7 @@ def test_empty_tag(self, ml_framework, state, shape, request): assert math.allclose(debugger.snapshots[0], math.reshape(initial_state, shape)) def test_provided_tag(self, ml_framework, state, shape, request): - """Test a snapshot is recorded property when provided a tag""" + """Test a snapshot is recorded properly when provided a tag""" state = request.getfixturevalue(state) initial_state = math.asarray(state, like=ml_framework) @@ -134,12 +134,12 @@ def test_snapshot_with_measurement(self, ml_framework, state, shape, request): @pytest.mark.parametrize("ml_framework", ml_frameworks_list) -class TestBroadcasting: # pylint: disable=too-few-public-methods +class TestOperation: # pylint: disable=too-few-public-methods """Tests that broadcasted operations (not channels) are applied correctly.""" broadcasted_ops = [ - qml.TRX(np.array([np.pi, np.pi / 2]), wires=2, subspace=(0, 1)), - qml.TRY(np.array([np.pi, np.pi / 2]), wires=2, subspace=(0, 1)), + qml.TRX(np.array([np.pi, np.pi / 2]), wires=0, subspace=(0, 1)), + qml.TRY(np.array([np.pi, np.pi / 2]), wires=1, subspace=(0, 1)), qml.TRZ(np.array([np.pi, np.pi / 2]), wires=2, subspace=(1, 2)), qml.QutritUnitary( np.array([unitary_group.rvs(27), unitary_group.rvs(27)]), @@ -147,41 +147,62 @@ class TestBroadcasting: # pylint: disable=too-few-public-methods ), ] unbroadcasted_ops = [ - qml.THadamard(wires=2), - qml.TClock(wires=2), + qml.THadamard(wires=0), + qml.TClock(wires=1), qml.TShift(wires=2), qml.TAdd(wires=[1, 2]), - qml.TRX(np.pi / 3, wires=2, subspace=(0, 2)), - qml.TRY(2 * np.pi / 3, wires=2, subspace=(1, 2)), + qml.TRX(np.pi / 3, wires=0, subspace=(0, 2)), + qml.TRY(2 * np.pi / 3, wires=1, subspace=(1, 2)), qml.TRZ(np.pi / 6, wires=2, subspace=(0, 1)), qml.QutritUnitary(unitary_group.rvs(27), wires=[0, 1, 2]), ] num_qutrits = 3 num_batched = 2 - dims = (3**num_qutrits, 3**num_qutrits) + + def expand_matrices(op, batch_size=0): + """Find expanded operator matrices, since qml.matrix isn't working for qutrits #4367""" + pre_wires_identity = np.eye(3 ** op.wires[0]) + post_wires_identity = np.eye(3 ** (2 - op.wires[-1])) + mat = op.matrix() + + def expand_matrix(matrix): + return reduce(np.kron, (pre_wires_identity, matrix, post_wires_identity)) + + if batch_size: + [expand_matrix(mat[i]) for i in range(batch_size)] + return expand_matrix(mat) + + @classmethod + def get_expected_state(cls, expanded_operator, state): + """Finds expected state after applying operator""" + flattened_state = state.reshape((3**cls.num_qutrits,) * 2) + adjoint_matrix = np.conj(expanded_operator).T + new_state = expanded_operator @ flattened_state @ adjoint_matrix + return new_state.reshape([3] * (cls.num_qutrits * 2)) + + @pytest.mark.parametrize("op", unbroadcasted_ops) + def test_no_broadcasting(self, op, ml_framework, three_qutrit_state): + """Tests that unbatched operations are applied correctly to an unbatched state.""" + state = three_qutrit_state + res = apply_operation(op, qml.math.asarray(state, like=ml_framework)) + + expanded_operator = self.expand_matrices(op) + expected = self.get_expected_state(expanded_operator, state) + + assert qml.math.get_interface(res) == ml_framework + assert qml.math.allclose(res, expected) @pytest.mark.parametrize("op", broadcasted_ops) def test_broadcasted_op(self, op, ml_framework, three_qutrit_state): """Tests that batched operations are applied correctly to an unbatched state.""" - state = three_qutrit_state - flattened_state = state.reshape(self.dims) - res = apply_operation(op, qml.math.asarray(state, like=ml_framework)) - missing_wires = 3 - len(op.wires) - mat = op.matrix() - expanded_mats = [ - np.kron(np.eye(3**missing_wires), mat[i]) if missing_wires else mat[i] - for i in range(self.num_batched) - ] - expected = [] + expanded_operators = self.expand_matrices(op, self.num_batched) - for i in range(self.num_batched): - expanded_mat = expanded_mats[i] - adjoint_mat = np.conj(expanded_mat).T - expected.append( - (expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * (self.num_qutrits * 2)) - ) + def get_expected(m): + return self.get_expected_state(m, state) + + expected = [get_expected(expanded_operators[i]) for i in range(self.num_batched)] assert qml.math.get_interface(res) == ml_framework assert qml.math.allclose(res, expected) @@ -190,19 +211,13 @@ def test_broadcasted_op(self, op, ml_framework, three_qutrit_state): def test_broadcasted_state(self, op, ml_framework, three_qutrit_batched_state): """Tests that unbatched operations are applied correctly to a batched state.""" state = three_qutrit_batched_state - res = apply_operation(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) - missing_wires = self.num_qutrits - len(op.wires) - mat = op.matrix() - expanded_mat = np.kron(np.eye(3**missing_wires), mat) if missing_wires else mat - adjoint_mat = np.conj(expanded_mat).T - expected = [] - - for i in range(self.num_batched): - flattened_state = state[i].reshape(self.dims) - expected.append( - (expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * (self.num_qutrits * 2)) - ) + expanded_operator = self.expand_matrices(op) + + def get_expected(s): + return self.get_expected_state(expanded_operator, s) + + expected = [get_expected(state[i]) for i in range(self.num_batched)] assert qml.math.get_interface(res) == ml_framework assert qml.math.allclose(res, expected) @@ -211,23 +226,14 @@ def test_broadcasted_state(self, op, ml_framework, three_qutrit_batched_state): def test_broadcasted_op_broadcasted_state(self, op, ml_framework, three_qutrit_batched_state): """Tests that batched operations are applied correctly to a batched state.""" state = three_qutrit_batched_state - res = apply_operation(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) - missing_wires = self.num_qutrits - len(op.wires) - mat = op.matrix() - expanded_mats = [ - np.kron(np.eye(3**missing_wires), mat[i]) if missing_wires else mat[i] + expanded_operators = self.expand_matrices(op, True) + + expected = [ + self.get_expected_state(expanded_operators[i], state[i]) for i in range(self.num_batched) ] - expected = [] - - for i in range(self.num_batched): - expanded_mat = expanded_mats[i] - adjoint_mat = np.conj(expanded_mat).T - flattened_state = state[i].reshape(self.dims) - expected.append( - (expanded_mat @ flattened_state @ adjoint_mat).reshape([3] * (self.num_qutrits * 2)) - ) + assert qml.math.get_interface(res) == ml_framework assert qml.math.allclose(res, expected) @@ -242,10 +248,36 @@ def test_batch_size_set_if_missing(self, ml_framework, one_qutrit_state): assert op.batch_size == self.num_batched +@pytest.mark.parametrize("wire", [0, 1]) @pytest.mark.parametrize("ml_framework", ml_frameworks_list) class TestChannels: # pylint: disable=too-few-public-methods """Tests that Channel operations are applied correctly.""" + num_qutrits = 2 + num_batched = 2 + + @classmethod + def expand_krons(cls, op): + """Find expanded operator kraus matrices""" + pre_wires_identity = np.eye(3 ** op.wires[0]) + post_wires_identity = np.eye(3 ** ((cls.num_qutrits - 1) - op.wires[-1])) + krons = op.kraus_matrices() + return [reduce(np.kron, (pre_wires_identity, kron, post_wires_identity)) for kron in krons] + + @classmethod + def get_expected_state(cls, expanded_krons, state): + """Finds expected state after applying channel""" + flattened_state = state.reshape((3**cls.num_qutrits,) * 2) + adjoint_krons = np.conj(np.transpose(expanded_krons, (0, 2, 1))) + new_state = np.sum( + [ + expanded_krons[i] @ flattened_state @ adjoint_krons[i] + for i in range(cls.num_batched) + ], + axis=0, + ) + return new_state.reshape([3] * (cls.num_qutrits * 2)) + class CustomChannel(Channel): num_params = 1 num_wires = 1 @@ -261,47 +293,29 @@ def compute_kraus_matrices(p): ).astype(complex) return [K0, K1] - def test_non_broadcasted_state(self, ml_framework, two_qutrit_state): + def test_non_broadcasted_state(self, ml_framework, wire, two_qutrit_state): """Tests that Channel operations are applied correctly to a state.""" state = two_qutrit_state - test_channel = self.CustomChannel(0.3, wires=1) + test_channel = self.CustomChannel(0.3, wires=wire) res = apply_operation(test_channel, math.asarray(state, like=ml_framework)) - flattened_state = state.reshape(9, 9) - mat = test_channel.kraus_matrices() - - expanded_mats = [np.kron(np.eye(3), mat[i]) for i in range(len(mat))] - expected = np.zeros((9, 9)).astype(complex) - for i in range(len(mat)): - expanded_mat = expanded_mats[i] - adjoint_mat = np.conj(expanded_mat).T - expected += expanded_mat @ flattened_state @ adjoint_mat - expected = expected.reshape([3] * 4) + expanded_krons = self.expand_krons(test_channel) + expected = self.get_expected_state(expanded_krons, state) assert qml.math.get_interface(res) == ml_framework - assert res.shape == expected.shape assert qml.math.allclose(res, expected) - def test_broadcasted_state(self, ml_framework, two_qutrit_batched_state): + def test_broadcasted_state(self, ml_framework, wire, two_qutrit_batched_state): """Tests that Channel operations are applied correctly to a batched state.""" state = two_qutrit_batched_state - num_batched = two_qutrit_batched_state.shape[0] - test_channel = self.CustomChannel(0.3, wires=1) + test_channel = self.CustomChannel(0.3, wires=wire) res = apply_operation(test_channel, math.asarray(state, like=ml_framework)) - mat = test_channel.kraus_matrices() - expanded_mats = [np.kron(np.eye(3), mat[i]) for i in range(len(mat))] - expected = [np.zeros((9, 9)).astype(complex) for _ in range(num_batched)] - for i in range(num_batched): - flattened_state = state[i].reshape(9, 9) - for j in range(len(mat)): - expanded_mat = expanded_mats[j] - adjoint_mat = np.conj(expanded_mat).T - expected[i] += expanded_mat @ flattened_state @ adjoint_mat - expected[i] = expected[i].reshape([3] * 4) - expected = np.array(expected) + expanded_krons = self.expand_krons(test_channel) + expected = [ + self.get_expected_state(expanded_krons, state[i]) for i in range(self.num_batched) + ] assert qml.math.get_interface(res) == ml_framework - assert res.shape == expected.shape assert qml.math.allclose(res, expected) From 544b4a61d81333ce2f9cf0a1508ba6b8a12cf227 Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Wed, 17 Jan 2024 19:39:06 -0800 Subject: [PATCH 82/89] Fixed what to sum channels over --- tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index bdeb626ce80..fda2e9deb95 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -272,7 +272,7 @@ def get_expected_state(cls, expanded_krons, state): new_state = np.sum( [ expanded_krons[i] @ flattened_state @ adjoint_krons[i] - for i in range(cls.num_batched) + for i in range(expanded_krons.shape[0]) ], axis=0, ) From e58a5cf7e29031c47339b054c7fa64490cf623d8 Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Wed, 17 Jan 2024 23:02:14 -0800 Subject: [PATCH 83/89] Fixed channels tests --- .../devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index fda2e9deb95..aa3031d3972 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -271,8 +271,8 @@ def get_expected_state(cls, expanded_krons, state): adjoint_krons = np.conj(np.transpose(expanded_krons, (0, 2, 1))) new_state = np.sum( [ - expanded_krons[i] @ flattened_state @ adjoint_krons[i] - for i in range(expanded_krons.shape[0]) + expanded_kron @ flattened_state @ adjoint_krons[i] + for i, expanded_kron in enumerate(expanded_krons) ], axis=0, ) From 66f3b2c427c0991254484068d23bc61c34b20b68 Mon Sep 17 00:00:00 2001 From: Gabe PC Date: Wed, 17 Jan 2024 23:11:57 -0800 Subject: [PATCH 84/89] Fixed operator test --- .../qutrit_mixed/test_qutrit_mixed_apply_operation.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index aa3031d3972..c962fa9fa69 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -13,8 +13,8 @@ # limitations under the License. """Unit tests for create_initial_state in devices/qutrit_mixed/apply_operation.""" -import pytest from functools import reduce +import pytest import numpy as np from scipy.stats import unitary_group import pennylane as qml @@ -159,17 +159,18 @@ class TestOperation: # pylint: disable=too-few-public-methods num_qutrits = 3 num_batched = 2 - def expand_matrices(op, batch_size=0): + @classmethod + def expand_matrices(cls, op, batch_size=0): """Find expanded operator matrices, since qml.matrix isn't working for qutrits #4367""" pre_wires_identity = np.eye(3 ** op.wires[0]) - post_wires_identity = np.eye(3 ** (2 - op.wires[-1])) + post_wires_identity = np.eye(3 ** ((cls.num_qutrits - 1) - op.wires[-1])) mat = op.matrix() def expand_matrix(matrix): return reduce(np.kron, (pre_wires_identity, matrix, post_wires_identity)) if batch_size: - [expand_matrix(mat[i]) for i in range(batch_size)] + return [expand_matrix(mat[i]) for i in range(batch_size)] return expand_matrix(mat) @classmethod @@ -227,7 +228,7 @@ def test_broadcasted_op_broadcasted_state(self, op, ml_framework, three_qutrit_b """Tests that batched operations are applied correctly to a batched state.""" state = three_qutrit_batched_state res = apply_operation(op, qml.math.asarray(state, like=ml_framework), is_state_batched=True) - expanded_operators = self.expand_matrices(op, True) + expanded_operators = self.expand_matrices(op, self.num_batched) expected = [ self.get_expected_state(expanded_operators[i], state[i]) From 8a70f058bedcd9d346596202d80d0cf9666c5bca Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:10:44 -0800 Subject: [PATCH 85/89] Update pennylane/devices/qutrit_mixed/apply_operation.py Co-authored-by: Olivia Di Matteo <2068515+glassnotes@users.noreply.github.com> --- pennylane/devices/qutrit_mixed/apply_operation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 602647b0005..8b6447cbd6a 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -97,7 +97,7 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: def apply_operation( op: qml.operation.Operator, state, is_state_batched: bool = False, debugger=None ): - """Apply and operator to a given state. + """Apply an operation to a given state. Args: op (Operator): The operation to apply to ``state`` From 9b6cd7a303505a38215aa589698e23acadcb47a3 Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:11:33 -0800 Subject: [PATCH 86/89] change variable name Co-authored-by: Olivia Di Matteo <2068515+glassnotes@users.noreply.github.com> --- tests/devices/qutrit_mixed/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/devices/qutrit_mixed/conftest.py b/tests/devices/qutrit_mixed/conftest.py index 1ab06d40fab..2963174072a 100644 --- a/tests/devices/qutrit_mixed/conftest.py +++ b/tests/devices/qutrit_mixed/conftest.py @@ -22,10 +22,10 @@ def get_random_mixed_state(num_qutrits): rng = np.random.default_rng(seed=4774) basis = unitary_group(dim=dim, seed=584545).rvs() - Schmidt_weights = rng.dirichlet(np.ones(dim), size=1).astype(complex)[0] + schmidt_weights = rng.dirichlet(np.ones(dim), size=1).astype(complex)[0] mixed_state = np.zeros((dim, dim)).astype(complex) for i in range(dim): - mixed_state += Schmidt_weights[i] * np.outer(np.conj(basis[i]), basis[i]) + mixed_state += schmidt_weights[i] * np.outer(np.conj(basis[i]), basis[i]) return mixed_state.reshape([3] * (2 * num_qutrits)) From 80dee63da513ffce8bcf5b9262bb554440554632 Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:12:06 -0800 Subject: [PATCH 87/89] Update tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py Co-authored-by: Mudit Pandey --- tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py index c962fa9fa69..c5bb0255ab2 100644 --- a/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py +++ b/tests/devices/qutrit_mixed/test_qutrit_mixed_apply_operation.py @@ -11,7 +11,7 @@ # 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 create_initial_state in devices/qutrit_mixed/apply_operation.""" +"""Unit tests for apply_operation in devices/qutrit_mixed/apply_operation.""" from functools import reduce import pytest From d48210bc941618973cda7288e9bff2ba4bb80519 Mon Sep 17 00:00:00 2001 From: Gabriel Bottrill <78718539+Gabriel-Bottrill@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:13:17 -0800 Subject: [PATCH 88/89] removed TODO for channels --- pennylane/devices/qutrit_mixed/apply_operation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane/devices/qutrit_mixed/apply_operation.py b/pennylane/devices/qutrit_mixed/apply_operation.py index 8b6447cbd6a..8a9b94a6eda 100644 --- a/pennylane/devices/qutrit_mixed/apply_operation.py +++ b/pennylane/devices/qutrit_mixed/apply_operation.py @@ -72,7 +72,6 @@ def apply_operation_einsum(op: qml.operation.Operator, state, is_state_batched: # Shape kraus operators kraus_shape = [len(kraus)] + [QUDIT_DIM] * num_ch_wires * 2 if not isinstance(op, Channel): - # TODO Channels broadcasting doesn't seem to be implemented for qubits, should they be for qutrit? mat = op.matrix() dim = QUDIT_DIM**num_ch_wires batch_size = math.get_batch_size(mat, (dim, dim), dim**2) From b9f574b15510826854b6ad91fb9432e46da6deb9 Mon Sep 17 00:00:00 2001 From: gabrielLydian Date: Tue, 23 Jan 2024 15:04:25 -0800 Subject: [PATCH 89/89] Forced failing tests to use x64 --- tests/devices/qubit/test_apply_operation.py | 2 ++ tests/fourier/test_reconstruct.py | 2 ++ tests/gradients/core/test_metric_tensor.py | 3 +++ 3 files changed, 7 insertions(+) diff --git a/tests/devices/qubit/test_apply_operation.py b/tests/devices/qubit/test_apply_operation.py index 9135ec0a192..df2cf6d7b82 100644 --- a/tests/devices/qubit/test_apply_operation.py +++ b/tests/devices/qubit/test_apply_operation.py @@ -1062,6 +1062,8 @@ def test_correctness_jax(self, op_wires, state_wires, batch_dim): when applying it to a Jax state.""" import jax + jax.config.update("jax_enable_x64", True) + batched = batch_dim is not None shape = [batch_dim] + [2] * state_wires if batched else [2] * state_wires # Input state diff --git a/tests/fourier/test_reconstruct.py b/tests/fourier/test_reconstruct.py index 1a611e833b4..843c3f0cfeb 100644 --- a/tests/fourier/test_reconstruct.py +++ b/tests/fourier/test_reconstruct.py @@ -194,6 +194,8 @@ def test_differentiability_jax(self, fun, num_frequency, base_f, expected_grad): functions are differentiable for JAX input variables.""" import jax + jax.config.update("jax_enable_x64", True) + # Convert fun to have integer frequencies _fun = lambda x: fun(x / base_f) _rec = _reconstruct_equ(_fun, num_frequency, interface="jax") diff --git a/tests/gradients/core/test_metric_tensor.py b/tests/gradients/core/test_metric_tensor.py index ef07dbd0248..cf01151df3e 100644 --- a/tests/gradients/core/test_metric_tensor.py +++ b/tests/gradients/core/test_metric_tensor.py @@ -1147,6 +1147,9 @@ def circuit(*params): @pytest.mark.parametrize("interface", ["auto", "jax"]) def test_correct_output_jax(self, ansatz, params, interface): from jax import numpy as jnp + from jax import config + + config.update("jax_enable_x64", True) expected = autodiff_metric_tensor(ansatz, self.num_wires)(*params) dev = qml.device("default.qubit.jax", wires=self.num_wires + 1)