diff --git a/environment.yml b/environment.yml index ad2aa2b..5d9baf6 100644 --- a/environment.yml +++ b/environment.yml @@ -49,3 +49,4 @@ dependencies: - ase==3.22 - tqdm==4.66 - git+https://github.com/lab-cosmo/equistore.git@e5b9dc365369ba2584ea01e9d6a4d648008aaab8#subdirectory=python/equistore-core + - qstack/qstack-qml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..d26a68e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,64 @@ +[build-system] +requires = [ + "setuptools", +] +build-backend = "setuptools.build_meta" + +[project] +name = "qstack" +#version = "0.0.0" +dynamic = ["version"] +description = "Stack of codes for dedicated pre- and post-processing tasks for Quantum Machine Learning" +readme = "README.md" +license = {file = "LICENSE"} + +authors = [ + {name = "Alberto Fabrizio"}, # orcid = "0000-0002-4440-3149" + {name = "Ksenia Briling", email = "ksenia.briling@epfl.ch"}, # orcid = "0000-0003-2250-9898" + {name = "Yannick Calvino", email = "yannick.calvinoalonso@epfl.ch"}, # orcid = "0009-0008-9573-7772" + {name = "Liam Marsh", email = "liam.marsh@epfl.ch"}, # orcid = "0000-0002-7276-5673" +] +maintainers = [ + {name = "Ksenia Briling", email = "ksenia.briling@epfl.ch"}, # orcid = "0000-0003-2250-9898" + {name = "Yannick Calvino", email = "yannick.calvinoalonso@epfl.ch"}, # orcid = "0009-0008-9573-7772" + {name = "Liam Marsh", email = "liam.marsh@epfl.ch"}, # orcid = "0000-0002-7276-5673" +] + +classifiers = [ + "Programming Language :: Python :: 3", + "Programming Language :: C", + "License :: OSI Approved :: MIT License", + "Development Status :: 3 - Alpha", + "Intended Audience :: Science/Research", + "Programming Language :: Python :: Implementation :: CPython", +] + +# note: minimal versions were just copy-pasted from requirements.txt, some earlier versions may also work +# note: maximal versions are just the latest versions to exist when compatibility checks were made, later versions may also work +requires-python = ">=3.9" +dependencies = [ + 'numpy >= 1.22.3, < 1.27', + 'scipy >= 1.1.0, < 1.14', + 'pyscf >= 2.0.1, < 2.6', + 'tqdm >= 4.66', +] + +[project.optional-dependencies] +#qml = ["qstack-qml @ file://qstack/qstack-qml"] # this is managed in setup.py because of course. +regression = ["scikit-learn >= 0.24.2, < 1.6"] +wigner = ["sympy >= 1.5, < 1.13"] +gmol = ["cell2mol @ git+https://github.com/lcmd-epfl/cell2mol.git@22473bbf12a013467137a55a63c88fbbdc95baa2"] # branch: dev, date: 2024-06-06 +equio = ["equistore-core @ git+https://github.com/lab-cosmo/equistore.git@e5b9dc365369ba2584ea01e9d6a4d648008aaab8#subdirectory=python/equistore-core"] +all = ["qstack[qml,regression,wigner,equio,gmol]"] + +[project.urls] +Repository = "https://github.com/lcmd-epfl/Q-stack.git" +"Bug Tracker" = "https://github.com/lcmd-epfl/Q-stack/issues" + + +[tool.setuptools.packages.find] +where = ["."] +exclude = ["tests","examples"] +namespaces = false +[tool.setuptools.package-data] +"*" = ['regression/lib/manh.c', 'spahm/rho/basis_opt/*.bas'] diff --git a/qstack/__init__.py b/qstack/__init__.py index 6dff763..71bb664 100644 --- a/qstack/__init__.py +++ b/qstack/__init__.py @@ -6,6 +6,13 @@ from qstack import fields from qstack import basis_opt from qstack import spahm -from qstack import regression from qstack import mathutils from qstack import orcaio + +try: + import sklearn +except ImportError: + pass +else: + from qstack import regression + del sklearn diff --git a/qstack/compound.py b/qstack/compound.py index 187a283..5d6f035 100644 --- a/qstack/compound.py +++ b/qstack/compound.py @@ -108,8 +108,18 @@ def gmol_to_mol(fin, basis="def2-svp"): pyscf Mole: pyscf Mole object. """ - from cell2mol.tmcharge_common import Cell, atom, molecule, ligand, metal - from cell2mol.tmcharge_common import labels2formula + try: + from cell2mol.tmcharge_common import Cell, atom, molecule, ligand, metal + from cell2mol.tmcharge_common import labels2formula + except ImportError: + print(""" + +ERROR: cannot import cell2mol. Have you installed qstack with the \gmol\" option?\n\n +(for instance, with `pip install qstack[gmol] or `pip install qstack[all]``) + +""") + raise + # Open and read the file if fin.endswith(".gmol"): diff --git a/qstack/fields/decomposition.py b/qstack/fields/decomposition.py index 4bf1d1c..9df85c4 100644 --- a/qstack/fields/decomposition.py +++ b/qstack/fields/decomposition.py @@ -1,7 +1,7 @@ import numpy as np import scipy from pyscf import scf -import qstack +from qstack import compound def decompose(mol, dm, auxbasis): """Fit molecular density onto an atom-centered basis. @@ -15,8 +15,8 @@ def decompose(mol, dm, auxbasis): A copy of the pyscf Mole object with the auxbasis basis in a pyscf Mole object, and a 1D numpy array containing the decomposition coefficients. """ - auxmol = qstack.compound.make_auxmol(mol, auxbasis) - S, eri2c, eri3c = qstack.fields.decomposition.get_integrals(mol, auxmol) + auxmol = compound.make_auxmol(mol, auxbasis) + S, eri2c, eri3c = get_integrals(mol, auxmol) c = get_coeff(dm, eri2c, eri3c) return auxmol, c diff --git a/qstack/fields/excited.py b/qstack/fields/excited.py index e617269..89d0664 100644 --- a/qstack/fields/excited.py +++ b/qstack/fields/excited.py @@ -1,7 +1,8 @@ import numpy as np import pyscf from pyscf import scf, tdscf -from qstack import compound, fields +from qstack import compound +from qstack.fields import moments def get_cis(mf, nstates): """ @@ -26,7 +27,7 @@ def get_cis_tdm(td): def get_holepart(mol, x, coeff): """Computes the hole and particle density matrices (atomic orbital basis) of selected states. - + Args: mol (pyscf Mole): pyscf Mole object. x (numpy ndarray): Response vector (nstates×occ×virt) normalized to 1. @@ -71,11 +72,11 @@ def exciton_properties_c(mol, hole, part): part (numpy ndarray): Particle density matrix. Returns: - Three floats: the hole-particle distance, the hole size, and the particle size respectively. + Three floats: the hole-particle distance, the hole size, and the particle size respectively. """ - hole_N, hole_r, hole_r2 = fields.moments.r2_c(hole, mol) - part_N, part_r, part_r2 = fields.moments.r2_c(part, mol) + hole_N, hole_r, hole_r2 = moments.r2_c(hole, mol) + part_N, part_r, part_r2 = moments.r2_c(part, mol) dist = np.linalg.norm(hole_r-part_r) hole_extent = np.sqrt(hole_r2-hole_r@hole_r) @@ -91,7 +92,7 @@ def exciton_properties_dm(mol, hole, part): part (numpy ndarray): Particle density matrix. Returns: - Three floats: the hole-particle distance, the hole size, and the particle size respectively. + Three floats: the hole-particle distance, the hole size, and the particle size respectively. """ with mol.with_common_orig((0,0,0)): diff --git a/qstack/fields/moments.py b/qstack/fields/moments.py index 104d950..4002154 100644 --- a/qstack/fields/moments.py +++ b/qstack/fields/moments.py @@ -1,6 +1,6 @@ import numpy as np import pyscf -from qstack import compound, fields +from qstack import compound def first(mol, rho): """ Computes the transition dipole moments. diff --git a/qstack/mathutils/wigner.py b/qstack/mathutils/wigner.py index 0ea4c6d..c169867 100755 --- a/qstack/mathutils/wigner.py +++ b/qstack/mathutils/wigner.py @@ -2,8 +2,8 @@ import sys import sympy as sp -from sympy import symbols, Symbol, simplify, expand, cancel, expand_trig, Ynm, Ynm_c, Matrix, poly, zeros from .xyz_integrals_sym import xyz as xyzint +from sympy import symbols, Symbol, simplify, expand, cancel, expand_trig, Ynm, Ynm_c, Matrix, poly, zeros # variables diff --git a/qstack/mathutils/xyz_integrals_sym.py b/qstack/mathutils/xyz_integrals_sym.py index 7ed99c0..5873dab 100755 --- a/qstack/mathutils/xyz_integrals_sym.py +++ b/qstack/mathutils/xyz_integrals_sym.py @@ -1,7 +1,17 @@ #!/usr/bin/env python3 import sys -import sympy + +try: + import sympy +except ImportError: + print(""" + +ERROR: cannot import sympy. Have you installed qstack with the \"wigner\" option?\n\n +(for instance, with `pip install qstack[wigner]` or `pip install qstack[all]`) + +""") + raise def xyz(n,m,k): # computes the integral of x^2k y^2n z^2m over a sphere diff --git a/qstack/qml/__init__.py b/qstack/qml/__init__.py deleted file mode 100644 index 53e6300..0000000 --- a/qstack/qml/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from qstack.qml import slatm diff --git a/qstack/qstack-qml/LICENSE b/qstack/qstack-qml/LICENSE new file mode 120000 index 0000000..30cff74 --- /dev/null +++ b/qstack/qstack-qml/LICENSE @@ -0,0 +1 @@ +../../LICENSE \ No newline at end of file diff --git a/qstack/qstack-qml/README.md b/qstack/qstack-qml/README.md new file mode 100644 index 0000000..ee544c8 --- /dev/null +++ b/qstack/qstack-qml/README.md @@ -0,0 +1,52 @@ +[![Lifecycle: +experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental) +============================================== +Q-stack (QML) +============================================== +

qstack logo

+ + +## Contents +* [Contents](#Contents-) +* [About](#about-) +* [Install](#install-) +* [Examples](#examples-) +* [References](#references-) +* [Acknowledgements](#acknowledgements-) + +## About [↑](#contents) + +Q-stack is a stack of codes for dedicated pre- and post-processing tasks for Quantum Machine Learning (QML). It is a work in progress. Stay tuned for updates! + +This sub-part of Q-stack contains a few geometry-only descriptors, which are the following ones: + +- $B^2R^2$ (https://github.com/lcmd-epfl/b2r2-reaction-rep) +- $\mathrm{SLATM}_d$ + + +## Install [↑](#contents) + +The installation of this sub-part can be done executing the following command: + +``` +python -m pip install git+https://github.com/lcmd-epfl/Q-stack.git#subdirectory=qstack/qstack-qml +``` +## Examples [↑](#contents) +Q-stack comes with several example codes that illustrate some of its key capabilities. Examples for firs sub-part will follow shortly. + + +## References [↑](#contents) + +* P. van Gerwen, A. Fabrizio, M. Wodrich, and C. Corminboeuf, + “Physics-based representations for machine learning properties of chemical reactions”, + Mach. Learn.: Sci. Technol. **3**, 045005 (2022) + [![DOI](https://img.shields.io/badge/DOI-10.1088/2632--2153/ac8f1a-blue)](https://doi.org/10.1088/2632-2153/ac8f1a) + + +## Acknowledgements [↑](#contents) +The authors of Q-stack acknowledge the National Centre of Competence in Research (NCCR) +"Materials' Revolution: Computational Design and Discovery of Novel Materials (MARVEL)" +of the Swiss National Science Foundation (SNSF, grant number 182892) +and the European Research Council (ERC, grant agreement no 817977). + +![acknowledgements logos](./images/ackw.png) diff --git a/qstack/qstack-qml/pyproject.toml b/qstack/qstack-qml/pyproject.toml new file mode 100644 index 0000000..e7e2146 --- /dev/null +++ b/qstack/qstack-qml/pyproject.toml @@ -0,0 +1,50 @@ +[build-system] +requires = [ + "setuptools", +] +build-backend = "setuptools.build_meta" + +[project] +name = "qstack-qml" +version = "0.0.1" +description = "A set of geometric descriptors used by qstack (codes for dedicated pre- and post-processing tasks for Quantum Machine Learning)" +readme = "README.md" +license = {file = "LICENSE"} + +authors = [ + {name = "Ksenia Briling", email = "ksenia.briling@epfl.ch"}, # orcid = "0000-0003-2250-9898" + {name = "Yannick Calvino", email = "yannick.calvinoalonso@epfl.ch"}, # orcid = "0009-0008-9573-7772" +] +maintainers = [ + {name = "Ksenia Briling", email = "ksenia.briling@epfl.ch"}, # orcid = "0000-0003-2250-9898" + {name = "Yannick Calvino", email = "yannick.calvinoalonso@epfl.ch"}, # orcid = "0009-0008-9573-7772" + {name = "Liam Marsh", email = "liam.marsh@epfl.ch"}, # orcid = "0000-0002-7276-5673" +] + +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Development Status :: 3 - Alpha", + "Intended Audience :: Science/Research", +] + +# note: minimal versions were just copy-pasted from requirements.txt, some earlier versions may also work +# note: maximal versions are just the latest versions to exist when compatibility checks were made, later versions may also work +requires-python = ">= 3.9" +dependencies = [ + 'numpy >= 1.22.3, < 1.27', + 'scipy >= 1.1.0, < 1.14', + 'tqdm >= 4.66', + 'ase >= 3.22, < 3.24', +] + +[project.urls] +Repository = "https://github.com/lcmd-epfl/Q-stack.git" +"Bug Tracker" = "https://github.com/lcmd-epfl/Q-stack/issues" + + +[tool.setuptools.packages.find] +where = ["."] +exclude = ["tests","examples"] +namespaces = false + diff --git a/qstack/qstack-qml/qstack_qml/__init__.py b/qstack/qstack-qml/qstack_qml/__init__.py new file mode 100644 index 0000000..6242d15 --- /dev/null +++ b/qstack/qstack-qml/qstack_qml/__init__.py @@ -0,0 +1,3 @@ +from . import b2r2 +from . import slatm + diff --git a/qstack/qml/b2r2.py b/qstack/qstack-qml/qstack_qml/b2r2.py similarity index 100% rename from qstack/qml/b2r2.py rename to qstack/qstack-qml/qstack_qml/b2r2.py diff --git a/qstack/qml/slatm.py b/qstack/qstack-qml/qstack_qml/slatm.py similarity index 100% rename from qstack/qml/slatm.py rename to qstack/qstack-qml/qstack_qml/slatm.py diff --git a/qstack/regression/__init__.py b/qstack/regression/__init__.py index e69de29..101914b 100644 --- a/qstack/regression/__init__.py +++ b/qstack/regression/__init__.py @@ -0,0 +1,11 @@ +try: + import sklearn + del sklearn +except ImportError: + print(""" + +ERROR: cannot import scikit-learn. Have you installed qstack with the \"regression\" option?\n\n +(for instance, with `pip install qstack[regression] or `pip install qstack[all]``) + +""") + raise diff --git a/qstack/regression/kernel_utils.py b/qstack/regression/kernel_utils.py index 4478022..202609b 100644 --- a/qstack/regression/kernel_utils.py +++ b/qstack/regression/kernel_utils.py @@ -1,7 +1,7 @@ import argparse import numpy as np from types import SimpleNamespace -import qstack +from qstack.regression import __path__ as REGMODULE_PATH class ParseKwargs(argparse.Action): @@ -120,7 +120,6 @@ def get_covariance(mol1, mol2, max_sizes, kernel , sigma=None): .. todo:: Write the docstring """ - from qstack.regression.kernel_utils import get_kernel species = sorted(max_sizes.keys()) mol1_dict = mol_to_dict(mol1, species) mol2_dict = mol_to_dict(mol2, species) @@ -213,7 +212,6 @@ def cdist(X, Y): np.exp(K, K) return K - def my_kernel_c(akernel): """ @@ -225,9 +223,9 @@ def my_kernel_c(akernel): array_2d_double = np.ctypeslib.ndpointer(dtype=np.float64, ndim=2, flags='CONTIGUOUS') def kernel_func_c(X, Y, gamma): try: - manh = ctypes.cdll.LoadLibrary(qstack.regression.__path__[0]+"/lib/manh.so") + manh = ctypes.cdll.LoadLibrary(REGMODULE_PATH[0]+"/lib/manh.so") except: - manh = ctypes.cdll.LoadLibrary(qstack.regression.__path__[0]+"/lib/manh"+sysconfig.get_config_var('EXT_SUFFIX')) + manh = ctypes.cdll.LoadLibrary(REGMODULE_PATH[0]+"/lib/manh"+sysconfig.get_config_var('EXT_SUFFIX')) if akernel == 'myLfast': kfunc = manh.manh elif akernel == 'myG': diff --git a/qstack/spahm/rho/rep_completion.py b/qstack/spahm/rho/rep_completion.py index 465de75..54f266e 100755 --- a/qstack/spahm/rho/rep_completion.py +++ b/qstack/spahm/rho/rep_completion.py @@ -6,7 +6,7 @@ from os.path import join, isfile, isdir import numpy as np import pyscf -from qstack import compound, spahm +from qstack import compound import qstack.tools as tls from . import utils, dmb_rep_atom as dmba, dmb_rep_bond as dmbb diff --git a/requirements.txt b/requirements.txt index 0b18597..b34072a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ iniconfig==1.1.1 packaging==21.3 pluggy==1.0.0 py==1.11.0 +#cython==0.29.24 pyparsing==3.0.6 pyscf==2.0.1 pytest==6.2.5 @@ -12,6 +13,7 @@ numpy===1.22.3 scipy==1.10 toml==0.10.2 scikit-learn==0.24.2 +#scikit-learn==1.0.2 ase==3.22 tqdm==4.66 equistore-core @ git+https://github.com/lab-cosmo/equistore.git@e5b9dc365369ba2584ea01e9d6a4d648008aaab8#subdirectory=python/equistore-core diff --git a/setup.py b/setup.py index 002c85f..c44bca6 100644 --- a/setup.py +++ b/setup.py @@ -4,6 +4,9 @@ VERSION="0.0.1" +ROOT = os.path.realpath(os.path.dirname(__file__)) +QSTACK_QML = os.path.join(ROOT, "qstack", "qstack-qml") + def get_git_version_hash(): """Get tag/hash of the latest commit. Thanks to https://gist.github.com/nloadholtes/07a1716a89b53119021592a1d2b56db8""" @@ -33,29 +36,17 @@ def check_for_openmp(): return not result -def get_requirements(): - fname = f"{os.path.dirname(os.path.realpath(__file__))}/requirements.txt" - with open(fname) as f: - install_requires = f.read().splitlines() - return install_requires - - if __name__ == '__main__': openmp_enabled = check_for_openmp() setup( - name='qstack', version=get_git_version_hash(), - description='Stack of codes for dedicated pre- and post-processing tasks for Quantum Machine Learning', - url='https://github.com/lcmd-epfl/Q-stack', - python_requires='==3.9.*', - install_requires=get_requirements(), - packages=setuptools.find_packages(exclude=['tests', 'examples']), + extras_require = { + "qml":[f"qstack-qml @ file://{QSTACK_QML:s}"], + }, ext_modules=[Extension('qstack.regression.lib.manh', ['qstack/regression/lib/manh.c'], extra_compile_args=['-fopenmp', '-std=gnu11'] if openmp_enabled else ['-std=gnu11'], extra_link_args=['-lgomp'] if openmp_enabled else []) ], - include_package_data=True, - package_data={'': ['regression/lib/manh.c', 'spahm/rho/basis_opt/*.bas']}, ) diff --git a/tests/test_global.py b/tests/test_global.py old mode 100644 new mode 100755 diff --git a/tests/test_opt.py b/tests/test_opt.py old mode 100644 new mode 100755 diff --git a/tests/test_otpd.py b/tests/test_otpd.py index e27b5ef..af365b6 100755 --- a/tests/test_otpd.py +++ b/tests/test_otpd.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import os +import numpy as np from qstack import compound, fields def test_hf_otpd(): @@ -9,7 +10,7 @@ def test_hf_otpd(): mol = compound.xyz_to_mol(path+'/data/H2O.xyz', 'def2svp', charge=0, spin=0) dm = fields.dm.get_converged_dm(mol, xc="pbe") otpd, grid = fields.hf_otpd.hf_otpd(mol, dm, return_all=True) - assert(abs(otpd @ grid.weights-20.190382555868)<1e-10) + assert np.allclose(otpd @ grid.weights, 20.190382555868) if __name__ == '__main__': diff --git a/tests/test_rxn-repr.py b/tests/test_rxn-repr.py index 21f15b3..b059228 100755 --- a/tests/test_rxn-repr.py +++ b/tests/test_rxn-repr.py @@ -3,8 +3,8 @@ from types import SimpleNamespace import numpy as np import ase.io -from qstack.qml.slatm import get_slatm_rxn -from qstack.qml.b2r2 import get_b2r2 +from qstack_qml.slatm import get_slatm_rxn +from qstack_qml.b2r2 import get_b2r2 class Rxn_data: diff --git a/tests/test_slatm.py b/tests/test_slatm.py index 040764f..f63ecd4 100755 --- a/tests/test_slatm.py +++ b/tests/test_slatm.py @@ -1,7 +1,7 @@ import os import glob import numpy as np -from qstack.qml.slatm import get_slatm_for_dataset +from qstack_qml.slatm import get_slatm_for_dataset def test_slatm_global(): diff --git a/tests/test_spahm_b.py b/tests/test_spahm_b.py old mode 100644 new mode 100755 diff --git a/tests/test_utils.py b/tests/test_utils.py old mode 100644 new mode 100755