diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 0349e7a757f..4630e22253b 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -54,6 +54,11 @@

Improvements

+* The qchem openfermion-dependent tests are localized and collected in `tests.qchem.of_tests`. The + new module `test_structure` is created to collect the tests of the `qchem.structure` module in + one place and remove their dependency to openfermion. + [(#2593)](https://github.com/PennyLaneAI/pennylane/pull/2593) + * The developer-facing `pow` method has been added to `Operator` with concrete implementations for many classes. [(#2225)](https://github.com/PennyLaneAI/pennylane/pull/2225) diff --git a/tests/qchem/hf_tests/conftest.py b/tests/qchem/hf_tests/conftest.py deleted file mode 100644 index 8d840b15b42..00000000000 --- a/tests/qchem/hf_tests/conftest.py +++ /dev/null @@ -1,24 +0,0 @@ -import pennylane as qml -import pytest - - -@pytest.fixture(scope="session") -def tol(): - """Numerical tolerance for equality tests.""" - return {"rtol": 0, "atol": 1e-8} - - -@pytest.fixture( - scope="module", - params=[ - None, - qml.wires.Wires( - list("ab") + [-3, 42] + ["xyz", "23", "wireX"] + [f"w{i}" for i in range(20)] - ), - list(range(100, 120)), - {13 - i: "abcdefghijklmn"[i] for i in range(14)}, - ], -) -def custom_wires(request): - """Custom wire mapping for Pennylane<->OpenFermion conversion""" - return request.param diff --git a/tests/qchem/of_tests/test_active_space.py b/tests/qchem/of_tests/test_active_space.py deleted file mode 100644 index 469a0ad5cd8..00000000000 --- a/tests/qchem/of_tests/test_active_space.py +++ /dev/null @@ -1,71 +0,0 @@ -import os -import sys - -import pytest - -from pennylane import qchem - -# TODO: Bring pytest skip to relevant tests. -openfermion = pytest.importorskip("openfermion") -openfermionpyscf = pytest.importorskip("openfermionpyscf") - -ref_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_ref_files") - - -@pytest.mark.parametrize( - ("mol_name", "act_electrons", "act_orbitals", "core_ref", "active_ref"), - [ - ("lih", None, None, [], list(range(6))), - ("lih", 4, None, [], list(range(6))), - ("lih", 2, None, [0], list(range(1, 6))), - ("lih", None, 4, [], list(range(4))), - ("lih", 2, 3, [0], list(range(1, 4))), - ("lih_anion", 3, 4, [0], list(range(1, 5))), - ("lih_anion", 1, 4, [0, 1], list(range(2, 6))), - ], -) -def test_active_spaces(mol_name, act_electrons, act_orbitals, core_ref, active_ref): - r"""Test the correctness of the generated active spaces""" - - molecule = openfermion.MolecularData(filename=os.path.join(ref_dir, mol_name)) - - core, active = qchem.active_space( - molecule.n_electrons, - molecule.n_orbitals, - mult=molecule.multiplicity, - active_electrons=act_electrons, - active_orbitals=act_orbitals, - ) - - assert core == core_ref - assert active == active_ref - - -@pytest.mark.parametrize( - ("mol_name", "act_electrons", "act_orbitals", "message_match"), - [ - ("lih", 6, 5, "greater than the total number of electrons"), - ("lih", 1, 5, "should be even"), - ("lih", -1, 5, "has to be greater than 0."), - ("lih", 2, 6, "greater than the total number of orbitals"), - ("lih", 2, 1, "there are no virtual orbitals"), - ("lih_anion", 2, 5, "should be odd"), - ("lih_anion", 3, -2, "has to be greater than 0."), - ("lih_anion", 3, 6, "greater than the total number of orbitals"), - ("lih_anion", 3, 2, "there are no virtual orbitals"), - ("lih_anion_2", 1, 2, "greater than or equal to"), - ], -) -def test_inconsistent_active_spaces(mol_name, act_electrons, act_orbitals, message_match): - r"""Test that an error is raised if an inconsistent active space is generated""" - - molecule = openfermion.MolecularData(filename=os.path.join(ref_dir, mol_name)) - - with pytest.raises(ValueError, match=message_match): - qchem.active_space( - molecule.n_electrons, - molecule.n_orbitals, - mult=molecule.multiplicity, - active_electrons=act_electrons, - active_orbitals=act_orbitals, - ) diff --git a/tests/qchem/of_tests/test_excitations.py b/tests/qchem/of_tests/test_excitations.py deleted file mode 100644 index 6d171405519..00000000000 --- a/tests/qchem/of_tests/test_excitations.py +++ /dev/null @@ -1,65 +0,0 @@ -import pytest - -from pennylane import qchem - - -@pytest.mark.parametrize( - ( - "electrons", - "orbitals", - "delta_sz", - "n_singles", - "n_doubles", - "singles_exp", - "doubles_exp", - ), - [ - (1, 5, 0, 2, 0, [[0, 2], [0, 4]], []), - (1, 5, 1, 0, 0, [], []), - (1, 5, -1, 2, 0, [[0, 1], [0, 3]], []), - (2, 5, 0, 3, 2, [[0, 2], [0, 4], [1, 3]], [[0, 1, 2, 3], [0, 1, 3, 4]]), - (2, 5, 1, 2, 1, [[1, 2], [1, 4]], [[0, 1, 2, 4]]), - (2, 5, -1, 1, 0, [[0, 3]], []), - (2, 5, 2, 0, 0, [], []), - (3, 6, 1, 1, 0, [[1, 4]], []), - ( - 3, - 6, - -1, - 4, - 4, - [[0, 3], [0, 5], [2, 3], [2, 5]], - [[0, 1, 3, 5], [0, 2, 3, 4], [0, 2, 4, 5], [1, 2, 3, 5]], - ), - (3, 6, -2, 0, 1, [], [[0, 2, 3, 5]]), - (3, 4, 0, 1, 0, [[1, 3]], []), - (3, 4, 1, 0, 0, [], []), - (3, 4, -1, 2, 0, [[0, 3], [2, 3]], []), - (3, 4, 2, 0, 0, [], []), - ], -) -def test_excitations(electrons, orbitals, delta_sz, n_singles, n_doubles, singles_exp, doubles_exp): - r"""Test the correctness of the generated configurations""" - - singles, doubles = qchem.excitations(electrons, orbitals, delta_sz) - - assert len(singles) == len(singles_exp) - assert len(doubles) == len(doubles_exp) - assert singles == singles_exp - assert doubles == doubles_exp - - -@pytest.mark.parametrize( - ("electrons", "orbitals", "delta_sz", "message_match"), - [ - (0, 4, 0, "number of active electrons has to be greater than 0"), - (3, 2, 0, "has to be greater than the number of active electrons"), - (2, 4, 3, "Expected values for 'delta_sz'"), - (2, 4, 1.5, "Expected values for 'delta_sz'"), - ], -) -def test_inconsistent_excitations(electrons, orbitals, delta_sz, message_match): - r"""Test that an error is raised if a set of inconsistent arguments is input""" - - with pytest.raises(ValueError, match=message_match): - qchem.excitations(electrons, orbitals, delta_sz) diff --git a/tests/qchem/of_tests/test_excitations_to_wires.py b/tests/qchem/of_tests/test_excitations_to_wires.py deleted file mode 100644 index c1fe766fc93..00000000000 --- a/tests/qchem/of_tests/test_excitations_to_wires.py +++ /dev/null @@ -1,94 +0,0 @@ -import numpy as np -import pytest - -import pennylane as qml -from pennylane import qchem -from pennylane.templates.subroutines import UCCSD - - -@pytest.mark.parametrize( - ("singles", "doubles", "wires", "singles_wires_exp", "doubles_wires_exp"), - [ - ([[0, 2]], [], None, [[0, 1, 2]], []), - ([], [[0, 1, 2, 3]], None, [], [[[0, 1], [2, 3]]]), - ([[0, 1]], [[0, 1, 2, 4]], None, [[0, 1]], [[[0, 1], [2, 3, 4]]]), - ( - [[0, 1], [2, 4]], - [[0, 1, 2, 3], [0, 2, 4, 6]], - None, - [[0, 1], [2, 3, 4]], - [[[0, 1], [2, 3]], [[0, 1, 2], [4, 5, 6]]], - ), - ( - [[0, 1], [2, 4]], - [[0, 1, 2, 3], [0, 2, 4, 6]], - ["a0", "b1", "c2", "d3", "e4", "f5", "g6"], - [["a0", "b1"], ["c2", "d3", "e4"]], - [[["a0", "b1"], ["c2", "d3"]], [["a0", "b1", "c2"], ["e4", "f5", "g6"]]], - ), - ], -) -def test_mapping_from_excitations_to_wires( - singles, doubles, wires, singles_wires_exp, doubles_wires_exp -): - r"""Test the correctness of the mapping between indices of the single and double - excitations and the list of wires to be passed to the quantum circuit""" - - singles_wires, doubles_wires = qchem.excitations_to_wires(singles, doubles, wires=wires) - - assert len(singles_wires) == len(singles_wires_exp) - assert len(doubles_wires) == len(doubles_wires_exp) - assert singles_wires == singles_wires_exp - assert doubles_wires == doubles_wires_exp - - -@pytest.mark.parametrize( - ("singles", "doubles", "wires", "message_match"), - [ - ([], [], None, "'singles' and 'doubles' lists can not be both empty"), - ([[0, 2, 3]], [], None, "Expected entries of 'singles' to be of shape"), - ([[0, 2], [3]], [], None, "Expected entries of 'singles' to be of shape"), - ([], [[0, 1, 2, 3], [1, 3]], None, "Expected entries of 'doubles' to be of shape"), - ([], [[0, 1, 2, 3], [1, 3, 4, 5, 6]], None, "Expected entries of 'doubles' to be of shape"), - ( - [[0, 2]], - [[0, 1, 2, 3], [0, 2, 4, 6]], - ["a0", "b1", "c2", "d3", "e4", "f5"], - "Expected number of wires is", - ), - ], -) -def test_excitations_to_wires_exceptions(singles, doubles, wires, message_match): - r"""Test that the function 'excitations_to_wires()' throws an exception if ``singles``, - ``doubles`` or ``wires`` parameter has illegal shapes or size""" - - with pytest.raises(ValueError, match=message_match): - qchem.excitations_to_wires(singles, doubles, wires=wires) - - -@pytest.mark.parametrize( - ("weights", "singles", "doubles", "expected"), - [ - ( - np.array([3.90575761, -1.89772083, -1.36689032]), - [[0, 2], [1, 3]], - [[0, 1, 2, 3]], - [-0.14619406, -0.06502792, 0.14619406, 0.06502792], - ) - ], -) -def test_integration_with_uccsd(weights, singles, doubles, expected, tol): - """Test integration with the UCCSD template""" - - s_wires, d_wires = qchem.excitations_to_wires(singles, doubles) - N = 4 - wires = range(N) - dev = qml.device("default.qubit", wires=N) - - @qml.qnode(dev) - def circuit(weights): - UCCSD(weights, wires, s_wires=s_wires, d_wires=d_wires, init_state=np.array([1, 1, 0, 0])) - return [qml.expval(qml.PauliZ(w)) for w in range(N)] - - res = circuit(weights) - assert np.allclose(res, np.array(expected), **tol) diff --git a/tests/qchem/of_tests/test_hf_state.py b/tests/qchem/of_tests/test_hf_state.py deleted file mode 100644 index a94e1f45b2b..00000000000 --- a/tests/qchem/of_tests/test_hf_state.py +++ /dev/null @@ -1,36 +0,0 @@ -import numpy as np -import pytest - -from pennylane import qchem - - -@pytest.mark.parametrize( - ("electrons", "orbitals", "exp_state"), - [ - (2, 5, np.array([1, 1, 0, 0, 0])), - (1, 5, np.array([1, 0, 0, 0, 0])), - (5, 5, np.array([1, 1, 1, 1, 1])), - ], -) -def test_hf_state(electrons, orbitals, exp_state): - r"""Test the correctness of the generated occupation-number vector""" - - res_state = qchem.hf_state(electrons, orbitals) - - assert len(res_state) == len(exp_state) - assert np.allclose(res_state, exp_state) - - -@pytest.mark.parametrize( - ("electrons", "orbitals", "msg_match"), - [ - (0, 5, "number of active electrons has to be larger than zero"), - (-1, 5, "number of active electrons has to be larger than zero"), - (6, 5, "number of active orbitals cannot be smaller than the number of active"), - ], -) -def test_inconsistent_input(electrons, orbitals, msg_match): - r"""Test that an error is raised if a set of inconsistent arguments is input""" - - with pytest.raises(ValueError, match=msg_match): - qchem.hf_state(electrons, orbitals) diff --git a/tests/qchem/of_tests/test_molecular_hamiltonian.py b/tests/qchem/of_tests/test_molecular_hamiltonian.py index 2023c53c5a9..86d6c396f44 100644 --- a/tests/qchem/of_tests/test_molecular_hamiltonian.py +++ b/tests/qchem/of_tests/test_molecular_hamiltonian.py @@ -226,7 +226,7 @@ def test_mol_hamiltonian_with_read_structure(tmpdir): with open(filename, "w") as f: f.write(file_content) - symbols, coordinates = qchem.read_structure(str(filename)) + symbols, coordinates = qchem.read_structure(str(filename), outpath=tmpdir) H, num_qubits = qchem.molecular_hamiltonian(symbols, coordinates) assert len(H.terms()) == 2 assert num_qubits == 4 diff --git a/tests/qchem/of_tests/test_read_structure.py b/tests/qchem/of_tests/test_read_structure.py deleted file mode 100644 index 7ebd7e07f92..00000000000 --- a/tests/qchem/of_tests/test_read_structure.py +++ /dev/null @@ -1,46 +0,0 @@ -import os - -import numpy as np - -from pennylane import qchem - -ref_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_ref_files") - - -def test_reading_xyz_file(tmpdir): - r"""Test reading of the generated file 'structure.xyz'""" - - ref_symbols = ["C", "C", "N", "H", "H", "H", "H", "H"] - ref_coords = np.array( - [ - 0.68219113, - -0.85415621, - -1.04123909, - -1.34926445, - 0.23621577, - 0.61794044, - 1.29068294, - 0.25133357, - 1.40784596, - 0.83525895, - -2.88939124, - -1.16974047, - 1.26989596, - 0.19275206, - -2.69852891, - -2.57758643, - -1.05824663, - 1.61949529, - -2.17129532, - 2.04090421, - 0.11338357, - 2.06547065, - 2.00877887, - 1.20186582, - ] - ) - name = os.path.join(ref_dir, "gdb3.mol5.XYZ") - symbols, coordinates = qchem.read_structure(name, outpath=tmpdir) - - assert symbols == ref_symbols - assert np.allclose(coordinates, ref_coords) diff --git a/tests/qchem/hf_tests/test_basis_set.py b/tests/qchem/test_basis_set.py similarity index 100% rename from tests/qchem/hf_tests/test_basis_set.py rename to tests/qchem/test_basis_set.py diff --git a/tests/qchem/hf_tests/test_dipole.py b/tests/qchem/test_dipole.py similarity index 100% rename from tests/qchem/hf_tests/test_dipole.py rename to tests/qchem/test_dipole.py diff --git a/tests/qchem/hf_tests/test_hamiltonians.py b/tests/qchem/test_hamiltonians.py similarity index 100% rename from tests/qchem/hf_tests/test_hamiltonians.py rename to tests/qchem/test_hamiltonians.py diff --git a/tests/qchem/hf_tests/test_hartree_fock.py b/tests/qchem/test_hartree_fock.py similarity index 100% rename from tests/qchem/hf_tests/test_hartree_fock.py rename to tests/qchem/test_hartree_fock.py diff --git a/tests/qchem/hf_tests/test_integrals.py b/tests/qchem/test_integrals.py similarity index 100% rename from tests/qchem/hf_tests/test_integrals.py rename to tests/qchem/test_integrals.py diff --git a/tests/qchem/hf_tests/test_matrices.py b/tests/qchem/test_matrices.py similarity index 100% rename from tests/qchem/hf_tests/test_matrices.py rename to tests/qchem/test_matrices.py diff --git a/tests/qchem/hf_tests/test_molecule.py b/tests/qchem/test_molecule.py similarity index 100% rename from tests/qchem/hf_tests/test_molecule.py rename to tests/qchem/test_molecule.py diff --git a/tests/qchem/hf_tests/test_observable_hf.py b/tests/qchem/test_observable_hf.py similarity index 100% rename from tests/qchem/hf_tests/test_observable_hf.py rename to tests/qchem/test_observable_hf.py diff --git a/tests/qchem/hf_tests/test_particle_number.py b/tests/qchem/test_particle_number.py similarity index 100% rename from tests/qchem/hf_tests/test_particle_number.py rename to tests/qchem/test_particle_number.py diff --git a/tests/qchem/of_tests/test_ref_files/gdb3.mol5.XYZ b/tests/qchem/test_ref_files/gdb3.mol5.XYZ similarity index 100% rename from tests/qchem/of_tests/test_ref_files/gdb3.mol5.XYZ rename to tests/qchem/test_ref_files/gdb3.mol5.XYZ diff --git a/tests/qchem/hf_tests/test_spin.py b/tests/qchem/test_spin.py similarity index 100% rename from tests/qchem/hf_tests/test_spin.py rename to tests/qchem/test_spin.py diff --git a/tests/qchem/test_structure.py b/tests/qchem/test_structure.py new file mode 100644 index 00000000000..2460ef554aa --- /dev/null +++ b/tests/qchem/test_structure.py @@ -0,0 +1,313 @@ +# Copyright 2018-2022 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Unit tests for the functions of the structure module. +""" +import os +import pytest +import pennylane as qml +from pennylane import qchem +from pennylane import numpy as np +from pennylane.templates.subroutines import UCCSD + + +ref_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_ref_files") + + +def test_reading_xyz_file(tmpdir): + r"""Test reading of the generated file 'structure.xyz'""" + + ref_symbols = ["C", "C", "N", "H", "H", "H", "H", "H"] + ref_coords = np.array( + [ + 0.68219113, + -0.85415621, + -1.04123909, + -1.34926445, + 0.23621577, + 0.61794044, + 1.29068294, + 0.25133357, + 1.40784596, + 0.83525895, + -2.88939124, + -1.16974047, + 1.26989596, + 0.19275206, + -2.69852891, + -2.57758643, + -1.05824663, + 1.61949529, + -2.17129532, + 2.04090421, + 0.11338357, + 2.06547065, + 2.00877887, + 1.20186582, + ] + ) + name = os.path.join(ref_dir, "gdb3.mol5.XYZ") + symbols, coordinates = qchem.read_structure(name, outpath=tmpdir) + + assert symbols == ref_symbols + assert np.allclose(coordinates, ref_coords) + + +@pytest.mark.parametrize( + ( + "electrons", + "orbitals", + "delta_sz", + "n_singles", + "n_doubles", + "singles_exp", + "doubles_exp", + ), + [ + (1, 5, 0, 2, 0, [[0, 2], [0, 4]], []), + (1, 5, 1, 0, 0, [], []), + (1, 5, -1, 2, 0, [[0, 1], [0, 3]], []), + (2, 5, 0, 3, 2, [[0, 2], [0, 4], [1, 3]], [[0, 1, 2, 3], [0, 1, 3, 4]]), + (2, 5, 1, 2, 1, [[1, 2], [1, 4]], [[0, 1, 2, 4]]), + (2, 5, -1, 1, 0, [[0, 3]], []), + (2, 5, 2, 0, 0, [], []), + (3, 6, 1, 1, 0, [[1, 4]], []), + ( + 3, + 6, + -1, + 4, + 4, + [[0, 3], [0, 5], [2, 3], [2, 5]], + [[0, 1, 3, 5], [0, 2, 3, 4], [0, 2, 4, 5], [1, 2, 3, 5]], + ), + (3, 6, -2, 0, 1, [], [[0, 2, 3, 5]]), + (3, 4, 0, 1, 0, [[1, 3]], []), + (3, 4, 1, 0, 0, [], []), + (3, 4, -1, 2, 0, [[0, 3], [2, 3]], []), + (3, 4, 2, 0, 0, [], []), + ], +) +def test_excitations(electrons, orbitals, delta_sz, n_singles, n_doubles, singles_exp, doubles_exp): + r"""Test the correctness of the generated configurations""" + + singles, doubles = qchem.excitations(electrons, orbitals, delta_sz) + + assert len(singles) == len(singles_exp) + assert len(doubles) == len(doubles_exp) + assert singles == singles_exp + assert doubles == doubles_exp + + +@pytest.mark.parametrize( + ("electrons", "orbitals", "delta_sz", "message_match"), + [ + (0, 4, 0, "number of active electrons has to be greater than 0"), + (3, 2, 0, "has to be greater than the number of active electrons"), + (2, 4, 3, "Expected values for 'delta_sz'"), + (2, 4, 1.5, "Expected values for 'delta_sz'"), + ], +) +def test_inconsistent_excitations(electrons, orbitals, delta_sz, message_match): + r"""Test that an error is raised if a set of inconsistent arguments is input""" + + with pytest.raises(ValueError, match=message_match): + qchem.excitations(electrons, orbitals, delta_sz) + + +@pytest.mark.parametrize( + ("singles", "doubles", "wires", "singles_wires_exp", "doubles_wires_exp"), + [ + ([[0, 2]], [], None, [[0, 1, 2]], []), + ([], [[0, 1, 2, 3]], None, [], [[[0, 1], [2, 3]]]), + ([[0, 1]], [[0, 1, 2, 4]], None, [[0, 1]], [[[0, 1], [2, 3, 4]]]), + ( + [[0, 1], [2, 4]], + [[0, 1, 2, 3], [0, 2, 4, 6]], + None, + [[0, 1], [2, 3, 4]], + [[[0, 1], [2, 3]], [[0, 1, 2], [4, 5, 6]]], + ), + ( + [[0, 1], [2, 4]], + [[0, 1, 2, 3], [0, 2, 4, 6]], + ["a0", "b1", "c2", "d3", "e4", "f5", "g6"], + [["a0", "b1"], ["c2", "d3", "e4"]], + [[["a0", "b1"], ["c2", "d3"]], [["a0", "b1", "c2"], ["e4", "f5", "g6"]]], + ), + ], +) +def test_mapping_from_excitations_to_wires( + singles, doubles, wires, singles_wires_exp, doubles_wires_exp +): + r"""Test the correctness of the mapping between indices of the single and double + excitations and the list of wires to be passed to the quantum circuit""" + + singles_wires, doubles_wires = qchem.excitations_to_wires(singles, doubles, wires=wires) + + assert len(singles_wires) == len(singles_wires_exp) + assert len(doubles_wires) == len(doubles_wires_exp) + assert singles_wires == singles_wires_exp + assert doubles_wires == doubles_wires_exp + + +@pytest.mark.parametrize( + ("singles", "doubles", "wires", "message_match"), + [ + ([], [], None, "'singles' and 'doubles' lists can not be both empty"), + ([[0, 2, 3]], [], None, "Expected entries of 'singles' to be of shape"), + ([[0, 2], [3]], [], None, "Expected entries of 'singles' to be of shape"), + ([], [[0, 1, 2, 3], [1, 3]], None, "Expected entries of 'doubles' to be of shape"), + ([], [[0, 1, 2, 3], [1, 3, 4, 5, 6]], None, "Expected entries of 'doubles' to be of shape"), + ( + [[0, 2]], + [[0, 1, 2, 3], [0, 2, 4, 6]], + ["a0", "b1", "c2", "d3", "e4", "f5"], + "Expected number of wires is", + ), + ], +) +def test_excitations_to_wires_exceptions(singles, doubles, wires, message_match): + r"""Test that the function 'excitations_to_wires()' throws an exception if ``singles``, + ``doubles`` or ``wires`` parameter has illegal shapes or size""" + + with pytest.raises(ValueError, match=message_match): + qchem.excitations_to_wires(singles, doubles, wires=wires) + + +@pytest.mark.parametrize( + ("weights", "singles", "doubles", "expected"), + [ + ( + np.array([3.90575761, -1.89772083, -1.36689032]), + [[0, 2], [1, 3]], + [[0, 1, 2, 3]], + [-0.14619406, -0.06502792, 0.14619406, 0.06502792], + ) + ], +) +def test_excitation_integration_with_uccsd(weights, singles, doubles, expected): + """Test integration with the UCCSD template""" + + s_wires, d_wires = qchem.excitations_to_wires(singles, doubles) + N = 4 + wires = range(N) + dev = qml.device("default.qubit", wires=N) + + @qml.qnode(dev) + def circuit(weights): + UCCSD(weights, wires, s_wires=s_wires, d_wires=d_wires, init_state=np.array([1, 1, 0, 0])) + return [qml.expval(qml.PauliZ(w)) for w in range(N)] + + res = circuit(weights) + assert np.allclose(res, np.array(expected)) + + +@pytest.mark.parametrize( + ("electrons", "orbitals", "exp_state"), + [ + (2, 5, np.array([1, 1, 0, 0, 0])), + (1, 5, np.array([1, 0, 0, 0, 0])), + (5, 5, np.array([1, 1, 1, 1, 1])), + ], +) +def test_hf_state(electrons, orbitals, exp_state): + r"""Test the correctness of the generated occupation-number vector""" + + res_state = qchem.hf_state(electrons, orbitals) + + assert len(res_state) == len(exp_state) + assert np.allclose(res_state, exp_state) + + +@pytest.mark.parametrize( + ("electrons", "orbitals", "msg_match"), + [ + (0, 5, "number of active electrons has to be larger than zero"), + (-1, 5, "number of active electrons has to be larger than zero"), + (6, 5, "number of active orbitals cannot be smaller than the number of active"), + ], +) +def test__hf_state_inconsistent_input(electrons, orbitals, msg_match): + r"""Test that an error is raised if a set of inconsistent arguments is input""" + + with pytest.raises(ValueError, match=msg_match): + qchem.hf_state(electrons, orbitals) + + +@pytest.mark.parametrize( + ( + "n_electrons", + "n_orbitals", + "multiplicity", + "act_electrons", + "act_orbitals", + "core_ref", + "active_ref", + ), + [ + (4, 6, 1, None, None, [], list(range(6))), + (4, 6, 1, 4, None, [], list(range(6))), + (4, 6, 1, 2, None, [0], list(range(1, 6))), + (4, 6, 1, None, 4, [], list(range(4))), + (4, 6, 1, 2, 3, [0], list(range(1, 4))), + (5, 6, 2, 3, 4, [0], list(range(1, 5))), + (5, 6, 2, 1, 4, [0, 1], list(range(2, 6))), + ], +) +def test_active_spaces( + n_electrons, n_orbitals, multiplicity, act_electrons, act_orbitals, core_ref, active_ref +): + r"""Test the correctness of the generated active spaces""" + + core, active = qchem.active_space( + n_electrons, + n_orbitals, + mult=multiplicity, + active_electrons=act_electrons, + active_orbitals=act_orbitals, + ) + + assert core == core_ref + assert active == active_ref + + +@pytest.mark.parametrize( + ("n_electrons", "n_orbitals", "multiplicity", "act_electrons", "act_orbitals", "message_match"), + [ + (4, 6, 1, 6, 5, "greater than the total number of electrons"), + (4, 6, 1, 1, 5, "should be even"), + (4, 6, 1, -1, 5, "has to be greater than 0."), + (4, 6, 1, 2, 6, "greater than the total number of orbitals"), + (4, 6, 1, 2, 1, "there are no virtual orbitals"), + (5, 6, 2, 2, 5, "should be odd"), + (5, 6, 2, 3, -2, "has to be greater than 0."), + (5, 6, 2, 3, 6, "greater than the total number of orbitals"), + (5, 6, 2, 3, 2, "there are no virtual orbitals"), + (6, 6, 3, 1, 2, "greater than or equal to"), + ], +) +def test_inconsistent_active_spaces( + n_electrons, n_orbitals, multiplicity, act_electrons, act_orbitals, message_match +): + r"""Test that an error is raised if an inconsistent active space is generated""" + + with pytest.raises(ValueError, match=message_match): + qchem.active_space( + n_electrons, + n_orbitals, + mult=multiplicity, + active_electrons=act_electrons, + active_orbitals=act_orbitals, + ) diff --git a/tests/qchem/hf_tests/test_tapering.py b/tests/qchem/test_tapering.py similarity index 100% rename from tests/qchem/hf_tests/test_tapering.py rename to tests/qchem/test_tapering.py