From 2590eee12bd8564c24bb601e04e5e4e08027cfce Mon Sep 17 00:00:00 2001 From: Amintor Dusko <87949283+AmintorDusko@users.noreply.github.com> Date: Mon, 31 Oct 2022 08:54:39 -0400 Subject: [PATCH] Change Lightning to inherit from QubitDevice instead of DefaultQubit (#365) * Change Lightning to inherit from QubitDevice * update python tests * expand cmake hints for Kokkos search * NotImplementedError for SparseHamiltonian expval in the absence of Kokkos * update changelog * update apply method to rely on apply_lightning for rotations * update tests and conftest * Update pennylane_lightning/lightning_qubit.py * add black to requirements-dev * update adjoint jacobian tests * add stopping condition and tests * without binary skip decomposition test --- .github/CHANGELOG.md | 2 + cmake/process_options.cmake | 2 + pennylane_lightning/_version.py | 2 +- pennylane_lightning/lightning_qubit.py | 321 +++++++++++++++----- requirements-dev.txt | 1 + tests/conftest.py | 58 +++- tests/test_adjoint_jacobian.py | 58 +--- tests/test_apply.py | 393 +++++++++---------------- tests/test_decomposition.py | 44 +++ tests/test_device.py | 2 +- tests/test_expval.py | 45 ++- tests/test_gates.py | 141 ++++----- tests/test_measures.py | 9 +- tests/test_measures_sparse.py | 32 +- tests/test_serialize.py | 7 +- tests/test_var.py | 3 - tests/test_vjp.py | 1 - 17 files changed, 619 insertions(+), 502 deletions(-) create mode 100644 tests/test_decomposition.py diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 751571bd6f..9e0daf3f38 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -24,6 +24,8 @@ * Update gcc and g++ 10.x to 11.x in CI tests. This update brings improved support for newer C++ features. [(#370)](https://github.com/PennyLaneAI/pennylane-lightning/pull/370) +* Change Lightning to inherit from QubitDevice instead of DefaultQubit. +[(#365)](https://github.com/PennyLaneAI/pennylane-lightning/pull/365) ### Documentation diff --git a/cmake/process_options.cmake b/cmake/process_options.cmake index b249383b1f..388663aca2 100644 --- a/cmake/process_options.cmake +++ b/cmake/process_options.cmake @@ -123,6 +123,7 @@ if(ENABLE_KOKKOS) /usr /usr/local /opt + /opt/Kokkos ) if(Kokkos_FOUND) message(STATUS "Found existing Kokkos library") @@ -134,6 +135,7 @@ if(ENABLE_KOKKOS) /usr /usr/local /opt + /opt/KokkosKernels ) if(KokkosKernels_FOUND) message(STATUS "Found existing Kokkos Kernels library") diff --git a/pennylane_lightning/_version.py b/pennylane_lightning/_version.py index 2c421a9714..89c0bdcca5 100644 --- a/pennylane_lightning/_version.py +++ b/pennylane_lightning/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.27.0-dev11" +__version__ = "0.27.0-dev12" diff --git a/pennylane_lightning/lightning_qubit.py b/pennylane_lightning/lightning_qubit.py index 8774d2574b..6ee8da254f 100644 --- a/pennylane_lightning/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit.py @@ -18,17 +18,15 @@ from typing import List from warnings import warn from os import getenv -from itertools import islice +from itertools import islice, product import numpy as np from pennylane import ( + QubitDevice, math, - gradients, BasisState, QubitStateVector, - QubitUnitary, Projector, - Hermitian, Rot, QuantumFunctionError, DeviceError, @@ -38,8 +36,9 @@ from pennylane.measurements import MeasurementProcess, Expectation, State from pennylane.wires import Wires -# Remove after the next release of PL -# Add from pennylane import matrix +# tolerance for numerical errors +tolerance = 1e-10 + import pennylane as qml from ._version import __version__ @@ -70,17 +69,86 @@ def _chunk_iterable(it, num_chunks): return iter(lambda: tuple(islice(it, num_chunks)), ()) -def _remove_snapshot_from_operations(operations): - operations = operations.copy() - operations.discard("Snapshot") - return operations - - -class LightningQubit(DefaultQubit): +allowed_operations = { + "Identity", + "BasisState", + "QubitStateVector", + "QubitUnitary", + "ControlledQubitUnitary", + "MultiControlledX", + "DiagonalQubitUnitary", + "PauliX", + "PauliY", + "PauliZ", + "MultiRZ", + "Hadamard", + "S", + "Adjoint(S)", + "T", + "Adjoint(T)", + "SX", + "Adjoint(SX)", + "CNOT", + "SWAP", + "ISWAP", + "PSWAP", + "Adjoint(ISWAP)", + "SISWAP", + "Adjoint(SISWAP)", + "SQISW", + "CSWAP", + "Toffoli", + "CY", + "CZ", + "PhaseShift", + "ControlledPhaseShift", + "CPhase", + "RX", + "RY", + "RZ", + "Rot", + "CRX", + "CRY", + "CRZ", + "CRot", + "IsingXX", + "IsingYY", + "IsingZZ", + "IsingXY", + "SingleExcitation", + "SingleExcitationPlus", + "SingleExcitationMinus", + "DoubleExcitation", + "DoubleExcitationPlus", + "DoubleExcitationMinus", + "QubitCarry", + "QubitSum", + "OrbitalRotation", + "QFT", + "ECR", +} + +allowed_observables = { + "PauliX", + "PauliY", + "PauliZ", + "Hadamard", + "Hermitian", + "Identity", + "Projector", + "SparseHamiltonian", + "Hamiltonian", + "Sum", + "SProd", + "Prod", + "Exp", +} + + +class LightningQubit(QubitDevice): """PennyLane Lightning device. - An extension of PennyLane's built-in ``default.qubit`` device that interfaces with C++ to - perform fast linear algebra calculations. + A device that interfaces with C++ to perform fast linear algebra calculations. Use of this device requires pre-built binaries or compilation from source. Check out the :doc:`/installation` guide for more details. @@ -102,9 +170,10 @@ class LightningQubit(DefaultQubit): version = __version__ author = "Xanadu Inc." _CPP_BINARY_AVAILABLE = True - operations = _remove_snapshot_from_operations(DefaultQubit.operations) + operations = allowed_operations + observables = allowed_observables - def __init__(self, wires, *, c_dtype=np.complex128, shots=None, batch_obs=False): + def __init__(self, wires, *, c_dtype=np.complex128, shots=None, batch_obs=False, analytic=None): if c_dtype is np.complex64: r_dtype = np.float32 self.use_csingle = True @@ -113,9 +182,57 @@ def __init__(self, wires, *, c_dtype=np.complex128, shots=None, batch_obs=False) self.use_csingle = False else: raise TypeError(f"Unsupported complex Type: {c_dtype}") - super().__init__(wires, r_dtype=r_dtype, c_dtype=c_dtype, shots=shots) + super().__init__(wires, shots=shots, r_dtype=r_dtype, c_dtype=c_dtype, analytic=analytic) self._batch_obs = batch_obs + # Create the initial state. Internally, we store the + # state as an array of dimension [2]*wires. + self._state = self._create_basis_state(0) + self._pre_rotated_state = self._state + + @property + def stopping_condition(self): + """.BooleanFn: Returns the stopping condition for the device. The returned + function accepts a queuable object (including a PennyLane operation + and observable) and returns ``True`` if supported by the device.""" + + def accepts_obj(obj): + if obj.name == "QFT" and len(obj.wires) >= 10: + return False + if obj.name == "GroverOperator" and len(obj.wires) >= 13: + return False + if getattr(obj, "has_matrix", False): + return not (qml.operation.is_trainable(obj)) + return obj.name in self.observables.union(self.operations) + + return qml.BooleanFn(accepts_obj) + + def _create_basis_state(self, index): + """Return a computational basis state over all wires. + Args: + index (int): integer representing the computational basis state + Returns: + array[complex]: complex array of shape ``[2]*self.num_wires`` + representing the statevector of the basis state + Note: This function does not support broadcasted inputs yet. + """ + state = np.zeros(2**self.num_wires, dtype=np.complex128) + state[index] = 1 + state = self._asarray(state, dtype=self.C_DTYPE) + return self._reshape(state, [2] * self.num_wires) + + @classmethod + def capabilities(cls): + capabilities = super().capabilities().copy() + capabilities.update( + model="qubit", + supports_inverse_operations=True, + supports_analytic_computation=True, + supports_broadcasting=False, + returns_state=True, + ) + return capabilities + @staticmethod def _asarray(arr, dtype=None): arr = np.asarray(arr) # arr is not copied @@ -127,56 +244,103 @@ def _asarray(arr, dtype=None): dtype = arr.dtype # We allocate a new aligned memory and copy data to there if alignment or dtype mismatches - # Note that get_alignment does not neccsarily returns CPUMemoryModel(Unaligned) even for - # numpy allocated memory as the memory location happens to be aligend. + # Note that get_alignment does not necessarily return CPUMemoryModel(Unaligned) even for + # numpy allocated memory as the memory location happens to be aligned. if int(get_alignment(arr)) < int(best_alignment()) or arr.dtype != dtype: new_arr = allocate_aligned_array(arr.size, np.dtype(dtype)).reshape(arr.shape) np.copyto(new_arr, arr) arr = new_arr return arr - @classmethod - def capabilities(cls): - capabilities = super().capabilities().copy() - capabilities.update( - model="qubit", - supports_inverse_operations=True, - supports_analytic_computation=True, - supports_broadcasting=False, - returns_state=True, - ) - capabilities.pop("passthru_devices", None) - return capabilities + def reset(self): + """Reset the device""" + super().reset() - def apply(self, operations, rotations=None, **kwargs): - # State preparation is currently done in Python - if operations: # make sure operations[0] exists - if isinstance(operations[0], QubitStateVector): - self._apply_state_vector(operations[0].parameters[0].copy(), operations[0].wires) - del operations[0] - elif isinstance(operations[0], BasisState): - self._apply_basis_state(operations[0].parameters[0], operations[0].wires) - del operations[0] + # init the state vector to |00..0> + self._state = self._create_basis_state(0) + self._pre_rotated_state = self._state - for operation in operations: - if isinstance(operation, (QubitStateVector, BasisState)): - raise DeviceError( - "Operation {} cannot be used after other Operations have already been " - "applied on a {} device.".format(operation.name, self.short_name) - ) + @property + def state(self): + # Flattening the state. + shape = (1 << self.num_wires,) + return self._reshape(self._pre_rotated_state, shape) - if operations: - self._pre_rotated_state = self.apply_lightning(self._state, operations) - else: - self._pre_rotated_state = self._state + def _apply_state_vector(self, state, device_wires): + """Initialize the internal state vector in a specified state. + Args: + state (array[complex]): normalized input state of length ``2**len(wires)`` + or broadcasted state of shape ``(batch_size, 2**len(wires))`` + device_wires (Wires): wires that get initialized in the state + """ - if rotations: - if any(isinstance(r, QubitUnitary) for r in rotations): - super().apply(operations=[], rotations=rotations) - else: - self._state = self.apply_lightning(np.copy(self._pre_rotated_state), rotations) - else: - self._state = self._pre_rotated_state + # translate to wire labels used by device + device_wires = self.map_wires(device_wires) + + state = self._asarray(state, dtype=self.C_DTYPE) + output_shape = [2] * self.num_wires + + if not qml.math.is_abstract(state): + norm = qml.math.linalg.norm(state, axis=-1, ord=2) + if not qml.math.allclose(norm, 1.0, atol=tolerance): + raise ValueError("Sum of amplitudes-squared does not equal one.") + + if len(device_wires) == self.num_wires and Wires(sorted(device_wires)) == device_wires: + # Initialize the entire device state with the input state + self._state = self._reshape(state, output_shape) + return + + # generate basis states on subset of qubits via the cartesian product + basis_states = np.array(list(product([0, 1], repeat=len(device_wires)))) + + # get basis states to alter on full set of qubits + unravelled_indices = np.zeros((2 ** len(device_wires), self.num_wires), dtype=int) + unravelled_indices[:, device_wires] = basis_states + + # get indices for which the state is changed to input state vector elements + ravelled_indices = np.ravel_multi_index(unravelled_indices.T, [2] * self.num_wires) + + state = self._scatter(ravelled_indices, state, [2**self.num_wires]) + state = self._reshape(state, output_shape) + self._state = self._asarray(state, dtype=self.C_DTYPE) + + def _apply_basis_state(self, state, wires): + """Initialize the state vector 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 + + Note: This function does not support broadcasted inputs yet. + """ + # translate to wire labels used by device + device_wires = self.map_wires(wires) + + # length of basis state parameter + n_basis_state = len(state) + + if not set(state.tolist()).issubset({0, 1}): + raise ValueError("BasisState parameter must consist of 0 or 1 integers.") + + if n_basis_state != len(device_wires): + raise ValueError("BasisState parameter and wires must be of equal length.") + + # get computational basis state number + basis_states = 2 ** (self.num_wires - 1 - np.array(device_wires)) + basis_states = qml.math.convert_like(basis_states, state) + num = int(qml.math.dot(state, basis_states)) + + self._state = self._create_basis_state(num) + + # To be able to validate the adjoint method [_validate_adjoint_method(device)], + # the qnode requires the definition of: + # ["_apply_operation", "_apply_unitary", "adjoint_jacobian"] + def _apply_operation(): + pass + + def _apply_unitary(): + pass def apply_lightning(self, state, operations): """Apply a list of operations to the state tensor. @@ -210,7 +374,6 @@ def apply_lightning(self, state, operations): method = getattr(sim, name, None) wires = self.wires.indices(o.wires) - if method is None: # Inverse can be set to False since qml.matrix(o) is already in inverted form method = getattr(sim, "applyMatrix") @@ -226,6 +389,33 @@ def apply_lightning(self, state, operations): return np.reshape(state_vector, state.shape) + def apply(self, operations, rotations=None, **kwargs): + # State preparation is currently done in Python + if operations: # make sure operations[0] exists + if isinstance(operations[0], QubitStateVector): + self._apply_state_vector(operations[0].parameters[0].copy(), operations[0].wires) + del operations[0] + elif isinstance(operations[0], BasisState): + self._apply_basis_state(operations[0].parameters[0], operations[0].wires) + del operations[0] + + for operation in operations: + if isinstance(operation, (QubitStateVector, BasisState)): + raise DeviceError( + "Operation {} cannot be used after other Operations have already been " + "applied on a {} device.".format(operation.name, self.short_name) + ) + + if operations: + self._pre_rotated_state = self.apply_lightning(self._state, operations) + else: + self._pre_rotated_state = self._state + + if rotations: + self._state = self.apply_lightning(np.copy(self._pre_rotated_state), rotations) + else: + self._state = self._pre_rotated_state + @staticmethod def _check_adjdiff_supported_measurements(measurements: List[MeasurementProcess]): """Check whether given list of measurement is supported by adjoint_diff. @@ -409,13 +599,13 @@ def vjp(self, measurements, dy, starting_state=None, use_device_state=False): vjp_f = dev.vjp([qml.state()], dy) vjp = vjp_f(tape) - computes :math:`w = (w_1,\cdots,w_m)` where + computes :math:`w = (w_1,\\cdots,w_m)` where .. math:: - w_k = \\langle v| \\frac{\partial}{\partial \\theta_k} | \psi_{\pmb{\\theta}} \\rangle. + w_k = \\langle v| \\frac{\\partial}{\\partial \\theta_k} | \\psi_{\\pmb{\\theta}} \\rangle. - Here, :math:`m` is the total number of trainable parameters, :math:`\pmb{\\theta}` is the vector of trainable parameters and :math:`\psi_{\pmb{\\theta}}` + Here, :math:`m` is the total number of trainable parameters, :math:`\\pmb{\\theta}` is the vector of trainable parameters and :math:`\\psi_{\\pmb{\\theta}}` is the output quantum state. Args: @@ -573,10 +763,8 @@ def probability(self, wires=None, shot_range=None, bin_size=None): # To support np.complex64 based on the type of self._state dtype = self._state.dtype ket = np.ravel(self._state) - state_vector = StateVectorC64(ket) if self.use_csingle else StateVectorC128(ket) M = MeasuresC64(state_vector) if self.use_csingle else MeasuresC128(state_vector) - return M.probs(device_wires) def generate_samples(self): @@ -639,7 +827,9 @@ def expval(self, observable, shot_range=None, bin_size=None): CSR_SparseHamiltonian.indices, CSR_SparseHamiltonian.data, ) - return super().expval(observable, shot_range=shot_range, bin_size=bin_size) + raise NotImplementedError( + "The expval of a SparseHamiltonian requires Kokkos and Kokkos Kernels." + ) if observable.name in ["Hamiltonian", "Hermitian"]: ob_serialized = _serialize_ob(observable, self.wire_map, use_csingle=self.use_csingle) @@ -698,7 +888,6 @@ class LightningQubit(DefaultQubit): # pragma: no cover version = __version__ author = "Xanadu Inc." _CPP_BINARY_AVAILABLE = False - operations = _remove_snapshot_from_operations(DefaultQubit.operations) def __init__(self, wires, *, c_dtype=np.complex128, **kwargs): warn( diff --git a/requirements-dev.txt b/requirements-dev.txt index 08be6260f0..0f7d01f8c8 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,3 +6,4 @@ pybind11 pytest pytest-cov pytest-mock +black>=21 diff --git a/tests/conftest.py b/tests/conftest.py index 8569c88520..437751c14f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -20,10 +20,10 @@ import numpy as np import pennylane as qml -from pennylane_lightning import LightningQubit # defaults TOL = 1e-6 +TOL_STOCHASTIC = 0.05 U = np.array( [ @@ -32,8 +32,38 @@ ] ) -U2 = np.array([[0, 1, 1, 1], [1, 0, 1, -1], [1, -1, 0, 1], [1, 1, -1, 0]]) / np.sqrt(3) -A = np.array([[1.02789352, 1.61296440 - 0.3498192j], [1.61296440 + 0.3498192j, 1.23920938 + 0j]]) +U2 = np.array( + [ + [ + -0.07843244 - 3.57825948e-01j, + 0.71447295 - 5.38069384e-02j, + 0.20949966 + 6.59100734e-05j, + -0.50297381 + 2.35731613e-01j, + ], + [ + -0.26626692 + 4.53837083e-01j, + 0.27771991 - 2.40717436e-01j, + 0.41228017 - 1.30198687e-01j, + 0.01384490 - 6.33200028e-01j, + ], + [ + -0.69254712 - 2.56963068e-02j, + -0.15484858 + 6.57298384e-02j, + -0.53082141 + 7.18073414e-02j, + -0.41060450 - 1.89462315e-01j, + ], + [ + -0.09686189 - 3.15085273e-01j, + -0.53241387 - 1.99491763e-01j, + 0.56928622 + 3.97704398e-01j, + -0.28671074 - 6.01574497e-02j, + ], + ] +) + +THETA = np.linspace(0.11, 1, 3) +PHI = np.linspace(0.32, 1, 3) +VARPHI = np.linspace(0.02, 1, 3) @pytest.fixture(scope="session") @@ -42,22 +72,22 @@ def tol(): return float(os.environ.get("TOL", TOL)) +@pytest.fixture(scope="session") +def tol_stochastic(): + """Numerical tolerance for equality tests.""" + return TOL_STOCHASTIC + + @pytest.fixture(scope="session", params=[2, 3]) def n_subsystems(request): """Number of qubits or qumodes.""" return request.param +# General qubit_device fixture, for any number of wires. @pytest.fixture(scope="function", params=[np.complex64, np.complex128]) -def qubit_device_1_wire(request): - return LightningQubit(wires=1, c_dtype=request.param) - - -@pytest.fixture(scope="function", params=[np.complex64, np.complex128]) -def qubit_device_2_wires(request): - return LightningQubit(wires=2, c_dtype=request.param) - +def qubit_device(request): + def _device(wires): + return qml.device("lightning.qubit", wires=wires, c_dtype=request.param) -@pytest.fixture(scope="function", params=[np.complex64, np.complex128]) -def qubit_device_3_wires(request): - return LightningQubit(wires=3, c_dtype=request.param) + return _device diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index 22c1bbaf6f..cab9326b2f 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -355,7 +355,7 @@ def test_gradients_pauliz(self, op, obs, dev): qml.Rot(1.3, -2.3, 0.5, wires=[0]) qml.RZ(-0.5, wires=0) - qml.RY(0.5, wires=1).inv() + qml.adjoint(qml.RY(0.5, wires=1), lazy=False) qml.CNOT(wires=[0, 1]) qml.expval(obs(wires=0)) @@ -399,7 +399,7 @@ def test_gradients_hermitian(self, op, dev): qml.Rot(1.3, -2.3, 0.5, wires=[0]) qml.RZ(-0.5, wires=0) - qml.RY(0.5, wires=1).inv() + qml.adjoint(qml.RY(0.5, wires=1), lazy=False) qml.CNOT(wires=[0, 1]) qml.expval( @@ -819,60 +819,22 @@ def f(params1, params2): assert np.allclose(grad_adjoint, grad_fd, atol=tol) -@pytest.mark.parametrize( - "r_dtype,c_dtype", [[np.float32, np.complex64], [np.float64, np.complex128]] -) -def test_qchem_expvalcost_correct(r_dtype, c_dtype): - """EvpvalCost with qchem Hamiltonian work corectly""" - from pennylane import qchem - - symbols = ["Li", "H"] - geometry = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 2.969280527]) - H, qubits = qchem.molecular_hamiltonian( - symbols, geometry, active_electrons=2, active_orbitals=5 - ) - active_electrons = 2 - hf_state = qchem.hf_state(active_electrons, qubits) - - def circuit_1(params, wires): - qml.BasisState(hf_state, wires=wires) - qml.RX(params[0], wires=0) - qml.RY(params[0], wires=1) - qml.RZ(params[0], wires=2) - qml.Hadamard(wires=1) - - diff_method = "adjoint" - dev_lig = qml.device("lightning.qubit", wires=qubits, c_dtype=c_dtype) - cost_fn_lig = qml.ExpvalCost(circuit_1, H, dev_lig, optimize=False, diff_method=diff_method) - circuit_gradient_lig = qml.grad(cost_fn_lig, argnum=0) - params = np.array([0.123], requires_grad=True) - grads_lig = circuit_gradient_lig(params) - - dev_def = qml.device("default.qubit", wires=qubits) - cost_fn_def = qml.ExpvalCost(circuit_1, H, dev_def, optimize=False, diff_method=diff_method) - circuit_gradient_def = qml.grad(cost_fn_def, argnum=0) - params = np.array([0.123], requires_grad=True) - grads_def = circuit_gradient_def(params) - - assert np.allclose(grads_lig, grads_def) - - def circuit_ansatz(params, wires): """Circuit ansatz containing all the parametrized gates""" qml.QubitStateVector(unitary_group.rvs(2**4, random_state=0)[0], wires=wires) qml.RX(params[0], wires=wires[0]) qml.RY(params[1], wires=wires[1]) - qml.RX(params[2], wires=wires[2]).inv() + qml.adjoint(qml.RX(params[2], wires=wires[2])) qml.RZ(params[0], wires=wires[3]) qml.CRX(params[3], wires=[wires[3], wires[0]]) qml.PhaseShift(params[4], wires=wires[2]) qml.CRY(params[5], wires=[wires[2], wires[1]]) - qml.CRZ(params[5], wires=[wires[0], wires[3]]).inv() - qml.PhaseShift(params[6], wires=wires[0]).inv() + qml.adjoint(qml.CRZ(params[5], wires=[wires[0], wires[3]])) + qml.adjoint(qml.PhaseShift(params[6], wires=wires[0])) qml.Rot(params[6], params[7], params[8], wires=wires[0]) - # # qml.Rot(params[8], params[8], params[9], wires=wires[1]).inv() + qml.adjoint(qml.Rot(params[8], params[8], params[9], wires=wires[1])) qml.MultiRZ(params[11], wires=[wires[0], wires[1]]) - # # qml.PauliRot(params[12], "XXYZ", wires=[wires[0], wires[1], wires[2], wires[3]]) + qml.PauliRot(params[12], "XXYZ", wires=[wires[0], wires[1], wires[2], wires[3]]) qml.CPhase(params[12], wires=[wires[3], wires[2]]) qml.IsingXX(params[13], wires=[wires[1], wires[0]]) qml.IsingXY(params[14], wires=[wires[3], wires[2]]) @@ -881,7 +843,7 @@ def circuit_ansatz(params, wires): qml.U1(params[15], wires=wires[0]) qml.U2(params[16], params[17], wires=wires[0]) qml.U3(params[18], params[19], params[20], wires=wires[1]) - # # qml.CRot(params[21], params[22], params[23], wires=[wires[1], wires[2]]).inv() # expected tofail + qml.adjoint(qml.CRot(params[21], params[22], params[23], wires=[wires[1], wires[2]])) qml.SingleExcitation(params[24], wires=[wires[2], wires[0]]) qml.DoubleExcitation(params[25], wires=[wires[2], wires[0], wires[1], wires[3]]) qml.SingleExcitationPlus(params[26], wires=[wires[0], wires[2]]) @@ -894,7 +856,7 @@ def circuit_ansatz(params, wires): @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test__tape_qchem(tol): - """The circit Ansatz with a QChem Hamiltonian produces correct results""" + """Tests the circuit Ansatz with a QChem Hamiltonian produces correct results""" H, qubits = qml.qchem.molecular_hamiltonian( ["H", "H"], np.array([0.0, 0.1, 0.0, 0.0, -0.1, 0.0]) @@ -910,7 +872,7 @@ def circuit(params): dev_dq = qml.device("default.qubit", wires=4) circuit_lq = qml.QNode(circuit, dev_lq, diff_method="adjoint") - circuit_dq = qml.QNode(circuit, dev_lq, diff_method="parameter-shift") + circuit_dq = qml.QNode(circuit, dev_dq, diff_method="parameter-shift") assert np.allclose(qml.grad(circuit_lq)(params), qml.grad(circuit_dq)(params), tol) diff --git a/tests/test_apply.py b/tests/test_apply.py index abe408ebc3..2488683e96 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -25,61 +25,7 @@ from pennylane_lightning import LightningQubit from pennylane_lightning.lightning_qubit import CPP_BINARY_AVAILABLE -U2 = np.array( - [ - [ - -0.07843244 - 3.57825948e-01j, - 0.71447295 - 5.38069384e-02j, - 0.20949966 + 6.59100734e-05j, - -0.50297381 + 2.35731613e-01j, - ], - [ - -0.26626692 + 4.53837083e-01j, - 0.27771991 - 2.40717436e-01j, - 0.41228017 - 1.30198687e-01j, - 0.01384490 - 6.33200028e-01j, - ], - [ - -0.69254712 - 2.56963068e-02j, - -0.15484858 + 6.57298384e-02j, - -0.53082141 + 7.18073414e-02j, - -0.41060450 - 1.89462315e-01j, - ], - [ - -0.09686189 - 3.15085273e-01j, - -0.53241387 - 1.99491763e-01j, - 0.56928622 + 3.97704398e-01j, - -0.28671074 - 6.01574497e-02j, - ], - ] -) - - -U_toffoli = np.diag([1 for i in range(8)]) -U_toffoli[6:8, 6:8] = np.array([[0, 1], [1, 0]]) - -U_swap = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) - -U_cswap = np.array( - [ - [1, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 1, 0], - [0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 1], - ] -) - - -H = np.array([[1.02789352, 1.61296440 - 0.3498192j], [1.61296440 + 0.3498192j, 1.23920938 + 0j]]) - - -THETA = np.linspace(0.11, 1, 3) -PHI = np.linspace(0.32, 1, 3) -VARPHI = np.linspace(0.02, 1, 3) +from conftest import THETA, PHI, VARPHI, TOL_STOCHASTIC class TestApply: @@ -112,25 +58,25 @@ class TestApply: @pytest.mark.parametrize("operation,input,expected_output", test_data_no_parameters) def test_apply_operation_single_wire_no_parameters( - self, qubit_device_1_wire, tol, operation, input, expected_output + self, qubit_device, tol, operation, input, expected_output ): """Tests that applying an operation yields the expected output state for single wire operations that have no parameters.""" - dev = qubit_device_1_wire + dev = qubit_device(wires=1) dev._state = np.array(input).astype(dev.C_DTYPE) dev.apply([operation(wires=[0])]) - assert np.allclose(qubit_device_1_wire._state, np.array(expected_output), atol=tol, rtol=0) + assert np.allclose(dev._state, np.array(expected_output), atol=tol, rtol=0) assert dev._state.dtype == dev.C_DTYPE @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") @pytest.mark.parametrize("operation,input,expected_output", test_data_no_parameters) @pytest.mark.parametrize("C", [np.complex64, np.complex128]) def test_apply_operation_preserve_pointer_single_wire_no_parameters( - self, qubit_device_1_wire, operation, input, expected_output, C + self, qubit_device, operation, input, expected_output, C ): - dev = qubit_device_1_wire + dev = qubit_device(wires=1) dev._state = dev._asarray(input, dtype=C) pointer_before, _ = dev._state.__array_interface__["data"] dev.apply([operation(wires=[0])]) @@ -164,27 +110,27 @@ def test_apply_operation_preserve_pointer_single_wire_no_parameters( @pytest.mark.parametrize("operation,input,expected_output", test_data_two_wires_no_parameters) def test_apply_operation_two_wires_no_parameters( - self, qubit_device_2_wires, tol, operation, input, expected_output + self, qubit_device, tol, operation, input, expected_output ): """Tests that applying an operation yields the expected output state for two wire operations that have no parameters.""" - dev = qubit_device_2_wires + dev = qubit_device(wires=2) dev._state = np.array(input).reshape(2 * [2]).astype(dev.C_DTYPE) dev.apply([operation(wires=[0, 1])]) - assert np.allclose(qubit_device_2_wires.state, np.array(expected_output), atol=tol, rtol=0) + assert np.allclose(dev.state, np.array(expected_output), atol=tol, rtol=0) assert dev._state.dtype == dev.C_DTYPE @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") @pytest.mark.parametrize("operation,input,expected_output", test_data_two_wires_no_parameters) @pytest.mark.parametrize("C", [np.complex64, np.complex128]) def test_apply_operation_preserve_pointer_two_wires_no_parameters( - self, qubit_device_2_wires, operation, input, expected_output, C + self, qubit_device, operation, input, expected_output, C ): - dev = qubit_device_2_wires + dev = qubit_device(wires=2) dev._state = dev._asarray(input, dtype=C).reshape(2 * [2]) pointer_before, _ = dev._state.__array_interface__["data"] - qubit_device_2_wires.apply([operation(wires=[0, 1])]) + dev.apply([operation(wires=[0, 1])]) pointer_after, _ = dev._state.__array_interface__["data"] assert pointer_before == pointer_after @@ -201,25 +147,25 @@ def test_apply_operation_preserve_pointer_two_wires_no_parameters( @pytest.mark.parametrize("operation,input,expected_output", test_data_three_wires_no_parameters) def test_apply_operation_three_wires_no_parameters( - self, qubit_device_3_wires, tol, operation, input, expected_output + self, qubit_device, tol, operation, input, expected_output ): """Tests that applying an operation yields the expected output state for three wire operations that have no parameters.""" - dev = qubit_device_3_wires + dev = qubit_device(wires=3) dev._state = np.array(input).reshape(3 * [2]).astype(dev.C_DTYPE) dev.apply([operation(wires=[0, 1, 2])]) - assert np.allclose(qubit_device_3_wires.state, np.array(expected_output), atol=tol, rtol=0) + assert np.allclose(dev.state, np.array(expected_output), atol=tol, rtol=0) assert dev._state.dtype == dev.C_DTYPE @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") @pytest.mark.parametrize("operation,input,expected_output", test_data_three_wires_no_parameters) @pytest.mark.parametrize("C", [np.complex64, np.complex128]) def test_apply_operation_preserve_pointer_three_wires_no_parameters( - self, qubit_device_3_wires, operation, input, expected_output, C + self, qubit_device, operation, input, expected_output, C ): - dev = qubit_device_3_wires + dev = qubit_device(wires=3) dev._state = dev._asarray(input, dtype=C).reshape(3 * [2]) pointer_before, _ = dev._state.__array_interface__["data"] dev.apply([operation(wires=[0, 1, 2])]) @@ -249,16 +195,17 @@ def test_apply_operation_preserve_pointer_three_wires_no_parameters( ], ) def test_apply_operation_state_preparation( - self, qubit_device_2_wires, tol, operation, expected_output, par + self, qubit_device, tol, operation, expected_output, par ): """Tests that applying an operation yields the expected output state for single wire operations that have no parameters.""" par = np.array(par) - qubit_device_2_wires.reset() - qubit_device_2_wires.apply([operation(par, wires=[0, 1])]) + dev = qubit_device(wires=2) + dev.reset() + dev.apply([operation(par, wires=[0, 1])]) - assert np.allclose(qubit_device_2_wires.state, np.array(expected_output), atol=tol, rtol=0) + assert np.allclose(dev.state, np.array(expected_output), atol=tol, rtol=0) """ operation,input,expected_output,par """ test_data_single_wire_with_parameters = [ @@ -323,16 +270,16 @@ def test_apply_operation_state_preparation( "operation,input,expected_output,par", test_data_single_wire_with_parameters ) def test_apply_operation_single_wire_with_parameters( - self, qubit_device_1_wire, tol, operation, input, expected_output, par + self, qubit_device, tol, operation, input, expected_output, par ): """Tests that applying an operation yields the expected output state for single wire operations that have parameters.""" - dev = qubit_device_1_wire + dev = qubit_device(wires=1) dev._state = np.array(input).astype(dev.C_DTYPE) dev.apply([operation(*par, wires=[0])]) - assert np.allclose(qubit_device_1_wire.state, np.array(expected_output), atol=tol, rtol=0) + assert np.allclose(dev.state, np.array(expected_output), atol=tol, rtol=0) assert dev._state.dtype == dev.C_DTYPE @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") @@ -341,9 +288,9 @@ def test_apply_operation_single_wire_with_parameters( ) @pytest.mark.parametrize("C", [np.complex64, np.complex128]) def test_apply_operation_preserve_pointer_single_wire_with_parameters( - self, qubit_device_1_wire, operation, input, expected_output, par, C + self, qubit_device, operation, input, expected_output, par, C ): - dev = qubit_device_1_wire + dev = qubit_device(wires=1) dev._state = dev._asarray(input, dtype=C) pointer_before, _ = dev._state.__array_interface__["data"] dev.apply([operation(*par, wires=[0])]) @@ -473,16 +420,16 @@ def test_apply_operation_preserve_pointer_single_wire_with_parameters( "operation,input,expected_output,par", test_data_two_wires_with_parameters ) def test_apply_operation_two_wires_with_parameters( - self, qubit_device_2_wires, tol, operation, input, expected_output, par + self, qubit_device, tol, operation, input, expected_output, par ): """Tests that applying an operation yields the expected output state for two wire operations that have parameters.""" - dev = qubit_device_2_wires + dev = qubit_device(wires=2) dev._state = np.array(input).reshape(2 * [2]).astype(dev.C_DTYPE) dev.apply([operation(*par, wires=[0, 1])]) - assert np.allclose(qubit_device_2_wires.state, np.array(expected_output), atol=tol, rtol=0) + assert np.allclose(dev.state, np.array(expected_output), atol=tol, rtol=0) assert dev._state.dtype == dev.C_DTYPE @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") @@ -491,9 +438,9 @@ def test_apply_operation_two_wires_with_parameters( ) @pytest.mark.parametrize("C", [np.complex64, np.complex128]) def test_apply_operation_preserve_pointer_two_wires_with_parameters( - self, qubit_device_2_wires, operation, input, expected_output, par, C + self, qubit_device, operation, input, expected_output, par, C ): - dev = qubit_device_2_wires + dev = qubit_device(wires=2) dev._state = dev._asarray(input, dtype=C).reshape(2 * [2]) pointer_before, _ = dev._state.__array_interface__["data"] dev.apply([operation(*par, wires=[0, 1])]) @@ -501,46 +448,39 @@ def test_apply_operation_preserve_pointer_two_wires_with_parameters( assert pointer_before == pointer_after - def test_apply_errors_qubit_state_vector(self, qubit_device_2_wires): + def test_apply_errors_qubit_state_vector(self, qubit_device): """Test that apply fails for incorrect state preparation, and > 2 qubit gates""" + dev = qubit_device(wires=2) with pytest.raises(ValueError, match="Sum of amplitudes-squared does not equal one."): - qubit_device_2_wires.apply([qml.QubitStateVector(np.array([1, -1]), wires=[0])]) - - with pytest.raises( - ValueError, - match=r"State vector must have shape \(2\*\*wires,\) or \(batch_size, 2\*\*wires\).", - ): - p = np.array([1, 0, 1, 1, 0]) / np.sqrt(3) - qubit_device_2_wires.apply([qml.QubitStateVector(p, wires=[0, 1])]) + dev.apply([qml.QubitStateVector(np.array([1, -1]), wires=[0])]) with pytest.raises( DeviceError, match="Operation QubitStateVector cannot be used after other Operations have already been applied ", ): - qubit_device_2_wires.reset() - qubit_device_2_wires.apply( + dev.reset() + dev.apply( [qml.RZ(0.5, wires=[0]), qml.QubitStateVector(np.array([0, 1, 0, 0]), wires=[0, 1])] ) - def test_apply_errors_basis_state(self, qubit_device_2_wires): + def test_apply_errors_basis_state(self, qubit_device): + dev = qubit_device(wires=2) with pytest.raises( ValueError, match="BasisState parameter must consist of 0 or 1 integers." ): - qubit_device_2_wires.apply([qml.BasisState(np.array([-0.2, 4.2]), wires=[0, 1])]) + dev.apply([qml.BasisState(np.array([-0.2, 4.2]), wires=[0, 1])]) with pytest.raises( ValueError, match="BasisState parameter and wires must be of equal length." ): - qubit_device_2_wires.apply([qml.BasisState(np.array([0, 1]), wires=[0])]) + dev.apply([qml.BasisState(np.array([0, 1]), wires=[0])]) with pytest.raises( DeviceError, match="Operation BasisState cannot be used after other Operations have already been applied ", ): - qubit_device_2_wires.reset() - qubit_device_2_wires.apply( - [qml.RZ(0.5, wires=[0]), qml.BasisState(np.array([1, 1]), wires=[0, 1])] - ) + dev.reset() + dev.apply([qml.RZ(0.5, wires=[0]), qml.BasisState(np.array([1, 1]), wires=[0, 1])]) class TestExpval: @@ -567,17 +507,15 @@ class TestExpval: ], ) def test_expval_single_wire_no_parameters( - self, qubit_device_1_wire, tol, operation, input, expected_output + self, qubit_device, tol, operation, input, expected_output ): """Tests that expectation values are properly calculated for single-wire observables without parameters.""" - + dev = qubit_device(wires=1) obs = operation(wires=[0]) - qubit_device_1_wire.reset() - qubit_device_1_wire.apply( - [qml.QubitStateVector(np.array(input), wires=[0])], obs.diagonalizing_gates() - ) - res = qubit_device_1_wire.expval(obs) + dev.reset() + dev.apply([qml.QubitStateVector(np.array(input), wires=[0])], obs.diagonalizing_gates()) + res = dev.expval(obs) assert np.isclose(res, expected_output, atol=tol, rtol=0) @@ -620,17 +558,15 @@ class TestVar: ], ) def test_var_single_wire_no_parameters( - self, qubit_device_1_wire, tol, operation, input, expected_output + self, qubit_device, tol, operation, input, expected_output ): """Tests that variances are properly calculated for single-wire observables without parameters.""" - + dev = qubit_device(wires=1) obs = operation(wires=[0]) - qubit_device_1_wire.reset() - qubit_device_1_wire.apply( - [qml.QubitStateVector(np.array(input), wires=[0])], obs.diagonalizing_gates() - ) - res = qubit_device_1_wire.var(obs) + dev.reset() + dev.apply([qml.QubitStateVector(np.array(input), wires=[0])], obs.diagonalizing_gates()) + res = dev.var(obs) assert np.isclose(res, expected_output, atol=tol, rtol=0) @@ -653,7 +589,7 @@ def circuit(): class TestSample: """Tests that samples are properly calculated.""" - def test_sample_dimensions(self, qubit_device_2_wires): + def test_sample_dimensions(self, qubit_device): """Tests if the samples returned by the sample function have the correct dimensions """ @@ -661,31 +597,32 @@ def test_sample_dimensions(self, qubit_device_2_wires): # Explicitly resetting is necessary as the internal # state is set to None in __init__ and only properly # initialized during reset - qubit_device_2_wires.reset() + dev = qubit_device(wires=2) + dev.reset() - qubit_device_2_wires.apply([qml.RX(1.5708, wires=[0]), qml.RX(1.5708, wires=[1])]) + dev.apply([qml.RX(1.5708, wires=[0]), qml.RX(1.5708, wires=[1])]) - qubit_device_2_wires.shots = 10 - qubit_device_2_wires._wires_measured = {0} - qubit_device_2_wires._samples = qubit_device_2_wires.generate_samples() - s1 = qubit_device_2_wires.sample(qml.PauliZ(wires=[0])) + dev.shots = 10 + dev._wires_measured = {0} + dev._samples = dev.generate_samples() + s1 = dev.sample(qml.PauliZ(wires=[0])) assert np.array_equal(s1.shape, (10,)) - qubit_device_2_wires.reset() - qubit_device_2_wires.shots = 12 - qubit_device_2_wires._wires_measured = {1} - qubit_device_2_wires._samples = qubit_device_2_wires.generate_samples() - s2 = qubit_device_2_wires.sample(qml.PauliZ(wires=[1])) + dev.reset() + dev.shots = 12 + dev._wires_measured = {1} + dev._samples = dev.generate_samples() + s2 = dev.sample(qml.PauliZ(wires=[1])) assert np.array_equal(s2.shape, (12,)) - qubit_device_2_wires.reset() - qubit_device_2_wires.shots = 17 - qubit_device_2_wires._wires_measured = {0, 1} - qubit_device_2_wires._samples = qubit_device_2_wires.generate_samples() - s3 = qubit_device_2_wires.sample(qml.PauliX(0) @ qml.PauliZ(1)) + dev.reset() + dev.shots = 17 + dev._wires_measured = {0, 1} + dev._samples = dev.generate_samples() + s3 = dev.sample(qml.PauliX(0) @ qml.PauliZ(1)) assert np.array_equal(s3.shape, (17,)) - def test_sample_values(self, qubit_device_2_wires, tol): + def test_sample_values(self, qubit_device, tol): """Tests if the samples returned by sample have the correct values """ @@ -693,14 +630,15 @@ def test_sample_values(self, qubit_device_2_wires, tol): # Explicitly resetting is necessary as the internal # state is set to None in __init__ and only properly # initialized during reset - qubit_device_2_wires.reset() + dev = qubit_device(wires=2) + dev.reset() - qubit_device_2_wires.shots = 1000 - qubit_device_2_wires.apply([qml.RX(1.5708, wires=[0])]) - qubit_device_2_wires._wires_measured = {0} - qubit_device_2_wires._samples = qubit_device_2_wires.generate_samples() + dev.shots = 1000 + dev.apply([qml.RX(1.5708, wires=[0])]) + dev._wires_measured = {0} + dev._samples = dev.generate_samples() - s1 = qubit_device_2_wires.sample(qml.PauliZ(0)) + s1 = dev.sample(qml.PauliZ(0)) # s1 should only contain 1 and -1, which is guaranteed if # they square to 1 @@ -711,8 +649,6 @@ class TestLightningQubitIntegration: """Integration tests for lightning.qubit. This test ensures it integrates properly with the PennyLane interface, in particular QNode.""" - from pennylane_lightning import LightningQubit as lq - def test_load_default_qubit_device(self): """Test that the default plugin loads correctly""" @@ -754,12 +690,13 @@ def test_args(self): with pytest.raises(TypeError, match="missing 1 required positional argument: 'wires'"): qml.device("lightning.qubit") - def test_qubit_circuit(self, qubit_device_1_wire, tol): + def test_qubit_circuit(self, qubit_device, tol): """Test that the default qubit plugin provides correct result for a simple circuit""" p = 0.543 + dev = qubit_device(wires=1) - @qml.qnode(qubit_device_1_wire) + @qml.qnode(dev) def circuit(x): qml.RX(x, wires=0) return qml.expval(qml.PauliY(0)) @@ -768,12 +705,13 @@ def circuit(x): assert np.isclose(circuit(p), expected, atol=tol, rtol=0) - def test_qubit_identity(self, qubit_device_1_wire, tol): + def test_qubit_identity(self, qubit_device, tol): """Test that the default qubit plugin provides correct result for the Identity expectation""" p = 0.543 + dev = qubit_device(wires=1) - @qml.qnode(qubit_device_1_wire) + @qml.qnode(dev) def circuit(x): """Test quantum function""" qml.RX(x, wires=0) @@ -781,7 +719,7 @@ def circuit(x): assert np.isclose(circuit(p), 1, atol=tol, rtol=0) - def test_nonzero_shots(self, tol): + def test_nonzero_shots(self, tol_stochastic): """Test that the default qubit plugin provides correct result for high shot number""" shots = 10**4 @@ -799,7 +737,7 @@ def circuit(x): for _ in range(100): runs.append(circuit(p)) - assert np.isclose(np.mean(runs), -np.sin(p), atol=1e-2, rtol=0) + assert np.isclose(np.mean(runs), -np.sin(p), atol=tol_stochastic, rtol=0) # This test is ran against the state |0> with one Z expval @pytest.mark.parametrize( @@ -812,15 +750,15 @@ def circuit(x): ], ) def test_supported_gate_single_wire_no_parameters( - self, qubit_device_1_wire, tol, name, expected_output + self, qubit_device, tol, name, expected_output ): """Tests supported gates that act on a single wire that are not parameterized""" - + dev = qubit_device(wires=1) op = getattr(qml.ops, name) - assert qubit_device_1_wire.supports_operation(name) + assert dev.supports_operation(name) - @qml.qnode(qubit_device_1_wire) + @qml.qnode(dev) def circuit(): op(wires=0) return qml.expval(qml.PauliZ(0)) @@ -836,16 +774,14 @@ def circuit(): ("CZ", [-1 / 2, -1 / 2]), ], ) - def test_supported_gate_two_wires_no_parameters( - self, qubit_device_2_wires, tol, name, expected_output - ): + def test_supported_gate_two_wires_no_parameters(self, qubit_device, tol, name, expected_output): """Tests supported gates that act on two wires that are not parameterized""" - + dev = qubit_device(wires=2) op = getattr(qml.ops, name) - assert qubit_device_2_wires.supports_operation(name) + assert dev.supports_operation(name) - @qml.qnode(qubit_device_2_wires) + @qml.qnode(dev) def circuit(): qml.QubitStateVector(np.array([1 / 2, 0, 0, math.sqrt(3) / 2]), wires=[0, 1]) op(wires=[0, 1]) @@ -860,15 +796,15 @@ def circuit(): ], ) def test_supported_gate_three_wires_no_parameters( - self, qubit_device_3_wires, tol, name, expected_output + self, qubit_device, tol, name, expected_output ): """Tests supported gates that act on three wires that are not parameterized""" - + dev = qubit_device(wires=3) op = getattr(qml.ops, name) - assert qubit_device_3_wires.supports_operation(name) + assert dev.supports_operation(name) - @qml.qnode(qubit_device_3_wires) + @qml.qnode(dev) def circuit(): qml.BasisState(np.array([1, 0, 1]), wires=[0, 1, 2]) op(wires=[0, 1, 2]) @@ -888,16 +824,14 @@ def circuit(): ("QubitStateVector", [0, 1, 0, 0], [1, -1]), ], ) - def test_supported_state_preparation( - self, qubit_device_2_wires, tol, name, par, expected_output - ): + def test_supported_state_preparation(self, qubit_device, tol, name, par, expected_output): """Tests supported state preparations""" - + dev = qubit_device(wires=2) op = getattr(qml.ops, name) - assert qubit_device_2_wires.supports_operation(name) + assert dev.supports_operation(name) - @qml.qnode(qubit_device_2_wires) + @qml.qnode(dev) def circuit(): op(np.array(par), wires=[0, 1]) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)) @@ -913,14 +847,12 @@ def circuit(): ("BasisState", [1], [1], [1, -1]), ], ) - def test_basis_state_2_qubit_subset( - self, qubit_device_2_wires, tol, name, par, wires, expected_output - ): + def test_basis_state_2_qubit_subset(self, qubit_device, tol, name, par, wires, expected_output): """Tests qubit basis state preparation on subsets of qubits""" - + dev = qubit_device(wires=2) op = getattr(qml.ops, name) - @qml.qnode(qubit_device_2_wires) + @qml.qnode(dev) def circuit(): op(np.array(par), wires=wires) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)) @@ -939,15 +871,15 @@ def circuit(): ], ) def test_state_vector_2_qubit_subset( - self, qubit_device_2_wires, tol, name, par, wires, expected_output + self, qubit_device, tol, name, par, wires, expected_output ): """Tests qubit state vector preparation on subsets of 2 qubits""" - + dev = qubit_device(wires=2) op = getattr(qml.ops, name) par = np.array(par) - @qml.qnode(qubit_device_2_wires) + @qml.qnode(dev) def circuit(): op(par, wires=wires) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)) @@ -973,15 +905,15 @@ def circuit(): ], ) def test_state_vector_3_qubit_subset( - self, qubit_device_3_wires, tol, name, par, wires, expected_output + self, qubit_device, tol, name, par, wires, expected_output ): """Tests qubit state vector preparation on subsets of 3 qubits""" - + dev = qubit_device(wires=3) op = getattr(qml.ops, name) par = np.array(par) - @qml.qnode(qubit_device_3_wires) + @qml.qnode(dev) def circuit(): op(par, wires=wires) return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1)), qml.expval(qml.PauliZ(2)) @@ -1009,15 +941,15 @@ def circuit(): ], ) def test_supported_gate_single_wire_with_parameters( - self, qubit_device_1_wire, tol, name, par, expected_output + self, qubit_device, tol, name, par, expected_output ): """Tests supported gates that act on a single wire that are parameterized""" - + dev = qubit_device(wires=1) op = getattr(qml.ops, name) - assert qubit_device_1_wire.supports_operation(name) + assert dev.supports_operation(name) - @qml.qnode(qubit_device_1_wire) + @qml.qnode(dev) def circuit(): op(*par, wires=0) return qml.expval(qml.PauliZ(0)) @@ -1050,15 +982,15 @@ def circuit(): ], ) def test_supported_gate_two_wires_with_parameters( - self, qubit_device_2_wires, tol, name, par, expected_output + self, qubit_device, tol, name, par, expected_output ): """Tests supported gates that act on two wires that are parameterized""" - + dev = qubit_device(wires=2) op = getattr(qml.ops, name) - assert qubit_device_2_wires.supports_operation(name) + assert dev.supports_operation(name) - @qml.qnode(qubit_device_2_wires) + @qml.qnode(dev) def circuit(): qml.QubitStateVector(np.array([1 / 2, 0, 0, math.sqrt(3) / 2]), wires=[0, 1]) op(*par, wires=[0, 1]) @@ -1084,15 +1016,15 @@ def circuit(): ], ) def test_supported_observable_single_wire_no_parameters( - self, qubit_device_1_wire, tol, name, state, expected_output + self, qubit_device, tol, name, state, expected_output ): """Tests supported observables on single wires without parameters.""" - + dev = qubit_device(wires=1) obs = getattr(qml.ops, name) - assert qubit_device_1_wire.supports_observable(name) + assert dev.supports_observable(name) - @qml.qnode(qubit_device_1_wire) + @qml.qnode(dev) def circuit(): qml.QubitStateVector(np.array(state), wires=[0]) return qml.expval(obs(wires=[0])) @@ -1108,27 +1040,26 @@ def circuit(): ], ) def test_supported_observable_single_wire_with_parameters( - self, qubit_device_1_wire, tol, name, state, expected_output, par + self, qubit_device, tol, name, state, expected_output, par ): """Tests supported observables on single wires with parameters.""" - + dev = qubit_device(wires=1) obs = getattr(qml.ops, name) - assert qubit_device_1_wire.supports_observable(name) + assert dev.supports_observable(name) - @qml.qnode(qubit_device_1_wire) + @qml.qnode(dev) def circuit(): qml.QubitStateVector(np.array(state), wires=[0]) return qml.expval(obs(*par, wires=[0])) assert np.isclose(circuit(), expected_output, atol=tol, rtol=0) - def test_multi_samples_return_correlated_results(self, qubit_device_2_wires): + def test_multi_samples_return_correlated_results(self, qubit_device): """Tests if the samples returned by the sample function have the correct dimensions """ - - dev = qubit_device_2_wires + dev = qubit_device(wires=2) dev.shots = 1000 @qml.qnode(dev) @@ -1169,7 +1100,7 @@ def test_snapshot_is_ignored_without_shot(self): def circuit(): qml.Hadamard(0) qml.Snapshot() - qml.Snapshot().inv() + qml.adjoint(qml.Snapshot()) qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0)) @@ -1185,7 +1116,7 @@ def test_snapshot_is_ignored_with_shots(self): def circuit(): qml.Hadamard(0) qml.Snapshot() - qml.Snapshot().inv() + qml.adjoint(qml.Snapshot()) qml.CNOT(wires=[0, 1]) return qml.sample(qml.PauliZ(0)), qml.sample(qml.PauliZ(1)) @@ -1198,9 +1129,9 @@ def circuit(): class TestTensorExpval: """Test tensor expectation values""" - def test_paulix_pauliy(self, theta, phi, varphi, qubit_device_3_wires, tol): + def test_paulix_pauliy(self, theta, phi, varphi, qubit_device, tol): """Test that a tensor product involving PauliX and PauliY works correctly""" - dev = qubit_device_3_wires + dev = qubit_device(wires=3) dev.reset() obs = qml.PauliX(0) @ qml.PauliY(2) @@ -1222,9 +1153,9 @@ def test_paulix_pauliy(self, theta, phi, varphi, qubit_device_3_wires, tol): assert np.allclose(res, expected, atol=tol, rtol=0) - def test_pauliz_identity(self, theta, phi, varphi, qubit_device_3_wires, tol): + def test_pauliz_identity(self, theta, phi, varphi, qubit_device, tol): """Test that a tensor product involving PauliZ and Identity works correctly""" - dev = qubit_device_3_wires + dev = qubit_device(wires=3) dev.reset() obs = qml.PauliZ(0) @ qml.Identity(1) @ qml.PauliZ(2) @@ -1246,9 +1177,9 @@ def test_pauliz_identity(self, theta, phi, varphi, qubit_device_3_wires, tol): assert np.allclose(res, expected, atol=tol, rtol=0) - def test_pauliz_hadamard(self, theta, phi, varphi, qubit_device_3_wires, tol): + def test_pauliz_hadamard(self, theta, phi, varphi, qubit_device, tol): """Test that a tensor product involving PauliZ and PauliY and hadamard works correctly""" - dev = qubit_device_3_wires + dev = qubit_device(wires=3) obs = qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2) dev.reset() @@ -1274,10 +1205,9 @@ def test_pauliz_hadamard(self, theta, phi, varphi, qubit_device_3_wires, tol): class TestTensorVar: """Tests for variance of tensor observables""" - def test_paulix_pauliy(self, theta, phi, varphi, qubit_device_3_wires, tol): + def test_paulix_pauliy(self, theta, phi, varphi, qubit_device, tol): """Test that a tensor product involving PauliX and PauliY works correctly""" - dev = qubit_device_3_wires - + dev = qubit_device(wires=3) obs = qml.PauliX(0) @ qml.PauliY(2) dev.apply( @@ -1304,9 +1234,9 @@ def test_paulix_pauliy(self, theta, phi, varphi, qubit_device_3_wires, tol): assert np.allclose(res, expected, atol=tol, rtol=0) - def test_pauliz_hadamard(self, theta, phi, varphi, qubit_device_3_wires, tol): + def test_pauliz_hadamard(self, theta, phi, varphi, qubit_device, tol): """Test that a tensor product involving PauliZ and PauliY and hadamard works correctly""" - dev = qubit_device_3_wires + dev = qubit_device(wires=3) obs = qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2) dev.reset() @@ -1333,16 +1263,12 @@ def test_pauliz_hadamard(self, theta, phi, varphi, qubit_device_3_wires, tol): assert np.allclose(res, expected, atol=tol, rtol=0) -# Tolerance for non-analytic tests -TOL_STOCHASTIC = 0.05 - - @pytest.mark.parametrize("theta, phi, varphi", list(zip(THETA, PHI, VARPHI))) @pytest.mark.parametrize("shots", [None, 100000]) class TestTensorSample: """Test sampling tensor the tensor product of observables""" - def test_paulix_pauliy(self, theta, phi, varphi, shots, monkeypatch, tol): + def test_paulix_pauliy(self, theta, phi, varphi, shots, tol): """Test that a tensor product involving PauliX and PauliY works correctly""" tolerance = tol if shots is None else TOL_STOCHASTIC dev = qml.device("lightning.qubit", wires=3, shots=shots) @@ -1384,12 +1310,10 @@ def test_paulix_pauliy(self, theta, phi, varphi, shots, monkeypatch, tol): ) / 16 assert np.allclose(var, expected, atol=tolerance, rtol=0) - def test_pauliz_hadamard( - self, theta, phi, varphi, monkeypatch, shots, qubit_device_3_wires, tol - ): + def test_pauliz_hadamard(self, theta, phi, varphi, shots, qubit_device, tol): """Test that a tensor product involving PauliZ and PauliY and hadamard works correctly""" tolerance = tol if shots is None else TOL_STOCHASTIC - dev = qubit_device_3_wires + dev = qubit_device(wires=3) obs = qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2) dev.apply( [ @@ -1424,12 +1348,10 @@ def test_pauliz_hadamard( ) / 4 assert np.allclose(var, expected, atol=tolerance, rtol=0) - def test_qubitunitary_rotation_hadamard( - self, theta, phi, varphi, monkeypatch, shots, qubit_device_3_wires, tol - ): + def test_qubitunitary_rotation_hadamard(self, theta, phi, varphi, shots, qubit_device, tol): """Test that a tensor product involving PauliZ and PauliY and hadamard works correctly""" tolerance = tol if shots is None else TOL_STOCHASTIC - dev = qubit_device_3_wires + dev = qubit_device(wires=3) obs = qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2) dev.apply( [ @@ -1486,27 +1408,6 @@ def test_apply_identity_skipped(self, mocker, tol): assert np.allclose(dev._state, starting_state, atol=tol, rtol=0) assert dev._state.dtype == dev.C_DTYPE - def test_iter_identity_skipped(self, mocker, tol): - """Test identity operations do not perform additional computations.""" - dev = qml.device("lightning.qubit", wires=2) - if not hasattr(dev, "apply_lightning"): - pytest.skip("LightningQubit object has no attribute apply_lightning") - - starting_state = np.array([1, 0, 0, 0], dtype=dev.C_DTYPE) - op = [qml.Identity(0), qml.Identity(1)] - - spy_diagonal = mocker.spy(dev, "_apply_diagonal_unitary") - spy_einsum = mocker.spy(dev, "_apply_unitary_einsum") - spy_unitary = mocker.spy(dev, "_apply_unitary") - - res = dev.apply_lightning(starting_state, op) - assert np.allclose(res, starting_state, atol=tol, rtol=0) - assert dev._state.dtype == dev.C_DTYPE - - spy_diagonal.assert_not_called() - spy_einsum.assert_not_called() - spy_unitary.assert_not_called() - @pytest.mark.skipif(CPP_BINARY_AVAILABLE, reason="Test only applies when binaries are unavailable") def test_warning(): diff --git a/tests/test_decomposition.py b/tests/test_decomposition.py new file mode 100644 index 0000000000..9bd19ab016 --- /dev/null +++ b/tests/test_decomposition.py @@ -0,0 +1,44 @@ +# Copyright 2022 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 operation decomposition. +""" +import pytest +import numpy as np +import pennylane as qml +from pennylane_lightning import LightningQubit + +from pennylane_lightning.lightning_qubit import CPP_BINARY_AVAILABLE + +if not CPP_BINARY_AVAILABLE: + pytest.skip("No binary module found. Skipping.", allow_module_level=True) + + +class TestDenseMatrixDecompositionThreshold: + """Tests, for QFT and Grover operators, the automatic transition from full matrix to decomposition + on calculations.""" + + input = [ + (qml.QFT, 8, True), + (qml.QFT, 10, False), + (qml.GroverOperator, 8, True), + (qml.GroverOperator, 13, False), + ] + + @pytest.mark.parametrize("op, n_wires, condition", input) + def test_threshold(self, op, n_wires, condition): + + wires = np.linspace(0, n_wires - 1, n_wires, dtype=int) + op = op(wires=wires) + assert LightningQubit.stopping_condition.__get__(op)(op) == condition diff --git a/tests/test_device.py b/tests/test_device.py index 9a3bb52a20..7327b95c30 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """ -Unit tests :mod:`pennylane_lightning.LightningQubit` device can be creaated. +Unit tests :mod:`pennylane_lightning.LightningQubit` device can be created. """ import pytest import numpy as np diff --git a/tests/test_expval.py b/tests/test_expval.py index e12b017b4b..52e5e3245d 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -19,23 +19,16 @@ import numpy as np import pennylane as qml -from conftest import U, U2, A - - -np.random.seed(42) - -THETA = np.linspace(0.11, 1, 3) -PHI = np.linspace(0.32, 1, 3) -VARPHI = np.linspace(0.02, 1, 3) +from conftest import THETA, PHI, VARPHI @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) class TestExpval: """Test expectation values""" - def test_identity_expectation(self, theta, phi, qubit_device_3_wires, tol): + def test_identity_expectation(self, theta, phi, qubit_device, tol): """Test that identity expectation value (i.e. the trace) is 1""" - dev = qubit_device_3_wires + dev = qubit_device(wires=3) O1 = qml.Identity(wires=[0]) O2 = qml.Identity(wires=[1]) @@ -48,9 +41,9 @@ def test_identity_expectation(self, theta, phi, qubit_device_3_wires, tol): res = np.array([dev.expval(O1), dev.expval(O2)]) assert np.allclose(res, np.array([1, 1]), tol) - def test_pauliz_expectation(self, theta, phi, qubit_device_3_wires, tol): + def test_pauliz_expectation(self, theta, phi, qubit_device, tol): """Test that PauliZ expectation value is correct""" - dev = qubit_device_3_wires + dev = qubit_device(wires=3) O1 = qml.PauliZ(wires=[0]) O2 = qml.PauliZ(wires=[1]) @@ -62,9 +55,9 @@ def test_pauliz_expectation(self, theta, phi, qubit_device_3_wires, tol): res = np.array([dev.expval(O1), dev.expval(O2)]) assert np.allclose(res, np.array([np.cos(theta), np.cos(theta) * np.cos(phi)]), tol) - def test_paulix_expectation(self, theta, phi, qubit_device_3_wires, tol): + def test_paulix_expectation(self, theta, phi, qubit_device, tol): """Test that PauliX expectation value is correct""" - dev = qubit_device_3_wires + dev = qubit_device(wires=3) O1 = qml.PauliX(wires=[0]) O2 = qml.PauliX(wires=[1]) @@ -75,12 +68,12 @@ def test_paulix_expectation(self, theta, phi, qubit_device_3_wires, tol): res = np.array([dev.expval(O1), dev.expval(O2)], dtype=dev.C_DTYPE) assert np.allclose( - res, np.array([np.sin(theta) * np.sin(phi), np.sin(phi)], dtype=dev.C_DTYPE) + res, np.array([np.sin(theta) * np.sin(phi), np.sin(phi)], dtype=dev.C_DTYPE), tol * 10 ) - def test_pauliy_expectation(self, theta, phi, qubit_device_3_wires, tol): + def test_pauliy_expectation(self, theta, phi, qubit_device, tol): """Test that PauliY expectation value is correct""" - dev = qubit_device_3_wires + dev = qubit_device(wires=3) O1 = qml.PauliY(wires=[0]) O2 = qml.PauliY(wires=[1]) @@ -92,9 +85,9 @@ def test_pauliy_expectation(self, theta, phi, qubit_device_3_wires, tol): res = np.array([dev.expval(O1), dev.expval(O2)]) assert np.allclose(res, np.array([0, -np.cos(theta) * np.sin(phi)]), tol) - def test_hadamard_expectation(self, theta, phi, qubit_device_3_wires, tol): + def test_hadamard_expectation(self, theta, phi, qubit_device, tol): """Test that Hadamard expectation value is correct""" - dev = qubit_device_3_wires + dev = qubit_device(wires=3) O1 = qml.Hadamard(wires=[0]) O2 = qml.Hadamard(wires=[1]) @@ -197,10 +190,10 @@ def circuit(x, y): class TestTensorExpval: """Test tensor expectation values""" - def test_paulix_pauliy(self, theta, phi, varphi, qubit_device_3_wires, tol): + def test_paulix_pauliy(self, theta, phi, varphi, qubit_device, tol): """Test that a tensor product involving PauliX and PauliY works correctly""" - dev = qubit_device_3_wires + dev = qubit_device(wires=3) obs = qml.PauliX(0) @ qml.PauliY(2) dev.apply( @@ -219,10 +212,10 @@ def test_paulix_pauliy(self, theta, phi, varphi, qubit_device_3_wires, tol): assert np.allclose(res, expected, atol=tol) - def test_pauliz_identity(self, theta, phi, varphi, qubit_device_3_wires, tol): + def test_pauliz_identity(self, theta, phi, varphi, qubit_device, tol): """Test that a tensor product involving PauliZ and Identity works correctly""" - dev = qubit_device_3_wires + dev = qubit_device(wires=3) obs = qml.PauliZ(0) @ qml.Identity(1) @ qml.PauliZ(2) dev.apply( @@ -242,10 +235,10 @@ def test_pauliz_identity(self, theta, phi, varphi, qubit_device_3_wires, tol): assert np.allclose(res, expected, tol) - def test_pauliz_hadamard_pauliy(self, theta, phi, varphi, qubit_device_3_wires, tol): - """Test that a tensor product involving PauliZ and PauliY and hadamard + def test_pauliz_hadamard_pauliy(self, theta, phi, varphi, qubit_device, tol): + """Test that a tensor product involving PauliZ and PauliY and Hadamard works correctly""" - dev = qubit_device_3_wires + dev = qubit_device(wires=3) obs = qml.PauliZ(0) @ qml.Hadamard(1) @ qml.PauliY(2) dev.apply( diff --git a/tests/test_gates.py b/tests/test_gates.py index 7623cfb093..f5fba73304 100644 --- a/tests/test_gates.py +++ b/tests/test_gates.py @@ -26,55 +26,56 @@ @pytest.fixture def op(op_name): ops_list = { - "RX": qml.RX(0.123, wires=0), - "RY": qml.RY(1.434, wires=0), - "RZ": qml.RZ(2.774, wires=0), - "S": qml.S(wires=0), - "SX": qml.SX(wires=0), - "T": qml.T(wires=0), - "CNOT": qml.CNOT(wires=[0, 1]), - "CZ": qml.CZ(wires=[0, 1]), - "CY": qml.CY(wires=[0, 1]), - "SWAP": qml.SWAP(wires=[0, 1]), - "ISWAP": qml.ISWAP(wires=[0, 1]), - "SISWAP": qml.SISWAP(wires=[0, 1]), - "SQISW": qml.SQISW(wires=[0, 1]), - "CSWAP": qml.CSWAP(wires=[0, 1, 2]), - "PauliRot": qml.PauliRot(0.123, "Y", wires=0), - "IsingXX": qml.IsingXX(0.123, wires=[0, 1]), - "IsingXY": qml.IsingXY(0.123, wires=[0, 1]), - "IsingYY": qml.IsingYY(0.123, wires=[0, 1]), - "IsingZZ": qml.IsingZZ(0.123, wires=[0, 1]), - "Identity": qml.Identity(wires=0), - "Rot": qml.Rot(0.123, 0.456, 0.789, wires=0), - "Toffoli": qml.Toffoli(wires=[0, 1, 2]), - "PhaseShift": qml.PhaseShift(2.133, wires=0), - "ControlledPhaseShift": qml.ControlledPhaseShift(1.777, wires=[0, 2]), - "CPhase": qml.CPhase(1.777, wires=[0, 2]), - "MultiRZ": qml.MultiRZ(0.112, wires=[1, 2, 3]), - "CRX": qml.CRX(0.836, wires=[2, 3]), - "CRY": qml.CRY(0.721, wires=[2, 3]), - "CRZ": qml.CRZ(0.554, wires=[2, 3]), - "Hadamard": qml.Hadamard(wires=0), - "PauliX": qml.PauliX(wires=0), - "PauliY": qml.PauliY(wires=0), - "PauliZ": qml.PauliZ(wires=0), - "CRot": qml.CRot(0.123, 0.456, 0.789, wires=[0, 1]), - "DiagonalQubitUnitary": qml.DiagonalQubitUnitary(np.array([1.0, 1.0j]), wires=1), - "ControlledQubitUnitary": qml.ControlledQubitUnitary( - np.eye(2) * 1j, wires=[0], control_wires=[2] - ), - "MultiControlledX": qml.MultiControlledX(wires=(0, 1, 2), control_values="01"), - "SingleExcitation": qml.SingleExcitation(0.123, wires=[0, 3]), - "SingleExcitationPlus": qml.SingleExcitationPlus(0.123, wires=[0, 3]), - "SingleExcitationMinus": qml.SingleExcitationMinus(0.123, wires=[0, 3]), - "DoubleExcitation": qml.DoubleExcitation(0.123, wires=[0, 1, 2, 3]), - "DoubleExcitationPlus": qml.DoubleExcitationPlus(0.123, wires=[0, 1, 2, 3]), - "DoubleExcitationMinus": qml.DoubleExcitationMinus(0.123, wires=[0, 1, 2, 3]), - "QFT": qml.QFT(wires=0), - "QubitSum": qml.QubitSum(wires=[0, 1, 2]), - "QubitCarry": qml.QubitCarry(wires=[0, 1, 2, 3]), - "QubitUnitary": qml.QubitUnitary(np.eye(2) * 1j, wires=0), + "RX": [qml.RX, [], {"phi": 0.123, "wires": [0]}], + "RY": [qml.RY, [], {"phi": 1.434, "wires": [0]}], + "RZ": [qml.RZ, [], {"phi": 2.774, "wires": [0]}], + "S": [qml.S, [], {"wires": [0]}], + "SX": [qml.SX, [], {"wires": [0]}], + "T": [qml.T, [], {"wires": [0]}], + "CNOT": [qml.CNOT, [], {"wires": [0, 1]}], + "CZ": [qml.CZ, [], {"wires": [0, 1]}], + "CY": [qml.CY, [], {"wires": [0, 1]}], + "SWAP": [qml.SWAP, [], {"wires": [0, 1]}], + "ISWAP": [qml.ISWAP, [], {"wires": [0, 1]}], + "SISWAP": [qml.SISWAP, [], {"wires": [0, 1]}], + "SQISW": [qml.SQISW, [], {"wires": [0, 1]}], + "CSWAP": [qml.CSWAP, [], {"wires": [0, 1, 2]}], + "PauliRot": [qml.PauliRot, [0.123], {"pauli_word": "Y", "wires": [0]}], + "IsingXX": [qml.IsingXX, [], {"phi": 0.123, "wires": [0, 1]}], + "IsingXY": [qml.IsingXY, [], {"phi": 0.123, "wires": [0, 1]}], + "IsingYY": [qml.IsingYY, [], {"phi": 0.123, "wires": [0, 1]}], + "IsingZZ": [qml.IsingZZ, [], {"phi": 0.123, "wires": [0, 1]}], + "Identity": [qml.Identity, [], {"wires": [0]}], + "Rot": [qml.Rot, [], {"phi": 0.123, "theta": 0.456, "omega": 0.789, "wires": [0]}], + "Toffoli": [qml.Toffoli, [], {"wires": [0, 1, 2]}], + "PhaseShift": [qml.PhaseShift, [], {"phi": 2.133, "wires": [0]}], + "ControlledPhaseShift": [qml.ControlledPhaseShift, [], {"phi": 1.777, "wires": [0, 1]}], + "CPhase": [qml.CPhase, [], {"phi": 1.777, "wires": [0, 1]}], + "MultiRZ": [qml.MultiRZ, [], {"theta": 0.112, "wires": [0, 1, 2]}], + "CRX": [qml.CRX, [], {"phi": 0.123, "wires": [0, 1]}], + "CRY": [qml.CRY, [], {"phi": 0.123, "wires": [0, 1]}], + "CRZ": [qml.CRZ, [], {"phi": 0.123, "wires": [0, 1]}], + "Hadamard": [qml.Hadamard, [], {"wires": [0]}], + "PauliX": [qml.PauliX, [], {"wires": [0]}], + "PauliY": [qml.PauliY, [], {"wires": [0]}], + "PauliZ": [qml.PauliZ, [], {"wires": [0]}], + "CRot": [qml.CRot, [], {"phi": 0.123, "theta": 0.456, "omega": 0.789, "wires": [0, 1]}], + "DiagonalQubitUnitary": [qml.DiagonalQubitUnitary, [np.array([1.0, 1.0j])], {"wires": [0]}], + "MultiControlledX": [ + qml.MultiControlledX, + [], + {"wires": [0, 1, 2], "control_values": "01"}, + ], + "SingleExcitation": [qml.SingleExcitation, [0.123], {"wires": [0, 1]}], + "SingleExcitationPlus": [qml.SingleExcitationPlus, [0.123], {"wires": [0, 1]}], + "SingleExcitationMinus": [qml.SingleExcitationMinus, [0.123], {"wires": [0, 1]}], + "DoubleExcitation": [qml.DoubleExcitation, [0.123], {"wires": [0, 1, 2, 3]}], + "DoubleExcitationPlus": [qml.DoubleExcitationPlus, [0.123], {"wires": [0, 1, 2, 3]}], + "DoubleExcitationMinus": [qml.DoubleExcitationMinus, [0.123], {"wires": [0, 1, 2, 3]}], + "QFT": [qml.QFT, [], {"wires": [0]}], + "QubitSum": [qml.QubitSum, [], {"wires": [0, 1, 2]}], + "QubitCarry": [qml.QubitCarry, [], {"wires": [0, 1, 2, 3]}], + "QubitUnitary": [qml.QubitUnitary, [], {"U": np.eye(16) * 1j, "wires": [0, 1, 2, 3]}], } return ops_list.get(op_name) @@ -86,31 +87,17 @@ def test_gate_unitary_correct(op, op_name): if op_name in ("BasisState", "QubitStateVector"): pytest.skip("Skipping operation because it is a state preparation") - if op_name in ( - "ControlledQubitUnitary", - "QubitUnitary", - "MultiControlledX", - "DiagonalQubitUnitary", - ): - pytest.skip("Skipping operation.") # These are tested in the device test-suite if op == None: pytest.skip("Skipping operation.") - wires = op.num_wires - - if wires == -1: # This occurs for operations that do not have a predefined number of wires - wires = 4 + wires = len(op[2]["wires"]) dev = qml.device("lightning.qubit", wires=wires) - num_params = op.num_params - p = [0.1] * num_params - - op = getattr(qml, op_name) @qml.qnode(dev) def output(input): qml.BasisState(input, wires=range(wires)) - op(*p, wires=range(wires)) + op[0](*op[1], **op[2]) return qml.state() unitary = np.zeros((2**wires, 2**wires), dtype=np.complex128) @@ -119,7 +106,7 @@ def output(input): out = output(np.array(input)) unitary[:, i] = out - unitary_expected = qml.matrix(op(*p, wires=range(wires))) + unitary_expected = qml.matrix(op[0](*op[1], **op[2])) assert np.allclose(unitary, unitary_expected) @@ -131,31 +118,17 @@ def test_inverse_unitary_correct(op, op_name): if op_name in ("BasisState", "QubitStateVector"): pytest.skip("Skipping operation because it is a state preparation") - if op_name in ( - "ControlledQubitUnitary", - "QubitUnitary", - "MultiControlledX", - "DiagonalQubitUnitary", - ): - pytest.skip("Skipping operation.") # These are tested in the device test-suite if op == None: pytest.skip("Skipping operation.") - wires = op.num_wires - - if wires == -1: # This occurs for operations that do not have a predefined number of wires - wires = 4 + wires = len(op[2]["wires"]) dev = qml.device("lightning.qubit", wires=wires) - num_params = op.num_params - p = [0.1] * num_params - - op = getattr(qml, op_name) @qml.qnode(dev) def output(input): qml.BasisState(input, wires=range(wires)) - op(*p, wires=range(wires)).inv() + qml.adjoint(op[0](*op[1], **op[2])) return qml.state() unitary = np.zeros((2**wires, 2**wires), dtype=np.complex128) @@ -164,7 +137,7 @@ def output(input): out = output(np.array(input)) unitary[:, i] = out - unitary_expected = qml.matrix(op(*p, wires=range(wires)).inv()) + unitary_expected = qml.matrix(qml.adjoint(op[0](*op[1], **op[2]))) assert np.allclose(unitary, unitary_expected) @@ -229,7 +202,7 @@ def test_arbitrary_inv_unitary_correct(): @qml.qnode(dev) def output(input): qml.BasisState(input, wires=range(wires)) - qml.QubitUnitary(random_unitary, wires=range(2)).inv() + qml.adjoint(qml.QubitUnitary(random_unitary, wires=range(2))) return qml.state() unitary = np.zeros((2**wires, 2**wires), dtype=np.complex128) diff --git a/tests/test_measures.py b/tests/test_measures.py index 4c83c24b4c..690d9c1995 100644 --- a/tests/test_measures.py +++ b/tests/test_measures.py @@ -24,12 +24,9 @@ import pytest -try: - from pennylane_lightning.lightning_qubit_ops import ( - MeasuresC64, - MeasuresC128, - ) -except (ImportError, ModuleNotFoundError): +from pennylane_lightning.lightning_qubit import CPP_BINARY_AVAILABLE + +if not CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) diff --git a/tests/test_measures_sparse.py b/tests/test_measures_sparse.py index 54e06d6571..30226f86f9 100644 --- a/tests/test_measures_sparse.py +++ b/tests/test_measures_sparse.py @@ -22,8 +22,7 @@ try: from pennylane_lightning.lightning_qubit_ops import ( - MeasuresC64, - MeasuresC128, + Kokkos_info, ) except (ImportError, ModuleNotFoundError): pytest.skip("No binary module found. Skipping.", allow_module_level=True) @@ -36,6 +35,29 @@ class TestSparseExpval: def dev(self, request): return qml.device("lightning.qubit", wires=2, c_dtype=request.param) + @pytest.mark.skipif( + Kokkos_info()["USE_KOKKOS"] == True, reason="Kokkos and Kokkos Kernels are present." + ) + def test_create_device_with_unsupported_dtype(self, dev): + @qml.qnode(dev, diff_method="parameter-shift") + def circuit(): + qml.RX(0.4, wires=[0]) + qml.RY(-0.2, wires=[1]) + return qml.expval( + qml.SparseHamiltonian( + qml.utils.sparse_hamiltonian( + qml.Hamiltonian([1], [qml.PauliX(0) @ qml.Identity(1)]) + ), + wires=[0, 1], + ) + ) + + with pytest.raises( + NotImplementedError, + match="The expval of a SparseHamiltonian requires Kokkos and Kokkos Kernels.", + ): + circuit() + @pytest.mark.parametrize( "cases", [ @@ -47,6 +69,9 @@ def dev(self, request): [qml.Identity(0) @ qml.PauliZ(1), 0.98006657784124170], ], ) + @pytest.mark.skipif( + Kokkos_info()["USE_KOKKOS"] == False, reason="Requires Kokkos and Kokkos Kernels." + ) def test_sparse_Pauli_words(self, cases, tol, dev): """Test expval of some simple sparse Hamiltonian""" @@ -99,6 +124,9 @@ def dev(self, request): ], ], ) + @pytest.mark.skipif( + Kokkos_info()["USE_KOKKOS"] == False, reason="Requires Kokkos and Kokkos Kernels." + ) def test_sparse_Pauli_words(self, qubits, wires, H_sparse, hf_state, excitations, tol, dev): """Test expval of some simple sparse Hamiltonian""" diff --git a/tests/test_serialize.py b/tests/test_serialize.py index 25a976c83e..70fb544296 100644 --- a/tests/test_serialize.py +++ b/tests/test_serialize.py @@ -402,7 +402,6 @@ def test_basic_circuit(self): ), False, ) - print(s == s_expected) assert s == s_expected def test_skips_prep_circuit(self): @@ -477,7 +476,7 @@ def test_custom_wires_circuit(self): qml.CNOT(wires=["a", 3.2]) qml.SingleExcitation(0.5, wires=["a", 3.2]) qml.SingleExcitationPlus(0.4, wires=["a", 3.2]) - qml.SingleExcitationMinus(0.5, wires=["a", 3.2]).inv() + qml.adjoint(qml.SingleExcitationMinus(0.5, wires=["a", 3.2]), lazy=False) s = _serialize_ops(tape, wires_dict) s_expected = ( @@ -490,9 +489,9 @@ def test_custom_wires_circuit(self): "SingleExcitationPlus", "SingleExcitationMinus", ], - [[0.4], [0.6], [], [0.5], [0.4], [0.5]], + [[0.4], [0.6], [], [0.5], [0.4], [-0.5]], [[0], [1], [0, 1], [0, 1], [0, 1], [0, 1]], - [False, False, False, False, False, True], + [False, False, False, False, False, False], [[], [], [], [], [], []], ), False, diff --git a/tests/test_var.py b/tests/test_var.py index d8f4c5cd53..9d0c42a2de 100644 --- a/tests/test_var.py +++ b/tests/test_var.py @@ -19,9 +19,6 @@ import numpy as np import pennylane as qml -from conftest import U, U2, A - - np.random.seed(42) THETA = np.linspace(0.11, 1, 3) diff --git a/tests/test_vjp.py b/tests/test_vjp.py index 24da05e601..18f81e0294 100644 --- a/tests/test_vjp.py +++ b/tests/test_vjp.py @@ -14,7 +14,6 @@ """ Tests for the ``vjp`` method of LightningQubit. """ -from cmath import exp import pytest import math