From e76e301a8c5d2984eeabf980c912a297c6f738df Mon Sep 17 00:00:00 2001 From: Utkarsh Date: Thu, 18 Jul 2024 00:18:29 -0400 Subject: [PATCH] Extending support for larger atomic numbers (#5821) **Context:** Currently, we support only first- or second-row elements of the periodic table for molecule and Hamiltonian constructions. This PR aims to push this limit to all the elements. **Description of the Change:** Updates the `atomic_numbers` dictionary in the `qchem/basis_data.py` and adapts the qchem functionality with this update. **Benefits:** We can support more elements by letting users use `load_data=True` keyword argument. **Possible Drawbacks:** None **Related GitHub Issues:** --- doc/releases/changelog-dev.md | 4 ++ pennylane/qchem/basis_data.py | 39 +++++++++++++------ pennylane/qchem/basis_set.py | 9 ++++- pennylane/qchem/openfermion_pyscf.py | 5 +-- .../openfermion_pyscf_tests/test_dipole_of.py | 2 +- tests/qchem/test_basis_set.py | 9 +++++ tests/qchem/test_molecule.py | 2 +- 7 files changed, 51 insertions(+), 19 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index b8b5c7794cf..0b18c0c68e4 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -44,6 +44,9 @@ * Observable validation for `default.qubit` is now based on execution mode (analytic vs. finite shots) and measurement type (sample measurement vs. state measurement). [(#5890)](https://github.com/PennyLaneAI/pennylane/pull/5890) +* Molecules and Hamiltonians can now be constructed for all the elements present in the periodic table. + [(#5821)](https://github.com/PennyLaneAI/pennylane/pull/5821) +

Community contributions 🥳

* `DefaultQutritMixed` readout error has been added using parameters `readout_relaxation_probs` and @@ -92,6 +95,7 @@ This release contains contributions from (in alphabetical order): Guillermo Alonso, +Utkarsh Azad Astral Cai, Yushao Chen, Gabriel Bottrill, diff --git a/pennylane/qchem/basis_data.py b/pennylane/qchem/basis_data.py index eac4f8cdae6..5dc381dc0f7 100644 --- a/pennylane/qchem/basis_data.py +++ b/pennylane/qchem/basis_data.py @@ -20,18 +20,30 @@ import itertools +# Note: Below markers are added to prevent reformatting of this dictionary by black +# fmt: off +# IUPAC Periodic Table of the Elements: https://iupac.org/what-we-do/periodic-table-of-elements/ atomic_numbers = { - "H": 1, - "He": 2, - "Li": 3, - "Be": 4, - "B": 5, - "C": 6, - "N": 7, - "O": 8, - "F": 9, - "Ne": 10, + 'H': 1, 'He': 2, # Period 1 + 'Li': 3, 'Be': 4, 'B': 5, 'C': 6, 'N': 7, 'O': 8, 'F': 9, 'Ne': 10, # Period 2 + 'Na': 11, 'Mg': 12, 'Al': 13, 'Si': 14, 'P': 15, 'S': 16, 'Cl': 17, 'Ar': 18, # Period 3 + 'K': 19, 'Ca': 20, 'Sc': 21, 'Ti': 22, 'V': 23, 'Cr': 24, 'Mn': 25, 'Fe': 26, 'Co': 27, # Period 4 + 'Ni': 28, 'Cu': 29, 'Zn': 30, 'Ga': 31, 'Ge': 32, 'As': 33, 'Se': 34, 'Br': 35, 'Kr': 36, + 'Rb': 37, 'Sr': 38, 'Y': 39, 'Zr': 40, 'Nb': 41, 'Mo': 42, 'Tc': 43, 'Ru': 44, 'Rh': 45, # Period 5 + 'Pd': 46, 'Ag': 47, 'Cd': 48, 'In': 49, 'Sn': 50, 'Sb': 51, 'Te': 52, 'I': 53, 'Xe': 54, + 'Cs': 55, 'Ba': 56, # Period 6 + 'La': 57, 'Ce': 58, 'Pr': 59, 'Nd': 60, 'Pm': 61, 'Sm': 62, 'Eu': 63, 'Gd': 64, 'Tb': 65, # Lanthanides + 'Dy': 66, 'Ho': 67, 'Er': 68, 'Tm': 69, 'Yb': 70, 'Lu': 71, + 'Hf': 72, 'Ta': 73, 'W': 74, 'Re': 75, 'Os': 76, 'Ir': 77, 'Pt': 78, 'Au': 79, 'Hg': 80, # Period 6 + 'Tl': 81, 'Pb': 82, 'Bi': 83, 'Po': 84, 'At': 85, 'Rn': 86, + 'Fr': 87, 'Ra': 88, # Period 7 + 'Ac': 89, 'Th': 90, 'Pa': 91, 'U': 92, 'Np': 93, 'Pu': 94, 'Am': 95, 'Cm': 96, 'Bk': 97, # Actinides + 'Cf': 98, 'Es': 99, 'Fm': 100, 'Md': 101, 'No': 102, 'Lr': 103, + 'Rf': 104, 'Db': 105, 'Sg': 106, 'Bh': 107, 'Hs': 108, 'Mt': 109, 'Ds': 110, 'Rg': 111, # Period 7 + 'Cn': 112, 'Nh': 113, 'Fl': 114, 'Mc': 115, 'Lv': 116, 'Ts': 117, 'Og': 118 } +# fmt: on + STO3G = { "H": { @@ -797,11 +809,14 @@ def load_basisset(basis, element): "[3]": "F", "[4]": "G", "[5]": "H", + "[6]": "I", } - element = str(atomic_numbers[element]) + atomic_number = atomic_numbers.get(element, None) + if atomic_number is None: + raise ValueError(f"Requested element {element} doesn't exist in the periodic table.") - data = bse.get_basis(basis)["elements"][element]["electron_shells"] + data = bse.get_basis(basis)["elements"][str(atomic_number)]["electron_shells"] orbitals = [] exponents = [] diff --git a/pennylane/qchem/basis_set.py b/pennylane/qchem/basis_set.py index 6afac08f166..3e8fe2633cc 100644 --- a/pennylane/qchem/basis_set.py +++ b/pennylane/qchem/basis_set.py @@ -100,7 +100,14 @@ def atom_basis_data(name, atom, load_data=False): if load_data: basis = load_basisset(name, atom) else: - basis = basis_sets[name][atom] + basis = basis_sets[name].get(atom, None) + if basis is None: + raise ValueError( + f"The requested basis set data is not available for {atom}. " + "Please consider using `load_data=True` to download the basis set " + "from the external library basis-set-exchange that can be installed with: " + "pip install basis-set-exchange." + ) params = [] sp_count = 0 diff --git a/pennylane/qchem/openfermion_pyscf.py b/pennylane/qchem/openfermion_pyscf.py index 7e98ab2baa1..fd3031a7178 100644 --- a/pennylane/qchem/openfermion_pyscf.py +++ b/pennylane/qchem/openfermion_pyscf.py @@ -609,10 +609,7 @@ def dipole_of( for i in symbols: if i not in atomic_numbers: - raise ValueError( - f"Currently, only first- or second-row elements of the periodic table are supported;" - f" got element {i}" - ) + raise ValueError(f"Requested element {i} doesn't exist") hf_file = qml.qchem.meanfield(symbols, coordinates, name, charge, mult, basis, package, outpath) diff --git a/tests/qchem/openfermion_pyscf_tests/test_dipole_of.py b/tests/qchem/openfermion_pyscf_tests/test_dipole_of.py index 36fb9422b9e..739965c5813 100644 --- a/tests/qchem/openfermion_pyscf_tests/test_dipole_of.py +++ b/tests/qchem/openfermion_pyscf_tests/test_dipole_of.py @@ -274,7 +274,7 @@ def circuit(hf_state, obs): ("symbols", "coords", "mult", "msg_match"), [ (["H", "H"], x_h2, 2, "this functionality is constrained to Hartree-Fock states"), - (["H", "Ca"], x_h2, 1, "only first- or second-row elements of the periodic table"), + (["H", "Cx"], x_h2, 1, "Requested element Cx doesn't exist"), ], ) @pytest.mark.usefixtures("skip_if_no_openfermion_support") diff --git a/tests/qchem/test_basis_set.py b/tests/qchem/test_basis_set.py index 435f947d3b0..35df4301342 100644 --- a/tests/qchem/test_basis_set.py +++ b/tests/qchem/test_basis_set.py @@ -477,6 +477,15 @@ def test_mol_basis_data(self, basis_data): assert np.allclose(params, params_ref) + def test_mol_basis_data_error(self): + """Test that correct error is raised if the element is not present in the internal basis-sets""" + + with pytest.raises(ValueError, match="The requested basis set data is not available for"): + qchem.basis_set.atom_basis_data(name="sto-3g", atom="Os") + + with pytest.raises(ValueError, match="Requested element Ox doesn't exist"): + qchem.basis_data.load_basisset(basis="sto-3g", element="Ox") + class TestLoadBasis: """Tests for loading data from external libraries.""" diff --git a/tests/qchem/test_molecule.py b/tests/qchem/test_molecule.py index b4e797e5687..ea0195647ff 100644 --- a/tests/qchem/test_molecule.py +++ b/tests/qchem/test_molecule.py @@ -49,7 +49,7 @@ def test_basis_error(self, symbols, geometry): @pytest.mark.parametrize( ("symbols", "geometry"), [ - (["H", "Og"], np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 1.0]])), + (["H", "Ox"], np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 1.0]])), ], ) def test_symbol_error(self, symbols, geometry):