diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index a2d10f4488b..e8024f4d17a 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,12 +2,13 @@

New features since last release

-* A new hardware-efficient particle-conserving template has been implemented - to perform VQE-based quantum chemistry simulations. The new template applies - several layers of the particle-conserving entangler proposed in Fig. 2a +* Two new hardware-efficient particle-conserving templates have been implemented + to perform VQE-based quantum chemistry simulations. The new templates apply + several layers of the particle-conserving entanglers proposed in Figs. 2a and 2b of the article by Barkoutsos *et al*. in `arXiv:1805.04340 `_ [(#875)](https://github.com/PennyLaneAI/pennylane/pull/875) + [(#876)](https://github.com/PennyLaneAI/pennylane/pull/876) * The `Device` and `QubitDevice` classes have a new API method, `batch_execute()`. This method accepts a *list* of tapes, and returns a list of evaluated numerical values. diff --git a/doc/_static/templates/layers/particle_conserving_u2.png b/doc/_static/templates/layers/particle_conserving_u2.png new file mode 100644 index 00000000000..e4556d4e3cf Binary files /dev/null and b/doc/_static/templates/layers/particle_conserving_u2.png differ diff --git a/doc/_static/templates/layers/u2_decomposition.png b/doc/_static/templates/layers/u2_decomposition.png new file mode 100644 index 00000000000..65fe4b5a6c5 Binary files /dev/null and b/doc/_static/templates/layers/u2_decomposition.png differ diff --git a/doc/introduction/templates.rst b/doc/introduction/templates.rst index 8eaf49edd4c..000647b6c3f 100644 --- a/doc/introduction/templates.rst +++ b/doc/introduction/templates.rst @@ -110,6 +110,11 @@ neural network. Note arbitrary templates or operations can also be layered using :description: BasicEntanglerLayers :figure: ../_static/templates/layers/basic_entangler.png +.. customgalleryitem:: + :link: ../code/api/pennylane.templates.layers.ParticleConservingU2.html + :description: ParticleConservingU2 + :figure: ../_static/templates/layers/particle_conserving_u2.png + .. customgalleryitem:: :link: ../code/api/pennylane.templates.layers.ParticleConservingU1.html :description: ParticleConservingU1 diff --git a/pennylane/init.py b/pennylane/init.py index afb2cdb7eaf..bc583b43fd8 100644 --- a/pennylane/init.py +++ b/pennylane/init.py @@ -20,22 +20,77 @@ from pennylane import numpy as np +def particle_conserving_u2_uniform(n_layers, n_wires, low=0, high=2 * pi, seed=None): + r"""Creates a parameter array for :func:`~.ParticleConservingU2`, drawn from a uniform + distribution. + Each parameter is drawn uniformly at random from the half-open interval [``low``, ``high``). + The parameters define the trainable angles entering the Z rotation + :math:`R_\mathrm{z}(\vec{\theta})` and particle-conserving gate :math:`U_{2,\mathrm{ex}}` + implemented by the :func:`~.u2_ex_gate()`. + Args: + n_layers (int): number of layers + n_wires (int): number of qubits + low (float): lower endpoint of the parameter interval + high (float): upper endpoint of the parameter interval + seed (int): seed used in sampling the parameters, makes function call deterministic + Returns: + array: parameter array + """ + + if seed is not None: + np.random.seed(seed) + + if n_wires < 2: + raise ValueError( + "The number of qubits must be greater than one; got 'n_wires' = {}".format(n_wires) + ) + + params = np.random.uniform(low=low, high=high, size=(n_layers, 2 * n_wires - 1)) + return params + + +def particle_conserving_u2_normal(n_layers, n_wires, mean=0, std=0.1, seed=None): + r"""Creates a parameter array for :func:`~.ParticleConservingU2`, drawn from a normal + distribution. + Each parameter is drawn from a normal distribution with ``mean`` and standard deviation ``std``. + The parameters define the trainable angles entering the Z rotation + :math:`R_\mathrm{z}(\vec{\theta})` and particle-conserving gate :math:`U_{2,\mathrm{ex}}` + implemented by the :func:`~.u2_ex_gate()`. + Args: + n_layers (int): number of layers + n_wires (int): number of qubits + mean (float): mean of parameters + std (float): standard deviation of parameters + seed (int): seed used in sampling the parameters, makes function call deterministic + Returns: + array: parameter array + """ + + if seed is not None: + np.random.seed(seed) + + if n_wires < 2: + raise ValueError( + "The number of qubits must be greater than one; got 'n_wires' = {}".format(n_wires) + ) + + params = np.random.normal(loc=mean, scale=std, size=(n_layers, 2 * n_wires - 1)) + return params + + def particle_conserving_u1_uniform(n_layers, n_wires, low=0, high=2 * pi, seed=None): r"""Creates a parameter array for :func:`~.ParticleConservingU1`, drawn from a uniform distribution. - Each parameter is drawn uniformly at random from the half-open interval [``low``, ``high``). The parameters define the trainable angles entering the particle-conserving exchange gates :math:`U_{1,\mathrm{ex}}(\phi, \theta)` implemented by the :func:`~.u1_ex_gate()`. - Args: n_layers (int): number of layers n_wires (int): number of qubits low (float): lower endpoint of the parameter interval high (float): upper endpoint of the parameter interval seed (int): seed used in sampling the parameters, makes function call deterministic - Returns: array: parameter array """ @@ -55,19 +110,16 @@ def particle_conserving_u1_uniform(n_layers, n_wires, low=0, high=2 * pi, seed=N def particle_conserving_u1_normal(n_layers, n_wires, mean=0, std=0.1, seed=None): r"""Creates a parameter array for :func:`~.ParticleConservingU1`, drawn from a normal distribution. - Each parameter is drawn from a normal distribution with ``mean`` and standard deviation ``std``. The parameters define the trainable angles entering the particle-conserving exchange gates :math:`U_{1,\mathrm{ex}}(\phi, \theta)` implemented by the :func:`~.u1_ex_gate()`. - Args: n_layers (int): number of layers n_wires (int): number of qubits mean (float): mean of parameters std (float): standard deviation of parameters seed (int): seed used in sampling the parameters, makes function call deterministic - Returns: array: parameter array """ diff --git a/pennylane/templates/layers/__init__.py b/pennylane/templates/layers/__init__.py index 44ca27e9b16..8295205ff4a 100644 --- a/pennylane/templates/layers/__init__.py +++ b/pennylane/templates/layers/__init__.py @@ -21,4 +21,5 @@ from .cv_neural_net import CVNeuralNetLayers from .simplified_two_design import SimplifiedTwoDesign from .basic_entangler import BasicEntanglerLayers +from .particle_conserving_u2 import ParticleConservingU2 from .particle_conserving_u1 import ParticleConservingU1 diff --git a/pennylane/templates/layers/particle_conserving_u2.py b/pennylane/templates/layers/particle_conserving_u2.py new file mode 100644 index 00000000000..eb24bb182d8 --- /dev/null +++ b/pennylane/templates/layers/particle_conserving_u2.py @@ -0,0 +1,179 @@ +# Copyright 2018-2020 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. +r""" +Contains the hardware efficient ``ParticleConservingU2`` template. +""" +import pennylane as qml + +# pylint: disable-msg=too-many-branches,too-many-arguments,protected-access +from pennylane.templates.decorator import template +from pennylane.ops import CNOT, CRX, RZ +from pennylane.templates.utils import ( + check_shape, + get_shape, +) +from pennylane.wires import Wires + + +def u2_ex_gate(phi, wires=None): + r"""Implements the two-qubit exchange gate :math:`U_{2,\mathrm{ex}}` proposed in + `arXiv:1805.04340 `_ to build particle-conserving VQE ansatze + for Quantum Chemistry simulations. + + The unitary matrix :math:`U_{2, \mathrm{ex}}` acts on the Hilbert space of two qubits + + .. math:: + + U_{2, \mathrm{ex}}(\phi) = \left(\begin{array}{cccc} + 1 & 0 & 0 & 0 \\ + 0 & \mathrm{cos}(\phi) & -i\;\mathrm{sin}(\phi) & 0 \\ + 0 & -i\;\mathrm{sin}(\phi) & \mathrm{cos}(\phi) & 0 \\ + 0 & 0 & 0 & 1 \\ + \end{array}\right). + + Args: + phi (float): angle entering the controlled-RX operator :math:`CRX(2\phi)` + wires (list[Wires]): the two wires ``n`` and ``m`` the circuit acts on + """ + + CNOT(wires=wires) + CRX(2 * phi, wires=wires[::-1]) + CNOT(wires=wires) + + +@template +def ParticleConservingU2(weights, wires, init_state=None): + r"""Implements the heuristic VQE ansatz for Quantum Chemistry simulations using the + particle-conserving entangler :math:`U_\mathrm{ent}(\vec{\theta}, \vec{\phi})` proposed in + `arXiv:1805.04340 `_. + + This template prepares :math:`N`-qubit trial states by applying :math:`D` layers of the entangler + block :math:`U_\mathrm{ent}(\vec{\theta}, \vec{\phi})` to the Hartree-Fock state + + .. math:: + + \vert \Psi(\vec{\theta}, \vec{\phi}) \rangle = \hat{U}^{(D)}_\mathrm{ent}(\vec{\theta}_D, + \vec{\phi}_D) \dots \hat{U}^{(2)}_\mathrm{ent}(\vec{\theta}_2, \vec{\phi}_2) + \hat{U}^{(1)}_\mathrm{ent}(\vec{\theta}_1, \vec{\phi}_1) \vert \mathrm{HF}\rangle, + + where :math:`\hat{U}^{(i)}_\mathrm{ent}(\vec{\theta}_i, \vec{\phi}_i) = + \hat{R}_\mathrm{z}(\vec{\theta}_i) \hat{U}_\mathrm{2,\mathrm{ex}}(\vec{\phi}_i)`. + The circuit implementing the entangler blocks is shown in the figure below: + + | + + .. figure:: ../../_static/templates/layers/particle_conserving_u2.png + :align: center + :width: 60% + :target: javascript:void(0); + + | + + Each layer contains :math:`N` rotation gates :math:`R_\mathrm{z}(\vec{\theta})` and + :math:`N-1` particle-conserving exchange gates :math:`U_{2,\mathrm{ex}}(\phi)` + that act on pairs of nearest-neighbors qubits. The repeated units across several qubits are + shown in dotted boxes. The unitary matrix representing :math:`U_{2,\mathrm{ex}}(\phi)` + (`arXiv:1805.04340 `_) is decomposed into its elementary + gates and implemented in the :func:`~.u2_ex_gate` function using PennyLane quantum operations. + + | + + .. figure:: ../../_static/templates/layers/u2_decomposition.png + :align: center + :width: 60% + :target: javascript:void(0); + + | + + + Args: + weights (array[float]): Array of weights of shape ``(D, M)`` where ``D`` is the number of + layers and ``M`` = ``2N-1`` is the total number of rotation ``(N)`` and exchange + ``(N-1)`` gates per layer. + wires (Iterable or Wires): Wires that the template acts on. Accepts an iterable of numbers + or strings, or a Wires object. + init_state (array[int]): length ``len(wires)`` vector representing the Hartree-Fock state + used to initialize the wires. + + Raises: + ValueError: if inputs do not have the correct format + + .. UsageDetails:: + + + #. The number of wires has to be equal to the number of spin orbitals included in + the active space. + + #. The number of trainable parameters scales with the number of layers :math:`D` as + :math:`D(2N-1)`. + + An example of how to use this template is shown below: + + .. code-block:: python + + import pennylane as qml + from pennylane.templates import ParticleConservingU2 + + from functools import partial + + # Build the electronic Hamiltonian from a local .xyz file + h, qubits = qml.qchem.molecular_hamiltonian("h2", "h2.xyz") + + # Define the HF state + ref_state = qml.qchem.hf_state(2, qubits) + + # Define the device + dev = qml.device('default.qubit', wires=qubits) + + # Define the ansatz + ansatz = partial(ParticleConservingU2, init_state=ref_state) + + # Define the cost function + cost_fn = qml.VQECost(ansatz, h, dev) + + # Compute the expectation value of 'h' for a given set of parameters + layers = 1 + params = qml.init.particle_conserving_u2_normal(layers, qubits) + print(cost_fn(params)) + """ + + wires = Wires(wires) + + layers = weights.shape[0] + + if len(wires) < 2: + raise ValueError( + "This template requires the number of qubits to be greater than one;" + "got a wire sequence with {} elements".format(len(wires)) + ) + + expected_shape = (layers, 2 * len(wires) - 1) + check_shape( + weights, + expected_shape, + msg="'weights' must be of shape {}; got {}".format(expected_shape, get_shape(weights)), + ) + + nm_wires = [wires.subset([l, l + 1]) for l in range(0, len(wires) - 1, 2)] + nm_wires += [wires.subset([l, l + 1]) for l in range(1, len(wires) - 1, 2)] + + qml.BasisState(init_state, wires=wires) + + for l in range(layers): + + for j, _ in enumerate(wires): + RZ(weights[l, j], wires=wires[j]) + + for i, wires_ in enumerate(nm_wires): + u2_ex_gate(weights[l, len(wires) + i], wires=wires_) diff --git a/tests/templates/test_integration.py b/tests/templates/test_integration.py index cfd11768597..a2868d46a2e 100644 --- a/tests/templates/test_integration.py +++ b/tests/templates/test_integration.py @@ -148,6 +148,10 @@ {'wires': [0, 1, 2, 3], 's_wires': [[0, 1, 2], [1, 2, 3]], 'd_wires': [[[0, 1], [2, 3]]], 'init_state':np.array([1, 1, 0, 0])}, 4), + (qml.templates.ParticleConservingU2, + {'weights': np.array([[0.35172862, 0.60808317, 1.44397231]])}, + {'wires': [0, 1], 'init_state': np.array([1, 0])}, + 2), (qml.templates.ParticleConservingU1, {'weights': np.array([[[ 0.17586701, -0.20382066]]])}, {'wires': [0, 1], 'init_state':np.array([1, 0])}, @@ -188,7 +192,7 @@ # before they are called in a quantum function. # These templates will be skipped in tests of that nature. -NO_OP_BEFORE = ["AmplitudeEmbedding", "UCCSD", "ParticleConservingU1"] +NO_OP_BEFORE = ["AmplitudeEmbedding", "UCCSD", "ParticleConservingU2", "ParticleConservingU1"] # Each entry to QUBIT_INIT and CV_INIT adds a template with specified inputs to the # integration tests ``TestIntegrationInitFunctions`` diff --git a/tests/templates/test_layers.py b/tests/templates/test_layers.py index 3f8bcc59d3d..e915d61c5b4 100644 --- a/tests/templates/test_layers.py +++ b/tests/templates/test_layers.py @@ -26,6 +26,7 @@ RandomLayers, BasicEntanglerLayers, SimplifiedTwoDesign, + ParticleConservingU2, ParticleConservingU1, ) from pennylane.templates.layers.random import random_layer @@ -707,6 +708,145 @@ def circuit(weights): assert exp == target_exp +class TestParticleConservingU2: + """Tests for the ParticleConservingU2 template from the pennylane.templates.layers module.""" + + @pytest.mark.parametrize( + "layers, qubits, init_state", + [ + (2, 4, np.array([1, 1, 0, 0])), + (1, 6, np.array([1, 1, 0, 0, 0, 0])), + (1, 5, np.array([1, 1, 0, 0, 0])), + ], + ) + def test_u2_operations(self, layers, qubits, init_state): + """Test the correctness of the ParticleConservingU2 template including the gate count + and order, the wires each operation acts on and the correct use of parameters + in the circuit.""" + weights = np.random.normal(0, 2 * np.pi, (layers, 2 * qubits - 1)) + + n_gates = 1 + (qubits + (qubits - 1) * 3) * layers + + exp_gates = ( + [qml.RZ] * qubits + ([qml.CNOT] + [qml.CRX] + [qml.CNOT]) * (qubits - 1) + ) * layers + + with pennylane._queuing.OperationRecorder() as rec: + ParticleConservingU2(weights, wires=range(qubits), init_state=init_state) + + # number of gates + assert len(rec.queue) == n_gates + + # initialization + assert isinstance(rec.queue[0], qml.BasisState) + + # order of gates + for op1, op2 in zip(rec.queue[1:], exp_gates): + assert isinstance(op1, op2) + + # gate parameter + params = np.array( + [ + rec.queue[i].parameters + for i in range(1, n_gates) + if rec.queue[i].parameters != [] + ] + ) + weights[:, qubits:] = weights[:, qubits:] * 2 + assert np.allclose(params.flatten(), weights.flatten()) + + # gate wires + wires = Wires(range(qubits)) + nm_wires = [wires.subset([l, l + 1]) for l in range(0, qubits - 1, 2)] + nm_wires += [wires.subset([l, l + 1]) for l in range(1, qubits - 1, 2)] + + exp_wires = [] + for _ in range(layers): + for i in range(qubits): + exp_wires.append(wires.subset([i])) + for j in nm_wires: + exp_wires.append(j) + exp_wires.append(j[::-1]) + exp_wires.append(j) + + res_wires = [rec.queue[i]._wires for i in range(1, n_gates)] + + assert res_wires == exp_wires + + @pytest.mark.parametrize( + ("weights", "wires", "msg_match"), + [ + ( + np.array([[-0.080, 2.629, -0.710, 5.383, 0.646, -2.872, -3.856]]), + [0], + "This template requires the number of qubits to be greater than one", + ), + ( + np.array([[-0.080, 2.629, -0.710, 5.383]]), + [0, 1, 2, 3], + "'weights' must be of shape", + ), + ( + np.array( + [ + [-0.080, 2.629, -0.710, 5.383, 0.646, -2.872], + [-0.080, 2.629, -0.710, 5.383, 0.646, -2.872], + ] + ), + [0, 1, 2, 3], + "'weights' must be of shape", + ), + ], + ) + def test_u2_exceptions(self, weights, wires, msg_match): + """Test that ParticleConservingU2 throws an exception if the parameters have illegal + shapes, types or values.""" + N = len(wires) + init_state = np.array([1, 1, 0, 0]) + + dev = qml.device("default.qubit", wires=N) + + @qml.qnode(dev) + def circuit(): + ParticleConservingU2( + weights=weights, + wires=wires, + init_state=init_state, + ) + return qml.expval(qml.PauliZ(0)) + + with pytest.raises(ValueError, match=msg_match): + circuit() + + @pytest.mark.parametrize( + ("weights", "wires", "expected"), + [ + ( + np.array([[-2.712, -1.958, 1.875, 1.811, 0.296, -0.412, 1.723]]), + [0, 1, 2, 3], + [-1.0, 0.95402475, -0.95402475, 1.0], + ) + ], + ) + def test_u2_integration(self, weights, wires, expected): + """Test integration with PennyLane and gradient calculations""" + + N = len(wires) + dev = qml.device("default.qubit", wires=N) + + @qml.qnode(dev) + def circuit(): + ParticleConservingU2( + weights, + wires, + init_state=np.array([1, 1, 0, 0]), + ) + return [qml.expval(qml.PauliZ(w)) for w in range(N)] + + res = circuit() + assert np.allclose(res, np.array(expected)) + + class TestParticleConservingU1: """Tests for the ParticleConservingU1 template from the pennylane.templates.layers module.""" @@ -868,6 +1008,45 @@ def circuit(weights): exp = np.array([-0.99993177, -0.9853332, 0.98531251, 0.99995246]) assert np.allclose(res, np.array(exp), atol=tol) + + @pytest.mark.parametrize( + ("init_state", "exp_state"), + [ + (np.array([0, 0]), np.array([1.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j])), + ( + np.array([0, 1]), + np.array([0.0 + 0.0j, 0.862093 + 0.0j, 0.0 - 0.506749j, 0.0 + 0.0j]), + ), + ( + np.array([1, 0]), + np.array([0.0 + 0.0j, 0.0 - 0.506749j, 0.862093 + 0.0j, 0.0 + 0.0j]), + ), + (np.array([1, 1]), np.array([0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 1.0 + 0.0j])), + ], + ) + def test_decomposition_u2ex(self, init_state, exp_state, tol): + """Test the decomposition of the U_{2, ex}` exchange gate by asserting the prepared + state.""" + + N = 2 + wires = range(N) + wires = Wires(wires) + + weight = 0.53141 + + dev = qml.device("default.qubit", wires=N) + + @qml.qnode(dev) + def circuit(weight): + qml.BasisState(init_state, wires=wires) + qml.templates.layers.particle_conserving_u2.u2_ex_gate(weight, wires) + return qml.expval(qml.PauliZ(0)) + + circuit(weight) + + assert np.allclose(dev.state, exp_state, atol=tol) + + @pytest.mark.parametrize( ("init_state", "exp_state"), [ diff --git a/tests/test_init.py b/tests/test_init.py index 2bdd380067a..935329af6d0 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -352,6 +352,54 @@ def test_zero_wires(self, init, sgntr): assert p.flatten().shape == (0,) + def test_particle_conserving_u2_init(self, tol): + """Test the functions 'particle_conserving_u2_uniform' and + 'particle_conserving_u2_normal'.""" + + n_layers = 2 + n_wires = 4 + + # check the shape + exp_shape = (n_layers, 2 * n_wires - 1) + params = qml.init.particle_conserving_u2_uniform(n_layers, n_wires) + assert params.shape == exp_shape + + params = qml.init.particle_conserving_u2_normal(n_layers, n_wires) + assert params.shape == exp_shape + + # check deterministic output for a fixed seed + seed = 1975 + p1 = qml.init.particle_conserving_u2_uniform(n_layers, n_wires, seed=seed) + p2 = qml.init.particle_conserving_u2_uniform(n_layers, n_wires, seed=seed) + assert np.allclose(p1, p2, atol=tol) + + p1 = qml.init.particle_conserving_u2_normal(n_layers, n_wires, seed=seed) + p2 = qml.init.particle_conserving_u2_normal(n_layers, n_wires, seed=seed) + assert np.allclose(p1, p2, atol=tol) + + # check that the output is different for different seeds + p1 = qml.init.particle_conserving_u2_uniform(n_layers, n_wires, seed=seed) + p2 = qml.init.particle_conserving_u2_uniform(n_layers, n_wires, seed=seed+1) + assert not np.allclose(p1, p2, atol=tol) + + p1 = qml.init.particle_conserving_u2_normal(n_layers, n_wires, seed=seed) + p2 = qml.init.particle_conserving_u2_normal(n_layers, n_wires, seed=seed+1) + assert not np.allclose(p1, p2, atol=tol) + + def test_particle_conserving_u2_init_exceptions(self): + """Test exceptions the functions 'particle_conserving_u2_uniform' and + 'particle_conserving_u2_normal'.""" + + n_layers = 4 + n_wires = 1 + + msg_match = "The number of qubits must be greater than one" + + with pytest.raises(ValueError, match=msg_match): + qml.init.particle_conserving_u2_uniform(n_layers, n_wires) + + with pytest.raises(ValueError, match=msg_match): + qml.init.particle_conserving_u2_normal(n_layers, n_wires) def test_particle_conserving_u1_init(self, tol): """Test the functions 'particle_conserving_u1_uniform' and @@ -380,14 +428,13 @@ def test_particle_conserving_u1_init(self, tol): # check that the output is different for different seeds p1 = qml.init.particle_conserving_u1_uniform(n_layers, n_wires, seed=seed) - p2 = qml.init.particle_conserving_u1_uniform(n_layers, n_wires, seed=seed+1) + p2 = qml.init.particle_conserving_u1_uniform(n_layers, n_wires, seed=seed + 1) assert not np.allclose(p1, p2, atol=tol) p1 = qml.init.particle_conserving_u1_normal(n_layers, n_wires, seed=seed) - p2 = qml.init.particle_conserving_u1_normal(n_layers, n_wires, seed=seed+1) + p2 = qml.init.particle_conserving_u1_normal(n_layers, n_wires, seed=seed + 1) assert not np.allclose(p1, p2, atol=tol) - def test_particle_conserving_u1_init_exceptions(self): """Test exceptions the functions 'particle_conserving_u1_uniform' and 'particle_conserving_u1_normal'."""