From fabb46cf6b7975d0e6798de87b9ad5d8f383a2e3 Mon Sep 17 00:00:00 2001 From: Chae-Yeun Park Date: Fri, 6 May 2022 18:42:10 -0400 Subject: [PATCH 01/13] Update handling data types --- pennylane_lightning/lightning_qubit.py | 133 ++++++----------------- tests/conftest.py | 18 ++-- tests/test_adjoint_jacobian.py | 131 ++++++++++------------ tests/test_apply.py | 76 ++++++------- tests/test_device.py | 38 +++++++ tests/test_expval.py | 9 +- tests/test_measures.py | 143 ++++++------------------- tests/test_vjp.py | 129 +++++++--------------- 8 files changed, 247 insertions(+), 430 deletions(-) create mode 100644 tests/test_device.py diff --git a/pennylane_lightning/lightning_qubit.py b/pennylane_lightning/lightning_qubit.py index 7891ea7cf7..b103a38db7 100644 --- a/pennylane_lightning/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit.py @@ -110,8 +110,16 @@ class LightningQubit(DefaultQubit): _CPP_BINARY_AVAILABLE = True operations = _remove_snapshot_from_operations(DefaultQubit.operations) - def __init__(self, wires, *, shots=None, batch_obs=False): - super().__init__(wires, shots=shots) + def __init__(self, wires, *, c_dtype=np.complex128, shots=None, batch_obs=False): + if c_dtype is np.complex64: + r_dtype = np.float32 + self.use_csingle = True + elif c_dtype is np.complex128: + r_dtype = np.float64 + 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) self._batch_obs = batch_obs @classmethod @@ -144,12 +152,8 @@ def apply(self, operations, rotations=None, **kwargs): "applied on a {} device.".format(operation.name, self.short_name) ) - # Get the Type of self._state - # as the reference type - dtype = self._state.dtype - if operations: - self._pre_rotated_state = self.apply_lightning(self._state, operations, dtype=dtype) + self._pre_rotated_state = self.apply_lightning(self._state, operations) else: self._pre_rotated_state = self._state @@ -158,12 +162,11 @@ def apply(self, operations, rotations=None, **kwargs): super().apply(operations=[], rotations=rotations) else: self._state = self.apply_lightning( - np.copy(self._pre_rotated_state), rotations, dtype=dtype - ) + np.copy(self._pre_rotated_state), rotations) else: self._state = self._pre_rotated_state - def apply_lightning(self, state, operations, dtype=np.complex128): + def apply_lightning(self, state, operations): """Apply a list of operations to the state tensor. Args: @@ -177,14 +180,12 @@ def apply_lightning(self, state, operations, dtype=np.complex128): """ state_vector = np.ravel(state) - if dtype == np.complex64: + if self.use_csingle: # use_csingle sim = StateVectorC64(state_vector) - elif dtype == np.complex128: + else: # self.C_DTYPE is np.complex128 by default sim = StateVectorC128(state_vector) - else: - raise TypeError(f"Unsupported complex Type: {dtype}") # Skip over identity operations instead of performing # matrix multiplication with the identity. @@ -265,14 +266,7 @@ def adjoint_jacobian(self, tape, starting_state=None, use_device_state=False): ) # To support np.complex64 based on the type of self._state - dtype = self._state.dtype - if dtype == np.complex64: - use_csingle = True - elif dtype == np.complex128: - use_csingle = False - else: - raise TypeError(f"Unsupported complex Type: {dtype}") - + if len(tape.trainable_params) == 0: return np.array(0) @@ -288,14 +282,13 @@ def adjoint_jacobian(self, tape, starting_state=None, use_device_state=False): self.execute(tape) ket = np.ravel(self._pre_rotated_state) - if use_csingle: + if self.use_csingle: adj = AdjointJacobianC64() - ket = ket.astype(np.complex64) else: adj = AdjointJacobianC128() - obs_serialized = _serialize_obs(tape, self.wire_map, use_csingle=use_csingle) - ops_serialized, use_sp = _serialize_ops(tape, self.wire_map, use_csingle=use_csingle) + obs_serialized = _serialize_obs(tape, self.wire_map, use_csingle=self.use_csingle) + ops_serialized, use_sp = _serialize_ops(tape, self.wire_map, use_csingle=self.use_csingle) ops_serialized = adj.create_ops_list(*ops_serialized) @@ -306,7 +299,7 @@ def adjoint_jacobian(self, tape, starting_state=None, use_device_state=False): trainable_params if not use_sp else [i - 1 for i in trainable_params[first_elem:]] ) # exclude first index if explicitly setting sv - state_vector = StateVectorC64(ket) if use_csingle else StateVectorC128(ket) + state_vector = StateVectorC64(ket) if self.use_csingle else StateVectorC128(ket) # If requested batching over observables, chunk into OMP_NUM_THREADS sized chunks. # This will allow use of Lightning with adjoint for large-qubit numbers AND large @@ -368,11 +361,9 @@ def compute_vjp(self, dy, jac, num=None): if math.allclose(dy, 0): return math.convert_like(np.zeros([num_params]), dy) - # To support np.complex64 based on the type of self._state - dtype = self._state.dtype - if dtype == np.complex64: + if self.C_DTYPE == np.complex64: VJP = VectorJacobianProductC64() - elif dtype == np.complex128: + elif self.C_DTYPE == np.complex128: VJP = VectorJacobianProductC128() else: raise TypeError(f"Unsupported complex Type: {dtype}") @@ -416,16 +407,7 @@ def vjp(self, tape, dy, starting_state=None, use_device_state=False): if math.allclose(dy, 0): return lambda _: math.convert_like(np.zeros([num_params]), dy) - # To support np.complex64 based on the type of self._state - dtype = self._state.dtype - if dtype == np.complex64: - use_csingle = True - elif dtype == np.complex128: - use_csingle = False - else: - raise TypeError(f"Unsupported complex Type: {dtype}") - - V = VectorJacobianProductC64() if use_csingle else VectorJacobianProductC128() + V = VectorJacobianProductC64() if self.use_csingle else VectorJacobianProductC128() fn = V.vjp_fn(math.reshape(dy, [-1]), tape.num_params) @@ -442,11 +424,8 @@ def processing_fn(tape): self.execute(tape) ket = np.ravel(self._pre_rotated_state) - if use_csingle: - ket = ket.astype(np.complex64) - - obs_serialized = _serialize_obs(tape, self.wire_map, use_csingle=use_csingle) - ops_serialized, use_sp = _serialize_ops(tape, self.wire_map, use_csingle=use_csingle) + obs_serialized = _serialize_obs(tape, self.wire_map, use_csingle=self.use_csingle) + ops_serialized, use_sp = _serialize_ops(tape, self.wire_map, use_csingle=self.use_csingle) ops_serialized = V.create_ops_list(*ops_serialized) @@ -457,7 +436,7 @@ def processing_fn(tape): trainable_params if not use_sp else [i - 1 for i in trainable_params[first_elem:]] ) # exclude first index if explicitly setting sv - state_vector = StateVectorC64(ket) if use_csingle else StateVectorC128(ket) + state_vector = StateVectorC64(ket) if self.use_csingle else StateVectorC128(ket) return fn(state_vector, obs_serialized, ops_serialized, tp_shift) @@ -543,21 +522,10 @@ 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 - if dtype == np.complex64: - use_csingle = True - elif dtype == np.complex128: - use_csingle = False - else: - raise TypeError(f"Unsupported complex Type: {dtype}") - - # Initialization of state ket = np.ravel(self._state) - if use_csingle: - ket = ket.astype(np.complex64) - - state_vector = StateVectorC64(ket) if use_csingle else StateVectorC128(ket) - M = MeasuresC64(state_vector) if use_csingle else MeasuresC128(state_vector) + 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) @@ -568,23 +536,11 @@ def generate_samples(self): array[int]: array of samples in binary representation with shape ``(dev.shots, dev.num_wires)`` """ - # To support np.complex64 based on the type of self._state - dtype = self._state.dtype - if dtype == np.complex64: - use_csingle = True - elif dtype == np.complex128: - use_csingle = False - else: - raise TypeError(f"Unsupported complex Type: {dtype}") - # Initialization of state ket = np.ravel(self._state) - if use_csingle: - ket = ket.astype(np.complex64) - - state_vector = StateVectorC64(ket) if use_csingle else StateVectorC128(ket) - M = MeasuresC64(state_vector) if use_csingle else MeasuresC128(state_vector) + 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.generate_samples(len(self.wires), self.shots).astype(int) @@ -619,21 +575,12 @@ def expval(self, observable, shot_range=None, bin_size=None): # To support np.complex64 based on the type of self._state dtype = self._state.dtype - if dtype == np.complex64: - use_csingle = True - elif dtype == np.complex128: - use_csingle = False - else: - raise TypeError(f"Unsupported complex Type: {dtype}") # Initialization of state ket = np.ravel(self._pre_rotated_state) - if use_csingle: - ket = ket.astype(np.complex64) - - state_vector = StateVectorC64(ket) if use_csingle else StateVectorC128(ket) - M = MeasuresC64(state_vector) if use_csingle else MeasuresC128(state_vector) + state_vector = StateVectorC64(ket) if self.use_csingle else StateVectorC128(ket) + M = MeasuresC64(state_vector) if self.use_csingle else MeasuresC128(state_vector) # translate to wire labels used by device observable_wires = self.map_wires(observable.wires) @@ -667,23 +614,11 @@ def var(self, observable, shot_range=None, bin_size=None): samples = self.sample(observable, shot_range=shot_range, bin_size=bin_size) return np.squeeze(np.var(samples, axis=0)) - # To support np.complex64 based on the type of self._state - dtype = self._state.dtype - if dtype == np.complex64: - use_csingle = True - elif dtype == np.complex128: - use_csingle = False - else: - raise TypeError(f"Unsupported complex Type: {dtype}") - # Initialization of state ket = np.ravel(self._pre_rotated_state) - if use_csingle: - ket = ket.astype(np.complex64) - - state_vector = StateVectorC64(ket) if use_csingle else StateVectorC128(ket) - M = MeasuresC64(state_vector) if use_csingle else MeasuresC128(state_vector) + state_vector = StateVectorC64(ket) if self.use_csingle else StateVectorC128(ket) + M = MeasuresC64(state_vector) if self.use_csingle else MeasuresC128(state_vector) # translate to wire labels used by device observable_wires = self.map_wires(observable.wires) diff --git a/tests/conftest.py b/tests/conftest.py index 034482bfbc..8569c88520 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -48,16 +48,16 @@ def n_subsystems(request): return request.param -@pytest.fixture(scope="function") -def qubit_device_1_wire(): - return LightningQubit(wires=1) +@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") -def qubit_device_2_wires(): - return LightningQubit(wires=2) +@pytest.fixture(scope="function", params=[np.complex64, np.complex128]) +def qubit_device_2_wires(request): + return LightningQubit(wires=2, c_dtype=request.param) -@pytest.fixture(scope="function") -def qubit_device_3_wires(): - return LightningQubit(wires=3) +@pytest.fixture(scope="function", params=[np.complex64, np.complex128]) +def qubit_device_3_wires(request): + return LightningQubit(wires=3, c_dtype=request.param) diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index 41dc5f9cac..4e20ce1a80 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -67,9 +67,9 @@ def Rz(theta): class TestAdjointJacobian: """Tests for the adjoint_jacobian method""" - @pytest.fixture - def dev(self): - return qml.device("lightning.qubit", wires=2) + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return qml.device("lightning.qubit", wires=2, c_dtype=request.param) def test_not_expval(self, dev): """Test if a QuantumFunctionError is raised for a tape with measurements that are not @@ -162,31 +162,11 @@ def test_unsupported_hermitian_expectation(self, dev): ): dev.adjoint_jacobian(tape) - @pytest.mark.skipif( - not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" - ) - @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") - def test_unsupported_complex_type(self, dev): - dev._state = dev._asarray(dev._state, np.complex256) - - with qml.tape.QuantumTape() as tape: - qml.QubitStateVector(np.array([1.0, -1.0]) / np.sqrt(2), wires=0) - qml.RX(0.3, wires=[0]) - qml.expval(qml.PauliZ(0)) - - tape.trainable_params = {1} - - with pytest.raises(TypeError, match="Unsupported complex Type: complex256"): - dev.adjoint_jacobian(tape) - @pytest.mark.parametrize("theta", np.linspace(-2 * np.pi, 2 * np.pi, 7)) @pytest.mark.parametrize("G", [qml.RX, qml.RY, qml.RZ]) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_pauli_rotation_gradient(self, G, theta, tol, dev, C): + def test_pauli_rotation_gradient(self, G, theta, dev): """Tests that the automatic gradients of Pauli rotations are correct.""" - dev._state = dev._asarray(dev._state, C) - with qml.tape.QuantumTape() as tape: qml.QubitStateVector(np.array([1.0, -1.0]) / np.sqrt(2), wires=0) G(theta, wires=[0]) @@ -195,21 +175,21 @@ def test_pauli_rotation_gradient(self, G, theta, tol, dev, C): tape.trainable_params = {1} calculated_val = dev.adjoint_jacobian(tape) + + h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 # compare to finite differences - tapes, fn = qml.gradients.finite_diff(tape) + tapes, fn = qml.gradients.finite_diff(tape, h=h) numeric_val = fn(qml.execute(tapes, dev, None)) assert np.allclose(calculated_val, numeric_val[0][2], atol=tol, rtol=0) @pytest.mark.parametrize("theta", np.linspace(-2 * np.pi, 2 * np.pi, 7)) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_Rot_gradient(self, theta, tol, dev, C): + def test_Rot_gradient(self, theta, dev): """Tests that the device gradient of an arbitrary Euler-angle-parameterized gate is correct.""" params = np.array([theta, theta**3, np.sqrt(2) * theta]) - dev._state = dev._state.astype(C) - with qml.tape.QuantumTape() as tape: qml.QubitStateVector(np.array([1.0, -1.0]) / np.sqrt(2), wires=0) qml.Rot(*params, wires=[0]) @@ -219,18 +199,18 @@ def test_Rot_gradient(self, theta, tol, dev, C): calculated_val = dev.adjoint_jacobian(tape) + h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 + tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + # compare to finite differences - tapes, fn = qml.gradients.finite_diff(tape) + tapes, fn = qml.gradients.finite_diff(tape, h=h) numeric_val = fn(qml.execute(tapes, dev, None)) assert np.allclose(calculated_val, numeric_val[0][2:], atol=tol, rtol=0) @pytest.mark.parametrize("par", [1, -2, 1.623, -0.051, 0]) # integers, floats, zero - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_ry_gradient(self, par, tol, dev, C): + def test_ry_gradient(self, par, tol, dev): """Test that the gradient of the RY gate matches the exact analytic formula.""" - dev._state = dev._state.astype(C) - with qml.tape.QuantumTape() as tape: qml.RY(par, wires=[0]) qml.expval(qml.PauliX(0)) @@ -244,13 +224,10 @@ def test_ry_gradient(self, par, tol, dev, C): # different methods must agree assert np.allclose(grad_A, exact, atol=tol, rtol=0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_rx_gradient(self, tol, dev, C): + def test_rx_gradient(self, tol, dev): """Test that the gradient of the RX gate matches the known formula.""" a = 0.7418 - dev._state = dev._state.astype(C) - with qml.tape.QuantumTape() as tape: qml.RX(a, wires=0) qml.expval(qml.PauliZ(0)) @@ -260,14 +237,11 @@ def test_rx_gradient(self, tol, dev, C): expected_jacobian = -np.sin(a) assert np.allclose(dev_jacobian, expected_jacobian, atol=tol, rtol=0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_multiple_rx_gradient(self, tol, C): + def test_multiple_rx_gradient(self, tol): """Tests that the gradient of multiple RX gates in a circuit yields the correct result.""" dev = qml.device("lightning.qubit", wires=3) params = np.array([np.pi, np.pi / 2, np.pi / 3]) - dev._state = dev._state.astype(C) - with qml.tape.QuantumTape() as tape: qml.RX(params[0], wires=0) qml.RX(params[1], wires=1) @@ -297,13 +271,10 @@ def test_multiple_rx_gradient(self, tol, C): qml.Rot(0.2, -0.1, 0.2, wires=0), ], ) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_gradients(self, op, obs, tol, dev, C): + def test_gradients(self, op, obs, dev): """Tests that the gradients of circuits match between the finite difference and device methods.""" - dev._state = dev._state.astype(C) - # op.num_wires and op.num_params must be initialized a priori with qml.tape.QuantumTape() as tape: qml.Hadamard(wires=0) @@ -322,18 +293,18 @@ def test_gradients(self, op, obs, tol, dev, C): dev.trainable_params = set(range(1, 1 + op.num_params)) - grad_F = (lambda t, fn: fn(qml.execute(t, dev, None)))(*qml.gradients.finite_diff(tape)) + h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + + grad_F = (lambda t, fn: fn(qml.execute(t, dev, None)))(*qml.gradients.finite_diff(tape, h = h)) grad_D = dev.adjoint_jacobian(tape) assert np.allclose(grad_D, grad_F, atol=tol, rtol=0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_gradient_gate_with_multiple_parameters(self, tol, dev, C): + def test_gradient_gate_with_multiple_parameters(self, dev): """Tests that gates with multiple free parameters yield correct gradients.""" x, y, z = [0.5, 0.3, -0.7] - dev._state = dev._state.astype(C) - with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) @@ -342,8 +313,11 @@ def test_gradient_gate_with_multiple_parameters(self, tol, dev, C): tape.trainable_params = {1, 2, 3} + h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + grad_D = dev.adjoint_jacobian(tape) - tapes, fn = qml.gradients.finite_diff(tape) + tapes, fn = qml.gradients.finite_diff(tape, h=h) grad_F = fn(qml.execute(tapes, dev, None)) # gradient has the correct shape and every element is nonzero @@ -352,14 +326,11 @@ def test_gradient_gate_with_multiple_parameters(self, tol, dev, C): # the different methods agree assert np.allclose(grad_D, grad_F, atol=tol, rtol=0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_use_device_state(self, tol, dev, C): + def test_use_device_state(self, tol, dev): """Tests that when using the device state, the correct answer is still returned.""" x, y, z = [0.5, 0.3, -0.7] - dev._state = dev._state.astype(C) - with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) @@ -375,13 +346,10 @@ def test_use_device_state(self, tol, dev, C): assert np.allclose(dM1, dM2, atol=tol, rtol=0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_provide_starting_state(self, tol, dev, C): + def test_provide_starting_state(self, tol, dev): """Tests provides correct answer when provided starting state.""" x, y, z = [0.5, 0.3, -0.7] - dev._state = dev._state.astype(C) - with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) @@ -401,9 +369,9 @@ def test_provide_starting_state(self, tol, dev, C): class TestAdjointJacobianQNode: """Test QNode integration with the adjoint_jacobian method""" - @pytest.fixture - def dev(self): - return qml.device("lightning.qubit", wires=2) + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return qml.device("lightning.qubit", wires=2, c_dtype=request.param) def test_finite_shots_warning(self): """Tests that a warning is raised when computing the adjoint diff on a device with finite shots""" @@ -424,7 +392,7 @@ def circ(x): ): qml.grad(circ)(0.1) - def test_qnode(self, mocker, tol, dev): + def test_qnode(self, mocker, dev): """Test that specifying diff_method allows the adjoint method to be selected""" args = np.array([0.54, 0.1, 0.5], requires_grad=True) @@ -450,7 +418,10 @@ def circuit(x, y, z): spy.assert_called() - qnode2 = QNode(circuit, dev, diff_method="finite-diff") + h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + + qnode2 = QNode(circuit, dev, diff_method="finite-diff", h=h) grad_fn = qml.grad(qnode2) grad_F = grad_fn(*args) @@ -509,7 +480,7 @@ def cost(p1, p2): assert np.allclose(grad_D[0], expected, atol=tol, rtol=0) - def test_gradient_repeated_gate_parameters(self, mocker, tol, dev): + def test_gradient_repeated_gate_parameters(self, mocker, dev): """Tests that repeated use of a free parameter in a multi-parameter gate yields correct gradients.""" params = np.array([0.8, 1.3], requires_grad=True) @@ -521,7 +492,10 @@ def circuit(params): spy_analytic = mocker.spy(dev, "adjoint_jacobian") - cost = QNode(circuit, dev, diff_method="finite-diff") + h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + + cost = QNode(circuit, dev, diff_method="finite-diff", h=h) grad_fn = qml.grad(cost) grad_F = grad_fn(params) @@ -548,11 +522,19 @@ def f(params1, params2): qml.RY(tf.cos(params2), wires=[0]) return qml.expval(qml.PauliZ(0)) - params1 = tf.Variable(0.3, dtype=tf.float64) - params2 = tf.Variable(0.4, dtype=tf.float64) + if dev.R_DTYPE == np.float32: + tf_r_dtype = tf.float32 + else: + tf_r_dtype = tf.float64 + + params1 = tf.Variable(0.3, dtype=tf_r_dtype) + params2 = tf.Variable(0.4, dtype=tf_r_dtype) + + h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 qnode1 = QNode(f, dev, interface="tf", diff_method="adjoint") - qnode2 = QNode(f, dev, interface="tf", diff_method="finite-diff") + qnode2 = QNode(f, dev, interface="tf", diff_method="finite-diff", h=h) with tf.GradientTape() as tape: res1 = qnode1(params1, params2) @@ -564,7 +546,7 @@ def f(params1, params2): g2 = tape.gradient(res2, [params1, params2]) - assert np.allclose(g1, g2) + assert np.allclose(g1, g2, atol=tol) def test_interface_torch(self, dev): """Test if gradients agree between the adjoint and finite-diff methods when using the @@ -609,13 +591,16 @@ def f(params1, params2): params1 = jax.numpy.array(0.3) params2 = jax.numpy.array(0.4) + h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + qnode_adjoint = QNode(f, dev, interface="jax", diff_method="adjoint") - qnode_fd = QNode(f, dev, interface="jax", diff_method="finite-diff") + qnode_fd = QNode(f, dev, interface="jax", diff_method="finite-diff", h=h) grad_adjoint = jax.grad(qnode_adjoint)(params1, params2) grad_fd = jax.grad(qnode_fd)(params1, params2) - assert np.allclose(grad_adjoint, grad_fd) + assert np.allclose(grad_adjoint, grad_fd, atol=tol) def circuit_ansatz(params, wires): diff --git a/tests/test_apply.py b/tests/test_apply.py index 76368abd2e..ad3e3888cd 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -89,18 +89,6 @@ class TestApply: from pennylane_lightning import LightningQubit as lq - @pytest.mark.skipif( - not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" - ) - @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") - def test_apply_operation_raise_type_error(self, qubit_device_1_wire): - """Tests that applying an operation yields the expected output state for single wire - operations that have no parameters.""" - - with pytest.raises(TypeError, match="Unsupported complex Type: complex256"): - qubit_device_1_wire._state = np.array([1, 0]).astype(np.complex256) - qubit_device_1_wire.apply([qml.PauliX(wires=[0])]) - test_data_no_parameters = [ (qml.PauliX, [1, 0], np.array([0, 1])), (qml.PauliX, [1 / math.sqrt(2), 1 / math.sqrt(2)], [1 / math.sqrt(2), 1 / math.sqrt(2)]), @@ -123,17 +111,18 @@ def test_apply_operation_raise_type_error(self, qubit_device_1_wire): ] @pytest.mark.parametrize("operation,input,expected_output", test_data_no_parameters) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) def test_apply_operation_single_wire_no_parameters( - self, qubit_device_1_wire, tol, operation, input, expected_output, C + self, qubit_device_1_wire, tol, operation, input, expected_output ): """Tests that applying an operation yields the expected output state for single wire operations that have no parameters.""" - qubit_device_1_wire._state = np.array(input).astype(C) - qubit_device_1_wire.apply([operation(wires=[0])]) + dev = qubit_device_1_wire + 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 dev._state.dtype == dev.C_DTYPE test_data_two_wires_no_parameters = [ (qml.CNOT, [1, 0, 0, 0], [1, 0, 0, 0]), @@ -160,16 +149,17 @@ def test_apply_operation_single_wire_no_parameters( ] @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_two_wires_no_parameters( - self, qubit_device_2_wires, tol, operation, input, expected_output, C + self, qubit_device_2_wires, tol, operation, input, expected_output ): """Tests that applying an operation yields the expected output state for two wire operations that have no parameters.""" - qubit_device_2_wires._state = np.array(input).reshape(2 * [2]).astype(C) - qubit_device_2_wires.apply([operation(wires=[0, 1])]) + dev = qubit_device_2_wires + 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 dev._state.dtype == dev.C_DTYPE test_data_three_wires_no_parameters = [ (qml.CSWAP, [1, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0]), @@ -182,17 +172,18 @@ def test_apply_operation_two_wires_no_parameters( ] @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_three_wires_no_parameters( - self, qubit_device_3_wires, tol, operation, input, expected_output, C + self, qubit_device_3_wires, tol, operation, input, expected_output ): """Tests that applying an operation yields the expected output state for three wire operations that have no parameters.""" - - qubit_device_3_wires._state = np.array(input).reshape(3 * [2]).astype(C) - qubit_device_3_wires.apply([operation(wires=[0, 1, 2])]) + + dev = qubit_device_3_wires + 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 dev._state.dtype == dev.C_DTYPE @pytest.mark.parametrize( "operation,expected_output,par", @@ -289,18 +280,18 @@ def test_apply_operation_state_preparation( @pytest.mark.parametrize( "operation,input,expected_output,par", test_data_single_wire_with_parameters ) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) def test_apply_operation_single_wire_with_parameters( - self, qubit_device_1_wire, tol, operation, input, expected_output, par, C + self, qubit_device_1_wire, tol, operation, input, expected_output, par ): """Tests that applying an operation yields the expected output state for single wire operations that have parameters.""" - qubit_device_1_wire._state = np.array(input).astype(C) - - qubit_device_1_wire.apply([operation(*par, wires=[0])]) + dev = qubit_device_1_wire + 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 dev._state.dtype == dev.C_DTYPE """ operation,input,expected_output,par """ test_data_two_wires_with_parameters = [ @@ -416,17 +407,18 @@ def test_apply_operation_single_wire_with_parameters( @pytest.mark.parametrize( "operation,input,expected_output,par", test_data_two_wires_with_parameters ) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) def test_apply_operation_two_wires_with_parameters( - self, qubit_device_2_wires, tol, operation, input, expected_output, par, C + self, qubit_device_2_wires, tol, operation, input, expected_output, par ): """Tests that applying an operation yields the expected output state for two wire operations that have parameters.""" - qubit_device_2_wires._state = np.array(input).reshape(2 * [2]).astype(C) - qubit_device_2_wires.apply([operation(*par, wires=[0, 1])]) + dev = qubit_device_2_wires + 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 dev._state.dtype == dev.C_DTYPE def test_apply_errors_qubit_state_vector(self, qubit_device_2_wires): """Test that apply fails for incorrect state preparation, and > 2 qubit gates""" @@ -1401,34 +1393,34 @@ def test_qubitunitary_rotation_hadamard( class TestApplyLightningMethod: """Unit tests for the apply_lightning method.""" - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_apply_identity_skipped(self, mocker, C, tol): + def test_apply_identity_skipped(self, mocker, tol): """Test identity operation does not perform additional computations.""" dev = qml.device("lightning.qubit", wires=1) - dev._state = dev._asarray(dev._state, C) + dev._state = dev._asarray(dev._state).astype(dev.C_DTYPE) - starting_state = np.array([1, 0], dtype=C) + starting_state = np.array([1, 0], dtype=dev.C_DTYPE) op = [qml.Identity(0)] dev.apply(op) assert np.allclose(dev._state, starting_state, atol=tol, rtol=0) + assert dev._state.dtype == dev.C_DTYPE - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_iter_identity_skipped(self, mocker, C, tol): + 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=C) + 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, dtype=C) + 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() diff --git a/tests/test_device.py b/tests/test_device.py new file mode 100644 index 0000000000..7dcf0dc62f --- /dev/null +++ b/tests/test_device.py @@ -0,0 +1,38 @@ +# 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 :mod:`pennylane_lightning.LightningQubit` device can be creaated. +""" +import pytest +import numpy as np +import pennylane as qml + +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) + +def test_create_device(): + dev = qml.device("lightning.qubit", wires=1) + +@pytest.mark.parametrize("C", [np.complex64, np.complex128]) +def test_create_device_with_dtype(C): + dev = qml.device("lightning.qubit", wires=1, c_dtype=C) + +@pytest.mark.skipif( + not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" +) +def test_create_device_with_unsupported_dtype(): + with pytest.raises(TypeError, match="Unsupported complex Type:"): + dev = qml.device("lightning.qubit", wires=1, c_dtype=np.complex256) diff --git a/tests/test_expval.py b/tests/test_expval.py index ce1e66b5c4..15c30356bb 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -62,8 +62,7 @@ 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) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_paulix_expectation(self, theta, phi, qubit_device_3_wires, tol, C): + def test_paulix_expectation(self, theta, phi, qubit_device_3_wires, tol): """Test that PauliX expectation value is correct""" dev = qubit_device_3_wires O1 = qml.PauliX(wires=[0]) @@ -74,8 +73,8 @@ def test_paulix_expectation(self, theta, phi, qubit_device_3_wires, tol, C): rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], ) - res = np.array([dev.expval(O1), dev.expval(O2)], dtype=C) - assert np.allclose(res, np.array([np.sin(theta) * np.sin(phi), np.sin(phi)], dtype=C)) + 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)) def test_pauliy_expectation(self, theta, phi, qubit_device_3_wires, tol): """Test that PauliY expectation value is correct""" @@ -133,7 +132,7 @@ def test_paulix_pauliy(self, theta, phi, varphi, qubit_device_3_wires, tol): expected = np.sin(theta) * np.sin(phi) * np.sin(varphi) - assert np.allclose(res, expected) + assert np.allclose(res, expected, atol=tol) def test_pauliz_identity(self, theta, phi, varphi, qubit_device_3_wires, tol): """Test that a tensor product involving PauliZ and Identity works diff --git a/tests/test_measures.py b/tests/test_measures.py index e4ecf4e1a4..5d9d626341 100644 --- a/tests/test_measures.py +++ b/tests/test_measures.py @@ -50,34 +50,22 @@ def circuit(x): class TestProbs: """Test Probs in Lightning""" - @pytest.fixture - def dev(self): - return qml.device("lightning.qubit", wires=2) + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return qml.device("lightning.qubit", wires=2, c_dtype=request.param) def test_probs_dtype64(self, dev): """Test if probs changes the state dtype""" dev._state = dev._asarray( - np.array([1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0]).astype(np.complex64) + np.array([1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0]).astype(dev.C_DTYPE) ) p = dev.probability(wires=[0, 1]) - assert dev._state.dtype == np.complex64 + assert dev._state.dtype == dev.C_DTYPE assert np.allclose(p, [0.5, 0.5, 0, 0]) - @pytest.mark.skipif( - not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" - ) - def test_probs_dtype_error(self, dev): - """Test if probs raise error with complex256""" - dev._state = np.array([1, 0, 0, 0]).astype(np.complex256) - - with pytest.raises(TypeError, match="Unsupported complex Type:"): - dev.probability(wires=[0, 1]) - - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_probs_H(self, tol, dev, C): + def test_probs_H(self, tol, dev): """Test probs with Hadamard""" - dev._state = dev._asarray(dev._state, C) @qml.qnode(dev) def circuit(): @@ -93,11 +81,9 @@ def circuit(): [[], [0.9165164490394898, 0.0, 0.08348355096051052, 0.0]], ], ) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) @pytest.mark.xfail - def test_probs_tape_nowires(self, cases, tol, dev, C): + def test_probs_tape_nowires(self, cases, tol, dev): """Test probs with a circuit on wires=[0]""" - dev._state = dev._asarray(dev._state, C) x, y, z = [0.5, 0.3, -0.7] @@ -119,10 +105,8 @@ def circuit(): [[0], [0.9165164490394898, 0.08348355096051052]], ], ) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_probs_tape_wire0(self, cases, tol, dev, C): + def test_probs_tape_wire0(self, cases, tol, dev): """Test probs with a circuit on wires=[0]""" - dev._state = dev._asarray(dev._state, C) x, y, z = [0.5, 0.3, -0.7] @@ -169,10 +153,8 @@ def circuit(): [[0], [0.938791280945186, 0.061208719054813635]], ], ) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_probs_tape_wire01(self, cases, tol, dev, C): + def test_probs_tape_wire01(self, cases, tol, dev): """Test probs with a circuit on wires=[0,1]""" - dev._state = dev._asarray(dev._state, C) @qml.qnode(dev) def circuit(): @@ -186,28 +168,18 @@ def circuit(): class TestExpval: """Tests for the expval function""" - @pytest.fixture - def dev(self): - return qml.device("lightning.qubit", wires=2) + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return qml.device("lightning.qubit", wires=2, c_dtype=request.param) def test_expval_dtype64(self, dev): """Test if expval changes the state dtype""" - dev._state = np.array([1, 0]).astype(np.complex64) + dev._state = np.array([1, 0]).astype(dev.C_DTYPE) e = dev.expval(qml.PauliX(0)) - assert dev._state.dtype == np.complex64 + assert dev._state.dtype == dev.C_DTYPE assert np.allclose(e, 0.0) - @pytest.mark.skipif( - not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" - ) - def test_expval_dtype_error(self, dev): - """Test if expval raise error with complex256""" - dev._state = np.array([1, 0]).astype(np.complex256) - - with pytest.raises(TypeError, match="Unsupported complex Type:"): - dev.expval(qml.PauliX(0)) - @pytest.mark.parametrize( "cases", [ @@ -219,10 +191,8 @@ def test_expval_dtype_error(self, dev): [qml.PauliZ(1), 1.0], ], ) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_expval_qml_tape_wire0(self, cases, tol, dev, C): + def test_expval_qml_tape_wire0(self, cases, tol, dev): """Test expval with a circuit on wires=[0]""" - dev._state = dev._asarray(dev._state, C) x, y, z = [0.5, 0.3, -0.7] @@ -246,10 +216,8 @@ def circuit(): [qml.PauliZ(1), 0.9800665778412417], ], ) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_expval_wire01(self, cases, tol, dev, C): + def test_expval_wire01(self, cases, tol, dev): """Test expval with a circuit on wires=[0,1]""" - dev._state = dev._asarray(dev._state, C) @qml.qnode(dev) def circuit(): @@ -259,10 +227,8 @@ def circuit(): assert np.allclose(circuit(), cases[1], atol=tol, rtol=0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_value(self, C, dev, tol): + def test_value(self, dev, tol): """Test that the expval interface works""" - dev._state = dev._asarray(dev._state, C) @qml.qnode(dev) def circuit(x): @@ -275,11 +241,9 @@ def circuit(x): assert np.allclose(res, expected, atol=tol, rtol=0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_not_an_observable(self, C, dev): + def test_not_an_observable(self, dev): """Test that a qml.QuantumFunctionError is raised if the provided argument is not an observable""" - dev._state = dev._asarray(dev._state, C) @qml.qnode(dev) def circuit(): @@ -289,10 +253,8 @@ def circuit(): with pytest.raises(qml.QuantumFunctionError, match="CNOT is not an observable"): circuit() - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_observable_return_type_is_expectation(self, C, dev): + def test_observable_return_type_is_expectation(self, dev): """Test that the return type of the observable is :attr:`ObservableReturnTypes.Expectation`""" - dev._state = dev._asarray(dev._state, C) @qml.qnode(dev) def circuit(): @@ -306,9 +268,9 @@ def circuit(): class TestVar: """Tests for the var function""" - @pytest.fixture - def dev(self): - return qml.device("lightning.qubit", wires=2) + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return qml.device("lightning.qubit", wires=2, c_dtype=request.param) def test_var_dtype64(self, dev): """Test if var changes the state dtype""" @@ -318,16 +280,6 @@ def test_var_dtype64(self, dev): assert dev._state.dtype == np.complex64 assert np.allclose(v, 1.0) - @pytest.mark.skipif( - not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" - ) - def test_expval_dtype_error(self, dev): - """Test if var raise error with complex256""" - dev._state = np.array([1, 0]).astype(np.complex256) - - with pytest.raises(TypeError, match="Unsupported complex Type:"): - dev.var(qml.PauliX(0)) - @pytest.mark.parametrize( "cases", [ @@ -339,10 +291,8 @@ def test_expval_dtype_error(self, dev): [qml.PauliZ(1), -4.440892098500626e-16], ], ) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_var_qml_tape_wire0(self, cases, tol, dev, C): + def test_var_qml_tape_wire0(self, cases, tol, dev): """Test var with a circuit on wires=[0]""" - dev._state = dev._asarray(dev._state, C) x, y, z = [0.5, 0.3, -0.7] @@ -366,10 +316,8 @@ def circuit(): [qml.PauliZ(1), 0.03946950299855745], ], ) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_var_qml_tape_wire01(self, cases, tol, dev, C): + def test_var_qml_tape_wire01(self, cases, tol, dev): """Test var with a circuit on wires=[0,1]""" - dev._state = dev._asarray(dev._state, C) @qml.qnode(dev) def circuit(): @@ -379,10 +327,8 @@ def circuit(): assert np.allclose(circuit(), cases[1], atol=tol, rtol=0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_value(self, C, dev, tol): + def test_value(self, dev, tol): """Test that the var function works""" - dev._state = dev._asarray(dev._state, C) @qml.qnode(dev) def circuit(x): @@ -395,11 +341,9 @@ def circuit(x): assert np.allclose(res, expected, atol=tol, rtol=0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_not_an_observable(self, C, dev): + def test_not_an_observable(self, dev): """Test that a qml.QuantumFunctionError is raised if the provided argument is not an observable""" - dev._state = dev._asarray(dev._state, C) @qml.qnode(dev) def circuit(): @@ -409,10 +353,8 @@ def circuit(): with pytest.raises(qml.QuantumFunctionError, match="CNOT is not an observable"): res = circuit() - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_observable_return_type_is_variance(self, C, dev): + def test_observable_return_type_is_variance(self, dev): """Test that the return type of the observable is :attr:`ObservableReturnTypes.Variance`""" - dev._state = dev._asarray(dev._state, C) @qml.qnode(dev) def circuit(): @@ -460,7 +402,7 @@ class TestWiresInExpval: @pytest.mark.parametrize("C", [np.complex64, np.complex128]) def test_wires_expval(self, wires1, wires2, C, tol): """Test that the expectation of a circuit is independent from the wire labels used.""" - dev1 = qml.device("lightning.qubit", wires=wires1) + dev1 = qml.device("lightning.qubit", wires=wires1, c_dtype=C) dev1._state = dev1._asarray(dev1._state, C) dev2 = qml.device("lightning.qubit", wires=wires2) @@ -490,20 +432,15 @@ def circuit2(): class TestSample: """Tests that samples are properly calculated.""" - @pytest.fixture - def dev(self): - return qml.device("lightning.qubit", wires=2, shots=1000) + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return qml.device("lightning.qubit", wires=2, shots=1000, c_dtype=request.param) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_sample_dimensions(self, dev, C): + def test_sample_dimensions(self, dev): """Tests if the samples returned by sample have the correct dimensions """ - # Explicitly resetting is necessary as the internal - # state is set to None in __init__ and only properly - # initialized during reset - dev._state = dev._asarray(dev._state, C) dev.apply([qml.RX(1.5708, wires=[0]), qml.RX(1.5708, wires=[1])]) dev.shots = 10 @@ -526,17 +463,11 @@ def test_sample_dimensions(self, dev, C): s3 = dev.sample(qml.PauliX(0) @ qml.PauliZ(1)) assert np.array_equal(s3.shape, (17,)) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_sample_values(self, dev, C, tol): + def test_sample_values(self, dev, tol): """Tests if the samples returned by sample have the correct values """ - # Explicitly resetting is necessary as the internal - # state is set to None in __init__ and only properly - # initialized during reset - dev._state = dev._asarray(dev._state, C) - dev.apply([qml.RX(1.5708, wires=[0])]) dev._wires_measured = {0} dev._samples = dev.generate_samples() @@ -547,14 +478,6 @@ def test_sample_values(self, dev, C, tol): # they square to 1 assert np.allclose(s1**2, 1, atol=tol, rtol=0) - def test_sample_unsupported_type(self, dev): - """Test if generate_samples raise error with complex256""" - - dev._state = np.array([1, 0]).astype(np.complex256) - - with pytest.raises(TypeError, match="Unsupported complex Type:"): - dev._samples = dev.generate_samples() - class TestWiresInVar: """Test different Wires settings in Lightning's var.""" diff --git a/tests/test_vjp.py b/tests/test_vjp.py index 72c98fea07..77a884aa8b 100644 --- a/tests/test_vjp.py +++ b/tests/test_vjp.py @@ -32,27 +32,12 @@ class TestComputeVJP: """Tests for the numeric computation of VJPs""" - @pytest.fixture - def dev(self): - return qml.device("lightning.qubit", wires=2) + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return qml.device("lightning.qubit", wires=2, c_dtype=request.param) - @pytest.mark.skipif( - not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" - ) - def test_unsupported_complex_type(self, dev): - dev._state = dev._asarray(dev._state, np.complex256) - - dy = np.array([[1.0, 2.0], [3.0, 4.0]]) - jac = np.array([[[1.0, 0.1, 0.2], [0.2, 0.6, 0.1]], [[0.4, -0.7, 1.2], [-0.5, -0.6, 0.7]]]) - - with pytest.raises(TypeError, match="Unsupported complex Type: complex256"): - dev.compute_vjp(dy, jac) - - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_computation(self, tol, dev, C): + def test_computation(self, tol, dev): """Test that the correct VJP is returned""" - dev._state = dev._asarray(dev._state, C) - dy = np.array([[1.0, 2.0], [3.0, 4.0]]) jac = np.array([[[1.0, 0.1, 0.2], [0.2, 0.6, 0.1]], [[0.4, -0.7, 1.2], [-0.5, -0.6, 0.7]]]) @@ -60,13 +45,11 @@ def test_computation(self, tol, dev, C): expected = np.tensordot(dy, jac, axes=[[0, 1], [0, 1]]) assert vjp.shape == (3,) + assert vjp.dtype == dev.R_DTYPE assert np.allclose(vjp, expected, atol=tol, rtol=0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_computation_num(self, tol, dev, C): + def test_computation_num(self, tol, dev): """Test that the correct VJP is returned""" - dev._state = dev._asarray(dev._state, C) - dy = np.array([[1.0, 2.0], [3.0, 4.0]]) jac = np.array([[[1.0, 0.1, 0.2], [0.2, 0.6, 0.1]], [[0.4, -0.7, 1.2], [-0.5, -0.6, 0.7]]]) @@ -74,12 +57,12 @@ def test_computation_num(self, tol, dev, C): expected = np.tensordot(dy, jac, axes=[[0, 1], [0, 1]]) assert vjp.shape == (3,) + assert vjp.dtype == dev.R_DTYPE assert np.allclose(vjp, expected, atol=tol, rtol=0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_computation_num_error(self, dev, C): + def test_computation_num_error(self, dev): """Test that the correct VJP is returned""" - dev._state = dev._asarray(dev._state, C) + dev._state = dev._asarray(dev._state) dy = np.array([[1.0, 2.0], [3.0, 4.0]]) jac = np.array([[[1.0, 0.1, 0.2], [0.2, 0.6, 0.1]], [[0.4, -0.7, 1.2], [-0.5, -0.6, 0.7]]]) @@ -87,10 +70,9 @@ def test_computation_num_error(self, dev, C): with pytest.raises(ValueError, match="Invalid size for the gradient-output vector"): dev.compute_vjp(dy, jac, num=3) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_jacobian_is_none(self, dev, C): + def test_jacobian_is_none(self, dev): """A None Jacobian returns a None VJP""" - dev._state = dev._asarray(dev._state, C) + dev._state = dev._asarray(dev._state) dy = np.array([[1.0, 2.0], [3.0, 4.0]]) jac = None @@ -98,10 +80,9 @@ def test_jacobian_is_none(self, dev, C): vjp = dev.compute_vjp(dy, jac) assert vjp is None - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_zero_dy(self, dev, C): + def test_zero_dy(self, dev): """A zero dy vector will return a zero matrix""" - dev._state = dev._asarray(dev._state, C) + dev._state = dev._asarray(dev._state) dy = np.zeros([2, 2]) jac = np.array([[[1.0, 0.1, 0.2], [0.2, 0.6, 0.1]], [[0.4, -0.7, 1.2], [-0.5, -0.6, 0.7]]]) @@ -124,55 +105,45 @@ def test_torch_tensor_dy(self, dev): """Test vjp_compute using the Torch interface""" torch = pytest.importorskip("torch") - dtype = getattr(torch, "float32") + if dev.R_DTYPE == np.float32: + torch_r_dtype = torch.float32 + else: + torch_r_dtype = torch.float64 - dy = torch.ones(4, dtype=dtype) - jac = torch.ones((4, 4), dtype=dtype) + dy = torch.ones(4, dtype=torch_r_dtype) + jac = torch.ones((4, 4), dtype=torch_r_dtype) - expected = torch.tensor([4.0, 4.0, 4.0, 4.0], dtype=dtype) + expected = torch.tensor([4.0, 4.0, 4.0, 4.0], dtype=torch_r_dtype) vjp = dev.compute_vjp(dy, jac) + assert vjp.dtype == torch_r_dtype assert torch.all(vjp == expected) def test_tf_tensor_dy(self, dev): """Test vjp_compute using the Tensorflow interface""" tf = pytest.importorskip("tensorflow") - dy = tf.ones(4, dtype=tf.float32) - jac = tf.ones((4, 4), dtype=tf.float32) + if dev.R_DTYPE == np.float32: + tf_r_dtype = tf.float32 + else: + tf_r_dtype = tf.float64 - expected = tf.constant([4.0, 4.0, 4.0, 4.0], dtype=tf.float32) + dy = tf.ones(4, dtype=tf_r_dtype) + jac = tf.ones((4, 4), dtype=tf_r_dtype) + + expected = tf.constant([4.0, 4.0, 4.0, 4.0], dtype=tf_r_dtype) vjp = dev.compute_vjp(dy, jac) + + assert vjp.dtype == dev.R_DTYPE #? assert tf.reduce_all(vjp == expected) class TestVectorJacobianProduct: """Tests for the `vjp` function""" - @pytest.fixture - def dev(self): - return qml.device("lightning.qubit", wires=2) - - @pytest.mark.skipif( - not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" - ) - def test_unsupported_complex_type(self, dev): - dev._state = dev._asarray(dev._state, np.complex256) - - x, y, z = [0.5, 0.3, -0.7] - - with qml.tape.QuantumTape() as tape: - qml.RX(0.4, wires=[0]) - qml.Rot(x, y, z, wires=[0]) - qml.RY(-0.2, wires=[0]) - qml.expval(qml.PauliZ(0)) - - tape.trainable_params = {1, 2, 3} - - dy = np.array([1.0]) - - with pytest.raises(TypeError, match="Unsupported complex Type: complex256"): - dev.vjp(tape, dy)(tape) + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return qml.device("lightning.qubit", wires=2, c_dtype=request.param) @pytest.mark.parametrize("C", [np.complex64, np.complex128]) def test_use_device_state(self, tol, dev, C): @@ -497,35 +468,9 @@ def test_prob_expectation_values(self, dev, C): class TestBatchVectorJacobianProduct: """Tests for the batch_vjp function""" - @pytest.fixture - def dev(self): - return qml.device("lightning.qubit", wires=2) - - @pytest.mark.skipif( - not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" - ) - def test_unsupported_complex_type(self, dev): - dev._state = dev._asarray(dev._state, np.complex256) - - with qml.tape.QuantumTape() as tape1: - qml.RX(0.4, wires=0) - qml.CNOT(wires=[0, 1]) - qml.expval(qml.PauliZ(0)) - - with qml.tape.QuantumTape() as tape2: - qml.RX(0.4, wires=0) - qml.RX(0.6, wires=0) - qml.CNOT(wires=[0, 1]) - qml.expval(qml.PauliZ(0)) - - tape1.trainable_params = {0} - tape2.trainable_params = {0, 1} - - tapes = [tape1, tape2] - dys = [np.array([1.0]), np.array([1.0])] - - with pytest.raises(TypeError, match="Unsupported complex Type: complex256"): - dev.batch_vjp(tapes, dys) + @pytest.fixture(params=[np.complex64, np.complex128]) + def dev(self, request): + return qml.device("lightning.qubit", wires=2, c_dtype=request.param) @pytest.mark.parametrize("C", [np.complex64, np.complex128]) def test_one_tape_no_trainable_parameters(self, dev, C): From 85b9cb983692f635147938a302ace013f6949797 Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Fri, 6 May 2022 22:42:56 +0000 Subject: [PATCH 02/13] Auto update version --- pennylane_lightning/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/_version.py b/pennylane_lightning/_version.py index d1a7ed1d64..018b2a730b 100644 --- a/pennylane_lightning/_version.py +++ b/pennylane_lightning/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.24.0-dev5" +__version__ = "0.24.0-dev6" From c10d9a6141a47ff3de0138fea43bb14f7fef5736 Mon Sep 17 00:00:00 2001 From: Chae-Yeun Park Date: Fri, 6 May 2022 18:50:01 -0400 Subject: [PATCH 03/13] More update --- pennylane_lightning/lightning_qubit.py | 9 ++- tests/test_adjoint_jacobian.py | 6 +- tests/test_apply.py | 8 +- tests/test_device.py | 3 + tests/test_expval.py | 4 +- tests/test_vjp.py | 104 ++++++------------------- 6 files changed, 44 insertions(+), 90 deletions(-) diff --git a/pennylane_lightning/lightning_qubit.py b/pennylane_lightning/lightning_qubit.py index b103a38db7..ee2d03902d 100644 --- a/pennylane_lightning/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit.py @@ -161,8 +161,7 @@ def apply(self, operations, rotations=None, **kwargs): 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) + self._state = self.apply_lightning(np.copy(self._pre_rotated_state), rotations) else: self._state = self._pre_rotated_state @@ -266,7 +265,7 @@ def adjoint_jacobian(self, tape, starting_state=None, use_device_state=False): ) # To support np.complex64 based on the type of self._state - + if len(tape.trainable_params) == 0: return np.array(0) @@ -425,7 +424,9 @@ def processing_fn(tape): ket = np.ravel(self._pre_rotated_state) obs_serialized = _serialize_obs(tape, self.wire_map, use_csingle=self.use_csingle) - ops_serialized, use_sp = _serialize_ops(tape, self.wire_map, use_csingle=self.use_csingle) + ops_serialized, use_sp = _serialize_ops( + tape, self.wire_map, use_csingle=self.use_csingle + ) ops_serialized = V.create_ops_list(*ops_serialized) diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index 4e20ce1a80..fb7f6faf53 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -175,7 +175,7 @@ def test_pauli_rotation_gradient(self, G, theta, dev): tape.trainable_params = {1} calculated_val = dev.adjoint_jacobian(tape) - + h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 @@ -296,7 +296,9 @@ def test_gradients(self, op, obs, dev): h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 - grad_F = (lambda t, fn: fn(qml.execute(t, dev, None)))(*qml.gradients.finite_diff(tape, h = h)) + grad_F = (lambda t, fn: fn(qml.execute(t, dev, None)))( + *qml.gradients.finite_diff(tape, h=h) + ) grad_D = dev.adjoint_jacobian(tape) assert np.allclose(grad_D, grad_F, atol=tol, rtol=0) diff --git a/tests/test_apply.py b/tests/test_apply.py index ad3e3888cd..eb7f188631 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -122,7 +122,7 @@ def test_apply_operation_single_wire_no_parameters( dev.apply([operation(wires=[0])]) assert np.allclose(qubit_device_1_wire._state, np.array(expected_output), atol=tol, rtol=0) - assert dev._state.dtype == dev.C_DTYPE + assert dev._state.dtype == dev.C_DTYPE test_data_two_wires_no_parameters = [ (qml.CNOT, [1, 0, 0, 0], [1, 0, 0, 0]), @@ -159,7 +159,7 @@ def test_apply_operation_two_wires_no_parameters( dev.apply([operation(wires=[0, 1])]) assert np.allclose(qubit_device_2_wires.state, np.array(expected_output), atol=tol, rtol=0) - assert dev._state.dtype == dev.C_DTYPE + assert dev._state.dtype == dev.C_DTYPE test_data_three_wires_no_parameters = [ (qml.CSWAP, [1, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0]), @@ -177,13 +177,13 @@ def test_apply_operation_three_wires_no_parameters( ): """Tests that applying an operation yields the expected output state for three wire operations that have no parameters.""" - + dev = qubit_device_3_wires 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 dev._state.dtype == dev.C_DTYPE + assert dev._state.dtype == dev.C_DTYPE @pytest.mark.parametrize( "operation,expected_output,par", diff --git a/tests/test_device.py b/tests/test_device.py index 7dcf0dc62f..9a3bb52a20 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -23,13 +23,16 @@ if not CPP_BINARY_AVAILABLE: pytest.skip("No binary module found. Skipping.", allow_module_level=True) + def test_create_device(): dev = qml.device("lightning.qubit", wires=1) + @pytest.mark.parametrize("C", [np.complex64, np.complex128]) def test_create_device_with_dtype(C): dev = qml.device("lightning.qubit", wires=1, c_dtype=C) + @pytest.mark.skipif( not hasattr(np, "complex256"), reason="Numpy only defines complex256 in Linux-like system" ) diff --git a/tests/test_expval.py b/tests/test_expval.py index 15c30356bb..b85a856254 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -74,7 +74,9 @@ 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)) + assert np.allclose( + res, np.array([np.sin(theta) * np.sin(phi), np.sin(phi)], dtype=dev.C_DTYPE) + ) def test_pauliy_expectation(self, theta, phi, qubit_device_3_wires, tol): """Test that PauliY expectation value is correct""" diff --git a/tests/test_vjp.py b/tests/test_vjp.py index 77a884aa8b..5dd29a4626 100644 --- a/tests/test_vjp.py +++ b/tests/test_vjp.py @@ -134,7 +134,7 @@ def test_tf_tensor_dy(self, dev): expected = tf.constant([4.0, 4.0, 4.0, 4.0], dtype=tf_r_dtype) vjp = dev.compute_vjp(dy, jac) - assert vjp.dtype == dev.R_DTYPE #? + assert vjp.dtype == dev.R_DTYPE # ? assert tf.reduce_all(vjp == expected) @@ -145,11 +145,8 @@ class TestVectorJacobianProduct: def dev(self, request): return qml.device("lightning.qubit", wires=2, c_dtype=request.param) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_use_device_state(self, tol, dev, C): + def test_use_device_state(self, tol, dev): """Tests that when using the device state, the correct answer is still returned.""" - dev._state = dev._asarray(dev._state, C) - x, y, z = [0.5, 0.3, -0.7] with qml.tape.QuantumTape() as tape: @@ -171,11 +168,8 @@ def test_use_device_state(self, tol, dev, C): assert np.allclose(vjp1, vjp2, atol=tol, rtol=0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_provide_starting_state(self, tol, dev, C): + def test_provide_starting_state(self, tol, dev): """Tests provides correct answer when provided starting state.""" - dev._state = dev._asarray(dev._state, C) - x, y, z = [0.5, 0.3, -0.7] with qml.tape.QuantumTape() as tape: @@ -197,12 +191,9 @@ def test_provide_starting_state(self, tol, dev, C): assert np.allclose(vjp1, vjp2, atol=tol, rtol=0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_not_expval(self, dev, C): + def test_not_expval(self, dev): """Test if a QuantumFunctionError is raised for a tape with measurements that are not expectation values""" - dev._state = dev._asarray(dev._state, C) - with qml.tape.QuantumTape() as tape: qml.RX(0.1, wires=0) qml.var(qml.PauliZ(0)) @@ -212,12 +203,10 @@ def test_not_expval(self, dev, C): with pytest.raises(qml.QuantumFunctionError, match="Adjoint differentiation method does"): dev.vjp(tape, dy)(tape) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_finite_shots_warns(self, C): + def test_finite_shots_warns(self): """Tests warning raised when finite shots specified""" dev = qml.device("lightning.qubit", wires=1, shots=1) - dev._state = dev._asarray(dev._state, C) with qml.tape.QuantumTape() as tape: qml.expval(qml.PauliZ(0)) @@ -232,11 +221,9 @@ def test_finite_shots_warns(self, C): from pennylane_lightning import LightningQubit as lq @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_unsupported_op(self, dev, C): + def test_unsupported_op(self, dev): """Test if a QuantumFunctionError is raised for an unsupported operation, i.e., multi-parameter operations that are not qml.Rot""" - dev._state = dev._asarray(dev._state, C) with qml.tape.QuantumTape() as tape: qml.CRot(0.1, 0.2, 0.3, wires=[0, 1]) @@ -260,10 +247,8 @@ def test_unsupported_op(self, dev, C): dev.vjp(tape, dy)(tape) @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_proj_unsupported(self, dev, C): + def test_proj_unsupported(self, dev): """Test if a QuantumFunctionError is raised for a Projector observable""" - dev._state = dev._asarray(dev._state, C) with qml.tape.QuantumTape() as tape: qml.CRX(0.1, wires=[0, 1]) @@ -286,10 +271,7 @@ def test_proj_unsupported(self, dev, C): dev.vjp(tape, dy)(tape) @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_unsupported_hermitian_expectation(self, dev, C): - dev._state = dev._asarray(dev._state, C) - + def test_unsupported_hermitian_expectation(self, dev): obs = np.array([[1, 0], [0, -1]], dtype=np.complex128, requires_grad=False) with qml.tape.QuantumTape() as tape: @@ -312,11 +294,8 @@ def test_unsupported_hermitian_expectation(self, dev, C): ): dev.vjp(tape, dy)(tape) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_no_trainable_parameters(self, dev, C): + def test_no_trainable_parameters(self, dev): """A tape with no trainable parameters will simply return None""" - dev._state = dev._asarray(dev._state, C) - x = 0.4 with qml.tape.QuantumTape() as tape: @@ -332,10 +311,9 @@ def test_no_trainable_parameters(self, dev, C): assert vjp is None - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_no_trainable_parameters_NEW(self, dev, C): + def test_no_trainable_parameters_NEW(self, dev): """A tape with no trainable parameters will simply return None""" - dev._state = dev._asarray(dev._state, C) + dev._state = dev._asarray(dev._state) x = 0.4 @@ -351,11 +329,8 @@ def test_no_trainable_parameters_NEW(self, dev, C): assert vjp is None - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_no_trainable_parameters_(self, dev, C): + def test_no_trainable_parameters_(self, dev): """A tape with no trainable parameters will simply return None""" - dev._state = dev._asarray(dev._state, C) - x = 0.4 with qml.tape.QuantumTape() as tape: @@ -371,11 +346,8 @@ def test_no_trainable_parameters_(self, dev, C): assert vjp is None - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_zero_dy(self, dev, C): + def test_zero_dy(self, dev): """A zero dy vector will return no tapes and a zero matrix""" - dev._state = dev._asarray(dev._state, C) - x = 0.4 y = 0.6 @@ -393,12 +365,9 @@ def test_zero_dy(self, dev, C): assert np.all(vjp == np.zeros([len(tape.trainable_params)])) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_single_expectation_value(self, tol, dev, C): + def test_single_expectation_value(self, tol, dev): """Tests correct output shape and evaluation for a tape with a single expval output""" - dev._state = dev._asarray(dev._state, C) - x = 0.543 y = -0.654 @@ -417,12 +386,9 @@ def test_single_expectation_value(self, tol, dev, C): expected = np.array([-np.sin(y) * np.sin(x), np.cos(y) * np.cos(x)]) assert np.allclose(vjp, expected, atol=tol, rtol=0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_multiple_expectation_values(self, tol, dev, C): + def test_multiple_expectation_values(self, tol, dev): """Tests correct output shape and evaluation for a tape with multiple expval outputs""" - dev._state = dev._asarray(dev._state, C) - x = 0.543 y = -0.654 @@ -442,11 +408,10 @@ def test_multiple_expectation_values(self, tol, dev, C): expected = np.array([-np.sin(x), 2 * np.cos(y)]) assert np.allclose(vjp, expected, atol=tol, rtol=0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_prob_expectation_values(self, dev, C): + def test_prob_expectation_values(self, dev): """Tests correct output shape and evaluation for a tape with prob and expval outputs""" - dev._state = dev._asarray(dev._state, C) + dev._state = dev._asarray(dev._state) x = 0.543 y = -0.654 @@ -472,11 +437,8 @@ class TestBatchVectorJacobianProduct: def dev(self, request): return qml.device("lightning.qubit", wires=2, c_dtype=request.param) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_one_tape_no_trainable_parameters(self, dev, C): + def test_one_tape_no_trainable_parameters(self, dev): """A tape with no trainable parameters will simply return None""" - dev._state = dev._asarray(dev._state, C) - with qml.tape.QuantumTape() as tape1: qml.RX(0.4, wires=0) qml.CNOT(wires=[0, 1]) @@ -500,11 +462,8 @@ def test_one_tape_no_trainable_parameters(self, dev, C): assert vjps[0] is None assert vjps[1] is not None - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_all_tapes_no_trainable_parameters(self, dev, C): + def test_all_tapes_no_trainable_parameters(self, dev): """If all tapes have no trainable parameters all outputs will be None""" - dev._state = dev._asarray(dev._state, C) - with qml.tape.QuantumTape() as tape1: qml.RX(0.4, wires=0) qml.CNOT(wires=[0, 1]) @@ -528,11 +487,8 @@ def test_all_tapes_no_trainable_parameters(self, dev, C): assert vjps[0] is None assert vjps[1] is None - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_zero_dy(self, dev, C): + def test_zero_dy(self, dev): """A zero dy vector will return no tapes and a zero matrix""" - dev._state = dev._asarray(dev._state, C) - with qml.tape.QuantumTape() as tape1: qml.RX(0.4, wires=0) qml.CNOT(wires=[0, 1]) @@ -555,10 +511,9 @@ def test_zero_dy(self, dev, C): assert np.allclose(vjps[0], 0) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_reduction_append(self, dev, C): + def test_reduction_append(self, dev): """Test the 'append' reduction strategy""" - dev._state = dev._asarray(dev._state, C) + dev._state = dev._asarray(dev._state) with qml.tape.QuantumTape() as tape1: qml.RX(0.4, wires=0) @@ -584,11 +539,8 @@ def test_reduction_append(self, dev, C): assert all(isinstance(v, np.ndarray) for v in vjps) assert all(len(v) == len(t.trainable_params) for t, v in zip(tapes, vjps)) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_reduction_append_callable(self, dev, C): + def test_reduction_append_callable(self, dev): """Test the 'append' reduction strategy""" - dev._state = dev._asarray(dev._state, C) - with qml.tape.QuantumTape() as tape1: qml.RX(0.4, wires=0) qml.CNOT(wires=[0, 1]) @@ -613,11 +565,8 @@ def test_reduction_append_callable(self, dev, C): assert all(isinstance(v, np.ndarray) for v in vjps) assert all(len(v) == len(t.trainable_params) for t, v in zip(tapes, vjps)) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_reduction_extend(self, dev, C): + def test_reduction_extend(self, dev): """Test the 'extend' reduction strategy""" - dev._state = dev._asarray(dev._state, C) - with qml.tape.QuantumTape() as tape1: qml.RX(0.4, wires=0) qml.CNOT(wires=[0, 1]) @@ -640,11 +589,8 @@ def test_reduction_extend(self, dev, C): assert len(vjps) == sum(len(t.trainable_params) for t in tapes) - @pytest.mark.parametrize("C", [np.complex64, np.complex128]) - def test_reduction_extend_callable(self, dev, C): + def test_reduction_extend_callable(self, dev): """Test the 'extend' reduction strategy""" - dev._state = dev._asarray(dev._state, C) - with qml.tape.QuantumTape() as tape1: qml.RX(0.4, wires=0) qml.CNOT(wires=[0, 1]) From cfac157f4f0bbf36529c44875b198f43a59ce799 Mon Sep 17 00:00:00 2001 From: Chae-Yeun Park Date: Sat, 7 May 2022 11:09:38 -0400 Subject: [PATCH 04/13] Temporary fix for a workflow --- .github/workflows/wheel_linux_x86_64.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/wheel_linux_x86_64.yml b/.github/workflows/wheel_linux_x86_64.yml index bb064d5d67..c665558f96 100644 --- a/.github/workflows/wheel_linux_x86_64.yml +++ b/.github/workflows/wheel_linux_x86_64.yml @@ -18,6 +18,9 @@ env: # Testing of built wheels CIBW_TEST_REQUIRES: numpy~=1.21 scipy pytest pytest-cov pytest-mock flaky + # Force to use master + CIBW_BEFORE_TEST: pip install git+https://github.com/PennyLaneAI/pennylane.git@master + CIBW_TEST_COMMAND: | pl-device-test --device=lightning.qubit --skip-ops -x --tb=short --no-flaky-report From bad6418e65c3225c43d3a06e9cf57bad69f21b9d Mon Sep 17 00:00:00 2001 From: Chae-Yeun Park Date: Mon, 9 May 2022 18:32:29 -0400 Subject: [PATCH 05/13] Fix --- pennylane_lightning/lightning_qubit.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/pennylane_lightning/lightning_qubit.py b/pennylane_lightning/lightning_qubit.py index ee2d03902d..77c467f0d4 100644 --- a/pennylane_lightning/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit.py @@ -264,8 +264,6 @@ def adjoint_jacobian(self, tape, starting_state=None, use_device_state=False): UserWarning, ) - # To support np.complex64 based on the type of self._state - if len(tape.trainable_params) == 0: return np.array(0) @@ -360,12 +358,10 @@ def compute_vjp(self, dy, jac, num=None): if math.allclose(dy, 0): return math.convert_like(np.zeros([num_params]), dy) - if self.C_DTYPE == np.complex64: + if self.use_csingle: VJP = VectorJacobianProductC64() - elif self.C_DTYPE == np.complex128: - VJP = VectorJacobianProductC128() else: - raise TypeError(f"Unsupported complex Type: {dtype}") + VJP = VectorJacobianProductC128() vjp_tensor = VJP.compute_vjp_from_jac( math.reshape(jac, [-1]), @@ -574,9 +570,6 @@ def expval(self, observable, shot_range=None, bin_size=None): samples = self.sample(observable, shot_range=shot_range, bin_size=bin_size) return np.squeeze(np.mean(samples, axis=0)) - # To support np.complex64 based on the type of self._state - dtype = self._state.dtype - # Initialization of state ket = np.ravel(self._pre_rotated_state) From 4d06650c0feb5f00998ef68126aaf80095a6cdb6 Mon Sep 17 00:00:00 2001 From: Chae-Yeun Park Date: Mon, 9 May 2022 18:58:01 -0400 Subject: [PATCH 06/13] Fix --- pennylane_lightning/lightning_qubit.py | 11 ++++++-- tests/test_adjoint_jacobian.py | 36 +++++++++++++++++++++----- tests/test_apply.py | 11 +++----- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/pennylane_lightning/lightning_qubit.py b/pennylane_lightning/lightning_qubit.py index 77c467f0d4..555abdb02b 100644 --- a/pennylane_lightning/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit.py @@ -631,7 +631,7 @@ class LightningQubit(DefaultQubit): # pragma: no cover _CPP_BINARY_AVAILABLE = False operations = _remove_snapshot_from_operations(DefaultQubit.operations) - def __init__(self, *args, **kwargs): + def __init__(self, wires, *, c_dtype=np.complex128, **kwargs): warn( "Pre-compiled binaries for lightning.qubit are not available. Falling back to " "using the Python-based default.qubit implementation. To manually compile from " @@ -639,4 +639,11 @@ def __init__(self, *args, **kwargs): "https://pennylane-lightning.readthedocs.io/en/latest/installation.html.", UserWarning, ) - super().__init__(*args, **kwargs) + + if c_dtype is np.complex64: + r_dtype = np.float32 + elif c_dtype is np.complex128: + r_dtype = np.float64 + else: + raise TypeError(f"Unsupported complex Type: {c_dtype}") + super().__init__(wires, r_dtype=r_dtype, c_dtype=c_dtype, **kwargs) diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index fb7f6faf53..78bdc3f320 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -166,6 +166,8 @@ def test_unsupported_hermitian_expectation(self, dev): @pytest.mark.parametrize("G", [qml.RX, qml.RY, qml.RZ]) def test_pauli_rotation_gradient(self, G, theta, dev): """Tests that the automatic gradients of Pauli rotations are correct.""" + if dev.R_DTYPE == np.float32 and not lq._CPP_BINARY_AVAILABLE: + pytest.skip("Skip as default.qubit with a single precision floating point works poorly") with qml.tape.QuantumTape() as tape: qml.QubitStateVector(np.array([1.0, -1.0]) / np.sqrt(2), wires=0) @@ -185,11 +187,16 @@ def test_pauli_rotation_gradient(self, G, theta, dev): assert np.allclose(calculated_val, numeric_val[0][2], atol=tol, rtol=0) @pytest.mark.parametrize("theta", np.linspace(-2 * np.pi, 2 * np.pi, 7)) - def test_Rot_gradient(self, theta, dev): + def test_Rot_gradient(self, theta, dev, tol): """Tests that the device gradient of an arbitrary Euler-angle-parameterized gate is correct.""" + if dev.R_DTYPE == np.float32 and not lq._CPP_BINARY_AVAILABLE: + pytest.skip("Skip as default.qubit with a single precision floating point works poorly") + params = np.array([theta, theta**3, np.sqrt(2) * theta]) + print(dev.R_DTYPE, dev.C_DTYPE, dev._state.dtype) + with qml.tape.QuantumTape() as tape: qml.QubitStateVector(np.array([1.0, -1.0]) / np.sqrt(2), wires=0) qml.Rot(*params, wires=[0]) @@ -199,12 +206,10 @@ def test_Rot_gradient(self, theta, dev): calculated_val = dev.adjoint_jacobian(tape) - h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 - tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 - # compare to finite differences - tapes, fn = qml.gradients.finite_diff(tape, h=h) + tapes, fn = qml.gradients.finite_diff(tape) numeric_val = fn(qml.execute(tapes, dev, None)) + print(dev._state.dtype) assert np.allclose(calculated_val, numeric_val[0][2:], atol=tol, rtol=0) @pytest.mark.parametrize("par", [1, -2, 1.623, -0.051, 0]) # integers, floats, zero @@ -274,6 +279,8 @@ def test_multiple_rx_gradient(self, tol): def test_gradients(self, op, obs, dev): """Tests that the gradients of circuits match between the finite difference and device methods.""" + if dev.R_DTYPE == np.float32 and not lq._CPP_BINARY_AVAILABLE: + pytest.skip("Skip as default.qubit with a single precision floating point works poorly") # op.num_wires and op.num_params must be initialized a priori with qml.tape.QuantumTape() as tape: @@ -305,6 +312,9 @@ def test_gradients(self, op, obs, dev): def test_gradient_gate_with_multiple_parameters(self, dev): """Tests that gates with multiple free parameters yield correct gradients.""" + if dev.R_DTYPE == np.float32 and not lq._CPP_BINARY_AVAILABLE: + pytest.skip("Skip as default.qubit with a single precision floating point works poorly") + x, y, z = [0.5, 0.3, -0.7] with qml.tape.QuantumTape() as tape: @@ -394,6 +404,7 @@ def circ(x): ): qml.grad(circ)(0.1) + @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_qnode(self, mocker, dev): """Test that specifying diff_method allows the adjoint method to be selected""" args = np.array([0.54, 0.1, 0.5], requires_grad=True) @@ -482,6 +493,7 @@ def cost(p1, p2): assert np.allclose(grad_D[0], expected, atol=tol, rtol=0) + @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_gradient_repeated_gate_parameters(self, mocker, dev): """Tests that repeated use of a free parameter in a multi-parameter gate yields correct gradients.""" @@ -516,6 +528,9 @@ def circuit(params): def test_interface_tf(self, dev): """Test if gradients agree between the adjoint and finite-diff methods when using the TensorFlow interface""" + if dev.R_DTYPE == np.float32 and not lq._CPP_BINARY_AVAILABLE: + pytest.skip("Skip as default.qubit with a single precision floating point works poorly") + tf = pytest.importorskip("tensorflow") def f(params1, params2): @@ -553,6 +568,9 @@ def f(params1, params2): def test_interface_torch(self, dev): """Test if gradients agree between the adjoint and finite-diff methods when using the Torch interface""" + if dev.R_DTYPE == np.float32 and not lq._CPP_BINARY_AVAILABLE: + pytest.skip("Skip as default.qubit with a single precision floating point works poorly") + torch = pytest.importorskip("torch") def f(params1, params2): @@ -564,8 +582,11 @@ def f(params1, params2): params1 = torch.tensor(0.3, requires_grad=True) params2 = torch.tensor(0.4, requires_grad=True) + h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + qnode1 = QNode(f, dev, interface="torch", diff_method="adjoint") - qnode2 = QNode(f, dev, interface="torch", diff_method="finite-diff") + qnode2 = QNode(f, dev, interface="torch", diff_method="finite-diff", h=h) res1 = qnode1(params1, params2) res1.backward() @@ -582,6 +603,9 @@ def f(params1, params2): def test_interface_jax(self, dev): """Test if the gradients agree between adjoint and finite-difference methods in the jax interface""" + if dev.R_DTYPE == np.float32 and not lq._CPP_BINARY_AVAILABLE: + pytest.skip("Skip as default.qubit with a single precision floating point works poorly") + jax = pytest.importorskip("jax") def f(params1, params2): diff --git a/tests/test_apply.py b/tests/test_apply.py index eb7f188631..0b0b83b93d 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -637,11 +637,10 @@ def test_load_default_qubit_device(self): assert dev.shots is None assert dev.short_name == "lightning.qubit" + @pytest.mark.skipif(not CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_no_backprop(self): """Test that lightning.qubit does not support the backprop differentiation method.""" - if not CPP_BINARY_AVAILABLE: - pytest.skip("Skipping test because lightning.qubit is behaving like default.qubit") dev = qml.device("lightning.qubit", wires=2) @@ -652,12 +651,10 @@ def circuit(): with pytest.raises(qml.QuantumFunctionError): qml.QNode(circuit, dev, diff_method="backprop") + @pytest.mark.skipif(not CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_best_gets_lightning(self): """Test that the best differentiation method returns lightning qubit.""" - if not CPP_BINARY_AVAILABLE: - pytest.skip("Skipping test because lightning.qubit is behaving like default.qubit") - dev = qml.device("lightning.qubit", wires=2) def circuit(): @@ -1427,10 +1424,8 @@ def test_iter_identity_skipped(self, mocker, tol): spy_unitary.assert_not_called() +@pytest.mark.skipif(not CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_warning(): """Tests if a warning is raised when lightning.qubit binaries are not available""" - if CPP_BINARY_AVAILABLE: - pytest.skip("Test only applies when binaries are unavailable") - with pytest.warns(UserWarning, match="Pre-compiled binaries for lightning.qubit"): qml.device("lightning.qubit", wires=1) From 75507acd98a2bd9c8b380d66bd154c50c984aebb Mon Sep 17 00:00:00 2001 From: Dev version update bot Date: Mon, 9 May 2022 22:58:22 +0000 Subject: [PATCH 07/13] Auto update version --- pennylane_lightning/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/_version.py b/pennylane_lightning/_version.py index 018b2a730b..135a47fe33 100644 --- a/pennylane_lightning/_version.py +++ b/pennylane_lightning/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.24.0-dev6" +__version__ = "0.24.0-dev7" From a4b827138af6acf6fb5f4ee76f33bcc2f58900d3 Mon Sep 17 00:00:00 2001 From: Chae-Yeun Park Date: Mon, 9 May 2022 18:58:40 -0400 Subject: [PATCH 08/13] Remove obvious comment --- .github/workflows/wheel_linux_x86_64.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/wheel_linux_x86_64.yml b/.github/workflows/wheel_linux_x86_64.yml index c665558f96..b2b38a0951 100644 --- a/.github/workflows/wheel_linux_x86_64.yml +++ b/.github/workflows/wheel_linux_x86_64.yml @@ -18,7 +18,6 @@ env: # Testing of built wheels CIBW_TEST_REQUIRES: numpy~=1.21 scipy pytest pytest-cov pytest-mock flaky - # Force to use master CIBW_BEFORE_TEST: pip install git+https://github.com/PennyLaneAI/pennylane.git@master CIBW_TEST_COMMAND: | From 63ea01a2e2f026402d29e7ed2aeddfea4fc9da16 Mon Sep 17 00:00:00 2001 From: Chae-Yeun Park Date: Mon, 9 May 2022 19:06:59 -0400 Subject: [PATCH 09/13] Add docstring --- pennylane_lightning/lightning_qubit.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pennylane_lightning/lightning_qubit.py b/pennylane_lightning/lightning_qubit.py index 555abdb02b..df3030ef0b 100644 --- a/pennylane_lightning/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit.py @@ -100,6 +100,7 @@ class LightningQubit(DefaultQubit): the expectation values. Defaults to ``None`` if not specified. Setting to ``None`` results in computing statistics like expectation values and variances analytically. + c_dtype: Datatypes for statevector representation. Must be one of ``np.complex64`` or ``np.complex128``. """ name = "Lightning Qubit PennyLane plugin" From cb98402801d9cf2704a20bd3bb3b1c85c6c37d51 Mon Sep 17 00:00:00 2001 From: Chae-Yeun Park Date: Mon, 9 May 2022 19:32:20 -0400 Subject: [PATCH 10/13] Fix --- tests/test_adjoint_jacobian.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index 78bdc3f320..954414a38d 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -187,7 +187,7 @@ def test_pauli_rotation_gradient(self, G, theta, dev): assert np.allclose(calculated_val, numeric_val[0][2], atol=tol, rtol=0) @pytest.mark.parametrize("theta", np.linspace(-2 * np.pi, 2 * np.pi, 7)) - def test_Rot_gradient(self, theta, dev, tol): + def test_Rot_gradient(self, theta, dev): """Tests that the device gradient of an arbitrary Euler-angle-parameterized gate is correct.""" if dev.R_DTYPE == np.float32 and not lq._CPP_BINARY_AVAILABLE: @@ -195,8 +195,6 @@ def test_Rot_gradient(self, theta, dev, tol): params = np.array([theta, theta**3, np.sqrt(2) * theta]) - print(dev.R_DTYPE, dev.C_DTYPE, dev._state.dtype) - with qml.tape.QuantumTape() as tape: qml.QubitStateVector(np.array([1.0, -1.0]) / np.sqrt(2), wires=0) qml.Rot(*params, wires=[0]) @@ -206,10 +204,12 @@ def test_Rot_gradient(self, theta, dev, tol): calculated_val = dev.adjoint_jacobian(tape) + h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 + tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + # compare to finite differences - tapes, fn = qml.gradients.finite_diff(tape) + tapes, fn = qml.gradients.finite_diff(tape, h=h) numeric_val = fn(qml.execute(tapes, dev, None)) - print(dev._state.dtype) assert np.allclose(calculated_val, numeric_val[0][2:], atol=tol, rtol=0) @pytest.mark.parametrize("par", [1, -2, 1.623, -0.051, 0]) # integers, floats, zero From e860c681e61e725b3823a815246e5f6ec572f11d Mon Sep 17 00:00:00 2001 From: Chae-Yeun Park Date: Mon, 9 May 2022 19:42:14 -0400 Subject: [PATCH 11/13] Fix --- tests/test_apply.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_apply.py b/tests/test_apply.py index 0b0b83b93d..8a3b0a3805 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -1424,7 +1424,7 @@ def test_iter_identity_skipped(self, mocker, tol): spy_unitary.assert_not_called() -@pytest.mark.skipif(not CPP_BINARY_AVAILABLE, reason="Lightning binary required") +@pytest.mark.skipif(CPP_BINARY_AVAILABLE, reason="Test only applies when binaries are unavailable") def test_warning(): """Tests if a warning is raised when lightning.qubit binaries are not available""" with pytest.warns(UserWarning, match="Pre-compiled binaries for lightning.qubit"): From 352dcb75b5e0b9287da23b75ae8ccb0f1787579d Mon Sep 17 00:00:00 2001 From: Chae-Yeun Park Date: Tue, 10 May 2022 10:26:38 -0400 Subject: [PATCH 12/13] Fix float32 finte-diff --- tests/test_adjoint_jacobian.py | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/tests/test_adjoint_jacobian.py b/tests/test_adjoint_jacobian.py index 954414a38d..8b4d7f4015 100644 --- a/tests/test_adjoint_jacobian.py +++ b/tests/test_adjoint_jacobian.py @@ -166,8 +166,6 @@ def test_unsupported_hermitian_expectation(self, dev): @pytest.mark.parametrize("G", [qml.RX, qml.RY, qml.RZ]) def test_pauli_rotation_gradient(self, G, theta, dev): """Tests that the automatic gradients of Pauli rotations are correct.""" - if dev.R_DTYPE == np.float32 and not lq._CPP_BINARY_AVAILABLE: - pytest.skip("Skip as default.qubit with a single precision floating point works poorly") with qml.tape.QuantumTape() as tape: qml.QubitStateVector(np.array([1.0, -1.0]) / np.sqrt(2), wires=0) @@ -178,7 +176,7 @@ def test_pauli_rotation_gradient(self, G, theta, dev): calculated_val = dev.adjoint_jacobian(tape) - h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 # compare to finite differences @@ -190,8 +188,6 @@ def test_pauli_rotation_gradient(self, G, theta, dev): def test_Rot_gradient(self, theta, dev): """Tests that the device gradient of an arbitrary Euler-angle-parameterized gate is correct.""" - if dev.R_DTYPE == np.float32 and not lq._CPP_BINARY_AVAILABLE: - pytest.skip("Skip as default.qubit with a single precision floating point works poorly") params = np.array([theta, theta**3, np.sqrt(2) * theta]) @@ -279,8 +275,6 @@ def test_multiple_rx_gradient(self, tol): def test_gradients(self, op, obs, dev): """Tests that the gradients of circuits match between the finite difference and device methods.""" - if dev.R_DTYPE == np.float32 and not lq._CPP_BINARY_AVAILABLE: - pytest.skip("Skip as default.qubit with a single precision floating point works poorly") # op.num_wires and op.num_params must be initialized a priori with qml.tape.QuantumTape() as tape: @@ -300,7 +294,7 @@ def test_gradients(self, op, obs, dev): dev.trainable_params = set(range(1, 1 + op.num_params)) - h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 grad_F = (lambda t, fn: fn(qml.execute(t, dev, None)))( @@ -312,8 +306,6 @@ def test_gradients(self, op, obs, dev): def test_gradient_gate_with_multiple_parameters(self, dev): """Tests that gates with multiple free parameters yield correct gradients.""" - if dev.R_DTYPE == np.float32 and not lq._CPP_BINARY_AVAILABLE: - pytest.skip("Skip as default.qubit with a single precision floating point works poorly") x, y, z = [0.5, 0.3, -0.7] @@ -325,7 +317,7 @@ def test_gradient_gate_with_multiple_parameters(self, dev): tape.trainable_params = {1, 2, 3} - h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 grad_D = dev.adjoint_jacobian(tape) @@ -528,8 +520,6 @@ def circuit(params): def test_interface_tf(self, dev): """Test if gradients agree between the adjoint and finite-diff methods when using the TensorFlow interface""" - if dev.R_DTYPE == np.float32 and not lq._CPP_BINARY_AVAILABLE: - pytest.skip("Skip as default.qubit with a single precision floating point works poorly") tf = pytest.importorskip("tensorflow") @@ -547,7 +537,7 @@ def f(params1, params2): params1 = tf.Variable(0.3, dtype=tf_r_dtype) params2 = tf.Variable(0.4, dtype=tf_r_dtype) - h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 qnode1 = QNode(f, dev, interface="tf", diff_method="adjoint") @@ -568,8 +558,6 @@ def f(params1, params2): def test_interface_torch(self, dev): """Test if gradients agree between the adjoint and finite-diff methods when using the Torch interface""" - if dev.R_DTYPE == np.float32 and not lq._CPP_BINARY_AVAILABLE: - pytest.skip("Skip as default.qubit with a single precision floating point works poorly") torch = pytest.importorskip("torch") @@ -582,7 +570,7 @@ def f(params1, params2): params1 = torch.tensor(0.3, requires_grad=True) params2 = torch.tensor(0.4, requires_grad=True) - h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 qnode1 = QNode(f, dev, interface="torch", diff_method="adjoint") @@ -603,8 +591,6 @@ def f(params1, params2): def test_interface_jax(self, dev): """Test if the gradients agree between adjoint and finite-difference methods in the jax interface""" - if dev.R_DTYPE == np.float32 and not lq._CPP_BINARY_AVAILABLE: - pytest.skip("Skip as default.qubit with a single precision floating point works poorly") jax = pytest.importorskip("jax") @@ -617,7 +603,7 @@ def f(params1, params2): params1 = jax.numpy.array(0.3) params2 = jax.numpy.array(0.4) - h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 + h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 qnode_adjoint = QNode(f, dev, interface="jax", diff_method="adjoint") From 3ba6b18dec1d1b89251c5a6fb50b6541238acd2a Mon Sep 17 00:00:00 2001 From: Chae-Yeun Park Date: Tue, 10 May 2022 10:42:52 -0400 Subject: [PATCH 13/13] Update CHANGELOG.md --- .github/CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 73adcdf1fa..075e903613 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -6,6 +6,14 @@ ### Improvements +* Device `lightning.qubit` now accepts a datatype for a statevector. +[(#290)](https://github.com/PennyLaneAI/pennylane-lightning/pull/290) + +```python +dev1 = qml.device('lightning.qubit', wires=4, c_dtype=np.complex64) # for single precision +dev2 = qml.device('lightning.qubit', wires=4, c_dtype=np.complex128) # for double precision +``` + * Split matrix operations, refactor dispatch mechanisms, and add a benchmark suits. [(#274)](https://github.com/PennyLaneAI/pennylane-lightning/pull/274)