Skip to content

Commit 43503af

Browse files
authored
Setting the consistency of atoms and coordinates in function definitions (#16)
* Setting the consistency of atoms and coordinates for representations * Added cov test setup * Changed assert to raise exception * Remove Python2 dependencies * Cleaned f90 headers * Added cites and changed interface to be cite-consistent (#17) * Bump version to minor, show breaking interface
1 parent bbfac9b commit 43503af

40 files changed

+413
-1318
lines changed

.coveragerc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[report]
2+
exclude_also =
3+
def __repr__
4+
raise ValueError
5+
raise NotImplementedError

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ all: env
1616
env:
1717
${mamba} env create -f ./environment_dev.yaml -p ./env --quiet
1818
${python} -m pre_commit install
19-
# ${python} -m pip install -e .
19+
${python} -m pip install -e .
2020

2121
./.git/hooks/pre-commit:
2222
${python} -m pre_commit install
@@ -34,7 +34,7 @@ types:
3434
${python} -m monkeytype list-modules | grep ${pkg} | parallel -j${j} "${python} -m monkeytype apply {} > /dev/null && echo {}"
3535

3636
cov:
37-
${python} -m pytest -vrs --cov=${pkg} --cov-report html tests
37+
${python} -m pytest --cov=${pkg} --cov-config .coveragerc --cov-report html tests
3838

3939
compile:
4040
${python} _compile.py

README.rst

Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
====
2-
What
3-
====
1+
===============
2+
What is qmllib?
3+
===============
44

55
``qmllib`` is a Python/Fortran toolkit for representation of molecules and solids
66
for machine learning of properties of molecules and solids. The library is not
@@ -10,7 +10,7 @@ the goal is to provide usable and efficient implementations of concepts such as
1010
representations and kernels.
1111

1212
==============
13-
QML or QMLLib?
13+
QML or qmllib?
1414
==============
1515

1616
``qmllib`` represents the core library functionality derived from the original
@@ -19,9 +19,10 @@ applications, but without the high-level abstraction, for example SKLearn.
1919

2020
This package is and should stay free-function design oriented.
2121

22-
Breaking changes from ``qml``:
22+
If you are moving from ``qml`` to ``qmllib``, note that there are breaking
23+
changes to the interface to make it more consistent with both argument orders
24+
and function naming.
2325

24-
* FCHL representations callable interface to be consistent with other representations (e.i. atoms, coordinates)
2526

2627
==============
2728
How to install
@@ -52,6 +53,7 @@ or if you want a specific feature branch
5253
5354
pip install git+https://github.com/qmlcode/qmllib@feature_branch
5455
56+
5557
=================
5658
How to contribute
5759
=================
@@ -73,27 +75,72 @@ You know have a conda environment in `./env` and are ready to run
7375
7476
happy developing
7577

78+
7679
==========
7780
How to use
7881
==========
7982

80-
.. code-block:: python
81-
82-
raise NotImplementedError
83+
Notebook examples are coming. For now, see test files in ``tests/*``.
8384

8485
===========
8586
How to cite
8687
===========
8788

88-
.. code-block:: python
89-
90-
raise NotImplementedError
91-
92-
=========
93-
What TODO
94-
=========
95-
96-
* Setup ifort flags
97-
* Setup based on FCC env variable or --global-option flags
98-
* Find MKL from env (for example conda)
99-
* Find what numpy has been linked too (lapack or mkl)
89+
Please cite the representation that you are using accordingly.
90+
91+
- | **Implementation**
92+
Toolkit for Quantum Chemistry Machine Learning,
93+
https://github.com/qmlcode/qmllib, <version or git commit>
94+
95+
- | **FCHL19** ``generate_fchl19``
96+
FCHL revisited: Faster and more accurate quantum machine learning,
97+
Christensen, Bratholm, Faber, Lilienfeld,
98+
J. Chem. Phys. 152, 044107 (2020),
99+
https://doi.org/10.1063/1.5126701
100+
101+
- | **FCHL18** ``generate_fchl18``
102+
Alchemical and structural distribution based representation for universal quantum machine learning,
103+
Faber, Christensen, Huang, Lilienfeld,
104+
J. Chem. Phys. 148, 241717 (2018),
105+
https://doi.org/10.1063/1.5020710
106+
107+
- | **Columb Matrix** ``generate_columnb_matrix_*``
108+
Fast and Accurate Modeling of Molecular Atomization Energies with Machine Learning,
109+
Rupp, Tkatchenko, Müller, Lilienfeld,
110+
Phys. Rev. Lett. 108, 058301 (2012)
111+
DOI: https://doi.org/10.1103/PhysRevLett.108.058301
112+
113+
- | **Bag of Bonds (BoB)** ``generate_bob``
114+
Assessment and Validation of Machine Learning Methods for Predicting Molecular Atomization Energies,
115+
Hansen, Montavon, Biegler, Fazli, Rupp, Scheffler, Lilienfeld, Tkatchenko, Müller,
116+
J. Chem. Theory Comput. 2013, 9, 8, 3404–3419
117+
https://doi.org/10.1021/ct400195d
118+
119+
- | **SLATM** ``generate_slatm``
120+
Understanding molecular representations in machine learning: The role of uniqueness and target similarity,
121+
Huang, Lilienfeld,
122+
J. Chem. Phys. 145, 161102 (2016)
123+
https://doi.org/10.1063/1.4964627
124+
125+
- | **ACSF** ``generate_acsf``
126+
Atom-centered symmetry functions for constructing high-dimensional neural network potentials,
127+
Behler,
128+
J Chem Phys 21;134(7):074106 (2011)
129+
https://doi.org/10.1063/1.3553717
130+
131+
- | **AARAD** ``generate_aarad``
132+
Alchemical and structural distribution based representation for universal quantum machine learning,
133+
Faber, Christensen, Huang, Lilienfeld,
134+
J. Chem. Phys. 148, 241717 (2018),
135+
https://doi.org/10.1063/1.5020710
136+
137+
138+
===================
139+
What is left to do?
140+
===================
141+
142+
- Compile based on ``FCC`` env variable
143+
- if ``ifort`` find the right flags
144+
- Find MKL from env (for example conda)
145+
- Find what numpy has been linked too (lapack or mkl)
146+
- Notebook examples

environment_dev.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ dependencies:
1414
- pre-commit
1515
- pyarrow
1616
- pytest
17+
- pytest-cov
1718
- scikit-learn
1819
- scipy
1920
# build

src/qmllib/kernels/fdistance.f90

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
subroutine fmanhattan_distance(A, B, D)
32

43
implicit none

src/qmllib/kernels/fgradient_kernels.f90

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,3 @@
1-
2-
3-
4-
5-
6-
7-
8-
9-
10-
11-
12-
13-
14-
15-
16-
17-
18-
19-
20-
21-
22-
231
subroutine fglobal_kernel(x1, x2, q1, q2, n1, n2, nm1, nm2, sigma, kernel)
242

253
implicit none

src/qmllib/kernels/fkernels.f90

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
subroutine fget_local_kernels_gaussian(q1, q2, n1, n2, sigmas, &
32
& nm1, nm2, nsigmas, kernels)
43

src/qmllib/kernels/fkpca.f90

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
subroutine fkpca(k, n, centering, kpca)
32

43
implicit none

src/qmllib/kernels/fkwasserstein.f90

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
module searchtools
32

43
implicit none

src/qmllib/kernels/gradient_kernels.py

Lines changed: 40 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,10 @@ def get_global_kernel(
5959
N1 = np.array([len(Q) for Q in Q1], dtype=np.int32)
6060
N2 = np.array([len(Q) for Q in Q2], dtype=np.int32)
6161

62-
assert (
63-
N1.shape[0] == X1.shape[0]
64-
), "Error: List of charges does not match shape of representations"
65-
assert (
66-
N2.shape[0] == X2.shape[0]
67-
), "Error: List of charges does not match shape of representations"
62+
if not (N1.shape[0] == X1.shape[0]):
63+
raise ValueError("List of charges does not match shape of representations")
64+
if not (N2.shape[0] == X2.shape[0]):
65+
raise ValueError("Error: List of charges does not match shape of representations")
6866

6967
Q1_input = np.zeros((max(N1), X1.shape[0]), dtype=np.int32)
7068
Q2_input = np.zeros((max(N2), X2.shape[0]), dtype=np.int32)
@@ -114,12 +112,10 @@ def get_local_kernels(
114112
N1 = np.array([len(Q) for Q in Q1], dtype=np.int32)
115113
N2 = np.array([len(Q) for Q in Q2], dtype=np.int32)
116114

117-
assert (
118-
N1.shape[0] == X1.shape[0]
119-
), "Error: List of charges does not match shape of representations"
120-
assert (
121-
N2.shape[0] == X2.shape[0]
122-
), "Error: List of charges does not match shape of representations"
115+
if not (N1.shape[0] == X1.shape[0]):
116+
raise ValueError("Error: List of charges does not match shape of representations")
117+
if not (N2.shape[0] == X2.shape[0]):
118+
raise ValueError("Error: List of charges does not match shape of representations")
123119

124120
Q1_input = np.zeros((max(N1), X1.shape[0]), dtype=np.int32)
125121
Q2_input = np.zeros((max(N2), X2.shape[0]), dtype=np.int32)
@@ -176,12 +172,10 @@ def get_local_kernel(
176172
N1 = np.array([len(Q) for Q in Q1], dtype=np.int32)
177173
N2 = np.array([len(Q) for Q in Q2], dtype=np.int32)
178174

179-
assert (
180-
N1.shape[0] == X1.shape[0]
181-
), "Error: List of charges does not match shape of representations"
182-
assert (
183-
N2.shape[0] == X2.shape[0]
184-
), "Error: List of charges does not match shape of representations"
175+
if not (N1.shape[0] == X1.shape[0]):
176+
raise ValueError("List of charges does not match shape of representations")
177+
if not (N2.shape[0] == X2.shape[0]):
178+
raise ValueError("List of charges does not match shape of representations")
185179

186180
Q1_input = np.zeros((max(N1), X1.shape[0]), dtype=np.int32)
187181
Q2_input = np.zeros((max(N2), X2.shape[0]), dtype=np.int32)
@@ -228,9 +222,8 @@ def get_local_symmetric_kernels(X1: ndarray, Q1: List[List[int]], SIGMAS: List[f
228222

229223
N1 = np.array([len(Q) for Q in Q1], dtype=np.int32)
230224

231-
assert (
232-
N1.shape[0] == X1.shape[0]
233-
), "Error: List of charges does not match shape of representations"
225+
if not (N1.shape[0] == X1.shape[0]):
226+
raise ValueError("Error: List of charges does not match shape of representations")
234227

235228
Q1_input = np.zeros((max(N1), X1.shape[0]), dtype=np.int32)
236229
for i, q in enumerate(Q1):
@@ -275,9 +268,8 @@ def get_local_symmetric_kernel(
275268

276269
N1 = np.array([len(Q) for Q in Q1], dtype=np.int32)
277270

278-
assert (
279-
N1.shape[0] == X1.shape[0]
280-
), "Error: List of charges does not match shape of representations"
271+
if not (N1.shape[0] == X1.shape[0]):
272+
raise ValueError("Error: List of charges does not match shape of representations")
281273

282274
Q1_input = np.zeros((max(N1), X1.shape[0]), dtype=np.int32)
283275
for i, q in enumerate(Q1):
@@ -329,12 +321,10 @@ def get_atomic_local_kernel(
329321
N1 = np.array([len(Q) for Q in Q1], dtype=np.int32)
330322
N2 = np.array([len(Q) for Q in Q2], dtype=np.int32)
331323

332-
assert (
333-
N1.shape[0] == X1.shape[0]
334-
), "Error: List of charges does not match shape of representations"
335-
assert (
336-
N2.shape[0] == X2.shape[0]
337-
), "Error: List of charges does not match shape of representations"
324+
if not (N1.shape[0] == X1.shape[0]):
325+
raise ValueError("List of charges does not match shape of representations")
326+
if not (N2.shape[0] == X2.shape[0]):
327+
raise ValueError("List of charges does not match shape of representations")
338328

339329
Q1_input = np.zeros((max(N1), X1.shape[0]), dtype=np.int32)
340330
Q2_input = np.zeros((max(N2), X2.shape[0]), dtype=np.int32)
@@ -394,12 +384,10 @@ def get_atomic_local_gradient_kernel(
394384
N1 = np.array([len(Q) for Q in Q1], dtype=np.int32)
395385
N2 = np.array([len(Q) for Q in Q2], dtype=np.int32)
396386

397-
assert (
398-
N1.shape[0] == X1.shape[0]
399-
), "Error: List of charges does not match shape of representations"
400-
assert (
401-
N2.shape[0] == X2.shape[0]
402-
), "Error: List of charges does not match shape of representations"
387+
if not (N1.shape[0] == X1.shape[0]):
388+
raise ValueError("List of charges does not match shape of representations")
389+
if not (N2.shape[0] == X2.shape[0]):
390+
raise ValueError("List of charges does not match shape of representations")
403391

404392
Q1_input = np.zeros((max(N1), X1.shape[0]), dtype=np.int32)
405393
Q2_input = np.zeros((max(N2), X2.shape[0]), dtype=np.int32)
@@ -475,12 +463,10 @@ def get_local_gradient_kernel(
475463
N1 = np.array([len(Q) for Q in Q1], dtype=np.int32)
476464
N2 = np.array([len(Q) for Q in Q2], dtype=np.int32)
477465

478-
assert (
479-
N1.shape[0] == X1.shape[0]
480-
), "Error: List of charges does not match shape of representations"
481-
assert (
482-
N2.shape[0] == X2.shape[0]
483-
), "Error: List of charges does not match shape of representations"
466+
if not (N1.shape[0] == X1.shape[0]):
467+
raise ValueError("List of charges does not match shape of representations")
468+
if not (N2.shape[0] == X2.shape[0]):
469+
raise ValueError("List of charges does not match shape of representations")
484470

485471
Q1_input = np.zeros((max(N1), X1.shape[0]), dtype=np.int32)
486472
Q2_input = np.zeros((max(N2), X2.shape[0]), dtype=np.int32)
@@ -552,12 +538,10 @@ def get_gdml_kernel(
552538
N1 = np.array([len(Q) for Q in Q1], dtype=np.int32)
553539
N2 = np.array([len(Q) for Q in Q2], dtype=np.int32)
554540

555-
assert (
556-
N1.shape[0] == X1.shape[0]
557-
), "Error: List of charges does not match shape of representations"
558-
assert (
559-
N2.shape[0] == X2.shape[0]
560-
), "Error: List of charges does not match shape of representations"
541+
if not (N1.shape[0] == X1.shape[0]):
542+
raise ValueError("List of charges does not match shape of representations")
543+
if not (N2.shape[0] == X2.shape[0]):
544+
raise ValueError("List of charges does not match shape of representations")
561545

562546
Q1_input = np.zeros((max(N1), X1.shape[0]), dtype=np.int32)
563547
Q2_input = np.zeros((max(N2), X2.shape[0]), dtype=np.int32)
@@ -627,9 +611,8 @@ def get_symmetric_gdml_kernel(
627611

628612
N1 = np.array([len(Q) for Q in Q1], dtype=np.int32)
629613

630-
assert (
631-
N1.shape[0] == X1.shape[0]
632-
), "Error: List of charges does not match shape of representations"
614+
if not (N1.shape[0] == X1.shape[0]):
615+
raise ValueError("List of charges does not match shape of representations")
633616

634617
Q1_input = np.zeros((max(N1), X1.shape[0]), dtype=np.int32)
635618

@@ -692,12 +675,10 @@ def get_gp_kernel(
692675
N1 = np.array([len(Q) for Q in Q1], dtype=np.int32)
693676
N2 = np.array([len(Q) for Q in Q2], dtype=np.int32)
694677

695-
assert (
696-
N1.shape[0] == X1.shape[0]
697-
), "Error: List of charges does not match shape of representations"
698-
assert (
699-
N2.shape[0] == X2.shape[0]
700-
), "Error: List of charges does not match shape of representations"
678+
if not (N1.shape[0] == X1.shape[0]):
679+
raise ValueError("List of charges does not match shape of representations")
680+
if not (N2.shape[0] == X2.shape[0]):
681+
raise ValueError("List of charges does not match shape of representations")
701682

702683
Q1_input = np.zeros((max(N1), X1.shape[0]), dtype=np.int32)
703684
Q2_input = np.zeros((max(N2), X2.shape[0]), dtype=np.int32)
@@ -765,9 +746,8 @@ def get_symmetric_gp_kernel(
765746

766747
N1 = np.array([len(Q) for Q in Q1], dtype=np.int32)
767748

768-
assert (
769-
N1.shape[0] == X1.shape[0]
770-
), "Error: List of charges does not match shape of representations"
749+
if not (N1.shape[0] == X1.shape[0]):
750+
raise ValueError("List of charges does not match shape of representations")
771751

772752
Q1_input = np.zeros((max(N1), X1.shape[0]), dtype=np.int32)
773753

0 commit comments

Comments
 (0)