diff --git a/docs/source/qml.rst b/docs/source/qml.rst index a24f9ff36..5a89437c7 100644 --- a/docs/source/qml.rst +++ b/docs/source/qml.rst @@ -8,7 +8,7 @@ Python API documentation qml\.representations module --------------------------- -.. automodule:: qml.representations +.. automodule:: qml.representations.representations :members: :undoc-members: :show-inheritance: @@ -16,7 +16,7 @@ qml\.representations module qml\.kernels module ------------------- -.. automodule:: qml.kernels +.. automodule:: qml.kernels.kernels :members: :undoc-members: :show-inheritance: @@ -32,7 +32,7 @@ qml\.distance module qml\.math module ---------------- -.. automodule:: qml.math +.. automodule:: qml.math.math :members: :undoc-members: :show-inheritance: @@ -45,30 +45,44 @@ qml\.Compound class :undoc-members: :show-inheritance: -.. qml\.arad module -.. ---------------- -.. -.. .. automodule:: qml.arad -.. :members: -.. :show-inheritance: -qml\.fchl module +qml\.fchl module, representations ---------------- -.. automodule:: qml.representations.fchl + .. automodule:: qml.fchl.fchl_representations :members: :show-inheritance: +qml\.fchl module, scalar kernels +---------------- + .. automodule:: qml.fchl.fchl_scalar_kernels + :members: + :show-inheritance: -qml\.wrappers module --------------------- +qml\.fchl module, force kernels +---------------- + + .. automodule:: qml.fchl.fchl_force_kernels + :members: + :show-inheritance: -.. automodule:: qml.kernels.wrappers +qml\.fchl module, electric-field kernels +---------------- + + .. automodule:: qml.fchl.fchl_electric_field_kernels :members: - :undoc-members: :show-inheritance: +.. qml\.wrappers module +.. -------------------- +.. +.. .. automodule:: qml.kernels.wrappers +.. :members: +.. :undoc-members: +.. :show-inheritance: + + .. qml\.data module .. ---------------- .. @@ -85,6 +99,12 @@ qml\.wrappers module .. :undoc-members: .. :show-inheritance: +qml\.arad module +---------------- + +.. automodule:: qml.arad.arad + :members: + :show-inheritance: qml\.aglaia module ------------------ diff --git a/qml/arad/arad.py b/qml/arad/arad.py index 64ebac7d2..1d226de6c 100644 --- a/qml/arad/arad.py +++ b/qml/arad/arad.py @@ -33,7 +33,7 @@ from .farad_kernels import fget_atomic_kernels_arad from .farad_kernels import fget_atomic_symmetric_kernels_arad -from ..representations.alchemy import PTP +from qml.data.alchemy import PTP def getAngle(sp,norms): epsilon = 10.* np.finfo(float).eps diff --git a/qml/data/__init__.py b/qml/data/__init__.py index c7a9bb4df..7b2ca3566 100644 --- a/qml/data/__init__.py +++ b/qml/data/__init__.py @@ -20,5 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. + from .xyzdataprovider import XYZDataProvider from .compound import Compound +from .alchemy import ELEMENT_NAME, NUCLEAR_CHARGE diff --git a/qml/representations/alchemy.py b/qml/data/alchemy.py similarity index 85% rename from qml/representations/alchemy.py rename to qml/data/alchemy.py index ad5111f20..dd59d7a5e 100644 --- a/qml/representations/alchemy.py +++ b/qml/data/alchemy.py @@ -26,6 +26,122 @@ import numpy as np from copy import copy +ELEMENT_NAME = { + 1: 'H' , + 2: 'He' , + 3: 'Li' , + 4: 'Be' , + 5: 'B' , + 6: 'C' , + 7: 'N' , + 8: 'O' , + 9: 'F' , + 10: 'Ne' , + 11: 'Na' , + 12: 'Mg' , + 13: 'Al' , + 14: 'Si' , + 15: 'P' , + 16: 'S' , + 17: 'Cl' , + 18: 'Ar' , + 19: 'K' , + 20: 'Ca' , + 21: 'Sc' , + 22: 'Ti' , + 23: 'V' , + 24: 'Cr' , + 25: 'Mn' , + 26: 'Fe' , + 27: 'Co' , + 28: 'Ni' , + 29: 'Cu' , + 30: 'Zn' , + 31: 'Ga' , + 32: 'Ge' , + 33: 'As' , + 34: 'Se' , + 35: 'Br' , + 36: 'Kr' , + 37: 'Rb' , + 38: 'Sr' , + 39: 'Y' , + 40: 'Zr' , + 41: 'Nb' , + 42: 'Mo' , + 43: 'Tc' , + 44: 'Ru' , + 45: 'Rh' , + 46: 'Pd' , + 47: 'Ag' , + 48: 'Cd' , + 49: 'In' , + 50: 'Sn' , + 51: 'Sb' , + 52: 'Te' , + 53: 'I' , + 54: 'Xe' , + 55: 'Cs' , + 56: 'Ba' , + 57: 'La' , + 58: 'Ce' , + 59: 'Pr' , + 60: 'Nd' , + 61: 'Pm' , + 62: 'Sm' , + 63: 'Eu' , + 64: 'Gd' , + 65: 'Tb' , + 66: 'Dy' , + 67: 'Ho' , + 68: 'Er' , + 69: 'Tm' , + 70: 'Yb' , + 71: 'Lu' , + 72: 'Hf' , + 73: 'Ta' , + 74: 'W' , + 75: 'Re' , + 76: 'Os' , + 77: 'Ir' , + 78: 'Pt' , + 79: 'Au' , + 80: 'Hg' , + 81: 'Tl' , + 82: 'Pb' , + 83: 'Bi' , + 84: 'Po' , + 85: 'At' , + 86: 'Rn' , + 87: 'Fr' , + 88: 'Ra' , + 89: 'Ac' , + 90: 'Th' , + 91: 'Pa' , + 92: 'U' , + 93: 'Np' , + 94: 'Pu' , + 95: 'Am' , + 96: 'Cm' , + 97: 'Bk' , + 98: 'Cf' , + 99: 'Es' , + 100: 'Fm' , + 101: 'Md' , + 102: 'No' , + 103: 'Lr' , + 104: 'Rf' , + 105: 'Db' , + 106: 'Sg' , + 107: 'Bh' , + 108: 'Hs' , + 109: 'Mt' , + 110: 'Ds' , + 111: 'Rg' , + 112: 'Cn' , + 114: 'Uuq', + 116: 'Uuh'} + NUCLEAR_CHARGE = { 'H' : 1, 'He' : 2, diff --git a/qml/data/compound.py b/qml/data/compound.py index 64c612c02..bdd48c66f 100644 --- a/qml/data/compound.py +++ b/qml/data/compound.py @@ -25,7 +25,7 @@ import numpy as np import collections -from ..representations.alchemy import NUCLEAR_CHARGE +from .alchemy import NUCLEAR_CHARGE from ..representations import generate_coulomb_matrix from ..representations import generate_atomic_coulomb_matrix diff --git a/qml/fchl/__init__.py b/qml/fchl/__init__.py index f8ed9ea85..833615cca 100644 --- a/qml/fchl/__init__.py +++ b/qml/fchl/__init__.py @@ -1,6 +1,6 @@ # MIT License # -# Copyright (c) 2017 Anders S. Christensen and Felix A. Faber +# Copyright (c) 2017 Anders S. Christensen, Felix A. Faber # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,4 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from .fchl import * +from .fchl_representations import * +from .fchl_scalar_kernels import * +from .fchl_force_kernels import * +from .fchl_electric_field_kernels import * diff --git a/qml/fchl/fchl_electric_field_kernels.py b/qml/fchl/fchl_electric_field_kernels.py new file mode 100644 index 000000000..6ba5ebc04 --- /dev/null +++ b/qml/fchl/fchl_electric_field_kernels.py @@ -0,0 +1,490 @@ +# MIT License +# +# Copyright (c) 2017-2018 Felix Faber and Anders Steen Christensen +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import absolute_import + +import numpy as np +import copy + +from .ffchl_module import fget_ef_gaussian_process_kernels_fchl +from .ffchl_module import fget_ef_atomic_local_kernels_fchl +from .ffchl_module import fget_ef_atomic_local_gradient_kernels_fchl + +from .fchl_kernel_functions import get_kernel_parameters + +from qml.data.alchemy import get_alchemy + + +# def get_local_kernels_ef(A, B, verbose=False, df=0.01, ef_scaling=0.01,\ +# two_body_scaling=np.sqrt(8), three_body_scaling=1.6, +# two_body_width=0.2, three_body_width=np.pi, +# two_body_power=4.0, three_body_power=2.0, +# cut_start=1.0, cut_distance=5.0, +# fourier_order=1, alchemy="periodic-table", +# alchemy_period_width=1.6, alchemy_group_width=1.6, +# kernel="gaussian", kernel_args=None): +# """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: +# +# :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` +# +# Where :math:`A_{i}` and :math:`B_{j}` are FCHL representation vectors. +# K is calculated analytically using an OpenMP parallel Fortran routine. +# Note, that this kernel will ONLY work with FCHL representations as input. +# +# :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). +# :type A: numpy array +# :param B: Array of FCHL representation - shape=(M, maxsize, 5, maxneighbors). +# :type B: numpy array +# +# :param two_body_scaling: Weight for 2-body terms. +# :type two_body_scaling: float +# :param three_body_scaling: Weight for 3-body terms. +# :type three_body_scaling: float +# +# :param two_body_width: Gaussian width for 2-body terms +# :type two_body_width: float +# :param three_body_width: Gaussian width for 3-body terms. +# :type three_body_width: float +# +# :param two_body_power: Powerlaw for :math:`r^{-n}` 2-body terms. +# :type two_body_power: float +# :param three_body_power: Powerlaw for Axilrod-Teller-Muto 3-body term +# :type three_body_power: float +# +# :param cut_start: The fraction of the cut-off radius at which cut-off damping start. +# :type cut_start: float +# :param cut_distance: Cut-off radius. (default=5 angstrom) +# :type cut_distance: float +# +# :param fourier_order: 3-body Fourier-expansion truncation order. +# :type fourier_order: integer +# :param alchemy: Type of alchemical interpolation ``"periodic-table"`` or ``"off"`` are possible options. Disabling alchemical interpolation can yield dramatic speedups. +# :type alchemy: string +# +# :param alchemy_period_width: Gaussian width along periods (columns) in the periodic table. +# :type alchemy_period_width: float +# :param alchemy_group_width: Gaussian width along groups (rows) in the periodic table. +# :type alchemy_group_width: float +# +# :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, M), +# :rtype: numpy array +# """ +# +# atoms_max = A.shape[1] +# neighbors_max = A.shape[3] +# +# assert B.shape[1] == atoms_max, "ERROR: Check FCHL representation sizes! code = 2" +# assert B.shape[3] == neighbors_max, "ERROR: Check FCHL representation sizes! code = 3" +# +# +# nm1 = A.shape[0] +# nm2 = B.shape[0] +# +# N1 = np.zeros((nm1),dtype=np.int32) +# N2 = np.zeros((nm2),dtype=np.int32) +# +# for a in range(nm1): +# N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) +# +# for a in range(nm2): +# N2[a] = len(np.where(B[a,:,1,0] > 0.0001)[0]) +# +# neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) +# neighbors2 = np.zeros((nm2, atoms_max), dtype=np.int32) +# +# for a, representation in enumerate(A): +# ni = N1[a] +# for i, x in enumerate(representation[:ni]): +# neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) +# +# for a, representation in enumerate(B): +# ni = N2[a] +# for i, x in enumerate(representation[:ni]): +# neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) +# +# doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) +# +# kernel_idx, kernel_parameters, n_kernels = get_kernel_parameters(kernel, kernel_args) +# +# return fget_kernels_fchl_ef(A, B, N1, N2, neighbors1, neighbors2, nm1, nm2, n_kernels, \ +# three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, +# three_body_scaling, doalchemy, two_body_power, three_body_power, ef_scaling, df, kernel_idx, kernel_parameters) + +def get_atomic_local_electric_field_gradient_kernels(A, B, verbose=False, df=0.01, ef_scaling=0.01,\ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: + + :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` + + Where :math:`A_{i}` and :math:`B_{j}` are FCHL representation vectors. + K is calculated analytically using an OpenMP parallel Fortran routine. + Note, that this kernel will ONLY work with FCHL representations as input. + + :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). + :type A: numpy array + :param B: Array of FCHL representation - shape=(M, maxsize, 5, maxneighbors). + :type B: numpy array + + :param two_body_scaling: Weight for 2-body terms. + :type two_body_scaling: float + :param three_body_scaling: Weight for 3-body terms. + :type three_body_scaling: float + + :param two_body_width: Gaussian width for 2-body terms + :type two_body_width: float + :param three_body_width: Gaussian width for 3-body terms. + :type three_body_width: float + + :param two_body_power: Powerlaw for :math:`r^{-n}` 2-body terms. + :type two_body_power: float + :param three_body_power: Powerlaw for Axilrod-Teller-Muto 3-body term + :type three_body_power: float + + :param cut_start: The fraction of the cut-off radius at which cut-off damping start. + :type cut_start: float + :param cut_distance: Cut-off radius. (default=5 angstrom) + :type cut_distance: float + + :param fourier_order: 3-body Fourier-expansion truncation order. + :type fourier_order: integer + :param alchemy: Type of alchemical interpolation ``"periodic-table"`` or ``"off"`` are possible options. Disabling alchemical interpolation can yield dramatic speedups. + :type alchemy: string + + :param alchemy_period_width: Gaussian width along periods (columns) in the periodic table. + :type alchemy_period_width: float + :param alchemy_group_width: Gaussian width along groups (rows) in the periodic table. + :type alchemy_group_width: float + + :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, M), + :rtype: numpy array + """ + + atoms_max = A.shape[1] + neighbors_max = A.shape[3] + + # assert B.shape[1] == atoms_max, "ERROR: Check FCHL representation sizes! code = 2" + # assert B.shape[3] == neighbors_max, "ERROR: Check FCHL representation sizes! code = 3" + + + nm1 = A.shape[0] + nm2 = B.shape[0] + + N1 = np.zeros((nm1),dtype=np.int32) + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + for a in range(nm2): + N2[a] = len(np.where(B[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + neighbors2 = np.zeros((nm2, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + for a, representation in enumerate(B): + ni = N2[a] + for i, x in enumerate(representation[:ni]): + neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, n_kernels = get_kernel_parameters(kernel, kernel_args) + + na1 = np.sum(N1) + + return fget_ef_atomic_local_gradient_kernels_fchl(A, B, verbose, N1, N2, neighbors1, neighbors2, nm1, nm2, na1, n_kernels, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, + three_body_scaling, doalchemy, two_body_power, three_body_power, ef_scaling, df, kernel_idx, kernel_parameters) + + +def get_gaussian_process_electric_field_kernels(A, B, verbose=False, fields=None, df=0.01, ef_scaling=0.01, + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: + + :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` + + Where :math:`A_{i}` and :math:`B_{j}` are FCHL representation vectors. + K is calculated analytically using an OpenMP parallel Fortran routine. + Note, that this kernel will ONLY work with FCHL representations as input. + + :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). + :type A: numpy array + :param B: Array of FCHL representation - shape=(M, maxsize, 5, maxneighbors). + :type B: numpy array + + :param two_body_scaling: Weight for 2-body terms. + :type two_body_scaling: float + :param three_body_scaling: Weight for 3-body terms. + :type three_body_scaling: float + + :param two_body_width: Gaussian width for 2-body terms + :type two_body_width: float + :param three_body_width: Gaussian width for 3-body terms. + :type three_body_width: float + + :param two_body_power: Powerlaw for :math:`r^{-n}` 2-body terms. + :type two_body_power: float + :param three_body_power: Powerlaw for Axilrod-Teller-Muto 3-body term + :type three_body_power: float + + :param cut_start: The fraction of the cut-off radius at which cut-off damping start. + :type cut_start: float + :param cut_distance: Cut-off radius. (default=5 angstrom) + :type cut_distance: float + + :param fourier_order: 3-body Fourier-expansion truncation order. + :type fourier_order: integer + :param alchemy: Type of alchemical interpolation ``"periodic-table"`` or ``"off"`` are possible options. Disabling alchemical interpolation can yield dramatic speedups. + :type alchemy: string + + :param alchemy_period_width: Gaussian width along periods (columns) in the periodic table. + :type alchemy_period_width: float + :param alchemy_group_width: Gaussian width along groups (rows) in the periodic table. + :type alchemy_group_width: float + + :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, M), + :rtype: numpy array + """ + + atoms_max = A.shape[1] + neighbors_max = A.shape[3] + + assert B.shape[1] == atoms_max, "ERROR: Check FCHL representation sizes! code = 2" + assert B.shape[3] == neighbors_max, "ERROR: Check FCHL representation sizes! code = 3" + + + nm1 = A.shape[0] + nm2 = B.shape[0] + + N1 = np.zeros((nm1),dtype=np.int32) + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + for a in range(nm2): + N2[a] = len(np.where(B[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + neighbors2 = np.zeros((nm2, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + for a, representation in enumerate(B): + ni = N2[a] + for i, x in enumerate(representation[:ni]): + neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, n_kernels = get_kernel_parameters(kernel, kernel_args) + + F1 = np.zeros((nm1,3)) + F2 = np.zeros((nm2,3)) + + if (fields is not None): + + F1 = np.array(fields[0]) + F2 = np.array(fields[1]) + + return fget_ef_gaussian_process_kernels_fchl(A, B, verbose, F1, F2, N1, N2, neighbors1, neighbors2, nm1, nm2, n_kernels, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, + three_body_scaling, doalchemy, two_body_power, three_body_power, ef_scaling, df, kernel_idx, kernel_parameters) + + +def get_kernels_ef_field(A, B, fields=None, df=0.01, ef_scaling=0.01,\ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + + atoms_max = A.shape[1] + neighbors_max = A.shape[3] + + assert B.shape[1] == atoms_max, "ERROR: Check FCHL representation sizes! code = 2" + assert B.shape[3] == neighbors_max, "ERROR: Check FCHL representation sizes! code = 3" + + + nm1 = A.shape[0] + nm2 = B.shape[0] + + N1 = np.zeros((nm1),dtype=np.int32) + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + for a in range(nm2): + N2[a] = len(np.where(B[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + neighbors2 = np.zeros((nm2, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + for a, representation in enumerate(B): + ni = N2[a] + for i, x in enumerate(representation[:ni]): + neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, n_kernels = get_kernel_parameters(kernel, kernel_args) + + F2 = np.zeros((nm2,3)) + + if (fields is not None): + + F2 = np.array(fields) + + na1 = np.sum(N1) + + return fget_kernels_fchl_ef_field(A, B, F2, N1, N2, neighbors1, neighbors2, nm1, nm2, n_kernels, na1, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, + three_body_scaling, doalchemy, two_body_power, three_body_power, ef_scaling, kernel_idx, kernel_parameters) + +# +# Non-functional kernel for polarizabilites - unsure why it won't work +# +# def get_local_kernels_pol(A, B, df=1e-5, ef_scaling=1.0,\ +# two_body_scaling=np.sqrt(8), three_body_scaling=1.6, +# two_body_width=0.2, three_body_width=np.pi, +# two_body_power=4.0, three_body_power=2.0, +# cut_start=1.0, cut_distance=5.0, +# fourier_order=1, alchemy="periodic-table", +# alchemy_period_width=1.6, alchemy_group_width=1.6, +# kernel="gaussian", kernel_args=None): +# """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: +# +# :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` +# +# Where :math:`A_{i}` and :math:`B_{j}` are FCHL representation vectors. +# K is calculated analytically using an OpenMP parallel Fortran routine. +# Note, that this kernel will ONLY work with FCHL representations as input. +# +# :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). +# :type A: numpy array +# :param B: Array of FCHL representation - shape=(M, maxsize, 5, maxneighbors). +# :type B: numpy array +# +# :param two_body_scaling: Weight for 2-body terms. +# :type two_body_scaling: float +# :param three_body_scaling: Weight for 3-body terms. +# :type three_body_scaling: float +# +# :param two_body_width: Gaussian width for 2-body terms +# :type two_body_width: float +# :param three_body_width: Gaussian width for 3-body terms. +# :type three_body_width: float +# +# :param two_body_power: Powerlaw for :math:`r^{-n}` 2-body terms. +# :type two_body_power: float +# :param three_body_power: Powerlaw for Axilrod-Teller-Muto 3-body term +# :type three_body_power: float +# +# :param cut_start: The fraction of the cut-off radius at which cut-off damping start. +# :type cut_start: float +# :param cut_distance: Cut-off radius. (default=5 angstrom) +# :type cut_distance: float +# +# :param fourier_order: 3-body Fourier-expansion truncation order. +# :type fourier_order: integer +# :param alchemy: Type of alchemical interpolation ``"periodic-table"`` or ``"off"`` are possible options. Disabling alchemical interpolation can yield dramatic speedups. +# :type alchemy: string +# +# :param alchemy_period_width: Gaussian width along periods (columns) in the periodic table. +# :type alchemy_period_width: float +# :param alchemy_group_width: Gaussian width along groups (rows) in the periodic table. +# :type alchemy_group_width: float +# +# :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, M), +# :rtype: numpy array +# """ +# +# atoms_max = A.shape[1] +# neighbors_max = A.shape[3] +# +# assert B.shape[1] == atoms_max, "ERROR: Check FCHL representation sizes! code = 2" +# assert B.shape[3] == neighbors_max, "ERROR: Check FCHL representation sizes! code = 3" +# +# +# nm1 = A.shape[0] +# nm2 = B.shape[0] +# +# N1 = np.zeros((nm1),dtype=np.int32) +# N2 = np.zeros((nm2),dtype=np.int32) +# +# for a in range(nm1): +# N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) +# +# for a in range(nm2): +# N2[a] = len(np.where(B[a,:,1,0] > 0.0001)[0]) +# +# neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) +# neighbors2 = np.zeros((nm2, atoms_max), dtype=np.int32) +# +# for a, representation in enumerate(A): +# ni = N1[a] +# for i, x in enumerate(representation[:ni]): +# neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) +# +# for a, representation in enumerate(B): +# ni = N2[a] +# for i, x in enumerate(representation[:ni]): +# neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) +# +# doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) +# +# kernel_idx, kernel_parameters, n_kernels = get_kernel_parameters(kernel, kernel_args) +# +# na1 = np.sum(N1) +# +# return fget_kernels_fchl_ef_2ndderiv(A, B, N1, N2, neighbors1, neighbors2, nm1, nm2, na1, n_kernels, \ +# three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, +# three_body_scaling, doalchemy, two_body_power, three_body_power, ef_scaling, df, kernel_idx, kernel_parameters) +# diff --git a/qml/fchl/fchl_force_kernels.py b/qml/fchl/fchl_force_kernels.py new file mode 100644 index 000000000..51a64b0b8 --- /dev/null +++ b/qml/fchl/fchl_force_kernels.py @@ -0,0 +1,487 @@ +# MIT License +# +# Copyright (c) 2017-2018 Felix Faber and Anders Steen Christensen +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import absolute_import + +import numpy as np +import copy + + +from .ffchl_module import fget_gaussian_process_kernels_fchl +from .ffchl_module import fget_local_gradient_kernels_fchl +from .ffchl_module import fget_local_hessian_kernels_fchl +from .ffchl_module import fget_local_symmetric_hessian_kernels_fchl + +from .ffchl_module import fget_force_alphas_fchl +from .ffchl_module import fget_atomic_local_gradient_kernels_fchl +from .ffchl_module import fget_atomic_local_gradient_5point_kernels_fchl + +from .fchl_kernel_functions import get_kernel_parameters + +from qml.data.alchemy import get_alchemy + + +def get_gaussian_process_kernels(A, B, verbose=False, dx=0.005, \ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + + nm1 = A.shape[0] + nm2 = B.shape[0] + + assert B.shape[1] == 3 + assert B.shape[2] == 2 + assert B.shape[5] == 5 + assert A.shape[2] == 5 + # assert B.shape[2] == 2 + # assert B.shape[5] == 5 + + atoms_max = B.shape[4] + assert A.shape[1] == atoms_max + assert B.shape[3] == atoms_max + + neighbors_max = B.shape[6] + assert A.shape[3] == neighbors_max + + + N1 = np.zeros((nm1),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm2): + N2[a] = len(np.where(B[a,0,0,0,:,1,0] > 0.0001)[0]) + + neighbors2 = np.zeros((nm2, 3, 2, atoms_max, atoms_max), dtype=np.int32) + + for m in range(nm2): + ni = N2[m] + for xyz in range(3): + for pm in range(2): + for i in range(ni): + for a, x in enumerate(B[m,xyz,pm,i,:ni]): + neighbors2[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + naq2 = np.sum(N2) * 3 + + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + return fget_gaussian_process_kernels_fchl(A, B, verbose, N1, N2, neighbors1, neighbors2, \ + nm1, nm2, naq2, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, dx, kernel_idx, kernel_parameters) + + +def get_local_gradient_kernels(A, B, verbose=False, dx=0.005, + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + + nm1 = A.shape[0] + nm2 = B.shape[0] + + assert B.shape[1] == 3 + assert B.shape[2] == 2 + assert B.shape[5] == 5 + assert A.shape[2] == 5 + # assert B.shape[2] == 2 + # assert B.shape[5] == 5 + + atoms_max = B.shape[4] + assert A.shape[1] == atoms_max + assert B.shape[3] == atoms_max + + neighbors_max = B.shape[6] + assert A.shape[3] == neighbors_max + + + N1 = np.zeros((nm1),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm2): + N2[a] = len(np.where(B[a,0,0,0,:,1,0] > 0.0001)[0]) + + neighbors2 = np.zeros((nm2, 3, 2, atoms_max, atoms_max), dtype=np.int32) + + for m in range(nm2): + ni = N2[m] + for xyz in range(3): + for pm in range(2): + for i in range(ni): + for a, x in enumerate(B[m,xyz,pm,i,:ni]): + neighbors2[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + naq2 = np.sum(N2) * 3 + + return fget_local_gradient_kernels_fchl(A, B, verbose, N1, N2, neighbors1, neighbors2, \ + nm1, nm2, naq2, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, dx, kernel_idx, kernel_parameters) + + +def get_local_hessian_kernels(A, B, verbose=False, dx=0.005, + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + + nm1 = A.shape[0] + nm2 = B.shape[0] + + assert A.shape[1] == 3 + assert A.shape[2] == 2 + assert A.shape[5] == 5 + assert B.shape[1] == 3 + assert B.shape[2] == 2 + assert B.shape[5] == 5 + + atoms_max = A.shape[4] + assert A.shape[3] == atoms_max + assert B.shape[3] == atoms_max + + neighbors_max = A.shape[6] + assert B.shape[6] == neighbors_max + + N1 = np.zeros((nm1),dtype=np.int32) + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,0,0,0,:,1,0] > 0.0001)[0]) + + for a in range(nm2): + N2[a] = len(np.where(B[a,0,0,0,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, 3, 2, atoms_max, atoms_max), dtype=np.int32) + neighbors2 = np.zeros((nm2, 3, 2, atoms_max, atoms_max), dtype=np.int32) + + for m in range(nm1): + ni = N1[m] + for xyz in range(3): + for pm in range(2): + for i in range(ni): + for a, x in enumerate(A[m,xyz,pm,i,:ni]): + neighbors1[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + for m in range(nm2): + ni = N2[m] + for xyz in range(3): + for pm in range(2): + for i in range(ni): + for a, x in enumerate(B[m,xyz,pm,i,:ni]): + neighbors2[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + + naq1 = np.sum(N1) * 3 + naq2 = np.sum(N2) * 3 + + # print naq1, naq2, nsigmas + return fget_local_hessian_kernels_fchl(A, B, verbose, N1, N2, neighbors1, neighbors2, \ + nm1, nm2, naq1, naq2, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, dx, kernel_idx, kernel_parameters) + + +def get_local_symmetric_hessian_kernels(A, verbose=False, dx=0.005, + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + + nm1 = A.shape[0] + + assert A.shape[1] == 3 + assert A.shape[2] == 2 + assert A.shape[5] == 5 + + atoms_max = A.shape[4] + assert A.shape[3] == atoms_max + + neighbors_max = A.shape[6] + + N1 = np.zeros((nm1),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,0,0,0,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, 3, 2, atoms_max, atoms_max), dtype=np.int32) + + for m in range(nm1): + ni = N1[m] + for xyz in range(3): + for pm in range(2): + for i in range(ni): + for a, x in enumerate(A[m,xyz,pm,i,:ni]): + neighbors1[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + + naq1 = np.sum(N1) * 3 + + return fget_local_symmetric_hessian_kernels_fchl(A, verbose, N1, neighbors1, nm1, naq1, nsigmas, \ + three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, dx, kernel_idx, kernel_parameters) + + +def get_force_alphas(A, B, F, verbose=False, energy=None, dx=0.005, regularization=1e-7, + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + nm1 = A.shape[0] + nm2 = B.shape[0] + + assert B.shape[1] == 3 + assert B.shape[2] == 2 + assert B.shape[5] == 5 + assert A.shape[2] == 5 + # assert B.shape[2] == 2 + # assert B.shape[5] == 5 + + atoms_max = B.shape[4] + assert A.shape[1] == atoms_max + assert B.shape[3] == atoms_max + + neighbors_max = B.shape[6] + assert A.shape[3] == neighbors_max + + + N1 = np.zeros((nm1),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm2): + N2[a] = len(np.where(B[a,0,0,0,:,1,0] > 0.0001)[0]) + + neighbors2 = np.zeros((nm2, 3, 2, atoms_max, atoms_max), dtype=np.int32) + + for m in range(nm2): + ni = N2[m] + for xyz in range(3): + for pm in range(2): + for i in range(ni): + for a, x in enumerate(B[m,xyz,pm,i,:ni]): + neighbors2[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + na1 = np.sum(N1) + naq2 = np.sum(N2) * 3 + + E = np.zeros((nm1)) + if energy is not None: + E = energy + + return fget_force_alphas_fchl(A, B, verbose, F, E, N1, N2, neighbors1, neighbors2, \ + nm1, nm2, na1, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, + three_body_power, dx, kernel_idx, kernel_parameters, regularization) + + +def get_atomic_local_gradient_kernels(A, B, verbose=False, dx = 0.005, + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + + nm1 = A.shape[0] + nm2 = B.shape[0] + + assert B.shape[1] == 3 + assert B.shape[2] == 2 + assert B.shape[5] == 5 + assert A.shape[2] == 5 + + atoms_max = B.shape[4] + assert A.shape[1] == atoms_max + assert B.shape[3] == atoms_max + + neighbors_max = B.shape[6] + assert A.shape[3] == neighbors_max + + + N1 = np.zeros((nm1),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm2): + N2[a] = len(np.where(B[a,0,0,0,:,1,0] > 0.0001)[0]) + + neighbors2 = np.zeros((nm2, 3, 2, atoms_max, atoms_max), dtype=np.int32) + + for m in range(nm2): + ni = N2[m] + for xyz in range(3): + for pm in range(2): + for i in range(ni): + for a, x in enumerate(B[m,xyz,pm,i,:ni]): + neighbors2[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + + na1 = np.sum(N1) + naq2 = np.sum(N2) * 3 + + return fget_atomic_local_gradient_kernels_fchl(A, B, verbose, N1, N2, neighbors1, neighbors2, \ + nm1, nm2, na1, naq2, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, + three_body_power, dx, kernel_idx, kernel_parameters) + + +def get_atomic_local_gradient_5point_kernels(A, B, verbose=False, dx = 0.005, + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + + nm1 = A.shape[0] + nm2 = B.shape[0] + + assert B.shape[1] == 3 + assert B.shape[2] == 5 + assert B.shape[5] == 5 + assert A.shape[2] == 5 + + atoms_max = B.shape[4] + assert A.shape[1] == atoms_max + assert B.shape[3] == atoms_max + + neighbors_max = B.shape[6] + assert A.shape[3] == neighbors_max + + + N1 = np.zeros((nm1),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm2): + N2[a] = len(np.where(B[a,0,0,0,:,1,0] > 0.0001)[0]) + + neighbors2 = np.zeros((nm2, 3, 5, atoms_max, atoms_max), dtype=np.int32) + + for m in range(nm2): + ni = N2[m] + for xyz in range(3): + for pm in range(5): + for i in range(ni): + for a, x in enumerate(B[m,xyz,pm,i,:ni]): + neighbors2[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + + na1 = np.sum(N1) + naq2 = np.sum(N2) * 3 + + return fget_atomic_local_gradient_5point_kernels_fchl(A, B, verbose, N1, N2, neighbors1, neighbors2, \ + nm1, nm2, na1, naq2, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, + three_body_power, dx, kernel_idx, kernel_parameters) diff --git a/qml/fchl/fchl_kernel_functions.py b/qml/fchl/fchl_kernel_functions.py new file mode 100644 index 000000000..0b90ac563 --- /dev/null +++ b/qml/fchl/fchl_kernel_functions.py @@ -0,0 +1,288 @@ +# MIT License +# +# Copyright (c) 2017 Anders Steen Christensen and Felix A. Faber +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import division +from __future__ import print_function + +import numpy as np +from copy import copy + +import scipy +from scipy.special import binom +from scipy.misc import factorial + +def get_gaussian_parameters(tags): + + if tags is None: + tags = {"sigma": [2.5],} + + parameters = np.array(tags["sigma"]) + + for i in range(len(parameters)): + parameters[i] = -0.5 / (parameters[i])**2 + + np.resize(parameters, (1,len(tags["sigma"]))) + + n_kernels = len(tags["sigma"]) + + return 1, parameters, n_kernels + + +def get_linear_parameters(tags): + + if tags is None: + tags = {"c": [0.0],} + + + parameters = np.array(tags["c"]) + + np.resize(parameters, (1,len(tags["c"]))) + + n_kernels = len(tags["c"]) + + return 2, parameters, n_kernels + + +def get_polynomial_parameters(tags): + + if tags is None: + tags = { + "alpha": [1.0], + "c": [0.0], + "d": [1.0] + } + + parameters = np.array([ + tags["alpha"], + tags["c"], + tags["d"] + ]).T + assert len(tags["alpha"]) == len(tags["c"]) + assert len(tags["alpha"]) == len(tags["d"]) + + n_kernels = len(tags["alpha"]) + return 3, parameters, n_kernels + + +def get_sigmoid_parameters(tags): + + if tags is None: + tags = { + "alpha": [1.0], + "c": [0.0], + + } + + parameters = np.array([ + tags["alpha"], + tags["c"], + ]).T + assert len(tags["alpha"]) == len(tags["c"]) + n_kernels = len(tags["alpha"]) + + return 4, parameters, n_kernels + + +def get_multiquadratic_parameters(tags): + + if tags is None: + tags = { + "c": [0.0], + + } + + parameters = np.array([ + tags["c"], + ]).T + + np.resize(parameters, (1,len(tags["c"]))) + n_kernels = len(tags["c"]) + + return 5, parameters, n_kernels + + +def get_inverse_multiquadratic_parameters(tags): + + if tags is None: + tags = { + "c": [0.0], + + } + + parameters = np.array([ + tags["c"], + ]).T + + np.resize(parameters, (1,len(tags["c"]))) + n_kernels = len(tags["c"]) + + return 6, parameters, n_kernels + + +def get_bessel_parameters(tags): + + if tags is None: + tags = { + "sigma": [1.0], + "v": [1.0], + "n": [1.0] + } + + parameters = np.array([ + tags["sigma"], + tags["v"], + tags["n"] + ]).T + assert len(tags["sigma"]) == len(tags["v"]) + assert len(tags["sigma"]) == len(tags["n"]) + + n_kernels = len(tags["sigma"]) + + return 7, parameters, n_kernels + +def get_l2_parameters(tags): + + if tags is None: + tags = { + "alpha": [1.0], + "c": [0.0], + + } + + parameters = np.array([ + tags["alpha"], + tags["c"], + ]).T + assert len(tags["alpha"]) == len(tags["c"]) + n_kernels = len(tags["alpha"]) + + return 8, parameters, n_kernels + + +def get_matern_parameters(tags): + + if tags is None: + tags = { + "sigma": [10.0], + "n": [2.0], + + } + + assert len(tags["sigma"]) == len(tags["n"]) + n_kernels = len(tags["sigma"]) + + n_max = int(max(tags["n"])) + 1 + + parameters = np.zeros((2+n_max, n_kernels)) + + for i in range(n_kernels): + parameters[0,i] = tags["sigma"][i] + parameters[1,i] = tags["n"][i] + + n = int(tags["n"][i]) + for k in range(0, n+1): + parameters[2+k,i] = float(factorial(n + k) * binom(n, k))/ factorial(2*n) + + parameters = parameters.T + + return 9, parameters, n_kernels + + +def get_cauchy_parameters(tags): + + if tags is None: + tags = { + "sigma": [1.0], + + } + + parameters = np.array([ + tags["sigma"], + ]).T + + np.resize(parameters, (1,len(tags["sigma"]))) + n_kernels = len(tags["sigma"]) + + return 10, parameters, n_kernels + +def get_polynomial2_parameters(tags): + + if tags is None: + tags = { + "coeff": [[1.0, 1.0, 1.0]], + } + + parameters = np.zeros((10,len(tags["coeff"]))) + + for i, c in enumerate(tags["coeff"]): + for j, v in enumerate(c): + parameters[j,i] = v + + n_kernels = len(tags["coeff"]) + parameters = parameters.T + return 11, parameters, n_kernels + +def get_kernel_parameters(name, tags): + + parameters = None + idx = 1 + n_kernels = 1 + + if name == "gaussian": + idx, parameters, n_kernels = get_gaussian_parameters(tags) + + elif name == "linear": + idx, parameters, n_kernels = get_linear_parameters(tags) + + elif name == "polynomial": + idx, parameters, n_kernels = get_polynomial_parameters(tags) + + elif name == "sigmoid": + idx, parameters, n_kernels = get_sigmoid_parameters(tags) + + elif name == "multiquadratic": + idx, parameters, n_kernels = get_multiquadratic_parameters(tags) + + elif name == "inverse-multiquadratic": + idx, parameters, n_kernels = get_inverse_multiquadratic_parameters(tags) + + elif name == "bessel": + idx, parameters, n_kernels = get_bessel_parameters(tags) + + elif name == "l2": + idx, parameters, n_kernels = get_l2_parameters(tags) + + elif name == "matern": + idx, parameters, n_kernels = get_matern_parameters(tags) + + elif name == "cauchy": + idx, parameters, n_kernels = get_cauchy_parameters(tags) + + elif name == "polynomial2": + idx, parameters, n_kernels = get_polynomial2_parameters(tags) + + else: + + print("QML ERROR: Unsupported kernel specification,", name) + exit() + + return idx, parameters, n_kernels diff --git a/qml/fchl/fchl_kernels.py b/qml/fchl/fchl_kernels.py new file mode 100644 index 000000000..bd3bb2c2d --- /dev/null +++ b/qml/fchl/fchl_kernels.py @@ -0,0 +1,1551 @@ +# MIT License +# +# Copyright (c) 2017-2018 Felix Faber and Anders Steen Christensen +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import absolute_import + +import numpy as np +import copy + + +from .ffchl_module import fget_kernels_fchl +from .ffchl_module import fget_symmetric_kernels_fchl +from .ffchl_module import fget_global_kernels_fchl +from .ffchl_module import fget_global_symmetric_kernels_fchl +from .ffchl_module import fget_atomic_kernels_fchl +from .ffchl_module import fget_atomic_symmetric_kernels_fchl + +from .ffchl_module import fget_local_full_kernels_fchl +from .ffchl_module import fget_local_gradient_kernels_fchl +from .ffchl_module import fget_local_hessian_kernels_fchl +from .ffchl_module import fget_local_symmetric_hessian_kernels_fchl + +from .ffchl_module import fget_local_invariant_alphas_fchl +from .ffchl_module import fget_atomic_gradient_kernels_fchl +from .ffchl_module import fget_local_atomic_kernels_fchl + +from .ffchl_module import fget_kernels_fchl_ef +from .ffchl_module import fget_kernels_fchl_ef_2ndderiv +from .ffchl_module import fget_kernels_fchl_ef_deriv +from .ffchl_module import fget_gp_kernels_fchl_ef +from .ffchl_module import fget_smooth_atomic_gradient_kernels_fchl + +from .ffchl_module import fget_kernels_fchl_ef_field + +from .fchl_kernel_functions import get_kernel_parameters + +from qml.data.alchemy import get_alchemy + + +def get_local_kernels(A, B, \ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: + + :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` + + Where :math:`A_{i}` and :math:`B_{j}` are FCHL representation vectors. + K is calculated analytically using an OpenMP parallel Fortran routine. + Note, that this kernel will ONLY work with FCHL representations as input. + + :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). + :type A: numpy array + :param B: Array of FCHL representation - shape=(M, maxsize, 5, maxneighbors). + :type B: numpy array + + :param two_body_scaling: Weight for 2-body terms. + :type two_body_scaling: float + :param three_body_scaling: Weight for 3-body terms. + :type three_body_scaling: float + + :param two_body_width: Gaussian width for 2-body terms + :type two_body_width: float + :param three_body_width: Gaussian width for 3-body terms. + :type three_body_width: float + + :param two_body_power: Powerlaw for :math:`r^{-n}` 2-body terms. + :type two_body_power: float + :param three_body_power: Powerlaw for Axilrod-Teller-Muto 3-body term + :type three_body_power: float + + :param cut_start: The fraction of the cut-off radius at which cut-off damping start. + :type cut_start: float + :param cut_distance: Cut-off radius. (default=5 angstrom) + :type cut_distance: float + + :param fourier_order: 3-body Fourier-expansion truncation order. + :type fourier_order: integer + :param alchemy: Type of alchemical interpolation ``"periodic-table"`` or ``"off"`` are possible options. Disabling alchemical interpolation can yield dramatic speedups. + :type alchemy: string + + :param alchemy_period_width: Gaussian width along periods (columns) in the periodic table. + :type alchemy_period_width: float + :param alchemy_group_width: Gaussian width along groups (rows) in the periodic table. + :type alchemy_group_width: float + + :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, M), + :rtype: numpy array + """ + + atoms_max = A.shape[1] + neighbors_max = A.shape[3] + + assert B.shape[1] == atoms_max, "ERROR: Check FCHL representation sizes! code = 2" + assert B.shape[3] == neighbors_max, "ERROR: Check FCHL representation sizes! code = 3" + + nm1 = A.shape[0] + nm2 = B.shape[0] + + N1 = np.zeros((nm1),dtype=np.int32) + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + for a in range(nm2): + N2[a] = len(np.where(B[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + neighbors2 = np.zeros((nm2, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + for a, representation in enumerate(B): + ni = N2[a] + for i, x in enumerate(representation[:ni]): + neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, n_kernels = get_kernel_parameters(kernel, kernel_args) + + return fget_kernels_fchl(A, B, N1, N2, neighbors1, neighbors2, nm1, nm2, n_kernels, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, kernel_idx, kernel_parameters) + + +def get_local_symmetric_kernels(A, \ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: + + :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - A_j\\|_2^2}{2\sigma^2} \\big)` + + Where :math:`A_{i}` and :math:`A_{j}` are FCHL representation vectors. + K is calculated analytically using an OpenMP parallel Fortran routine. + Note, that this kernel will ONLY work with FCHL representations as input. + + :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). + :type A: numpy array + + :param two_body_scaling: Weight for 2-body terms. + :type two_body_scaling: float + :param three_body_scaling: Weight for 3-body terms. + :type three_body_scaling: float + + :param two_body_width: Gaussian width for 2-body terms + :type two_body_width: float + :param three_body_width: Gaussian width for 3-body terms. + :type three_body_width: float + + :param two_body_power: Powerlaw for :math:`r^{-n}` 2-body terms. + :type two_body_power: float + :param three_body_power: Powerlaw for Axilrod-Teller-Muto 3-body term + :type three_body_power: float + + :param cut_start: The fraction of the cut-off radius at which cut-off damping start. + :type cut_start: float + :param cut_distance: Cut-off radius. (default=5 angstrom) + :type cut_distance: float + + :param fourier_order: 3-body Fourier-expansion truncation order. + :type fourier_order: integer + :param alchemy: Type of alchemical interpolation ``"periodic-table"`` or ``"off"`` are possible options. Disabling alchemical interpolation can yield dramatic speedups. + :type alchemy: string + + :param alchemy_period_width: Gaussian width along periods (columns) in the periodic table. + :type alchemy_period_width: float + :param alchemy_group_width: Gaussian width along groups (rows) in the periodic table. + :type alchemy_group_width: float + + :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, N), + :rtype: numpy array + """ + + atoms_max = A.shape[1] + neighbors_max = A.shape[3] + + nm1 = A.shape[0] + N1 = np.zeros((nm1),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + kernel_idx, kernel_parameters, n_kernels = get_kernel_parameters(kernel, kernel_args) + + return fget_symmetric_kernels_fchl(A, N1, neighbors1, nm1, n_kernels, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, kernel_idx, kernel_parameters) + + +def get_global_symmetric_kernels(A, \ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: + + :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - A_j\\|_2^2}{2\sigma^2} \\big)` + + Where :math:`A_{i}` and :math:`A_{j}` are FCHL representation vectors. + K is calculated analytically using an OpenMP parallel Fortran routine. + Note, that this kernel will ONLY work with FCHL representations as input. + + :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). + :type A: numpy array + + :param two_body_scaling: Weight for 2-body terms. + :type two_body_scaling: float + :param three_body_scaling: Weight for 3-body terms. + :type three_body_scaling: float + + :param two_body_width: Gaussian width for 2-body terms + :type two_body_width: float + :param three_body_width: Gaussian width for 3-body terms. + :type three_body_width: float + + :param two_body_power: Powerlaw for :math:`r^{-n}` 2-body terms. + :type two_body_power: float + :param three_body_power: Powerlaw for Axilrod-Teller-Muto 3-body term + :type three_body_power: float + + :param cut_start: The fraction of the cut-off radius at which cut-off damping start. + :type cut_start: float + :param cut_distance: Cut-off radius. (default=5 angstrom) + :type cut_distance: float + + :param fourier_order: 3-body Fourier-expansion truncation order. + :type fourier_order: integer + :param alchemy: Type of alchemical interpolation ``"periodic-table"`` or ``"off"`` are possible options. Disabling alchemical interpolation can yield dramatic speedups. + :type alchemy: string + + :param alchemy_period_width: Gaussian width along periods (columns) in the periodic table. + :type alchemy_period_width: float + :param alchemy_group_width: Gaussian width along groups (rows) in the periodic table. + :type alchemy_group_width: float + + :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, N), + :rtype: numpy array + """ + + atoms_max = A.shape[1] + neighbors_max = A.shape[3] + + nm1 = A.shape[0] + N1 = np.zeros((nm1),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + return fget_global_symmetric_kernels_fchl(A, N1, neighbors1, nm1, nsigmas, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, kernel_idx, kernel_parameters) + + +def get_global_kernels(A, B, \ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: + + :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` + + Where :math:`A_{i}` and :math:`B_{j}` are FCHL representation vectors. + K is calculated analytically using an OpenMP parallel Fortran routine. + Note, that this kernel will ONLY work with FCHL representations as input. + + :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). + :type A: numpy array + :param B: Array of FCHL representation - shape=(M, maxsize, 5, maxneighbors). + :type B: numpy array + + :param two_body_scaling: Weight for 2-body terms. + :type two_body_scaling: float + :param three_body_scaling: Weight for 3-body terms. + :type three_body_scaling: float + + :param two_body_width: Gaussian width for 2-body terms + :type two_body_width: float + :param three_body_width: Gaussian width for 3-body terms. + :type three_body_width: float + + :param two_body_power: Powerlaw for :math:`r^{-n}` 2-body terms. + :type two_body_power: float + :param three_body_power: Powerlaw for Axilrod-Teller-Muto 3-body term + :type three_body_power: float + + :param cut_start: The fraction of the cut-off radius at which cut-off damping start. + :type cut_start: float + :param cut_distance: Cut-off radius. (default=5 angstrom) + :type cut_distance: float + + :param fourier_order: 3-body Fourier-expansion truncation order. + :type fourier_order: integer + :param alchemy: Type of alchemical interpolation ``"periodic-table"`` or ``"off"`` are possible options. Disabling alchemical interpolation can yield dramatic speedups. + :type alchemy: string + + :param alchemy_period_width: Gaussian width along periods (columns) in the periodic table. + :type alchemy_period_width: float + :param alchemy_group_width: Gaussian width along groups (rows) in the periodic table. + :type alchemy_group_width: float + + :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, M), + :rtype: numpy array + """ + + atoms_max = A.shape[1] + neighbors_max = A.shape[3] + + assert B.shape[1] == atoms_max, "ERROR: Check FCHL representation sizes!" + assert B.shape[3] == neighbors_max, "ERROR: Check FCHL representation sizes!" + + nm1 = A.shape[0] + nm2 = B.shape[0] + + N1 = np.zeros((nm1),dtype=np.int32) + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + for a in range(nm2): + N2[a] = len(np.where(B[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + neighbors2 = np.zeros((nm2, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + for a, representation in enumerate(B): + ni = N2[a] + for i, x in enumerate(representation[:ni]): + neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + return fget_global_kernels_fchl(A, B, N1, N2, neighbors1, neighbors2, nm1, nm2, nsigmas, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, kernel_idx, kernel_parameters) + + +def get_atomic_kernels(A, B, \ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: + + :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` + + Where :math:`A_{i}` and :math:`B_{j}` are FCHL representation vectors. + K is calculated analytically using an OpenMP parallel Fortran routine. + Note, that this kernel will ONLY work with FCHL representations as input. + + :param A: Array of FCHL representation - shape=(N, maxsize, 5, size). + :type A: numpy array + :param B: Array of FCHL representation - shape=(M, maxsize, 5, size). + :type B: numpy array + :param sigma: List of kernel-widths. + :type sigma: list + + :param two_body_scaling: Weight for 2-body terms. + :type two_body_scaling: float + :param three_body_scaling: Weight for 3-body terms. + :type three_body_scaling: float + + :param two_body_width: Gaussian width for 2-body terms + :type two_body_width: float + :param three_body_width: Gaussian width for 3-body terms. + :type three_body_width: float + + :param two_body_power: Powerlaw for :math:`r^{-n}` 2-body terms. + :type two_body_power: float + :param three_body_power: Powerlaw for Axilrod-Teller-Muto 3-body term + :type three_body_power: float + + :param cut_start: The fraction of the cut-off radius at which cut-off damping start. + :type cut_start: float + :param cut_distance: Cut-off radius. (default=5 angstrom) + :type cut_distance: float + + :param fourier_order: 3-body Fourier-expansion truncation order. + :type fourier_order: integer + :param alchemy: Type of alchemical interpolation ``"periodic-table"`` or ``"off"`` are possible options. Disabling alchemical interpolation can yield dramatic speedups. + :type alchemy: string + + :param alchemy_period_width: Gaussian width along periods (columns) in the periodic table. + :type alchemy_period_width: float + :param alchemy_group_width: Gaussian width along groups (rows) in the periodic table. + :type alchemy_group_width: float + + :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, M), + :rtype: numpy array + """ + + assert len(A.shape) == 3 + assert len(B.shape) == 3 + + # assert B.shape[1] == atoms_max, "ERROR: Check FCHL representation sizes! code = 2" + # assert B.shape[3] == neighbors_max, "ERROR: Check FCHL representation sizes! code = 3" + + na1 = A.shape[0] + na2 = B.shape[0] + + neighbors1 = np.zeros((na1), dtype=np.int32) + neighbors2 = np.zeros((na2), dtype=np.int32) + + for i, x in enumerate(A): + neighbors1[i] = len(np.where(x[0]< cut_distance)[0]) + + for i, x in enumerate(B): + neighbors2[i] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + return fget_atomic_kernels_fchl(A, B, neighbors1, neighbors2, na1, na2, nsigmas, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, kernel_idx, kernel_parameters) + + +def get_atomic_symmetric_kernels(A, \ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: + + :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` + + Where :math:`A_{i}` and :math:`B_{j}` are FCHL representation vectors. + K is calculated analytically using an OpenMP parallel Fortran routine. + Note, that this kernel will ONLY work with FCHL representations as input. + + :param A: Array of FCHL representation - shape=(N, maxsize, 5, size). + :type A: numpy array + :param sigma: List of kernel-widths. + :type sigma: list + + :param two_body_scaling: Weight for 2-body terms. + :type two_body_scaling: float + :param three_body_scaling: Weight for 3-body terms. + :type three_body_scaling: float + + :param two_body_width: Gaussian width for 2-body terms + :type two_body_width: float + :param three_body_width: Gaussian width for 3-body terms. + :type three_body_width: float + + :param two_body_power: Powerlaw for :math:`r^{-n}` 2-body terms. + :type two_body_power: float + :param three_body_power: Powerlaw for Axilrod-Teller-Muto 3-body term + :type three_body_power: float + + :param cut_start: The fraction of the cut-off radius at which cut-off damping start. + :type cut_start: float + :param cut_distance: Cut-off radius. (default=5 angstrom) + :type cut_distance: float + + :param fourier_order: 3-body Fourier-expansion truncation order. + :type fourier_order: integer + :param alchemy: Type of alchemical interpolation ``"periodic-table"`` or ``"off"`` are possible options. Disabling alchemical interpolation can yield dramatic speedups. + :type alchemy: string + + :param alchemy_period_width: Gaussian width along periods (columns) in the periodic table. + :type alchemy_period_width: float + :param alchemy_group_width: Gaussian width along groups (rows) in the periodic table. + :type alchemy_group_width: float + + :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, M), + :rtype: numpy array + """ + + assert len(A.shape) == 3 + + na1 = A.shape[0] + + neighbors1 = np.zeros((na1), dtype=np.int32) + + for i, x in enumerate(A): + neighbors1[i] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + return fget_atomic_symmetric_kernels_fchl(A, neighbors1, na1, nsigmas, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, kernel_idx, kernel_parameters) + + +def get_local_full_kernels(A, B, dx=0.005, \ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + + nm1 = A.shape[0] + nm2 = B.shape[0] + + assert B.shape[1] == 3 + assert B.shape[2] == 2 + assert B.shape[5] == 5 + assert A.shape[2] == 5 + # assert B.shape[2] == 2 + # assert B.shape[5] == 5 + + atoms_max = B.shape[4] + assert A.shape[1] == atoms_max + assert B.shape[3] == atoms_max + + neighbors_max = B.shape[6] + assert A.shape[3] == neighbors_max + + + N1 = np.zeros((nm1),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm2): + N2[a] = len(np.where(B[a,0,0,0,:,1,0] > 0.0001)[0]) + + neighbors2 = np.zeros((nm2, 3, 2, atoms_max, atoms_max), dtype=np.int32) + + for m in range(nm2): + ni = N2[m] + for xyz in range(3): + for pm in range(2): + for i in range(ni): + for a, x in enumerate(B[m,xyz,pm,i,:ni]): + neighbors2[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + naq2 = np.sum(N2) * 3 + + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + return fget_local_full_kernels_fchl(A, B, N1, N2, neighbors1, neighbors2, \ + nm1, nm2, naq2, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, dx, kernel_idx, kernel_parameters) + + +def get_local_gradient_kernels(A, B, dx=0.005, + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + + nm1 = A.shape[0] + nm2 = B.shape[0] + + assert B.shape[1] == 3 + assert B.shape[2] == 2 + assert B.shape[5] == 5 + assert A.shape[2] == 5 + # assert B.shape[2] == 2 + # assert B.shape[5] == 5 + + atoms_max = B.shape[4] + assert A.shape[1] == atoms_max + assert B.shape[3] == atoms_max + + neighbors_max = B.shape[6] + assert A.shape[3] == neighbors_max + + + N1 = np.zeros((nm1),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm2): + N2[a] = len(np.where(B[a,0,0,0,:,1,0] > 0.0001)[0]) + + neighbors2 = np.zeros((nm2, 3, 2, atoms_max, atoms_max), dtype=np.int32) + + for m in range(nm2): + ni = N2[m] + for xyz in range(3): + for pm in range(2): + for i in range(ni): + for a, x in enumerate(B[m,xyz,pm,i,:ni]): + neighbors2[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + naq2 = np.sum(N2) * 3 + + return fget_local_gradient_kernels_fchl(A, B, N1, N2, neighbors1, neighbors2, \ + nm1, nm2, naq2, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, dx, kernel_idx, kernel_parameters) + + +def get_local_hessian_kernels(A, B, dx=0.005, + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + + nm1 = A.shape[0] + nm2 = B.shape[0] + + assert A.shape[1] == 3 + assert A.shape[2] == 2 + assert A.shape[5] == 5 + assert B.shape[1] == 3 + assert B.shape[2] == 2 + assert B.shape[5] == 5 + + atoms_max = A.shape[4] + assert A.shape[3] == atoms_max + assert B.shape[3] == atoms_max + + neighbors_max = A.shape[6] + assert B.shape[6] == neighbors_max + + N1 = np.zeros((nm1),dtype=np.int32) + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,0,0,0,:,1,0] > 0.0001)[0]) + + for a in range(nm2): + N2[a] = len(np.where(B[a,0,0,0,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, 3, 2, atoms_max, atoms_max), dtype=np.int32) + neighbors2 = np.zeros((nm2, 3, 2, atoms_max, atoms_max), dtype=np.int32) + + for m in range(nm1): + ni = N1[m] + for xyz in range(3): + for pm in range(2): + for i in range(ni): + for a, x in enumerate(A[m,xyz,pm,i,:ni]): + neighbors1[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + for m in range(nm2): + ni = N2[m] + for xyz in range(3): + for pm in range(2): + for i in range(ni): + for a, x in enumerate(B[m,xyz,pm,i,:ni]): + neighbors2[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + + naq1 = np.sum(N1) * 3 + naq2 = np.sum(N2) * 3 + + # print naq1, naq2, nsigmas + return fget_local_hessian_kernels_fchl(A, B, N1, N2, neighbors1, neighbors2, \ + nm1, nm2, naq1, naq2, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, dx, kernel_idx, kernel_parameters) + + +def get_local_symmetric_hessian_kernels(A, dx=0.005, + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + + nm1 = A.shape[0] + + assert A.shape[1] == 3 + assert A.shape[2] == 2 + assert A.shape[5] == 5 + + atoms_max = A.shape[4] + assert A.shape[3] == atoms_max + + neighbors_max = A.shape[6] + + N1 = np.zeros((nm1),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,0,0,0,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, 3, 2, atoms_max, atoms_max), dtype=np.int32) + + for m in range(nm1): + ni = N1[m] + for xyz in range(3): + for pm in range(2): + for i in range(ni): + for a, x in enumerate(A[m,xyz,pm,i,:ni]): + neighbors1[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + + naq1 = np.sum(N1) * 3 + + return fget_local_symmetric_hessian_kernels_fchl(A, N1, neighbors1, nm1, naq1, nsigmas, \ + three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, dx, kernel_idx, kernel_parameters) + + +def get_local_invariant_alphas(A, B, F, energy=None, dx=0.005, regularization=1e-7, + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + nm1 = A.shape[0] + nm2 = B.shape[0] + + assert B.shape[1] == 3 + assert B.shape[2] == 2 + assert B.shape[5] == 5 + assert A.shape[2] == 5 + # assert B.shape[2] == 2 + # assert B.shape[5] == 5 + + atoms_max = B.shape[4] + assert A.shape[1] == atoms_max + assert B.shape[3] == atoms_max + + neighbors_max = B.shape[6] + assert A.shape[3] == neighbors_max + + + N1 = np.zeros((nm1),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm2): + N2[a] = len(np.where(B[a,0,0,0,:,1,0] > 0.0001)[0]) + + neighbors2 = np.zeros((nm2, 3, 2, atoms_max, atoms_max), dtype=np.int32) + + for m in range(nm2): + ni = N2[m] + for xyz in range(3): + for pm in range(2): + for i in range(ni): + for a, x in enumerate(B[m,xyz,pm,i,:ni]): + neighbors2[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + na1 = np.sum(N1) + naq2 = np.sum(N2) * 3 + + E = np.zeros((nm1)) + if energy is not None: + E = energy + + return fget_local_invariant_alphas_fchl(A, B, F, E, N1, N2, neighbors1, neighbors2, \ + nm1, nm2, na1, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, + three_body_power, dx, kernel_idx, kernel_parameters, regularization) + + +def get_atomic_gradient_kernels(A, B, dx = 0.005, + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + + nm1 = A.shape[0] + nm2 = B.shape[0] + + assert B.shape[1] == 3 + assert B.shape[2] == 2 + assert B.shape[5] == 5 + assert A.shape[2] == 5 + + atoms_max = B.shape[4] + assert A.shape[1] == atoms_max + assert B.shape[3] == atoms_max + + neighbors_max = B.shape[6] + assert A.shape[3] == neighbors_max + + + N1 = np.zeros((nm1),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm2): + N2[a] = len(np.where(B[a,0,0,0,:,1,0] > 0.0001)[0]) + + neighbors2 = np.zeros((nm2, 3, 2, atoms_max, atoms_max), dtype=np.int32) + + for m in range(nm2): + ni = N2[m] + for xyz in range(3): + for pm in range(2): + for i in range(ni): + for a, x in enumerate(B[m,xyz,pm,i,:ni]): + neighbors2[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + + na1 = np.sum(N1) + naq2 = np.sum(N2) * 3 + + return fget_atomic_gradient_kernels_fchl(A, B, N1, N2, neighbors1, neighbors2, \ + nm1, nm2, na1, naq2, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, + three_body_power, dx, kernel_idx, kernel_parameters) + + +def get_local_atomic_kernels(A, B, \ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: + + :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` + + Where :math:`A_{i}` and :math:`B_{j}` are FCHL representation vectors. + K is calculated analytically using an OpenMP parallel Fortran routine. + Note, that this kernel will ONLY work with FCHL representations as input. + + :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). + :type A: numpy array + :param B: Array of FCHL representation - shape=(M, maxsize, 5, maxneighbors). + :type B: numpy array + :param sigma: List of kernel-widths. + :type sigma: list + :param t_width: Gaussian width for the angular (theta) terms. + :type t_width: float + :param d_width: Gaussian width for the distance terms. + :type d_width: float + :param cut_start: The fraction of the cut-off radius at which cut-off damping start + :type cut_start: float + :param cut_distance: Cut-off radius. + :type cut_distance: float + :param r_width: Gaussian width along rows in the periodic table. + :type r_width: float + :param c_width: Gaussian width along columns in the periodic table. + :type c_width: float + :param order: Fourier-expansion truncation order. + :type order: integer + :param scale_distance: Weight for distance-dependent terms. + :type scale_distance: float + :param scale_angular: Weight for angle-dependent terms. + :type scale_angular: float + + :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, M), + :rtype: numpy array + """ + + atoms_max = A.shape[1] + neighbors_max = A.shape[3] + + assert B.shape[1] == atoms_max, "ERROR: Check FCHL representation sizes! code = 2" + assert B.shape[3] == neighbors_max, "ERROR: Check FCHL representation sizes! code = 3" + + nm1 = A.shape[0] + nm2 = B.shape[0] + + N1 = np.zeros((nm1),dtype=np.int32) + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + for a in range(nm2): + N2[a] = len(np.where(B[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + neighbors2 = np.zeros((nm2, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + for a, representation in enumerate(B): + ni = N2[a] + for i, x in enumerate(representation[:ni]): + neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + na1 = np.sum(N1) + + return fget_local_atomic_kernels_fchl(A, B, N1, N2, neighbors1, neighbors2, \ + nm1, nm2, na1, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, \ + three_body_power, kernel_idx, kernel_parameters) + + +def get_local_kernels_ef(A, B, df=1e-5, ef_scaling=1.0,\ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: + + :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` + + Where :math:`A_{i}` and :math:`B_{j}` are FCHL representation vectors. + K is calculated analytically using an OpenMP parallel Fortran routine. + Note, that this kernel will ONLY work with FCHL representations as input. + + :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). + :type A: numpy array + :param B: Array of FCHL representation - shape=(M, maxsize, 5, maxneighbors). + :type B: numpy array + + :param two_body_scaling: Weight for 2-body terms. + :type two_body_scaling: float + :param three_body_scaling: Weight for 3-body terms. + :type three_body_scaling: float + + :param two_body_width: Gaussian width for 2-body terms + :type two_body_width: float + :param three_body_width: Gaussian width for 3-body terms. + :type three_body_width: float + + :param two_body_power: Powerlaw for :math:`r^{-n}` 2-body terms. + :type two_body_power: float + :param three_body_power: Powerlaw for Axilrod-Teller-Muto 3-body term + :type three_body_power: float + + :param cut_start: The fraction of the cut-off radius at which cut-off damping start. + :type cut_start: float + :param cut_distance: Cut-off radius. (default=5 angstrom) + :type cut_distance: float + + :param fourier_order: 3-body Fourier-expansion truncation order. + :type fourier_order: integer + :param alchemy: Type of alchemical interpolation ``"periodic-table"`` or ``"off"`` are possible options. Disabling alchemical interpolation can yield dramatic speedups. + :type alchemy: string + + :param alchemy_period_width: Gaussian width along periods (columns) in the periodic table. + :type alchemy_period_width: float + :param alchemy_group_width: Gaussian width along groups (rows) in the periodic table. + :type alchemy_group_width: float + + :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, M), + :rtype: numpy array + """ + + atoms_max = A.shape[1] + neighbors_max = A.shape[3] + + assert B.shape[1] == atoms_max, "ERROR: Check FCHL representation sizes! code = 2" + assert B.shape[3] == neighbors_max, "ERROR: Check FCHL representation sizes! code = 3" + + + nm1 = A.shape[0] + nm2 = B.shape[0] + + N1 = np.zeros((nm1),dtype=np.int32) + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + for a in range(nm2): + N2[a] = len(np.where(B[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + neighbors2 = np.zeros((nm2, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + for a, representation in enumerate(B): + ni = N2[a] + for i, x in enumerate(representation[:ni]): + neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) + + # charge1 = np.zeros((nm1, atoms_max)) + # charge2 = np.zeros((nm2, atoms_max)) + # + # for a, representation in enumerate(A): + # ni = N1[a] + # for i, x in enumerate(representation[:ni]): + # charge1[a,i] = Q1[a][i] + # + # for a, representation in enumerate(B): + # ni = N2[a] + # for i, x in enumerate(representation[:ni]): + # charge2[a,i] = Q2[a][i] + + # print(charge1) + # print(charge2) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, n_kernels = get_kernel_parameters(kernel, kernel_args) + + return fget_kernels_fchl_ef(A, B, N1, N2, neighbors1, neighbors2, nm1, nm2, n_kernels, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, + three_body_scaling, doalchemy, two_body_power, three_body_power, ef_scaling, df, kernel_idx, kernel_parameters) + +def get_local_kernels_ef_deriv(A, B, df=1e-5, ef_scaling=1.0,\ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: + + :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` + + Where :math:`A_{i}` and :math:`B_{j}` are FCHL representation vectors. + K is calculated analytically using an OpenMP parallel Fortran routine. + Note, that this kernel will ONLY work with FCHL representations as input. + + :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). + :type A: numpy array + :param B: Array of FCHL representation - shape=(M, maxsize, 5, maxneighbors). + :type B: numpy array + + :param two_body_scaling: Weight for 2-body terms. + :type two_body_scaling: float + :param three_body_scaling: Weight for 3-body terms. + :type three_body_scaling: float + + :param two_body_width: Gaussian width for 2-body terms + :type two_body_width: float + :param three_body_width: Gaussian width for 3-body terms. + :type three_body_width: float + + :param two_body_power: Powerlaw for :math:`r^{-n}` 2-body terms. + :type two_body_power: float + :param three_body_power: Powerlaw for Axilrod-Teller-Muto 3-body term + :type three_body_power: float + + :param cut_start: The fraction of the cut-off radius at which cut-off damping start. + :type cut_start: float + :param cut_distance: Cut-off radius. (default=5 angstrom) + :type cut_distance: float + + :param fourier_order: 3-body Fourier-expansion truncation order. + :type fourier_order: integer + :param alchemy: Type of alchemical interpolation ``"periodic-table"`` or ``"off"`` are possible options. Disabling alchemical interpolation can yield dramatic speedups. + :type alchemy: string + + :param alchemy_period_width: Gaussian width along periods (columns) in the periodic table. + :type alchemy_period_width: float + :param alchemy_group_width: Gaussian width along groups (rows) in the periodic table. + :type alchemy_group_width: float + + :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, M), + :rtype: numpy array + """ + + atoms_max = A.shape[1] + neighbors_max = A.shape[3] + + # assert B.shape[1] == atoms_max, "ERROR: Check FCHL representation sizes! code = 2" + # assert B.shape[3] == neighbors_max, "ERROR: Check FCHL representation sizes! code = 3" + + + nm1 = A.shape[0] + nm2 = B.shape[0] + + N1 = np.zeros((nm1),dtype=np.int32) + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + for a in range(nm2): + N2[a] = len(np.where(B[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + neighbors2 = np.zeros((nm2, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + for a, representation in enumerate(B): + ni = N2[a] + for i, x in enumerate(representation[:ni]): + neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, n_kernels = get_kernel_parameters(kernel, kernel_args) + + na1 = np.sum(N1) + + return fget_kernels_fchl_ef_deriv(A, B, N1, N2, neighbors1, neighbors2, nm1, nm2, na1, n_kernels, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, + three_body_scaling, doalchemy, two_body_power, three_body_power, ef_scaling, df, kernel_idx, kernel_parameters) + + +def get_gp_kernels_ef(A, B, fields=None, df=1e-5, ef_scaling=1.0,\ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: + + :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` + + Where :math:`A_{i}` and :math:`B_{j}` are FCHL representation vectors. + K is calculated analytically using an OpenMP parallel Fortran routine. + Note, that this kernel will ONLY work with FCHL representations as input. + + :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). + :type A: numpy array + :param B: Array of FCHL representation - shape=(M, maxsize, 5, maxneighbors). + :type B: numpy array + + :param two_body_scaling: Weight for 2-body terms. + :type two_body_scaling: float + :param three_body_scaling: Weight for 3-body terms. + :type three_body_scaling: float + + :param two_body_width: Gaussian width for 2-body terms + :type two_body_width: float + :param three_body_width: Gaussian width for 3-body terms. + :type three_body_width: float + + :param two_body_power: Powerlaw for :math:`r^{-n}` 2-body terms. + :type two_body_power: float + :param three_body_power: Powerlaw for Axilrod-Teller-Muto 3-body term + :type three_body_power: float + + :param cut_start: The fraction of the cut-off radius at which cut-off damping start. + :type cut_start: float + :param cut_distance: Cut-off radius. (default=5 angstrom) + :type cut_distance: float + + :param fourier_order: 3-body Fourier-expansion truncation order. + :type fourier_order: integer + :param alchemy: Type of alchemical interpolation ``"periodic-table"`` or ``"off"`` are possible options. Disabling alchemical interpolation can yield dramatic speedups. + :type alchemy: string + + :param alchemy_period_width: Gaussian width along periods (columns) in the periodic table. + :type alchemy_period_width: float + :param alchemy_group_width: Gaussian width along groups (rows) in the periodic table. + :type alchemy_group_width: float + + :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, M), + :rtype: numpy array + """ + + atoms_max = A.shape[1] + neighbors_max = A.shape[3] + + assert B.shape[1] == atoms_max, "ERROR: Check FCHL representation sizes! code = 2" + assert B.shape[3] == neighbors_max, "ERROR: Check FCHL representation sizes! code = 3" + + + nm1 = A.shape[0] + nm2 = B.shape[0] + + N1 = np.zeros((nm1),dtype=np.int32) + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + for a in range(nm2): + N2[a] = len(np.where(B[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + neighbors2 = np.zeros((nm2, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + for a, representation in enumerate(B): + ni = N2[a] + for i, x in enumerate(representation[:ni]): + neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, n_kernels = get_kernel_parameters(kernel, kernel_args) + + F1 = np.zeros((nm1,3)) + F2 = np.zeros((nm2,3)) + + if (fields is not None): + + F1 = np.array(fields[0]) + F2 = np.array(fields[1]) + + return fget_gp_kernels_fchl_ef(A, B, F1, F2, N1, N2, neighbors1, neighbors2, nm1, nm2, n_kernels, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, + three_body_scaling, doalchemy, two_body_power, three_body_power, ef_scaling, df, kernel_idx, kernel_parameters) + + +def get_local_kernels_pol(A, B, df=1e-5, ef_scaling=1.0,\ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: + + :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` + + Where :math:`A_{i}` and :math:`B_{j}` are FCHL representation vectors. + K is calculated analytically using an OpenMP parallel Fortran routine. + Note, that this kernel will ONLY work with FCHL representations as input. + + :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). + :type A: numpy array + :param B: Array of FCHL representation - shape=(M, maxsize, 5, maxneighbors). + :type B: numpy array + + :param two_body_scaling: Weight for 2-body terms. + :type two_body_scaling: float + :param three_body_scaling: Weight for 3-body terms. + :type three_body_scaling: float + + :param two_body_width: Gaussian width for 2-body terms + :type two_body_width: float + :param three_body_width: Gaussian width for 3-body terms. + :type three_body_width: float + + :param two_body_power: Powerlaw for :math:`r^{-n}` 2-body terms. + :type two_body_power: float + :param three_body_power: Powerlaw for Axilrod-Teller-Muto 3-body term + :type three_body_power: float + + :param cut_start: The fraction of the cut-off radius at which cut-off damping start. + :type cut_start: float + :param cut_distance: Cut-off radius. (default=5 angstrom) + :type cut_distance: float + + :param fourier_order: 3-body Fourier-expansion truncation order. + :type fourier_order: integer + :param alchemy: Type of alchemical interpolation ``"periodic-table"`` or ``"off"`` are possible options. Disabling alchemical interpolation can yield dramatic speedups. + :type alchemy: string + + :param alchemy_period_width: Gaussian width along periods (columns) in the periodic table. + :type alchemy_period_width: float + :param alchemy_group_width: Gaussian width along groups (rows) in the periodic table. + :type alchemy_group_width: float + + :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, M), + :rtype: numpy array + """ + + atoms_max = A.shape[1] + neighbors_max = A.shape[3] + + assert B.shape[1] == atoms_max, "ERROR: Check FCHL representation sizes! code = 2" + assert B.shape[3] == neighbors_max, "ERROR: Check FCHL representation sizes! code = 3" + + + nm1 = A.shape[0] + nm2 = B.shape[0] + + N1 = np.zeros((nm1),dtype=np.int32) + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + for a in range(nm2): + N2[a] = len(np.where(B[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + neighbors2 = np.zeros((nm2, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + for a, representation in enumerate(B): + ni = N2[a] + for i, x in enumerate(representation[:ni]): + neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, n_kernels = get_kernel_parameters(kernel, kernel_args) + + na1 = np.sum(N1) + + return fget_kernels_fchl_ef_2ndderiv(A, B, N1, N2, neighbors1, neighbors2, nm1, nm2, na1, n_kernels, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, + three_body_scaling, doalchemy, two_body_power, three_body_power, ef_scaling, df, kernel_idx, kernel_parameters) + + +def get_kernels_ef_field(A, B, fields=None, df=1e-5, ef_scaling=1.0,\ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + + atoms_max = A.shape[1] + neighbors_max = A.shape[3] + + assert B.shape[1] == atoms_max, "ERROR: Check FCHL representation sizes! code = 2" + assert B.shape[3] == neighbors_max, "ERROR: Check FCHL representation sizes! code = 3" + + + nm1 = A.shape[0] + nm2 = B.shape[0] + + N1 = np.zeros((nm1),dtype=np.int32) + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + for a in range(nm2): + N2[a] = len(np.where(B[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + neighbors2 = np.zeros((nm2, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + for a, representation in enumerate(B): + ni = N2[a] + for i, x in enumerate(representation[:ni]): + neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, n_kernels = get_kernel_parameters(kernel, kernel_args) + + F2 = np.zeros((nm2,3)) + + if (fields is not None): + + F2 = np.array(fields) + + na1 = np.sum(N1) + + return fget_kernels_fchl_ef_field(A, B, F2, N1, N2, neighbors1, neighbors2, nm1, nm2, n_kernels, na1, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, + three_body_scaling, doalchemy, two_body_power, three_body_power, ef_scaling, kernel_idx, kernel_parameters) + + +def get_smooth_atomic_gradient_kernels(A, B, dx = 0.005, + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + + nm1 = A.shape[0] + nm2 = B.shape[0] + + assert B.shape[1] == 3 + assert B.shape[2] == 5 + assert B.shape[5] == 5 + assert A.shape[2] == 5 + + atoms_max = B.shape[4] + assert A.shape[1] == atoms_max + assert B.shape[3] == atoms_max + + neighbors_max = B.shape[6] + assert A.shape[3] == neighbors_max + + + N1 = np.zeros((nm1),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm2): + N2[a] = len(np.where(B[a,0,0,0,:,1,0] > 0.0001)[0]) + + neighbors2 = np.zeros((nm2, 3, 5, atoms_max, atoms_max), dtype=np.int32) + + for m in range(nm2): + ni = N2[m] + for xyz in range(3): + for pm in range(5): + for i in range(ni): + for a, x in enumerate(B[m,xyz,pm,i,:ni]): + neighbors2[m,xyz,pm,i,a] = len(np.where(x[0]< cut_distance)[0]) + + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + + na1 = np.sum(N1) + naq2 = np.sum(N2) * 3 + + return fget_smooth_atomic_gradient_kernels_fchl(A, B, N1, N2, neighbors1, neighbors2, \ + nm1, nm2, na1, naq2, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, + three_body_power, dx, kernel_idx, kernel_parameters) diff --git a/qml/fchl/fchl_representations.py b/qml/fchl/fchl_representations.py new file mode 100644 index 000000000..60f8123a7 --- /dev/null +++ b/qml/fchl/fchl_representations.py @@ -0,0 +1,294 @@ +# MIT License +# +# Copyright (c) 2017-2018 Felix Faber and Anders Steen Christensen +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import print_function + +import numpy as np +import copy + +from qml.data.alchemy import get_alchemy +from qml.data.alchemy import ELEMENT_NAME + +def generate_representation(coordinates, nuclear_charges, + max_size=23, neighbors=23, cut_distance = 5.0, cell=None): + """ Generates a representation for the FCHL kernel module. + + :param coordinates: Input coordinates. + :type coordinates: numpy array + :param nuclear_charges: List of nuclear charges. + :type nuclear_charges: numpy array + :param max_size: Max number of atoms in representation. + :type max_size: integer + :param neighbors: Max number of atoms within the cut-off around an atom. (For periodic systems) + :type neighbors: integer + :param cell: Unit cell vectors. The presence of this keyword argument will generate a periodic representation. + :type cell: numpy array + :param cut_distance: Spatial cut-off distance - must be the same as used in the kernel function call. + :type cut_distance: float + :return: FCHL representation, shape = (size,5,neighbors). + :rtype: numpy array + """ + + size = max_size + + if cell is None: + neighbors=size + + L = len(coordinates) + coords = np.asarray(coordinates) + ocupationList = np.asarray(nuclear_charges) + M = np.zeros((size,5,neighbors)) + + if cell is not None: + coords = np.dot(coords,cell) + nExtend = (np.floor(cut_distance/np.linalg.norm(cell,2,axis = 0)) + 1).astype(int) + + for i in range(-nExtend[0],nExtend[0] + 1): + for j in range(-nExtend[1],nExtend[1] + 1): + for k in range(-nExtend[2],nExtend[2] + 1): + if i == -nExtend[0] and j == -nExtend[1] and k == -nExtend[2]: + coordsExt = coords + i*cell[0,:] + j*cell[1,:] + k*cell[2,:] + ocupationListExt = copy.copy(ocupationList) + else: + ocupationListExt = np.append(ocupationListExt,ocupationList) + coordsExt = np.append(coordsExt,coords + i*cell[0,:] + j*cell[1,:] + k*cell[2,:],axis = 0) + else: + coordsExt = copy.copy(coords) + ocupationListExt = copy.copy(ocupationList) + + M[:,0,:] = 1E+100 + + for i in range(L): + cD = - coords[i] + coordsExt[:] + + ocExt = np.asarray(ocupationListExt) + D1 = np.sqrt(np.sum(cD**2, axis = 1)) + args = np.argsort(D1) + D1 = D1[args] + ocExt = np.asarray([ocExt[l] for l in args]) + cD = cD[args] + + args = np.where(D1 < cut_distance)[0] + D1 = D1[args] + ocExt = np.asarray([ocExt[l] for l in args]) + cD = cD[args] + M[i,0,: len(D1)] = D1 + M[i,1,: len(D1)] = ocExt[:] + M[i,2:5,: len(D1)] = cD.T + return M + + +def generate_displaced_representations(coordinates, nuclear_charges, + max_size=23, neighbors=23, cut_distance = 5.0, cell=None, dx=0.005): + """ Generates displaced representations for the FCHL kernel module. + + :param coordinates: Input coordinates. + :type coordinates: numpy array + :param nuclear_charges: List of nuclear charges. + :type nuclear_charges: numpy array + :param max_size: Max number of atoms in representation. + :type max_size: integer + :param neighbors: Max number of atoms within the cut-off around an atom. (For periodic systems) + :type neighbors: integer + :param cell: Unit cell vectors. The presence of this keyword argument will generate a periodic representation. + :type cell: numpy array + :param dx: Real-space displacement for numerical derivatives, in units of angstrom. + :type dx: float + :param cut_distance: Spatial cut-off distance - must be the same as used in the kernel function call. + :type cut_distance: float + :return: FCHL representation, shape = (size,5,neighbors). + :rtype: numpy array + """ + size = max_size + if cell is None: + neighbors=size + reps = np.zeros((3,2,size,size,5,neighbors)) + + compound_size = len(nuclear_charges) + + for xyz in range(3): + + for i in range(compound_size): + for idisp, disp in enumerate([-dx, dx]): + + displaced_coordinates = copy.deepcopy(coordinates) + displaced_coordinates[i,xyz] += disp + + rep = generate_representation(displaced_coordinates, nuclear_charges, + max_size=size, neighbors=neighbors, cut_distance=cut_distance, cell=cell) + + reps[xyz,idisp,i,:,:,:] = rep[:,:,:] + + return reps + + +def generate_displaced_representations_5point(coordinates, nuclear_charges, + max_size=23, neighbors=23, cut_distance = 5.0, cell=None, dx=0.005): + """ Generates displaced representations for the FCHL kernel module, using a 5-point stencil. + + :param coordinates: Input coordinates. + :type coordinates: numpy array + :param nuclear_charges: List of nuclear charges. + :type nuclear_charges: numpy array + :param max_size: Max number of atoms in representation. + :type max_size: integer + :param neighbors: Max number of atoms within the cut-off around an atom. (For periodic systems) + :type neighbors: integer + :param cell: Unit cell vectors. The presence of this keyword argument will generate a periodic representation. + :type cell: numpy array + :param dx: Real-space displacement for numerical derivatives, in units of angstrom. + :type dx: float + :param cut_distance: Spatial cut-off distance - must be the same as used in the kernel function call. + :type cut_distance: float + :return: FCHL representation, shape = (size,5,neighbors). + :rtype: numpy array + """ + size = max_size + if cell is None: + neighbors=size + reps = np.zeros((3,5,size,size,5,neighbors)) + + compound_size = len(nuclear_charges) + + for xyz in range(3): + + for i in range(compound_size): + for idisp, disp in enumerate([-2*dx, -dx, 0.0, dx, 2*dx]): + + displaced_coordinates = copy.deepcopy(coordinates) + displaced_coordinates[i,xyz] += disp + + rep = generate_representation(displaced_coordinates, nuclear_charges, + max_size=size, neighbors=neighbors, cut_distance=cut_distance, cell=cell) + + reps[xyz,idisp,i,:,:,:] = rep[:,:,:] + + return reps + + +def generate_representation_electric_field(coordinates, nuclear_charges, + fictitious_charges="gasteiger", max_size=23, neighbors=23, cut_distance = 5.0): + """ Generates a representation for the FCHL kernel module, including fictitious partial charges. + For use with fist-order electric field-dependent properties, such as dipole moments and IR intensity. + + Good choices are charges from e.g. a force field model or a QM charge model, e.g. Mulliken, etc. + + :param coordinates: Input coordinates. + :type coordinates: numpy array + :param nuclear_charges: List of nuclear charges. + :type nuclear_charges: numpy array + :param partial_charges: Either list of fictitious partial charges, or a string representing the charge model from which these are to be calculated (the latter requires Open Babel with Pybel installed). Default option is "gasteiger". Please see the OpenBabel documentation for list of supported charges models. + :type partial_charges: numpy array or string. + :param max_size: Max number of atoms in representation. + :type max_size: integer + :param neighbors: Max number of atoms within the cut-off around an atom. (For periodic systems) + :type neighbors: integer + :param cell: Unit cell vectors. The presence of this keyword argument will generate a periodic representation. + :type cell: numpy array + :param cut_distance: Spatial cut-off distance - must be the same as used in the kernel function call. + :type cut_distance: float + :return: FCHL representation, shape = (size,5,neighbors). + :rtype: numpy array + """ + + partial_charges = None + + # If a list is given, assume these are the fictitious charges + if isinstance(fictitious_charges, (list,)) or \ + isinstance(fictitious_charges, (np.ndarray,)): + + assert(len(fictitious_charges) == len(nuclear_charges)), "Error: incorrect length of fictitious charge list" + + partial_charges = fictitious_charges + + # Otherwise, if a string is given, assume this is the name of a charge model + # in Open Babel//Pybel. + elif isinstance(fictitious_charges, (basestring,)): + + # Dirty hack for now. + try: + import pybel + import openbabel + except ImportError: + print("QML ERROR: Could not generate fictitious charges because OpenBabel/Pybel was not found.") + exit() + + temp_xyz = "%i\n\n" % len(nuclear_charges) + + for i, nuc in enumerate(nuclear_charges): + temp_xyz += "%s %f %f %f\n" \ + % (ELEMENT_NAME[nuc], coordinates[i][0], coordinates[i][1], coordinates[i][2]) + + mol = pybel.readstring("xyz", temp_xyz) + + this_charge_model = openbabel.OBChargeModel.FindType(fictitious_charges) + this_charge_model.ComputeCharges(mol.OBMol) + + partial_charges = [atom.partialcharge for atom in mol] + + else: + print("QML ERROR: Unable to parse argument for fictitious charges", fictitious_charges) + exit() + + size = max_size + neighbors=size + + L = len(coordinates) + coords = np.asarray(coordinates) + ocupationList = np.asarray(nuclear_charges) + partial_charges = np.asarray(partial_charges) + M = np.zeros((size,6,neighbors)) + + coordsExt = copy.copy(coords) + partialExt = copy.copy(partial_charges) + ocupationListExt = copy.copy(ocupationList) + + M[:,0,:] = 1E+100 + + for i in range(L): + cD = - coords[i] + coordsExt[:] + + ocExt = np.asarray(ocupationListExt) + qExt = np.asarray(partialExt) + + D1 = np.sqrt(np.sum(cD**2, axis = 1)) + args = np.argsort(D1) + D1 = D1[args] + + ocExt = np.asarray([ocExt[l] for l in args]) + qExt = np.asarray([qExt[l] for l in args]) + + cD = cD[args] + + args = np.where(D1 < cut_distance)[0] + D1 = D1[args] + ocExt = np.asarray([ocExt[l] for l in args]) + qExt = np.asarray([qExt[l] for l in args]) + + cD = cD[args] + M[i,0,: len(D1)] = D1 + M[i,1,: len(D1)] = ocExt[:] + M[i,2:5,: len(D1)] = cD.T + M[i,5,: len(D1)] = qExt[:] + + + return M diff --git a/qml/fchl/fchl.py b/qml/fchl/fchl_scalar_kernels.py similarity index 74% rename from qml/fchl/fchl.py rename to qml/fchl/fchl_scalar_kernels.py index d3e0aa587..52f6733bc 100644 --- a/qml/fchl/fchl.py +++ b/qml/fchl/fchl_scalar_kernels.py @@ -1,6 +1,6 @@ # MIT License # -# Copyright (c) 2017 Felix Faber and Anders Steen Christensen +# Copyright (c) 2017-2018 Felix Faber and Anders Steen Christensen # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,9 +20,12 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from __future__ import absolute_import + import numpy as np import copy + from .ffchl_module import fget_kernels_fchl from .ffchl_module import fget_symmetric_kernels_fchl from .ffchl_module import fget_global_kernels_fchl @@ -30,84 +33,20 @@ from .ffchl_module import fget_atomic_kernels_fchl from .ffchl_module import fget_atomic_symmetric_kernels_fchl -from ..representations.alchemy import get_alchemy - -def generate_representation(coordinates, nuclear_charges, - max_size=23, neighbors=23, cut_distance = 5.0, cell=None): - """ Generates a representation for the FCHL kernel module. - - :param coordinates: Input coordinates. - :type coordinates: numpy array - :param nuclear_charges: List of nuclear charges. - :type nuclear_charges: numpy array - :param max_size: Max number of atoms in representation. - :type max_size: integer - :param neighbors: Max number of atoms within the cut-off around an atom. (For periodic systems) - :type neighbors: integer - :param cell: Unit cell vectors. The presence of this keyword argument will generate a periodic representation. - :type cell: numpy array - :param cut_distance: Spatial cut-off distance - must be the same as used in the kernel function call. - :type cut_distance: float - :return: FCHL representation, shape = (size,5,neighbors). - :rtype: numpy array - """ +from .ffchl_module import fget_atomic_local_kernels_fchl - size = max_size - - if cell is None: - neighbors=size - - L = len(coordinates) - coords = np.asarray(coordinates) - ocupationList = np.asarray(nuclear_charges) - M = np.zeros((size,5,neighbors)) - - if cell is not None: - coords = np.dot(coords,cell) - nExtend = (np.floor(cut_distance/np.linalg.norm(cell,2,axis = 0)) + 1).astype(int) - - for i in range(-nExtend[0],nExtend[0] + 1): - for j in range(-nExtend[1],nExtend[1] + 1): - for k in range(-nExtend[2],nExtend[2] + 1): - if i == -nExtend[0] and j == -nExtend[1] and k == -nExtend[2]: - coordsExt = coords + i*cell[0,:] + j*cell[1,:] + k*cell[2,:] - ocupationListExt = copy.copy(ocupationList) - else: - ocupationListExt = np.append(ocupationListExt,ocupationList) - coordsExt = np.append(coordsExt,coords + i*cell[0,:] + j*cell[1,:] + k*cell[2,:],axis = 0) - else: - coordsExt = copy.copy(coords) - ocupationListExt = copy.copy(ocupationList) - - M[:,0,:] = 1E+100 - - for i in range(L): - cD = - coords[i] + coordsExt[:] - - ocExt = np.asarray(ocupationListExt) - D1 = np.sqrt(np.sum(cD**2, axis = 1)) - args = np.argsort(D1) - D1 = D1[args] - ocExt = np.asarray([ocExt[l] for l in args]) - cD = cD[args] - - args = np.where(D1 < cut_distance)[0] - D1 = D1[args] - ocExt = np.asarray([ocExt[l] for l in args]) - cD = cD[args] - M[i,0,: len(D1)] = D1 - M[i,1,: len(D1)] = ocExt[:] - M[i,2:5,: len(D1)] = cD.T - return M - - -def get_local_kernels(A, B, sigmas, \ +from .fchl_kernel_functions import get_kernel_parameters +from qml.data.alchemy import get_alchemy + + +def get_local_kernels(A, B, verbose=False,\ two_body_scaling=np.sqrt(8), three_body_scaling=1.6, two_body_width=0.2, three_body_width=np.pi, two_body_power=4.0, three_body_power=2.0, cut_start=1.0, cut_distance=5.0, fourier_order=1, alchemy="periodic-table", - alchemy_period_width=1.6, alchemy_group_width=1.6): + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` @@ -120,8 +59,6 @@ def get_local_kernels(A, B, sigmas, \ :type A: numpy array :param B: Array of FCHL representation - shape=(M, maxsize, 5, maxneighbors). :type B: numpy array - :param sigma: List of kernel-widths. - :type sigma: list :param two_body_scaling: Weight for 2-body terms. :type two_body_scaling: float @@ -188,24 +125,22 @@ def get_local_kernels(A, B, sigmas, \ for i, x in enumerate(representation[:ni]): neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) - nsigmas = len(sigmas) - doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) - sigmas = np.array(sigmas) - assert len(sigmas.shape) == 1, "Third argument (sigmas) is not a 1D list/numpy.array!" + kernel_idx, kernel_parameters, n_kernels = get_kernel_parameters(kernel, kernel_args) - return fget_kernels_fchl(A, B, N1, N2, neighbors1, neighbors2, sigmas, \ - nm1, nm2, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power) + return fget_kernels_fchl(A, B, verbose, N1, N2, neighbors1, neighbors2, nm1, nm2, n_kernels, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, kernel_idx, kernel_parameters) -def get_local_symmetric_kernels(A, sigmas, \ +def get_local_symmetric_kernels(A, verbose=False, \ two_body_scaling=np.sqrt(8), three_body_scaling=1.6, two_body_width=0.2, three_body_width=np.pi, two_body_power=4.0, three_body_power=2.0, cut_start=1.0, cut_distance=5.0, fourier_order=1, alchemy="periodic-table", - alchemy_period_width=1.6, alchemy_group_width=1.6): + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - A_j\\|_2^2}{2\sigma^2} \\big)` @@ -216,8 +151,6 @@ def get_local_symmetric_kernels(A, sigmas, \ :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). :type A: numpy array - :param sigma: List of kernel-widths. - :type sigma: list :param two_body_scaling: Weight for 2-body terms. :type two_body_scaling: float @@ -269,24 +202,21 @@ def get_local_symmetric_kernels(A, sigmas, \ for i, x in enumerate(representation[:ni]): neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) - nsigmas = len(sigmas) - doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + kernel_idx, kernel_parameters, n_kernels = get_kernel_parameters(kernel, kernel_args) - sigmas = np.array(sigmas) - assert len(sigmas.shape) == 1, "Second argument (sigmas) is not a 1D list/numpy.array!" + return fget_symmetric_kernels_fchl(A, verbose, N1, neighbors1, nm1, n_kernels, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, kernel_idx, kernel_parameters) - return fget_symmetric_kernels_fchl(A, N1, neighbors1, sigmas, \ - nm1, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power) - -def get_global_symmetric_kernels(A, sigmas, \ +def get_global_symmetric_kernels(A, verbose=False, \ two_body_scaling=np.sqrt(8), three_body_scaling=1.6, two_body_width=0.2, three_body_width=np.pi, two_body_power=4.0, three_body_power=2.0, cut_start=1.0, cut_distance=5.0, fourier_order=1, alchemy="periodic-table", - alchemy_period_width=1.6, alchemy_group_width=1.6): + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - A_j\\|_2^2}{2\sigma^2} \\big)` @@ -297,8 +227,6 @@ def get_global_symmetric_kernels(A, sigmas, \ :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). :type A: numpy array - :param sigma: List of kernel-widths. - :type sigma: list :param two_body_scaling: Weight for 2-body terms. :type two_body_scaling: float @@ -350,24 +278,21 @@ def get_global_symmetric_kernels(A, sigmas, \ for i, x in enumerate(representation[:ni]): neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) - nsigmas = len(sigmas) - doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) - sigmas = np.array(sigmas) - assert len(sigmas.shape) == 1, "Second argument (sigmas) is not a 1D list/numpy.array!" - - return fget_global_symmetric_kernels_fchl(A, N1, neighbors1, sigmas, \ - nm1, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power) + return fget_global_symmetric_kernels_fchl(A, verbose, N1, neighbors1, nm1, nsigmas, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, kernel_idx, kernel_parameters) -def get_global_kernels(A, B, sigmas, \ +def get_global_kernels(A, B, verbose=False, \ two_body_scaling=np.sqrt(8), three_body_scaling=1.6, two_body_width=0.2, three_body_width=np.pi, two_body_power=4.0, three_body_power=2.0, cut_start=1.0, cut_distance=5.0, fourier_order=1, alchemy="periodic-table", - alchemy_period_width=1.6, alchemy_group_width=1.6): + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` @@ -380,8 +305,6 @@ def get_global_kernels(A, B, sigmas, \ :type A: numpy array :param B: Array of FCHL representation - shape=(M, maxsize, 5, maxneighbors). :type B: numpy array - :param sigma: List of kernel-widths. - :type sigma: list :param two_body_scaling: Weight for 2-body terms. :type two_body_scaling: float @@ -448,24 +371,21 @@ def get_global_kernels(A, B, sigmas, \ for i, x in enumerate(representation[:ni]): neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) - nsigmas = len(sigmas) - doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) - sigmas = np.array(sigmas) - assert len(sigmas.shape) == 1, "Third argument (sigmas) is not a 1D list/numpy.array!" + return fget_global_kernels_fchl(A, B, verbose, N1, N2, neighbors1, neighbors2, nm1, nm2, nsigmas, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, kernel_idx, kernel_parameters) - return fget_global_kernels_fchl(A, B, N1, N2, neighbors1, neighbors2, sigmas, \ - nm1, nm2, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power) - -def get_atomic_kernels(A, B, sigmas, \ +def get_atomic_kernels(A, B, verbose=False, \ two_body_scaling=np.sqrt(8), three_body_scaling=1.6, two_body_width=0.2, three_body_width=np.pi, two_body_power=4.0, three_body_power=2.0, cut_start=1.0, cut_distance=5.0, fourier_order=1, alchemy="periodic-table", - alchemy_period_width=1.6, alchemy_group_width=1.6): + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` @@ -533,24 +453,21 @@ def get_atomic_kernels(A, B, sigmas, \ for i, x in enumerate(B): neighbors2[i] = len(np.where(x[0]< cut_distance)[0]) - nsigmas = len(sigmas) - doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + return fget_atomic_kernels_fchl(A, B, verbose, neighbors1, neighbors2, na1, na2, nsigmas, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, kernel_idx, kernel_parameters) - sigmas = np.array(sigmas) - assert len(sigmas.shape) == 1 - return fget_atomic_kernels_fchl(A, B, neighbors1, neighbors2, sigmas, \ - na1, na2, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power) - - -def get_atomic_symmetric_kernels(A, sigmas, \ +def get_atomic_symmetric_kernels(A, verbose=False, \ two_body_scaling=np.sqrt(8), three_body_scaling=1.6, two_body_width=0.2, three_body_width=np.pi, two_body_power=4.0, three_body_power=2.0, cut_start=1.0, cut_distance=5.0, fourier_order=1, alchemy="periodic-table", - alchemy_period_width=1.6, alchemy_group_width=1.6): + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` @@ -607,12 +524,97 @@ def get_atomic_symmetric_kernels(A, sigmas, \ for i, x in enumerate(A): neighbors1[i] = len(np.where(x[0]< cut_distance)[0]) - nsigmas = len(sigmas) + doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + return fget_atomic_symmetric_kernels_fchl(A, verbose, neighbors1, na1, nsigmas, \ + three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power, kernel_idx, kernel_parameters) + + +def get_atomic_local_kernels(A, B, verbose=False, \ + two_body_scaling=np.sqrt(8), three_body_scaling=1.6, + two_body_width=0.2, three_body_width=np.pi, + two_body_power=4.0, three_body_power=2.0, + cut_start=1.0, cut_distance=5.0, + fourier_order=1, alchemy="periodic-table", + alchemy_period_width=1.6, alchemy_group_width=1.6, + kernel="gaussian", kernel_args=None): + """ Calculates the Gaussian kernel matrix K, where :math:`K_{ij}`: + + :math:`K_{ij} = \\exp \\big( -\\frac{\\|A_i - B_j\\|_2^2}{2\sigma^2} \\big)` + + Where :math:`A_{i}` and :math:`B_{j}` are FCHL representation vectors. + K is calculated analytically using an OpenMP parallel Fortran routine. + Note, that this kernel will ONLY work with FCHL representations as input. + + :param A: Array of FCHL representation - shape=(N, maxsize, 5, maxneighbors). + :type A: numpy array + :param B: Array of FCHL representation - shape=(M, maxsize, 5, maxneighbors). + :type B: numpy array + :param sigma: List of kernel-widths. + :type sigma: list + :param t_width: Gaussian width for the angular (theta) terms. + :type t_width: float + :param d_width: Gaussian width for the distance terms. + :type d_width: float + :param cut_start: The fraction of the cut-off radius at which cut-off damping start + :type cut_start: float + :param cut_distance: Cut-off radius. + :type cut_distance: float + :param r_width: Gaussian width along rows in the periodic table. + :type r_width: float + :param c_width: Gaussian width along columns in the periodic table. + :type c_width: float + :param order: Fourier-expansion truncation order. + :type order: integer + :param scale_distance: Weight for distance-dependent terms. + :type scale_distance: float + :param scale_angular: Weight for angle-dependent terms. + :type scale_angular: float + + :return: Array of FCHL kernel matrices matrix - shape=(n_sigmas, N, M), + :rtype: numpy array + """ + + atoms_max = A.shape[1] + neighbors_max = A.shape[3] + + assert B.shape[1] == atoms_max, "ERROR: Check FCHL representation sizes! code = 2" + assert B.shape[3] == neighbors_max, "ERROR: Check FCHL representation sizes! code = 3" + + nm1 = A.shape[0] + nm2 = B.shape[0] + + N1 = np.zeros((nm1),dtype=np.int32) + N2 = np.zeros((nm2),dtype=np.int32) + + for a in range(nm1): + N1[a] = len(np.where(A[a,:,1,0] > 0.0001)[0]) + + for a in range(nm2): + N2[a] = len(np.where(B[a,:,1,0] > 0.0001)[0]) + + neighbors1 = np.zeros((nm1, atoms_max), dtype=np.int32) + neighbors2 = np.zeros((nm2, atoms_max), dtype=np.int32) + + for a, representation in enumerate(A): + ni = N1[a] + for i, x in enumerate(representation[:ni]): + neighbors1[a,i] = len(np.where(x[0]< cut_distance)[0]) + + for a, representation in enumerate(B): + ni = N2[a] + for i, x in enumerate(representation[:ni]): + neighbors2[a,i] = len(np.where(x[0]< cut_distance)[0]) doalchemy, pd = get_alchemy(alchemy, emax=100, r_width=alchemy_group_width, c_width=alchemy_period_width) - sigmas = np.array(sigmas) - assert len(sigmas.shape) == 1, "Second argument (sigmas) is not a 1D list/numpy.array!" + kernel_idx, kernel_parameters, nsigmas = get_kernel_parameters(kernel, kernel_args) + + na1 = np.sum(N1) + + return fget_atomic_local_kernels_fchl(A, B, verbose, N1, N2, neighbors1, neighbors2, \ + nm1, nm2, na1, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, \ + fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, \ + three_body_power, kernel_idx, kernel_parameters) - return fget_atomic_symmetric_kernels_fchl(A, neighbors1, sigmas, \ - na1, nsigmas, three_body_width, two_body_width, cut_start, cut_distance, fourier_order, pd, two_body_scaling, three_body_scaling, doalchemy, two_body_power, three_body_power) diff --git a/qml/fchl/ffchl_electric_field_kernels.f90 b/qml/fchl/ffchl_electric_field_kernels.f90 new file mode 100644 index 000000000..72d3794d3 --- /dev/null +++ b/qml/fchl/ffchl_electric_field_kernels.f90 @@ -0,0 +1,1448 @@ +subroutine fget_ef_gaussian_process_kernels_fchl(x1, x2, verbose, f1, f2, n1, n2, nneigh1, nneigh2, & + & nm1, nm2, nsigmas, t_width, d_width, cut_start, cut_distance, order, pd, & + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, ef_scale,& + & df, kernel_idx, parameters, kernels) + + use ffchl_module, only: scalar, get_angular_norm2, get_pmax, get_ksi_ef_field, init_cosp_sinp_ef_field, & + & get_selfscalar, get_ksi_ef, init_cosp_sinp_ef + + use ffchl_kernels, only: kernel + + use omp_lib, only: omp_get_wtime + + implicit none + + ! fchl descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x1 + double precision, dimension(:,:,:,:), intent(in) :: x2 + + ! Display output + logical, intent(in) :: verbose + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: n1 + integer, dimension(:), intent(in) :: n2 + + ! Electric field perturbations for each molecule + double precision, dimension(:,:), intent(in) :: f1 + double precision, dimension(:,:), intent(in) :: f2 + + ! Number of molecules + integer, intent(in) :: nm1 + integer, intent(in) :: nm2 + + ! Number of sigmas + integer, intent(in) :: nsigmas + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh1 + integer, dimension(:,:), intent(in) :: nneigh2 + + ! Angular Gaussian width + double precision, intent(in) :: t_width + + ! Distance Gaussian width + double precision, intent(in) :: d_width + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Truncation order for Fourier terms + integer, intent(in) :: order + + ! Periodic table distance matrix + double precision, dimension(:,:), intent(in) :: pd + + ! Scaling for angular and distance terms + double precision, intent(in) :: distance_scale + double precision, intent(in) :: angular_scale + + ! Switch alchemy on or off + logical, intent(in) :: alchemy + + ! Decaying power laws for two- and three-body terms + double precision, intent(in) :: two_body_power + double precision, intent(in) :: three_body_power + + + double precision, intent(in) :: ef_scale + double precision, intent(in) :: df + + ! Kernel ID and corresponding parameters + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters + + ! Resulting alpha vector + double precision, dimension(nsigmas,4*nm1,4*nm2), intent(out) :: kernels + + ! Internal counters + integer :: i, j + integer :: ni, nj + integer :: a, b + integer :: xyz, pm + integer :: xyz1, pm1 + integer :: xyz2, pm2 + integer :: idx_a + integer :: idx_b + + ! Temporary variables necessary for parallelization + double precision :: s12 + + ! Pre-computed terms in the full distance matrix + double precision, allocatable, dimension(:,:) :: self_scalar1 + double precision, allocatable, dimension(:,:) :: self_scalar2 + double precision, allocatable, dimension(:,:,:,:) :: self_scalar1_ef + double precision, allocatable, dimension(:,:,:,:) :: self_scalar2_ef + + ! Pre-computed two-body weights for nummerical differentation of electric field + double precision, allocatable, dimension(:,:,:) :: ksi1 + double precision, allocatable, dimension(:,:,:) :: ksi2 + double precision, allocatable, dimension(:,:,:,:,:) :: ksi1_ef + double precision, allocatable, dimension(:,:,:,:,:) :: ksi2_ef + + ! Pre-computed terms for the Fourier expansion of the three-body term + double precision, allocatable, dimension(:,:,:,:,:) :: sinp1 + double precision, allocatable, dimension(:,:,:,:,:) :: cosp1 + double precision, allocatable, dimension(:,:,:,:,:) :: sinp2 + double precision, allocatable, dimension(:,:,:,:,:) :: cosp2 + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: sinp1_ef + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: cosp1_ef + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: sinp2_ef + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: cosp2_ef + + ! Max index in the periodic table + integer :: pmax1 + integer :: pmax2 + + ! Angular normalization constant + double precision :: ang_norm2 + + ! Max number of neighbors + integer :: maxneigh1 + integer :: maxneigh2 + + ! Variables to calculate time + double precision :: t_start, t_end + + if (verbose) write (*,*) "CLEARING KERNEL MEM" + kernels(:,:,:) = 0.0d0 + + ! Get max number of neighbors + maxneigh1 = maxval(nneigh1) + maxneigh2 = maxval(nneigh2) + + ! Calculate angular normalization constant + ang_norm2 = get_angular_norm2(t_width) + + ! pmax = max nuclear charge + pmax1 = get_pmax(x1, n1) + pmax2 = get_pmax(x2, n2) + + ! Get two-body weight function + ksi1 = get_ksi_ef_field(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, & + & f1, ef_scale, verbose) + ksi1_ef = get_ksi_ef(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, ef_scale, df, verbose) + + ! Get two-body weight function + ksi2 = get_ksi_ef_field(x2, n2, nneigh2, two_body_power, cut_start, cut_distance, & + & f2, ef_scale, verbose) + ksi2_ef = get_ksi_ef(x2, n2, nneigh2, two_body_power, cut_start, cut_distance, ef_scale, df, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + allocate(sinp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp_ef_field(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1, sinp1, f1, ef_scale, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp2(nm2, maxval(n2), pmax2, order, maxneigh2)) + allocate(sinp2(nm2, maxval(n2), pmax2, order, maxneigh2)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp_ef_field(x2, n2, nneigh2, three_body_power, order, cut_start, cut_distance, & + & cosp2, sinp2, f2, ef_scale, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp1_ef(nm1, 3, 2, maxval(n1), pmax1, order, maxneigh1)) + allocate(sinp1_ef(nm1, 3, 2, maxval(n1), pmax1, order, maxneigh1)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp_ef(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1_ef, sinp1_ef, ef_scale, df, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp2_ef(nm2, 3, 2, maxval(n2), pmax2, order, maxneigh2)) + allocate(sinp2_ef(nm2, 3, 2, maxval(n2), pmax2, order, maxneigh2)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp_ef(x2, n2, nneigh2, three_body_power, order, cut_start, cut_distance, & + & cosp2_ef, sinp2_ef, ef_scale, df, verbose) + + ! Pre-calculate self-scalar terms + self_scalar1 = get_selfscalar(x1, nm1, n1, nneigh1, ksi1, sinp1, cosp1, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + ! Pre-calculate self-scalar terms + self_scalar2 = get_selfscalar(x2, nm2, n2, nneigh2, ksi2, sinp2, cosp2, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + ! Self-scalar derivatives + allocate(self_scalar1_ef(nm1, 3,2, maxval(n1))) + do a = 1, nm1 + ni = n1(a) + do xyz = 1, 3 + do pm = 1, 2 + do i = 1, ni + + self_scalar1_ef(a,xyz,pm,i) = scalar(x1(a,i,:,:), x1(a,i,:,:), nneigh1(a,i), nneigh1(a,i), & + & ksi1_ef(a,xyz,pm,i,:), ksi1_ef(a,xyz,pm,i,:), & + & sinp1_ef(a,xyz,pm,i,:,:,:), sinp1_ef(a,xyz,pm,i,:,:,:), & + & cosp1_ef(a,xyz,pm,i,:,:,:), cosp1_ef(a,xyz,pm,i,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2, distance_scale, angular_scale, alchemy) + + enddo + enddo + enddo + enddo + + ! Self-scalar derivatives + allocate(self_scalar2_ef(nm2, 3,2, maxval(n2))) + do a = 1, nm2 + ni = n2(a) + do xyz = 1, 3 + do pm = 1, 2 + do i = 1, ni + + self_scalar2_ef(a,xyz,pm,i) = scalar(x2(a,i,:,:), x2(a,i,:,:), nneigh2(a,i), nneigh2(a,i), & + & ksi2_ef(a,xyz,pm,i,:), ksi2_ef(a,xyz,pm,i,:), & + & sinp2_ef(a,xyz,pm,i,:,:,:), sinp2_ef(a,xyz,pm,i,:,:,:), & + & cosp2_ef(a,xyz,pm,i,:,:,:), cosp2_ef(a,xyz,pm,i,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2, distance_scale, angular_scale, alchemy) + + enddo + enddo + enddo + enddo + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL WITH FIELDS" + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(s12,ni,nj) + do a = 1, nm1 + ni = n1(a) + do i = 1, ni + do b = 1, nm2 + nj = n2(b) + do j = 1, nj + + s12 = scalar(x1(a,i,:,:), x2(b,j,:,:), & + & nneigh1(a,i), nneigh2(b,j), & + & ksi1(a,i,:), ksi2(b,j,:), & + & sinp1(a,i,:,:,:), sinp2(b,j,:,:,:), & + & cosp1(a,i,:,:,:), cosp2(b,j,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2, distance_scale, angular_scale, alchemy) + + kernels(:, a, b) = kernels(:, a, b) & + & + kernel(self_scalar1(a,i), self_scalar2(b,j), s12, & + & kernel_idx, parameters) + + enddo + enddo + enddo + enddo + !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL EF DERIVATIVE 1/2" + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(s12,ni,nj,idx_a,idx_b) + do a = 1, nm1 + ni = n1(a) + do i = 1, ni + + idx_a = a + + + do b = 1, nm2 + nj = n2(b) + do j = 1, nj + do xyz = 1, 3 + idx_b = (b - 1) * 3 + xyz + nm2 + + do pm = 1, 2 + + s12 = scalar(x1(a,i,:,:), x2(b,j,:,:), & + & nneigh1(a,i), nneigh2(b,j), & + & ksi1(a,i,:), ksi2_ef(b,xyz,pm,j,:), & + & sinp1(a,i,:,:,:), sinp2_ef(b,xyz,pm,j,:,:,:), & + & cosp1(a,i,:,:,:), cosp2_ef(b,xyz,pm,j,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2, distance_scale, angular_scale, alchemy) + + if (pm == 1) then + + kernels(:, idx_a, idx_b) = kernels(:, idx_a, idx_b) & + & + kernel(self_scalar1(a,i), self_scalar2_ef(b,xyz,pm,j), s12, & + & kernel_idx, parameters) / (2 * df) + + else + + kernels(:, idx_a, idx_b) = kernels(:, idx_a, idx_b) & + & - kernel(self_scalar1(a,i), self_scalar2_ef(b,xyz,pm,j), s12, & + & kernel_idx, parameters) / (2 * df) + + endif + + enddo + enddo + enddo + enddo + enddo + enddo + !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL EF DERIVATIVE 2/2" + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(s12,ni,nj,idx_a,idx_b) + + do a = 1, nm2 + ni = n2(a) + do i = 1, ni + + idx_a = a + + + do b = 1, nm1 + nj = n1(b) + do j = 1, nj + do xyz = 1, 3 + idx_b = (b - 1) * 3 + xyz + nm1 + + do pm = 1, 2 + + s12 = scalar(x2(a,i,:,:), x1(b,j,:,:), & + & nneigh2(a,i), nneigh1(b,j), & + & ksi2(a,i,:), ksi1_ef(b,xyz,pm,j,:), & + & sinp2(a,i,:,:,:), sinp1_ef(b,xyz,pm,j,:,:,:), & + & cosp2(a,i,:,:,:), cosp1_ef(b,xyz,pm,j,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2, distance_scale, angular_scale, alchemy) + + if (pm == 1) then + + ! kernels(:, idx_a, idx_b) = kernels(:, idx_a, idx_b) & + kernels(:, idx_b, idx_a) = kernels(:, idx_b, idx_a) & + & + kernel(self_scalar2(a,i), self_scalar1_ef(b,xyz,pm,j), s12, & + & kernel_idx, parameters) / (2 * df) + + else + + ! kernels(:, idx_a, idx_b) = kernels(:, idx_a, idx_b) & + kernels(:, idx_b, idx_a) = kernels(:, idx_b, idx_a) & + & - kernel(self_scalar2(a,i), self_scalar1_ef(b,xyz,pm,j), s12, & + & kernel_idx, parameters) / (2 * df) + + endif + + enddo + enddo + enddo + enddo + enddo + enddo + !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL EF HESSIAN " + + ! should be zero? + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(s12,ni,nj,idx_a,idx_b) + do a = 1, nm1 + + ni = n1(a) + do i = 1, ni + do xyz1 = 1, 3 + idx_a = (a - 1) * 3 + xyz1 + nm1 + do pm1 = 1, 2 + + + do b = 1, nm2 + + nj = n2(b) + do j = 1, nj + do xyz2 = 1, 3 + idx_b = (b - 1) * 3 + xyz2 + nm2 + do pm2 = 1, 2 + + s12 = scalar(x1(a,i,:,:), x2(b,j,:,:), & + & nneigh1(a,i), nneigh2(b,j), & + & ksi1_ef(a,xyz1,pm1,i,:), ksi2_ef(b,xyz2,pm2,j,:), & + & sinp1_ef(a,xyz1,pm1,i,:,:,:), sinp2_ef(b,xyz2,pm2,j,:,:,:), & + & cosp1_ef(a,xyz1,pm1,i,:,:,:), cosp2_ef(b,xyz2,pm2,j,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2, distance_scale, angular_scale, alchemy) + if (pm1 == pm2) then + + kernels(:, idx_a, idx_b) = kernels(:, idx_a, idx_b) & + & + kernel(self_scalar1_ef(a,xyz1,pm1,i), self_scalar2_ef(b,xyz2,pm2,j), s12, & + & kernel_idx, parameters) / (4 * df**2) + else + kernels(:, idx_a, idx_b) = kernels(:, idx_a, idx_b) & + & - kernel(self_scalar1_ef(a,xyz1,pm1,i), self_scalar2_ef(b,xyz2,pm2,j), s12, & + & kernel_idx, parameters) / (4 * df**2) + + endif + + enddo + enddo + enddo + enddo + enddo + enddo + enddo + enddo + !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + deallocate(self_scalar1) + deallocate(self_scalar1_ef) + deallocate(ksi1) + deallocate(ksi1_ef) + deallocate(cosp1) + deallocate(cosp1_ef) + deallocate(sinp1) + deallocate(sinp1_ef) + +end subroutine fget_ef_gaussian_process_kernels_fchl + + +subroutine fget_ef_atomic_local_kernels_fchl(x1, x2, verbose, f2, n1, n2, nneigh1, nneigh2, nm1, nm2, nsigmas, na1, & + & t_width, d_width, cut_start, cut_distance, order, pd, & + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, ef_scale,& + & kernel_idx, parameters, kernels) + + use ffchl_module, only: scalar, get_angular_norm2, get_pmax, get_ksi_ef_field, init_cosp_sinp_ef_field, & + & get_selfscalar, get_ksi, init_cosp_sinp + + use ffchl_kernels, only: kernel + + use omp_lib, only: omp_get_wtime + + implicit none + + ! fchl descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x1 + double precision, dimension(:,:,:,:), intent(in) :: x2 + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: n1 + integer, dimension(:), intent(in) :: n2 + + ! Display output + logical, intent(in) :: verbose + + ! Electric field perturbations for each molecule + double precision, dimension(:,:), intent(in) :: f2 + + ! Number of molecules + integer, intent(in) :: nm1 + integer, intent(in) :: nm2 + + ! Number of sigmas + integer, intent(in) :: nsigmas + + integer, intent(in) :: na1 + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh1 + integer, dimension(:,:), intent(in) :: nneigh2 + + ! Angular Gaussian width + double precision, intent(in) :: t_width + + ! Distance Gaussian width + double precision, intent(in) :: d_width + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Truncation order for Fourier terms + integer, intent(in) :: order + + ! Periodic table distance matrix + double precision, dimension(:,:), intent(in) :: pd + + ! Scaling for angular and distance terms + double precision, intent(in) :: distance_scale + double precision, intent(in) :: angular_scale + + ! Switch alchemy on or off + logical, intent(in) :: alchemy + + ! Decaying power laws for two- and three-body terms + double precision, intent(in) :: two_body_power + double precision, intent(in) :: three_body_power + + + double precision, intent(in) :: ef_scale + + ! Kernel ID and corresponding parameters + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters + + ! Resulting alpha vector + double precision, dimension(nsigmas,na1,nm2), intent(out) :: kernels + + ! Internal counters + integer :: i, j + integer :: ni, nj + integer :: a, b + integer :: idx1 + + ! Temporary variables necessary for parallelization + double precision :: s12 + + ! Pre-computed terms in the full distance matrix + double precision, allocatable, dimension(:,:) :: self_scalar1 + double precision, allocatable, dimension(:,:) :: self_scalar2 + + ! Pre-computed two-body weights for nummerical differentation of electric field + double precision, allocatable, dimension(:,:,:) :: ksi1 + double precision, allocatable, dimension(:,:,:) :: ksi2 + + ! Pre-computed terms for the Fourier expansion of the three-body term + double precision, allocatable, dimension(:,:,:,:,:) :: sinp1 + double precision, allocatable, dimension(:,:,:,:,:) :: cosp1 + double precision, allocatable, dimension(:,:,:,:,:) :: sinp2 + double precision, allocatable, dimension(:,:,:,:,:) :: cosp2 + + ! Max index in the periodic table + integer :: pmax1 + integer :: pmax2 + + ! Angular normalization constant + double precision :: ang_norm2 + + ! Max number of neighbors + integer :: maxneigh1 + integer :: maxneigh2 + + ! Variables to calculate time + double precision :: t_start, t_end + + if (verbose) write (*,*) "CLEARING KERNEL MEM" + kernels(:,:,:) = 0.0d0 + + ! Get max number of neighbors + maxneigh1 = maxval(nneigh1) + maxneigh2 = maxval(nneigh2) + + ! Calculate angular normalization constant + ang_norm2 = get_angular_norm2(t_width) + + ! pmax = max nuclear charge + pmax1 = get_pmax(x1, n1) + pmax2 = get_pmax(x2, n2) + + ! Get two-body weight function + ksi1 = get_ksi(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, verbose) + + ! Get two-body weight function + ksi2 = get_ksi_ef_field(x2, n2, nneigh2, two_body_power, cut_start, cut_distance, & + & f2, ef_scale, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + allocate(sinp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1, sinp1, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp2(nm2, maxval(n2), pmax2, order, maxneigh2)) + allocate(sinp2(nm2, maxval(n2), pmax2, order, maxneigh2)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp_ef_field(x2, n2, nneigh2, three_body_power, order, cut_start, cut_distance, & + & cosp2, sinp2, f2, ef_scale, verbose) + + ! Pre-calculate self-scalar terms + self_scalar1 = get_selfscalar(x1, nm1, n1, nneigh1, ksi1, sinp1, cosp1, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + ! Pre-calculate self-scalar terms + self_scalar2 = get_selfscalar(x2, nm2, n2, nneigh2, ksi2, sinp2, cosp2, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + + kernels(:,:,:) = 0.0d0 + + + ! write (*,*) nm1, nm2, na1 + ! write (*,*) size(kernels,dim=1), size(kernels,dim=2), size(kernels,dim=3) + + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL" + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(ni,nj,idx1,s12) + do a = 1, nm1 + ni = n1(a) + do i = 1, ni + + idx1 = sum(n1(:a)) - ni + i + + do b = 1, nm2 + nj = n2(b) + do j = 1, nj + + s12 = scalar(x1(a,i,:,:), x2(b,j,:,:), & + & nneigh1(a,i), nneigh2(b,j), ksi1(a,i,:), ksi2(b,j,:), & + & sinp1(a,i,:,:,:), sinp2(b,j,:,:,:), & + & cosp1(a,i,:,:,:), cosp2(b,j,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2, distance_scale, angular_scale, alchemy) + + kernels(:, idx1, b) = kernels(:, idx1, b) & + & + kernel(self_scalar1(a,i), self_scalar2(b,j), s12, & + & kernel_idx, parameters) + + enddo + enddo + + enddo + enddo + !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + deallocate(self_scalar1) + deallocate(self_scalar2) + deallocate(ksi1) + deallocate(ksi2) + deallocate(cosp1) + deallocate(cosp2) + deallocate(sinp1) + deallocate(sinp2) +end subroutine fget_ef_atomic_local_kernels_fchl + + +subroutine fget_ef_atomic_local_gradient_kernels_fchl(x1, x2, verbose, n1, n2, nneigh1, nneigh2, nm1, nm2, na1, nsigmas, & + & t_width, d_width, cut_start, cut_distance, order, pd, & + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, ef_scale,& + & df, kernel_idx, parameters, kernels) + + use ffchl_module, only: scalar, get_angular_norm2, get_pmax, get_ksi, init_cosp_sinp, & + & get_selfscalar, get_ksi_ef, init_cosp_sinp_ef + + use ffchl_kernels, only: kernel + + use omp_lib, only: omp_get_wtime + + implicit none + + ! fchl descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x1 + double precision, dimension(:,:,:,:), intent(in) :: x2 + + ! Display output + logical, intent(in) :: verbose + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: n1 + integer, dimension(:), intent(in) :: n2 + + ! Number of molecules + integer, intent(in) :: nm1 + integer, intent(in) :: nm2 + + ! Number of atoms in set 1 + integer, intent(in) :: na1 + + ! Number of sigmas + integer, intent(in) :: nsigmas + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh1 + integer, dimension(:,:), intent(in) :: nneigh2 + + ! Angular Gaussian width + double precision, intent(in) :: t_width + + ! Distance Gaussian width + double precision, intent(in) :: d_width + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Truncation order for Fourier terms + integer, intent(in) :: order + + ! Periodic table distance matrix + double precision, dimension(:,:), intent(in) :: pd + + ! Scaling for angular and distance terms + double precision, intent(in) :: distance_scale + double precision, intent(in) :: angular_scale + + ! Switch alchemy on or off + logical, intent(in) :: alchemy + + ! Decaying power laws for two- and three-body terms + double precision, intent(in) :: two_body_power + double precision, intent(in) :: three_body_power + + + double precision, intent(in) :: ef_scale + double precision, intent(in) :: df + + ! Kernel ID and corresponding parameters + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters + + ! Resulting alpha vector + double precision, dimension(nsigmas,na1,nm2*3), intent(out) :: kernels + + ! Internal counters + integer :: i, j + integer :: ni, nj + integer :: a, b + integer :: xyz, pm + integer :: idx_a + integer :: idx_b + + ! Temporary variables necessary for parallelization + double precision :: s12 + + ! Pre-computed terms in the full distance matrix + double precision, allocatable, dimension(:,:) :: self_scalar1 + double precision, allocatable, dimension(:,:,:,:) :: self_scalar2_ef + + ! Pre-computed two-body weights for nummerical differentation of electric field + double precision, allocatable, dimension(:,:,:) :: ksi1 + double precision, allocatable, dimension(:,:,:,:,:) :: ksi2_ef + + ! Pre-computed terms for the Fourier expansion of the three-body term + double precision, allocatable, dimension(:,:,:,:,:) :: sinp1 + double precision, allocatable, dimension(:,:,:,:,:) :: cosp1 + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: sinp2_ef + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: cosp2_ef + + ! Max index in the periodic table + integer :: pmax1 + integer :: pmax2 + + ! Angular normalization constant + double precision :: ang_norm2 + + ! Max number of neighbors + integer :: maxneigh1 + integer :: maxneigh2 + + ! Variables to calculate time + double precision :: t_start, t_end + + if (verbose) write (*,*) "CLEARING KERNEL MEM" + kernels(:,:,:) = 0.0d0 + + ! Get max number of neighbors + maxneigh1 = maxval(nneigh1) + maxneigh2 = maxval(nneigh2) + + ! Calculate angular normalization constant + ang_norm2 = get_angular_norm2(t_width) + + ! pmax = max nuclear charge + pmax1 = get_pmax(x1, n1) + pmax2 = get_pmax(x2, n2) + + ! Get two-body weight function + ksi1 = get_ksi(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, verbose) + ksi2_ef = get_ksi_ef(x2, n2, nneigh2, two_body_power, cut_start, cut_distance, ef_scale, df, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + allocate(sinp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1, sinp1, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp2_ef(nm2, 3, 2, maxval(n2), pmax2, order, maxneigh2)) + allocate(sinp2_ef(nm2, 3, 2, maxval(n2), pmax2, order, maxneigh2)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp_ef(x2, n2, nneigh2, three_body_power, order, cut_start, cut_distance, & + & cosp2_ef, sinp2_ef, ef_scale, df, verbose) + + ! Pre-calculate self-scalar terms + self_scalar1 = get_selfscalar(x1, nm1, n1, nneigh1, ksi1, sinp1, cosp1, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + allocate(self_scalar2_ef(nm2, 3,2, maxval(n2))) + do a = 1, nm2 + ni = n2(a) + do xyz = 1, 3 + do pm = 1, 2 + do i = 1, ni + + self_scalar2_ef(a,xyz,pm,i) = scalar(x2(a,i,:,:), x2(a,i,:,:), nneigh2(a,i), nneigh2(a,i), & + & ksi2_ef(a,xyz,pm,i,:), ksi2_ef(a,xyz,pm,i,:), & + & sinp2_ef(a,xyz,pm,i,:,:,:), sinp2_ef(a,xyz,pm,i,:,:,:), & + & cosp2_ef(a,xyz,pm,i,:,:,:), cosp2_ef(a,xyz,pm,i,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2, distance_scale, angular_scale, alchemy) + + enddo + enddo + enddo + enddo + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL EF DERIVATIVE" + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(s12,ni,nj,idx_a,idx_b) + + do a = 1, nm1 + ni = n1(a) + do i = 1, ni + + idx_a = sum(n1(:a)) - ni + i + + + do b = 1, nm2 + nj = n2(b) + do j = 1, nj + do xyz = 1, 3 + idx_b = (b - 1) * 3 + xyz + + do pm = 1, 2 + + s12 = scalar(x1(a,i,:,:), x2(b,j,:,:), & + & nneigh1(a,i), nneigh2(b,j), & + & ksi1(a,i,:), ksi2_ef(b,xyz,pm,j,:), & + & sinp1(a,i,:,:,:), sinp2_ef(b,xyz,pm,j,:,:,:), & + & cosp1(a,i,:,:,:), cosp2_ef(b,xyz,pm,j,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2, distance_scale, angular_scale, alchemy) + + if (pm == 1) then + + kernels(:, idx_a, idx_b) = kernels(:, idx_a, idx_b) & + & + kernel(self_scalar1(a,i), self_scalar2_ef(b,xyz,pm,j), s12, & + & kernel_idx, parameters) + + else + + kernels(:, idx_a, idx_b) = kernels(:, idx_a, idx_b) & + & - kernel(self_scalar1(a,i), self_scalar2_ef(b,xyz,pm,j), s12, & + & kernel_idx, parameters) + + endif + + enddo + enddo + enddo + enddo + enddo + enddo + !$OMP END PARALLEL DO + + kernels = kernels / (2 * df) + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + deallocate(self_scalar1) + deallocate(self_scalar2_ef) + deallocate(ksi1) + deallocate(ksi2_ef) + deallocate(cosp1) + deallocate(cosp2_ef) + deallocate(sinp1) + deallocate(sinp2_ef) + +end subroutine fget_ef_atomic_local_gradient_kernels_fchl + + + +! subroutine fget_ef_local_hessian_kernels_fchl(x1, x2, verbose, n1, n2, nneigh1, nneigh2, nm1, nm2, nsigmas, & +! & t_width, d_width, cut_start, cut_distance, order, pd, & +! & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, & +! & ef_scale, df, kernel_idx, parameters, kernels) +! +! use ffchl_module, only: scalar, get_angular_norm2, get_pmax, get_ksi, init_cosp_sinp, & +! & get_selfscalar, get_ksi_ef, init_cosp_sinp_ef +! +! use ffchl_kernels, only: kernel +! +! use omp_lib, only: omp_get_wtime +! +! implicit none +! +! ! fchl descriptors for the training set, format (i,maxatoms,5,maxneighbors) +! double precision, dimension(:,:,:,:), intent(in) :: x1 +! double precision, dimension(:,:,:,:), intent(in) :: x2 +! +! ! Display output +! logical, intent(in) :: verbose +! +! ! List of numbers of atoms in each molecule +! integer, dimension(:), intent(in) :: n1 +! integer, dimension(:), intent(in) :: n2 +! +! ! Number of molecules +! integer, intent(in) :: nm1 +! integer, intent(in) :: nm2 +! +! ! Number of sigmas +! integer, intent(in) :: nsigmas +! +! ! Number of neighbors for each atom in each compound +! integer, dimension(:,:), intent(in) :: nneigh1 +! integer, dimension(:,:), intent(in) :: nneigh2 +! +! ! Angular Gaussian width +! double precision, intent(in) :: t_width +! +! ! Distance Gaussian width +! double precision, intent(in) :: d_width +! +! ! Fraction of cut_distance at which cut-off starts +! double precision, intent(in) :: cut_start +! double precision, intent(in) :: cut_distance +! +! ! Truncation order for Fourier terms +! integer, intent(in) :: order +! +! ! Periodic table distance matrix +! double precision, dimension(:,:), intent(in) :: pd +! +! ! Scaling for angular and distance terms +! double precision, intent(in) :: distance_scale +! double precision, intent(in) :: angular_scale +! +! ! Switch alchemy on or off +! logical, intent(in) :: alchemy +! +! ! Decaying power laws for two- and three-body terms +! double precision, intent(in) :: two_body_power +! double precision, intent(in) :: three_body_power +! +! double precision, intent(in) :: ef_scale +! double precision, intent(in) :: df +! +! ! Kernel ID and corresponding parameters +! integer, intent(in) :: kernel_idx +! double precision, dimension(:,:), intent(in) :: parameters +! +! ! Resulting alpha vector +! ! double precision, dimension(nsigmas,nm1,nm2), intent(out) :: kernels +! double precision, dimension(nsigmas,nm1*3,nm2*3), intent(out) :: kernels +! +! double precision, dimension(nsigmas):: kernel_sum1 +! double precision, dimension(nsigmas):: kernel_sum2 +! +! ! Internal counters +! integer :: i, j +! integer :: ni, nj +! integer :: a, b +! integer :: xyz, pm +! integer :: xyz1, pm1 +! integer :: xyz2, pm2 +! +! integer :: idx_a, idx_b +! +! ! Temporary variables necessary for parallelization +! double precision :: s12 +! +! ! Pre-computed terms in the full distance matrix +! double precision, allocatable, dimension(:,:,:,:) :: self_scalar1_ef +! double precision, allocatable, dimension(:,:,:,:) :: self_scalar2_ef +! +! ! Pre-computed two-body weights for nummerical differentation of electric field +! double precision, allocatable, dimension(:,:,:,:,:) :: ksi1_ef +! double precision, allocatable, dimension(:,:,:,:,:) :: ksi2_ef +! +! ! ! Pre-computed terms for the Fourier expansion of the three-body term +! double precision, allocatable, dimension(:,:,:,:,:,:,:) :: sinp1_ef +! double precision, allocatable, dimension(:,:,:,:,:,:,:) :: sinp2_ef +! double precision, allocatable, dimension(:,:,:,:,:,:,:) :: cosp1_ef +! double precision, allocatable, dimension(:,:,:,:,:,:,:) :: cosp2_ef +! +! ! Max index in the periodic table +! integer :: pmax1 +! integer :: pmax2 +! +! ! Angular normalization constant +! double precision :: ang_norm2 +! +! ! Max number of neighbors +! integer :: maxneigh1 +! integer :: maxneigh2 +! +! ! Variables to calculate time +! double precision :: t_start, t_end +! +! write (*,*) "CLEARING KERNEL MEM" +! kernels(:,:,:) = 0.0d0 +! +! ! Get max number of neighbors +! maxneigh1 = maxval(nneigh1) +! maxneigh2 = maxval(nneigh2) +! +! ! Calculate angular normalization constant +! ang_norm2 = get_angular_norm2(t_width) +! +! ! pmax = max nuclear charge +! pmax1 = get_pmax(x1, n1) +! pmax2 = get_pmax(x2, n2) +! +! +! ! Get two-body weight function +! ksi1_ef = get_ksi_ef(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, ef_scale, df) +! ksi2_ef = get_ksi_ef(x2, n2, nneigh2, two_body_power, cut_start, cut_distance, ef_scale, df) +! +! +! ! Allocate three-body Fourier terms +! allocate(cosp1_ef(nm1, 3, 2, maxval(n1), pmax1, order, maxneigh1)) +! allocate(sinp1_ef(nm1, 3, 2, maxval(n1), pmax1, order, maxneigh1)) +! +! ! Initialize and pre-calculate three-body Fourier terms +! call init_cosp_sinp_ef(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & +! & cosp1_ef, sinp1_ef, ef_scale, df) +! +! +! ! Allocate three-body Fourier terms +! allocate(cosp2_ef(nm2, 3, 2, maxval(n2), pmax2, order, maxneigh2)) +! allocate(sinp2_ef(nm2, 3, 2, maxval(n2), pmax2, order, maxneigh2)) +! +! ! Initialize and pre-calculate three-body Fourier terms +! call init_cosp_sinp_ef(x2, n2, nneigh2, three_body_power, order, cut_start, cut_distance, & +! & cosp2_ef, sinp2_ef, ef_scale, df) +! +! +! allocate(self_scalar1_ef(nm1, 3,2, maxval(n1))) +! do a = 1, nm1 +! ni = n1(a) +! do xyz = 1, 3 +! do pm = 1, 2 +! do i = 1, ni +! +! self_scalar1_ef(a,xyz,pm,i) = scalar(x1(a,i,:,:), x1(a,i,:,:), nneigh1(a,i), nneigh1(a,i), & +! & ksi1_ef(a,xyz,pm,i,:), ksi1_ef(a,xyz,pm,i,:), & +! & sinp1_ef(a,xyz,pm,i,:,:,:), sinp1_ef(a,xyz,pm,i,:,:,:), & +! & cosp1_ef(a,xyz,pm,i,:,:,:), cosp1_ef(a,xyz,pm,i,:,:,:), & +! & t_width, d_width, cut_distance, order, & +! & pd, ang_norm2, distance_scale, angular_scale, alchemy) +! +! enddo +! enddo +! enddo +! enddo +! +! +! allocate(self_scalar2_ef(nm2, 3,2, maxval(n2))) +! do a = 1, nm2 +! ni = n2(a) +! do xyz = 1, 3 +! do pm = 1, 2 +! do i = 1, ni +! +! self_scalar2_ef(a,xyz,pm,i) = scalar(x2(a,i,:,:), x2(a,i,:,:), nneigh2(a,i), nneigh2(a,i), & +! & ksi2_ef(a,xyz,pm,i,:), ksi2_ef(a,xyz,pm,i,:), & +! & sinp2_ef(a,xyz,pm,i,:,:,:), sinp2_ef(a,xyz,pm,i,:,:,:), & +! & cosp2_ef(a,xyz,pm,i,:,:,:), cosp2_ef(a,xyz,pm,i,:,:,:), & +! & t_width, d_width, cut_distance, order, & +! & pd, ang_norm2, distance_scale, angular_scale, alchemy) +! +! enddo +! enddo +! enddo +! enddo +! +! t_start = omp_get_wtime() +! write (*,"(A)", advance="no") "KERNEL" +! +! !$OMP PARALLEL DO schedule(dynamic) PRIVATE(s12,ni,nj,idx_a,idx_b,kernel_sum1,kernel_sum2) +! do b = 1, nm2 +! nj = n2(b) +! do j = 1, nj +! +! do a = 1, nm1 +! ni = n1(a) +! do i = 1, ni +! +! do xyz1 = 1, 3 +! do xyz2 = 1, 3 +! +! idx_a = (a - 1) * 3 + xyz1 +! idx_b = (b - 1) * 3 + xyz2 +! +! do pm1 = 1, 2 +! do pm2 = 1, 2 +! +! s12 = scalar(x1(a,i,:,:), x2(b,j,:,:), & +! & nneigh1(a,i), nneigh2(b,j), & +! & ksi1_ef(a,xyz1,pm1,i,:), ksi2_ef(b,xyz2,pm2,j,:), & +! & sinp1_ef(a,xyz1,pm1,i,:,:,:), sinp2_ef(b,xyz2,pm2,j,:,:,:), & +! & cosp1_ef(a,xyz1,pm1,i,:,:,:), cosp2_ef(b,xyz2,pm2,j,:,:,:), & +! & t_width, d_width, cut_distance, order, & +! & pd, ang_norm2, distance_scale, angular_scale, alchemy) +! +! +! if (pm1 == pm2) then +! +! kernels(:, idx_a, idx_b) = kernels(:, idx_a, idx_b) & +! & + kernel(self_scalar1_ef(a,xyz1,pm1,i), self_scalar2_ef(b,xyz2,pm2,j), s12, & +! & kernel_idx, parameters) +! else +! +! kernels(:, idx_a, idx_b) = kernels(:, idx_a, idx_b) & +! & - kernel(self_scalar1_ef(a,xyz1,pm1,i), self_scalar2_ef(b,xyz2,pm2,j), s12, & +! & kernel_idx, parameters) +! +! endif +! enddo +! enddo +! enddo +! enddo +! enddo +! enddo +! enddo +! enddo +! !$OMP END PARALLEL DO +! +! kernels = kernels / (4 * df**2) +! +! t_end = omp_get_wtime() +! write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" +! +! end subroutine fget_ef_local_hessian_kernels_fchl + + + +! +! TODO: Fix, experimental code for polarizability +! +! subroutine fget_kernels_fchl_ef_2ndderiv(x1, x2, n1, n2, nneigh1, nneigh2, nm1, nm2, na1, nsigmas, & +! & t_width, d_width, cut_start, cut_distance, order, pd, & +! & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, ef_scale,& +! & df, kernel_idx, parameters, kernels) +! +! use ffchl_module, only: scalar, get_angular_norm2, get_pmax, get_ksi, init_cosp_sinp, & +! & get_selfscalar, get_ksi_pol, init_cosp_sinp_pol +! +! use ffchl_kernels, only: kernel +! +! use omp_lib, only: omp_get_wtime +! +! implicit none +! +! ! fchl descriptors for the training set, format (i,maxatoms,5,maxneighbors) +! double precision, dimension(:,:,:,:), intent(in) :: x1 +! double precision, dimension(:,:,:,:), intent(in) :: x2 +! +! ! List of numbers of atoms in each molecule +! integer, dimension(:), intent(in) :: n1 +! integer, dimension(:), intent(in) :: n2 +! +! ! Number of molecules +! integer, intent(in) :: nm1 +! integer, intent(in) :: nm2 +! +! ! Number of atoms in set 1 +! integer, intent(in) :: na1 +! +! ! Number of sigmas +! integer, intent(in) :: nsigmas +! +! ! Number of neighbors for each atom in each compound +! integer, dimension(:,:), intent(in) :: nneigh1 +! integer, dimension(:,:), intent(in) :: nneigh2 +! +! ! Angular Gaussian width +! double precision, intent(in) :: t_width +! +! ! Distance Gaussian width +! double precision, intent(in) :: d_width +! +! ! Fraction of cut_distance at which cut-off starts +! double precision, intent(in) :: cut_start +! double precision, intent(in) :: cut_distance +! +! ! Truncation order for Fourier terms +! integer, intent(in) :: order +! +! ! Periodic table distance matrix +! double precision, dimension(:,:), intent(in) :: pd +! +! ! Scaling for angular and distance terms +! double precision, intent(in) :: distance_scale +! double precision, intent(in) :: angular_scale +! +! ! Switch alchemy on or off +! logical, intent(in) :: alchemy +! +! ! Decaying power laws for two- and three-body terms +! double precision, intent(in) :: two_body_power +! double precision, intent(in) :: three_body_power +! +! +! double precision, intent(in) :: ef_scale +! double precision, intent(in) :: df +! +! ! Kernel ID and corresponding parameters +! integer, intent(in) :: kernel_idx +! double precision, dimension(:,:), intent(in) :: parameters +! +! ! Resulting alpha vector +! ! double precision, dimension(nsigmas,nm1,nm2), intent(out) :: kernels +! ! double precision, dimension(nsigmas,na1,nm2*3), intent(out) :: kernels +! double precision, dimension(nsigmas,na1,na1*3), intent(out) :: kernels +! +! ! Internal counters +! integer :: i, j +! integer :: ni, nj +! integer :: a, b +! integer :: xyz, pm +! integer :: idx_a +! integer :: idx_b +! integer :: qidx +! +! ! Temporary variables necessary for parallelization +! double precision :: s12 +! +! ! Pre-computed terms in the full distance matrix +! double precision, allocatable, dimension(:,:) :: self_scalar1 +! double precision, allocatable, dimension(:,:,:) :: self_scalar2_pol +! +! ! Pre-computed two-body weights for nummerical differentation of electric field +! double precision, allocatable, dimension(:,:,:) :: ksi1 +! double precision, allocatable, dimension(:,:,:,:) :: ksi2_pol +! +! ! Pre-computed terms for the Fourier expansion of the three-body term +! double precision, allocatable, dimension(:,:,:,:,:) :: sinp1 +! double precision, allocatable, dimension(:,:,:,:,:) :: cosp1 +! double precision, allocatable, dimension(:,:,:,:,:,:) :: sinp2_pol +! double precision, allocatable, dimension(:,:,:,:,:,:) :: cosp2_pol +! +! double precision, dimension(nsigmas) :: unperturbed +! double precision, dimension(nsigmas) :: test_plus, test_minus +! +! integer, dimension(3,2) :: idx +! +! ! Max index in the periodic table +! integer :: pmax1 +! integer :: pmax2 +! +! ! Angular normalization constant +! double precision :: ang_norm2 +! +! ! Max number of neighbors +! integer :: maxneigh1 +! integer :: maxneigh2 +! +! ! Variables to calculate time +! double precision :: t_start, t_end +! +! write (*,*) "CLEARING KERNEL MEM" +! kernels(:,:,:) = 0.0d0 +! +! ! Get max number of neighbors +! maxneigh1 = maxval(nneigh1) +! maxneigh2 = maxval(nneigh2) +! +! ! Calculate angular normalization constant +! ang_norm2 = get_angular_norm2(t_width) +! +! ! pmax = max nuclear charge +! pmax1 = get_pmax(x1, n1) +! pmax2 = get_pmax(x2, n2) +! +! ! Get two-body weight function +! ksi1 = get_ksi(x1, n1, nneigh1, two_body_power, cut_start, cut_distance) +! ksi2_pol = get_ksi_pol(x2, n2, nneigh2, two_body_power, cut_start, cut_distance, ef_scale, df) +! +! ! Allocate three-body Fourier terms +! allocate(cosp1(nm1, maxval(n1), pmax1, order, maxneigh1)) +! allocate(sinp1(nm1, maxval(n1), pmax1, order, maxneigh1)) +! +! ! Initialize and pre-calculate three-body Fourier terms +! call init_cosp_sinp(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & +! & cosp1, sinp1) +! +! ! Allocate three-body Fourier terms +! allocate(cosp2_pol(nm2, 19, maxval(n2), pmax2, order, maxneigh2)) +! allocate(sinp2_pol(nm2, 19, maxval(n2), pmax2, order, maxneigh2)) +! +! ! Initialize and pre-calculate three-body Fourier terms +! call init_cosp_sinp_pol(x2, n2, nneigh2, three_body_power, order, cut_start, cut_distance, & +! & cosp2_pol, sinp2_pol, ef_scale, df) +! +! ! Pre-calculate self-scalar terms +! self_scalar1 = get_selfscalar(x1, nm1, n1, nneigh1, ksi1, sinp1, cosp1, t_width, d_width, & +! & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy) +! +! allocate(self_scalar2_pol(nm2, 19, maxval(n2))) +! do a = 1, nm2 +! ni = n2(a) +! do xyz = 1, 19 +! do i = 1, ni +! +! self_scalar2_pol(a,xyz,i) = scalar(x2(a,i,:,:), x2(a,i,:,:), & +! & nneigh2(a,i), nneigh2(a,i), & +! & ksi2_pol(a,xyz,i,:), ksi2_pol(a,xyz,i,:), & +! & sinp2_pol(a,xyz,i,:,:,:), sinp2_pol(a,xyz,i,:,:,:), & +! & cosp2_pol(a,xyz,i,:,:,:), cosp2_pol(a,xyz,i,:,:,:), & +! & t_width, d_width, cut_distance, order, & +! & pd, ang_norm2, distance_scale, angular_scale, alchemy) +! +! enddo +! enddo +! enddo +! +! t_start = omp_get_wtime() +! write (*,"(A)", advance="no") "KERNEL EF DERIVATIVE" +! +! idx(1,:) = (/ 17, 3 /) !xx = (17) + (3) - 2*10 +! idx(2,:) = (/ 11, 9 /) !yy = (11) + (9) - 2*10 +! idx(3,:) = (/ 13, 7 /) !zz = (13) + (7) - 2*10 +! +! ! Loop over atoms/basis functions +! ! !$OMP PARALLEL DO schedule(dynamic) PRIVATE(s12,ni,nj,idx_a,idx_b) +! do a = 1, nm1 +! ni = n1(a) +! do i = 1, ni +! idx_a = sum(n1(:a)) - ni + i +! +! +! do b = 1, nm2 +! nj = n2(b) +! +! do j = 1, nj +! +! ! Precalculate L2 for unperturbed kernel (charge index = 10) +! qidx = 10 +! +! ! write(*,*) qidx +! ! isotropic (XX, YY and ZZ) +! s12 = scalar(x1(a,i,:,:), x2(b,j,:,:), & +! & nneigh1(a,i), nneigh2(b,j), & +! & ksi1(a,i,:), ksi2_pol(b,qidx,j,:), & +! & sinp1(a,i,:,:,:), sinp2_pol(b,qidx,j,:,:,:), & +! & cosp1(a,i,:,:,:), cosp2_pol(b,qidx,j,:,:,:), & +! & t_width, d_width, cut_distance, order, & +! & pd, ang_norm2, distance_scale, angular_scale, alchemy) +! +! unperturbed(:) = kernel(self_scalar1(a,i), self_scalar2_pol(b,qidx,j), s12, & +! & kernel_idx, parameters) +! ! write(*,*) s12, unperturbed(:) +! +! do xyz = 1, 3 +! +! ! idx_b = (b - 1) * 3 + xyz +! idx_b = (sum(n2(:b)) - nj + j) * 3 + xyz +! +! qidx = idx(xyz,1) +! ! write(*,*) xyz, qidx +! +! s12 = scalar(x1(a,i,:,:), x2(b,j,:,:), & +! & nneigh1(a,i), nneigh2(b,j), & +! & ksi1(a,i,:), ksi2_pol(b,qidx,j,:), & +! & sinp1(a,i,:,:,:), sinp2_pol(b,qidx,j,:,:,:), & +! & cosp1(a,i,:,:,:), cosp2_pol(b,qidx,j,:,:,:), & +! & t_width, d_width, cut_distance, order, & +! & pd, ang_norm2, distance_scale, angular_scale, alchemy) +! +! kernels(:, idx_a, idx_b) = kernels(:, idx_a, idx_b) & +! & + kernel(self_scalar1(a,i), self_scalar2_pol(b,qidx,j), s12, & +! & kernel_idx, parameters) +! +! test_plus(:) = kernel(self_scalar1(a,i), self_scalar2_pol(b,qidx,j), s12, & +! & kernel_idx, parameters) +! +! qidx = idx(xyz,2) +! ! write(*,*) qidx +! ! write(*,*) xyz, qidx +! +! ! write(*,*) s12, kernel(self_scalar1(a,i), self_scalar2_pol(b,qidx,j), s12, kernel_idx, parameters) +! +! s12 = scalar(x1(a,i,:,:), x2(b,j,:,:), & +! & nneigh1(a,i), nneigh2(b,j), & +! & ksi1(a,i,:), ksi2_pol(b,qidx,j,:), & +! & sinp1(a,i,:,:,:), sinp2_pol(b,qidx,j,:,:,:), & +! & cosp1(a,i,:,:,:), cosp2_pol(b,qidx,j,:,:,:), & +! & t_width, d_width, cut_distance, order, & +! & pd, ang_norm2, distance_scale, angular_scale, alchemy) +! +! kernels(:, idx_a, idx_b) = kernels(:, idx_a, idx_b) & +! & + kernel(self_scalar1(a,i), self_scalar2_pol(b,qidx,j), s12, & +! & kernel_idx, parameters) +! +! test_minus(:) = kernel(self_scalar1(a,i), self_scalar2_pol(b,qidx,j), s12, & +! & kernel_idx, parameters) +! +! ! write(*,*) s12, kernel(self_scalar1(a,i), self_scalar2_pol(b,qidx,j), s12, kernel_idx, parameters) +! +! write(*,*) kernels(:, idx_a, idx_b) +! kernels(:, idx_a, idx_b) = kernels(:, idx_a, idx_b) - 2* unperturbed(:) +! write(*,*) kernels(:, idx_a, idx_b) +! ! write(*,*) unperturbed(:) +! ! write(*,*) - 2* unperturbed(:) +! ! write(*,*) test_plus(:) +! ! write(*,*) test_minus(:) +! ! write(*,*) kernels(:, idx_a, idx_b) +! +! enddo +! enddo +! enddo +! +! enddo +! enddo +! ! !$OMP END PARALLEL DO +! +! t_end = omp_get_wtime() +! write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" +! +! write(*,*) "DF = ", df +! +! write (*,*) kernels(1,1,1) +! kernels(:,:,:) = kernels(:,:,:) / (df * df) +! write (*,*) kernels(1,1,1) +! +! deallocate(self_scalar1) +! deallocate(self_scalar2_pol) +! deallocate(ksi1) +! deallocate(ksi2_pol) +! deallocate(cosp1) +! deallocate(cosp2_pol) +! deallocate(sinp1) +! deallocate(sinp2_pol) +! +! end subroutine fget_kernels_fchl_ef_2ndderiv + + diff --git a/qml/fchl/ffchl_force_kernels.f90 b/qml/fchl/ffchl_force_kernels.f90 new file mode 100644 index 000000000..f99a10018 --- /dev/null +++ b/qml/fchl/ffchl_force_kernels.f90 @@ -0,0 +1,1891 @@ +subroutine fget_gaussian_process_kernels_fchl(x1, x2, verbose, n1, n2, nneigh1, nneigh2, & + & nm1, nm2, naq2, nsigmas, & + & t_width, d_width, cut_start, cut_distance, order, pd, & + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, dx, & + & kernel_idx, parameters, kernels) + + use ffchl_module, only: scalar, get_angular_norm2, & + & get_pmax, get_ksi, init_cosp_sinp, get_selfscalar, & + & get_pmax_displaced, get_ksi_displaced, init_cosp_sinp_displaced, get_selfscalar_displaced + + use ffchl_kernels, only: kernel + + use omp_lib, only: omp_get_wtime + + implicit none + + ! fchl descriptors for the training set, format (nm1,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x1 + + ! fchl descriptors for the training set, format (nm2,3,2,maxatoms,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:,:,:,:), intent(in) :: x2 + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: n1 + integer, dimension(:), intent(in) :: n2 + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh1 + integer, dimension(:,:,:,:,:), intent(in) :: nneigh2 + + ! Number of molecules + integer, intent(in) :: nm1 + integer, intent(in) :: nm2 + + ! Total number of force components + integer, intent(in) :: naq2 + + ! Number of kernels + integer, intent(in) :: nsigmas + + ! Angular Gaussian width + double precision, intent(in) :: t_width + + ! Distance Gaussian width + double precision, intent(in) :: d_width + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Truncation order for Fourier terms + integer, intent(in) :: order + + ! Periodic table distance matrix + double precision, dimension(:,:), intent(in) :: pd + + ! Scaling for angular and distance terms + double precision, intent(in) :: distance_scale + double precision, intent(in) :: angular_scale + + ! Switch alchemy on or off + logical, intent(in) :: alchemy + + ! Decaying power laws for two- and three-body terms + double precision, intent(in) :: two_body_power + double precision, intent(in) :: three_body_power + + ! Displacement for numerical differentiation + double precision, intent(in) :: dx + + ! Kernel ID and corresponding parameters + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters + + ! Resulting kernel matrix + double precision, dimension(nsigmas,nm1+naq2,nm1+naq2), intent(out) :: kernels + + ! Internal counters + integer :: i1, i2, j1, j2 + integer :: na, nb + integer :: a, b + + ! Temporary variables necessary for parallelization + double precision :: s12 + + ! Pre-computed terms in the full distance matrix + double precision, allocatable, dimension(:,:) :: self_scalar1 + double precision, allocatable, dimension(:,:,:,:,:) :: self_scalar2 + + ! Pre-computed two-body weights + double precision, allocatable, dimension(:,:,:) :: ksi1 + double precision, allocatable, dimension(:,:,:,:,:,:) :: ksi2 + + ! Pre-computed terms for the Fourier expansion of the three-body term + double precision, allocatable, dimension(:,:,:,:,:) :: sinp1 + double precision, allocatable, dimension(:,:,:,:,:) :: cosp1 + + ! Pre-computed terms for the Fourier expansion of the three-body term + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: sinp2 + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: cosp2 + + ! Indexes for numerical differentiation + integer :: xyz_pm1 + integer :: xyz_pm2 + integer :: idx1, idx2 + integer :: xyz1, pm1 + integer :: xyz2, pm2 + + ! Max index in the periodic table + integer :: pmax1 + integer :: pmax2 + + ! Angular normalization constant + double precision :: ang_norm2 + + ! Max number of neighbors + integer :: maxneigh1 + integer :: maxneigh2 + + ! Variables to calculate time + double precision :: t_start, t_end + + ! Angular normalization constant + ang_norm2 = get_angular_norm2(t_width) + + if (verbose) write (*,*) "CLEARING KERNEL MEM" + kernels = 0.0d0 + + ! Max number of neighbors in the representations + maxneigh1 = maxval(nneigh1) + maxneigh2 = maxval(nneigh2) + + ! pmax = max nuclear charge + pmax1 = get_pmax(x1, n1) + pmax2 = get_pmax_displaced(x2, n2) + + ! Get two-body weight function + ksi1 = get_ksi(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, verbose) + ksi2 = get_ksi_displaced(x2, n2, nneigh2, two_body_power, cut_start, cut_distance, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + allocate(sinp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1, sinp1, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp2(nm2, 3*2, maxval(n2), maxval(n2), pmax2, order, maxneigh2)) + allocate(sinp2(nm2, 3*2, maxval(n2), maxval(n2), pmax2, order, maxneigh2)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp_displaced(x2, n2, nneigh2, three_body_power, order, cut_start, & + & cut_distance, cosp2, sinp2, verbose) + + ! Pre-calculate self-scalar terms + self_scalar1 = get_selfscalar(x1, nm1, n1, nneigh1, ksi1, sinp1, cosp1, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + ! Pre-calculate self-scalar terms + self_scalar2 = get_selfscalar_displaced(x2, nm2, n2, nneigh2, ksi2, sinp2, cosp2, t_width, & + & d_width, cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL" + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(na,nb,s12) + do a = 1, nm1 + na = n1(a) + do j1 = 1, na + + do b = 1, nm1 + nb = n1(b) + do j2 = 1, nb + + s12 = scalar(x1(a,j1,:,:), x1(b,j2,:,:), & + & nneigh1(a,j1), nneigh1(b,j2), & + & ksi1(a,j1,:), ksi1(b,j2,:), & + & sinp1(a,j1,:,:,:), sinp1(b,j2,:,:,:), & + & cosp1(a,j1,:,:,:), cosp1(b,j2,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2,distance_scale, angular_scale, alchemy) + + kernels(:, a, b) = kernels(:, a, b) & + & + kernel(self_scalar1(a,j1), self_scalar1(b,j2), s12, & + & kernel_idx, parameters) + + enddo + enddo + enddo + enddo + !$OMP END PARALLEL do + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL GRADIENT" + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(na,nb,xyz_pm2,s12),& + !$OMP& PRIVATE(idx1,idx2) + do a = 1, nm1 + na = n1(a) + idx1 = a + do j1 = 1, na + + do b = 1, nm2 + nb = n2(b) + do xyz2 = 1, 3 + do pm2 = 1, 2 + xyz_pm2 = 2*xyz2 + pm2 - 2 + do i2 = 1, nb + idx2 = (sum(n2(:b)) - n2(b))* 3 + (i2 - 1) * 3 + xyz2 + nm1 + do j2 = 1, nb + + s12 = scalar(x1(a,j1,:,:), x2(b,xyz2,pm2,i2,j2,:,:), & + & nneigh1(a,j1), nneigh2(b,xyz2,pm2,i2,j2), & + & ksi1(a,j1,:), ksi2(b,xyz2,pm2,i2,j2,:), & + & sinp1(a,j1,:,:,:), sinp2(b,xyz_pm2,i2,j2,:,:,:), & + & cosp1(a,j1,:,:,:), cosp2(b,xyz_pm2,i2,j2,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2,distance_scale, angular_scale, alchemy) + + if (pm2 == 2) then + + kernels(:, idx1, idx2) = kernels(:, idx1,idx2) & + & + kernel(self_scalar1(a,j1), self_scalar2(b,xyz2,pm2,i2,j2), s12, & + & kernel_idx, parameters) + + kernels(:,idx2,idx1) = kernels(:,idx1,idx2) + + else + + kernels(:, idx1, idx2) = kernels(:, idx1,idx2) & + & - kernel(self_scalar1(a,j1), self_scalar2(b,xyz2,pm2,i2,j2), s12, & + & kernel_idx, parameters) + + kernels(:,idx2,idx1) = kernels(:,idx1,idx2) + + end if + + enddo + enddo + enddo + enddo + enddo + enddo + enddo + !$OMP END PARALLEL do + + kernels(:,:nm1,nm1+1:) = kernels(:,:nm1,nm1+1:) / (2 * dx) + kernels(:,nm1+1:,:nm1) = kernels(:,nm1+1:,:nm1) / (2 * dx) + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL HESSIAN" + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(na,nb,xyz_pm1,xyz_pm2,s12),& + !$OMP& PRIVATE(idx1,idx2) + do a = 1, nm1 + na = n1(a) + do xyz1 = 1, 3 + do pm1 = 1, 2 + xyz_pm1 = 2*xyz1 + pm1 - 2 + do i1 = 1, na + idx1 = (sum(n1(:a)) - n1(a))* 3 + (i1 - 1) * 3 + xyz1 + nm1 + do j1 = 1, na + + do b = a, nm1 + nb = n1(b) + do xyz2 = 1, 3 + do pm2 = 1, 2 + xyz_pm2 = 2*xyz2 + pm2 - 2 + do i2 = 1, nb + idx2 = (sum(n1(:b)) - n1(b))* 3 + (i2 - 1) * 3 + xyz2 + nm1 + do j2 = 1, nb + + + s12 = scalar(x2(a,xyz1,pm1,i1,j1,:,:), x2(b,xyz2,pm2,i2,j2,:,:), & + & nneigh2(a,xyz1,pm1,i1,j1), nneigh2(b,xyz2,pm2,i2,j2), & + & ksi2(a,xyz1,pm1,i1,j1,:), ksi2(b,xyz2,pm2,i2,j2,:), & + & sinp2(a,xyz_pm1,i1,j1,:,:,:), sinp2(b,xyz_pm2,i2,j2,:,:,:), & + & cosp2(a,xyz_pm1,i1,j1,:,:,:), cosp2(b,xyz_pm2,i2,j2,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2,distance_scale, angular_scale, alchemy) + + if (pm1 == pm2) then + + kernels(:, idx1, idx2) = kernels(:, idx1, idx2) & + & + kernel(self_scalar2(a,xyz1,pm1,i1,j1), self_scalar2(b,xyz2,pm2,i2,j2), s12,& + & kernel_idx, parameters) + + if (a /= b) then + kernels(:, idx2, idx1) = kernels(:, idx2, idx1) & + & + kernel(self_scalar2(a,xyz1,pm1,i1,j1), self_scalar2(b,xyz2,pm2,i2,j2), s12,& + & kernel_idx, parameters) + endif + + else + kernels(:, idx1, idx2) = kernels(:, idx1, idx2) & + & - kernel(self_scalar2(a,xyz1,pm1,i1,j1), self_scalar2(b,xyz2,pm2,i2,j2), s12,& + & kernel_idx, parameters) + + if (a /= b) then + kernels(:, idx2, idx1) = kernels(:, idx2, idx1) & + & - kernel(self_scalar2(a,xyz1,pm1,i1,j1), self_scalar2(b,xyz2,pm2,i2,j2), s12,& + & kernel_idx, parameters) + endif + + end if + + + enddo + enddo + enddo + enddo + enddo + enddo + enddo + enddo + enddo + enddo + !$OMP END PARALLEL do + + kernels(:,nm1+1:,nm1+1:) = kernels(:,nm1+1:,nm1+1:) / (4 * dx**2) + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end subroutine fget_gaussian_process_kernels_fchl + + +subroutine fget_local_gradient_kernels_fchl(x1, x2, verbose, n1, n2, nneigh1, nneigh2, & + & nm1, nm2, naq2, nsigmas, & + & t_width, d_width, cut_start, cut_distance, order, pd, & + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, dx, & + & kernel_idx, parameters, kernels) + + use ffchl_module, only: scalar, get_angular_norm2, & + & get_pmax, get_ksi, init_cosp_sinp, get_selfscalar, & + & get_pmax_displaced, get_ksi_displaced, init_cosp_sinp_displaced, get_selfscalar_displaced + + use ffchl_kernels, only: kernel + + use omp_lib, only: omp_get_wtime + + implicit none + + ! fchl descriptors for the training set, format (nm1,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x1 + + ! fchl descriptors for the training set, format (nm2,3,2,maxatoms,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:,:,:,:), intent(in) :: x2 + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: n1 + integer, dimension(:), intent(in) :: n2 + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh1 + integer, dimension(:,:,:,:,:), intent(in) :: nneigh2 + + ! Number of molecules + integer, intent(in) :: nm1 + integer, intent(in) :: nm2 + + ! Total number of force components + integer, intent(in) :: naq2 + + ! Number of kernels + integer, intent(in) :: nsigmas + + double precision, intent(in) :: t_width + + ! Distance Gaussian width + double precision, intent(in) :: d_width + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Truncation order for Fourier terms + integer, intent(in) :: order + + ! Periodic table distance matrix + double precision, dimension(:,:), intent(in) :: pd + + ! Scaling for angular and distance terms + double precision, intent(in) :: distance_scale + double precision, intent(in) :: angular_scale + + ! Switch alchemy on or off + logical, intent(in) :: alchemy + + ! Decaying power laws for two- and three-body terms + double precision, intent(in) :: two_body_power + double precision, intent(in) :: three_body_power + + ! Displacement for numerical differentiation + double precision, intent(in) :: dx + + ! Kernel ID and corresponding parameters + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters + + ! Resulting alpha vector + double precision, dimension(nsigmas,nm1,naq2), intent(out) :: kernels + + ! Internal counters + integer :: i2, j1, j2 + integer :: na, nb + integer :: a, b + + ! Temporary variables necessary for parallelization + double precision :: s12 + + ! Pre-computed terms in the full distance matrix + double precision, allocatable, dimension(:,:) :: self_scalar1 + double precision, allocatable, dimension(:,:,:,:,:) :: self_scalar2 + + ! Pre-computed two-body weights + double precision, allocatable, dimension(:,:,:) :: ksi1 + double precision, allocatable, dimension(:,:,:,:,:,:) :: ksi2 + + ! Pre-computed terms for the Fourier expansion of the three-body term + double precision, allocatable, dimension(:,:,:,:,:) :: sinp1 + double precision, allocatable, dimension(:,:,:,:,:) :: cosp1 + + ! Pre-computed terms for the Fourier expansion of the three-body term + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: sinp2 + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: cosp2 + + ! Indexes for numerical differentiation + integer :: idx1, idx2 + integer :: xyz_pm2 + integer :: xyz2, pm2 + + ! Max index in the periodic table + integer :: pmax1 + integer :: pmax2 + + ! Angular normalization constant + double precision :: ang_norm2 + + ! Max number of neighbors + integer :: maxneigh1 + integer :: maxneigh2 + + ! Variables to calculate time + double precision :: t_start, t_end + + if (verbose) write (*,*) "INIT, dx =", dx + + if (verbose) write (*,*) "CLEARING KERNEL MEM" + kernels = 0.0d0 + + ! Angular normalization constant + ang_norm2 = get_angular_norm2(t_width) + + ! Max number of neighbors in the representations + maxneigh1 = maxval(nneigh1) + maxneigh2 = maxval(nneigh2) + + ! pmax = max nuclear charge + pmax1 = get_pmax(x1, n1) + pmax2 = get_pmax_displaced(x2, n2) + + ! Get two-body weight function + ksi1 = get_ksi(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, verbose) + ksi2 = get_ksi_displaced(x2, n2, nneigh2, two_body_power, cut_start, cut_distance, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + allocate(sinp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1, sinp1, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp2(nm2, 3*2, maxval(n2), maxval(n2), pmax2, order, maxneigh2)) + allocate(sinp2(nm2, 3*2, maxval(n2), maxval(n2), pmax2, order, maxneigh2)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp_displaced(x2, n2, nneigh2, three_body_power, order, cut_start, & + & cut_distance, cosp2, sinp2, verbose) + + ! Pre-calculate self-scalar terms + self_scalar1 = get_selfscalar(x1, nm1, n1, nneigh1, ksi1, sinp1, cosp1, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + ! Pre-calculate self-scalar terms + self_scalar2 = get_selfscalar_displaced(x2, nm2, n2, nneigh2, ksi2, sinp2, cosp2, t_width, & + & d_width, cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL GRADIENT" + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(na,nb,xyz_pm2,s12),& + !$OMP& PRIVATE(idx1,idx2) + do a = 1, nm1 + na = n1(a) + idx1 = a + do j1 = 1, na + + do b = 1, nm2 + nb = n2(b) + do xyz2 = 1, 3 + do pm2 = 1, 2 + xyz_pm2 = 2*xyz2 + pm2 - 2 + do i2 = 1, nb + idx2 = (sum(n2(:b)) - n2(b))* 3 + (i2 - 1) * 3 + xyz2 + do j2 = 1, nb + + + s12 = scalar(x1(a,j1,:,:), x2(b,xyz2,pm2,i2,j2,:,:), & + & nneigh1(a,j1), nneigh2(b,xyz2,pm2,i2,j2), & + & ksi1(a,j1,:), ksi2(b,xyz2,pm2,i2,j2,:), & + & sinp1(a,j1,:,:,:), sinp2(b,xyz_pm2,i2,j2,:,:,:), & + & cosp1(a,j1,:,:,:), cosp2(b,xyz_pm2,i2,j2,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2,distance_scale, angular_scale, alchemy) + if (pm2 == 2) then + + kernels(:,idx1,idx2) = kernels(:,idx1,idx2) & + & + kernel(self_scalar1(a,j1), self_scalar2(b,xyz2,pm2,i2,j2), s12, & + & kernel_idx, parameters) + else + kernels(:,idx1,idx2) = kernels(:,idx1,idx2) & + & - kernel(self_scalar1(a,j1), self_scalar2(b,xyz2,pm2,i2,j2), s12, & + & kernel_idx, parameters) + end if + + + enddo + enddo + enddo + enddo + enddo + enddo + enddo + !$OMP END PARALLEL do + + kernels = kernels / (2 * dx) + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end subroutine fget_local_gradient_kernels_fchl + + +subroutine fget_local_hessian_kernels_fchl(x1, x2, verbose, n1, n2, nneigh1, nneigh2, & + & nm1, nm2, naq1, naq2, nsigmas, & + & t_width, d_width, cut_start, cut_distance, order, pd, & + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, dx, & + & kernel_idx, parameters, kernels) + + use ffchl_module, only: scalar, get_angular_norm2, & + & get_pmax_displaced, get_ksi_displaced, init_cosp_sinp_displaced, get_selfscalar_displaced + + use ffchl_kernels, only: kernel + + use omp_lib, only: omp_get_wtime + + implicit none + + ! fchl descriptors for the training set, format (nm1,3,2,maxatoms,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:,:,:,:), intent(in) :: x1 + double precision, dimension(:,:,:,:,:,:,:), intent(in) :: x2 + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: n1 + integer, dimension(:), intent(in) :: n2 + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:,:,:,:), intent(in) :: nneigh1 + integer, dimension(:,:,:,:,:), intent(in) :: nneigh2 + + ! Number of molecules + integer, intent(in) :: nm1 + integer, intent(in) :: nm2 + + ! Total number of force components + integer, intent(in) :: naq1 + integer, intent(in) :: naq2 + + ! Number of kernels + integer, intent(in) :: nsigmas + + double precision, intent(in) :: t_width + + ! Distance Gaussian width + double precision, intent(in) :: d_width + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Truncation order for Fourier terms + integer, intent(in) :: order + + ! Periodic table distance matrix + double precision, dimension(:,:), intent(in) :: pd + + ! Scaling for angular and distance terms + double precision, intent(in) :: distance_scale + double precision, intent(in) :: angular_scale + + ! Switch alchemy on or off + logical, intent(in) :: alchemy + + ! Decaying power laws for two- and three-body terms + double precision, intent(in) :: two_body_power + double precision, intent(in) :: three_body_power + + ! Displacement for numerical differentiation + double precision, intent(in) :: dx + + ! Kernel ID and corresponding parameters + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters + + ! Resulting alpha vector + double precision, dimension(nsigmas,naq1,naq2), intent(out) :: kernels + + ! Internal counters + integer :: i1, i2, j1, j2 + integer :: na, nb + integer :: a, b + + ! Temporary variables necessary for parallelization + double precision :: s12 + + ! Pre-computed terms in the full distance matrix + double precision, allocatable, dimension(:,:,:,:,:) :: self_scalar1 + double precision, allocatable, dimension(:,:,:,:,:) :: self_scalar2 + + ! Pre-computed two-body weights + double precision, allocatable, dimension(:,:,:,:,:,:) :: ksi1 + double precision, allocatable, dimension(:,:,:,:,:,:) :: ksi2 + + ! Pre-computed terms for the Fourier expansion of the three-body term + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: sinp1 + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: cosp1 + + ! Pre-computed terms for the Fourier expansion of the three-body term + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: sinp2 + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: cosp2 + + ! Indexes for numerical differentiation + integer :: xyz_pm1 + integer :: xyz_pm2 + integer :: idx1, idx2 + integer :: xyz1, pm1 + integer :: xyz2, pm2 + + ! Max index in the periodic table + integer :: pmax1 + integer :: pmax2 + + ! Angular normalization constant + double precision :: ang_norm2 + + ! Max number of neighbors + integer :: maxneigh1 + integer :: maxneigh2 + + ! Variables to calculate time + double precision :: t_start, t_end + + ! Angular normalization constant + ang_norm2 = get_angular_norm2(t_width) + + if (verbose) write (*,*) "INIT, dx =", dx + + if (verbose) write (*,*) "CLEARING KERNEL MEM" + kernels = 0.0d0 + + ! Max number of neighbors in the representations + maxneigh1 = maxval(nneigh1) + maxneigh2 = maxval(nneigh2) + + ! pmax = max nuclear charge + pmax1 = get_pmax_displaced(x1, n1) + pmax2 = get_pmax_displaced(x2, n2) + + ! Get two-body weight function + ksi1 = get_ksi_displaced(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, verbose) + ksi2 = get_ksi_displaced(x2, n2, nneigh2, two_body_power, cut_start, cut_distance, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp1(nm1, 3*2, maxval(n1), maxval(n1), pmax1, order, maxval(nneigh1))) + allocate(sinp1(nm1, 3*2, maxval(n1), maxval(n1), pmax1, order, maxval(nneigh1))) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp_displaced(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1, sinp1, verbose) + + ! Initialize and pre-calculate three-body Fourier terms + allocate(cosp2(nm2, 3*2, maxval(n2), maxval(n2), pmax2, order, maxneigh2)) + allocate(sinp2(nm2, 3*2, maxval(n2), maxval(n2), pmax2, order, maxneigh2)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp_displaced(x2, n2, nneigh2, three_body_power, order, cut_start, & + & cut_distance, cosp2, sinp2, verbose) + + ! Pre-calculate self-scalar terms + self_scalar1 = get_selfscalar_displaced(x1, nm1, n1, nneigh1, ksi1, sinp1, cosp1, t_width, & + & d_width, cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + ! Pre-calculate self-scalar terms + self_scalar2 = get_selfscalar_displaced(x2, nm2, n2, nneigh2, ksi2, sinp2, cosp2, t_width, & + & d_width, cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL HESSIAN" + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(na,nb,xyz_pm1,xyz_pm2,s12),& + !$OMP& PRIVATE(idx1,idx2) + do a = 1, nm1 + na = n1(a) + do xyz1 = 1, 3 + do pm1 = 1, 2 + xyz_pm1 = 2*xyz1 + pm1 - 2 + do i1 = 1, na + idx1 = (sum(n1(:a)) - n1(a))* 3 + (i1 - 1) * 3 + xyz1 + do j1 = 1, na + + do b = 1, nm2 + nb = n2(b) + do xyz2 = 1, 3 + do pm2 = 1, 2 + xyz_pm2 = 2*xyz2 + pm2 - 2 + do i2 = 1, nb + idx2 = (sum(n2(:b)) - n2(b))* 3 + (i2 - 1) * 3 + xyz2 + do j2 = 1, nb + + + s12 = scalar(x1(a,xyz1,pm1,i1,j1,:,:), x2(b,xyz2,pm2,i2,j2,:,:), & + & nneigh1(a,xyz1,pm1,i1,j1), nneigh2(b,xyz2,pm2,i2,j2), & + & ksi1(a,xyz1,pm1,i1,j1,:), ksi2(b,xyz2,pm2,i2,j2,:), & + & sinp1(a,xyz_pm1,i1,j1,:,:,:), sinp2(b,xyz_pm2,i2,j2,:,:,:), & + & cosp1(a,xyz_pm1,i1,j1,:,:,:), cosp2(b,xyz_pm2,i2,j2,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2,distance_scale, angular_scale, alchemy) + + if (pm1 == pm2) then + + kernels(:, idx1, idx2) = kernels(:, idx1, idx2) & + & + kernel(self_scalar1(a,xyz1,pm1,i1,j1), self_scalar2(b,xyz2,pm2,i2,j2), s12,& + & kernel_idx, parameters) + + else + kernels(:, idx1, idx2) = kernels(:, idx1, idx2) & + & - kernel(self_scalar1(a,xyz1,pm1,i1,j1), self_scalar2(b,xyz2,pm2,i2,j2), s12,& + & kernel_idx, parameters) + + end if + + + enddo + enddo + enddo + enddo + enddo + enddo + enddo + enddo + enddo + enddo + !$OMP END PARALLEL do + + kernels = kernels / (4 * dx**2) + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end subroutine fget_local_hessian_kernels_fchl + + +subroutine fget_local_symmetric_hessian_kernels_fchl(x1, verbose, n1, nneigh1, & + & nm1, naq1, nsigmas, & + & t_width, d_width, cut_start, cut_distance, order, pd, & + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, dx, & + & kernel_idx, parameters, kernels) + + use ffchl_module, only: scalar, get_angular_norm2, & + & get_pmax_displaced, get_ksi_displaced, init_cosp_sinp_displaced, get_selfscalar_displaced + + use ffchl_kernels, only: kernel + + use omp_lib, only: omp_get_wtime + + implicit none + + ! fchl descriptors for the training set, format (nm1,3,2,maxatoms,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:,:,:,:), intent(in) :: x1 + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: n1 + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:,:,:,:), intent(in) :: nneigh1 + + ! Number of molecules + integer, intent(in) :: nm1 + + ! Total number of force components + integer, intent(in) :: naq1 + + ! Number of kernels + integer, intent(in) :: nsigmas + + ! Angular Gaussian width + double precision, intent(in) :: t_width + + ! Distance Gaussian width + double precision, intent(in) :: d_width + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Truncation order for Fourier terms + integer, intent(in) :: order + + ! Periodic table distance matrix + double precision, dimension(:,:), intent(in) :: pd + + ! Scaling for angular and distance terms + double precision, intent(in) :: distance_scale + double precision, intent(in) :: angular_scale + + ! Switch alchemy on or off + logical, intent(in) :: alchemy + + ! Decaying power laws for two- and three-body terms + double precision, intent(in) :: two_body_power + double precision, intent(in) :: three_body_power + + ! Displacement for numerical differentiation + double precision, intent(in) :: dx + + ! Kernel ID and corresponding parameters + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters + + ! Resulting alpha vector + double precision, dimension(nsigmas,naq1,naq1), intent(out) :: kernels + + ! Internal counters + integer :: i1, i2, j1, j2 + integer :: na, nb + integer :: a, b + + ! Temporary variables necessary for parallelization + double precision :: s12 + + ! Pre-computed terms in the full distance matrix + double precision, allocatable, dimension(:,:,:,:,:) :: self_scalar1 + + ! Pre-computed two-body weights + double precision, allocatable, dimension(:,:,:,:,:,:) :: ksi1 + + ! Pre-computed terms for the Fourier expansion of the three-body term + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: sinp1 + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: cosp1 + + ! Indexes for numerical differentiation + integer :: xyz_pm1 + integer :: xyz_pm2 + integer :: xyz1, pm1 + integer :: xyz2, pm2 + integer :: idx1, idx2 + + ! Max index in the periodic table + integer :: pmax1 + + ! Angular normalization constant + double precision :: ang_norm2 + + ! Max number of neighbors + integer :: maxneigh1 + + ! Variables to calculate time + double precision :: t_start, t_end + + ! Angular normalization constant + ang_norm2 = get_angular_norm2(t_width) + + if (verbose) write (*,*) "INIT, dx =", dx + + if (verbose) write (*,*) "CLEARING KERNEL MEM" + kernels = 0.0d0 + + ! Max number of neighbors + maxneigh1 = maxval(nneigh1) + + ! pmax = max nuclear charge + pmax1 = get_pmax_displaced(x1, n1) + + ! Get two-body weight function + ksi1 = get_ksi_displaced(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp1(nm1, 3*2, maxval(n1), maxval(n1), pmax1, order, maxval(nneigh1))) + allocate(sinp1(nm1, 3*2, maxval(n1), maxval(n1), pmax1, order, maxval(nneigh1))) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp_displaced(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1, sinp1, verbose) + + ! Pre-calculate self-scalar terms + self_scalar1 = get_selfscalar_displaced(x1, nm1, n1, nneigh1, ksi1, sinp1, cosp1, t_width,& + & d_width, cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL HESSIAN" + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(na,nb,xyz_pm1,xyz_pm2,s12),& + !$OMP& PRIVATE(idx1,idx2) + do a = 1, nm1 + na = n1(a) + do xyz1 = 1, 3 + do pm1 = 1, 2 + xyz_pm1 = 2*xyz1 + pm1 - 2 + do i1 = 1, na + idx1 = (sum(n1(:a)) - n1(a))* 3 + (i1 - 1) * 3 + xyz1 + do j1 = 1, na + + do b = a, nm1 + nb = n1(b) + do xyz2 = 1, 3 + do pm2 = 1, 2 + xyz_pm2 = 2*xyz2 + pm2 - 2 + do i2 = 1, nb + idx2 = (sum(n1(:b)) - n1(b))* 3 + (i2 - 1) * 3 + xyz2 + do j2 = 1, nb + + + s12 = scalar(x1(a,xyz1,pm1,i1,j1,:,:), x1(b,xyz2,pm2,i2,j2,:,:), & + & nneigh1(a,xyz1,pm1,i1,j1), nneigh1(b,xyz2,pm2,i2,j2), & + & ksi1(a,xyz1,pm1,i1,j1,:), ksi1(b,xyz2,pm2,i2,j2,:), & + & sinp1(a,xyz_pm1,i1,j1,:,:,:), sinp1(b,xyz_pm2,i2,j2,:,:,:), & + & cosp1(a,xyz_pm1,i1,j1,:,:,:), cosp1(b,xyz_pm2,i2,j2,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2,distance_scale, angular_scale, alchemy) + + if (pm1 == pm2) then + + kernels(:, idx1, idx2) = kernels(:, idx1, idx2) & + & + kernel(self_scalar1(a,xyz1,pm1,i1,j1), self_scalar1(b,xyz2,pm2,i2,j2), s12,& + & kernel_idx, parameters) + if (a /= b) then + kernels(:,idx2,idx1) = kernels(:,idx2,idx1) & + & + kernel(self_scalar1(a,xyz1,pm1,i1,j1), self_scalar1(b,xyz2,pm2,i2,j2), s12,& + & kernel_idx, parameters) + endif + + else + kernels(:, idx1, idx2) = kernels(:, idx1, idx2) & + & - kernel(self_scalar1(a,xyz1,pm1,i1,j1), self_scalar1(b,xyz2,pm2,i2,j2), s12,& + & kernel_idx, parameters) + + if (a /= b) then + kernels(:,idx2,idx1) = kernels(:,idx2,idx1) & + & - kernel(self_scalar1(a,xyz1,pm1,i1,j1), self_scalar1(b,xyz2,pm2,i2,j2), s12,& + & kernel_idx, parameters) + endif + + end if + + enddo + enddo + enddo + enddo + enddo + enddo + enddo + enddo + enddo + enddo + !$OMP END PARALLEL do + + kernels = kernels / (4 * dx**2) + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end subroutine fget_local_symmetric_hessian_kernels_fchl + + +subroutine fget_force_alphas_fchl(x1, x2, verbose, forces, energies, n1, n2, & + & nneigh1, nneigh2, nm1, nm2, na1, nsigmas, & + & t_width, d_width, cut_start, cut_distance, order, pd, & + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, dx, & + & kernel_idx, parameters, llambda, alphas) + + use ffchl_module, only: scalar, get_angular_norm2, & + & get_pmax, get_ksi, init_cosp_sinp, get_selfscalar, & + & get_pmax_displaced, get_ksi_displaced, init_cosp_sinp_displaced, get_selfscalar_displaced + + use ffchl_kernels, only: kernel + + use omp_lib, only: omp_get_wtime + + implicit none + + ! fchl descriptors for the training set, format (nm1,3,2,maxatoms,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x1 + double precision, dimension(:,:,:,:,:,:,:), intent(in) :: x2 + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + double precision, dimension(:,:), intent(in) :: forces + double precision, dimension(:), intent(in) :: energies + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: n1 + integer, dimension(:), intent(in) :: n2 + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh1 + integer, dimension(:,:,:,:,:), intent(in) :: nneigh2 + + ! Number of molecules + integer, intent(in) :: nm1 + integer, intent(in) :: nm2 + + ! Number of atoms (and force components in each direction) + integer, intent(in) :: na1 + + ! Number of kernels + integer, intent(in) :: nsigmas + + ! Angular Gaussian width + double precision, intent(in) :: t_width + + ! Distance Gaussian width + double precision, intent(in) :: d_width + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Truncation order for Fourier terms + integer, intent(in) :: order + + ! Periodic table distance matrix + double precision, dimension(:,:), intent(in) :: pd + + ! Scaling for angular and distance terms + double precision, intent(in) :: distance_scale + double precision, intent(in) :: angular_scale + + ! Switch alchemy on or off + logical, intent(in) :: alchemy + + ! Decaying power laws for two- and three-body terms + double precision, intent(in) :: two_body_power + double precision, intent(in) :: three_body_power + + ! Displacement for numerical differentiation + double precision, intent(in) :: dx + + ! Kernel ID and corresponding parameters + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters + + ! Regularization Lambda + double precision, intent(in) :: llambda + + double precision, dimension(nsigmas,na1), intent(out) :: alphas + + ! Internal counters + integer :: i, j, k, i2, j1, j2 + integer :: na, nb, ni, nj + integer :: a, b + + ! Temporary variables necessary for parallelization + double precision :: s12 + + ! Pre-computed terms in the full distance matrix + double precision, allocatable, dimension(:,:) :: self_scalar1 + double precision, allocatable, dimension(:,:,:,:,:) :: self_scalar2 + + ! Pre-computed terms + double precision, allocatable, dimension(:,:,:) :: ksi1 + double precision, allocatable, dimension(:,:,:,:,:,:) :: ksi2 + + double precision, allocatable, dimension(:,:,:,:,:) :: sinp1 + double precision, allocatable, dimension(:,:,:,:,:) :: cosp1 + + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: sinp2 + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: cosp2 + + ! Indexes for numerical differentiation + integer :: xyz_pm2 + integer :: xyz2, pm2 + integer :: idx1 + integer :: idx2 + integer :: idx1_start + integer :: idx2_start + + ! 1/(2*dx) + double precision :: inv_2dx + + ! Max index in the periodic table + integer :: pmax1 + integer :: pmax2 + + ! Angular normalization constant + double precision :: ang_norm2 + + ! Max number of neighbors + integer :: maxneigh1 + integer :: maxneigh2 + + ! Info variable for BLAS/LAPACK calls + integer :: info + + ! Feature vector multiplied by the kernel derivatives + double precision, allocatable, dimension(:,:) :: y + + ! Numerical derivatives of kernel + double precision, allocatable, dimension(:,:,:) :: kernel_delta + + ! Scratch space for products of the kernel derivatives + double precision, allocatable, dimension(:,:,:) :: kernel_scratch + + ! Variables to calculate time + double precision :: t_start, t_end + + ! Kernel between molecules and atom + double precision, allocatable, dimension(:,:,:) :: kernel_ma + + inv_2dx = 1.0d0 / (2.0d0 * dx) + + ! Angular normalization constant + ang_norm2 = get_angular_norm2(t_width) + + if (verbose) write (*,*) "INIT, DX =", dx + + ! Max number of neighbors in the representations + maxneigh1 = maxval(nneigh1) + maxneigh2 = maxval(nneigh2) + + ! pmax = max nuclear charge + pmax1 = get_pmax(x1, n1) + pmax2 = get_pmax_displaced(x2, n2) + + ! Get two-body weight function + ksi1 = get_ksi(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, verbose) + ksi2 = get_ksi_displaced(x2, n2, nneigh2, two_body_power, cut_start, cut_distance, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + allocate(sinp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1, sinp1, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp2(nm2, 3*2, maxval(n2), maxval(n2), pmax2, order, maxneigh2)) + allocate(sinp2(nm2, 3*2, maxval(n2), maxval(n2), pmax2, order, maxneigh2)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp_displaced(x2, n2, nneigh2, three_body_power, order, cut_start, & + & cut_distance, cosp2, sinp2, verbose) + + ! Pre-calculate self-scalar terms + self_scalar1 = get_selfscalar(x1, nm1, n1, nneigh1, ksi1, sinp1, cosp1, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + ! Pre-calculate self-scalar terms + self_scalar2 = get_selfscalar_displaced(x2, nm2, n2, nneigh2, ksi2, sinp2, cosp2, t_width, & + & d_width, cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + + if (verbose) write (*,*) "CLEARING KERNEL MEM" + + allocate(kernel_delta(na1,na1,nsigmas)) + allocate(y(na1,nsigmas)) + y = 0.0d0 + + allocate(kernel_scratch(na1,na1,nsigmas)) + kernel_scratch = 0.0d0 + + ! Calculate kernel derivatives and add to kernel matrix + do xyz2 = 1, 3 + + if (verbose) write (*,"(A,I3,A)", advance="no") "KERNEL GRADIENT", xyz2, " / 3" + t_start = omp_get_wtime() + + kernel_delta = 0.0d0 + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(na,nb,xyz_pm2,s12), & + !$OMP& PRIVATE(idx1,idx2,idx1_start,idx2_start) + do a = 1, nm1 + na = n1(a) + idx1_start = sum(n1(:a)) - na + do j1 = 1, na + idx1 = idx1_start + j1 + + do b = 1, nm2 + nb = n2(b) + idx2_start = (sum(n2(:b)) - nb) + + do pm2 = 1, 2 + xyz_pm2 = 2*xyz2 + pm2 - 2 + do i2 = 1, nb + idx2 = idx2_start + i2 + do j2 = 1, nb + + + s12 = scalar(x1(a,j1,:,:), x2(b,xyz2,pm2,i2,j2,:,:), & + & nneigh1(a,j1), nneigh2(b,xyz2,pm2,i2,j2), & + & ksi1(a,j1,:), ksi2(b,xyz2,pm2,i2,j2,:), & + & sinp1(a,j1,:,:,:), sinp2(b,xyz_pm2,i2,j2,:,:,:), & + & cosp1(a,j1,:,:,:), cosp2(b,xyz_pm2,i2,j2,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2,distance_scale, angular_scale, alchemy) + + if (pm2 == 2) then + + kernel_delta(idx1,idx2,:) = kernel_delta(idx1,idx2,:) & + & + kernel(self_scalar1(a,j1), self_scalar2(b,xyz2,pm2,i2,j2), s12,& + kernel_idx, parameters) * inv_2dx + else + kernel_delta(idx1,idx2,:) = kernel_delta(idx1,idx2,:) & + & - kernel(self_scalar1(a,j1), self_scalar2(b,xyz2,pm2,i2,j2), s12,& + kernel_idx, parameters) * inv_2dx + + end if + + + enddo + enddo + enddo + enddo + enddo + enddo + !$OMP END PARALLEL do + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + do k = 1, nsigmas + + if (verbose) write (*,"(A,I12)", advance="no") " DSYRK() sigma =", k + t_start = omp_get_wtime() + + call dsyrk("U", "N", na1, na1, 1.0d0, kernel_delta(1,1,k), na1, & + & 1.0d0, kernel_scratch(1,1,k), na1) + + ! kernel_scratch(:,:,k) = kernel_scratch(:,:,k) & + ! & + matmul(kernel_delta(:,:,k),transpose(kernel_delta(:,:,k)))! * inv_2dx*inv_2dx + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + if (verbose) write (*,"(A,I12)", advance="no") " DGEMV() sigma =", k + t_start = omp_get_wtime() + + call dgemv("N", na1, na1, 1.0d0, kernel_delta(:,:,k), na1, & + & forces(:,xyz2), 1, 1.0d0, y(:,k), 1) + + ! y(:,k) = y(:,k) + matmul(kernel_delta(:,:,k), forces(:,xyz2))!* inv_2dx + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + enddo + + enddo + + deallocate(kernel_delta) + deallocate(self_scalar2) + deallocate(ksi2) + deallocate(cosp2) + deallocate(sinp2) + + allocate(kernel_MA(nm1,na1,nsigmas)) + kernel_MA = 0.0d0 + + if (verbose) write (*,"(A)", advance="no") "KERNEL" + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(ni,nj,idx1,s12,idx1_start) + do a = 1, nm1 + ni = n1(a) + idx1_start = sum(n1(:a)) - ni + do i = 1, ni + + idx1 = idx1_start + i + + do b = 1, nm1 + nj = n1(b) + do j = 1, nj + + s12 = scalar(x1(a,i,:,:), x1(b,j,:,:), & + & nneigh1(a,i), nneigh1(b,j), ksi1(a,i,:), ksi1(b,j,:), & + & sinp1(a,i,:,:,:), sinp1(b,j,:,:,:), & + & cosp1(a,i,:,:,:), cosp1(b,j,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2, distance_scale, angular_scale, alchemy) + + kernel_MA(b,idx1,:) = kernel_MA(b,idx1,:) & + & + kernel(self_scalar1(a,i), self_scalar1(b,j), s12,& + kernel_idx, parameters) + + enddo + enddo + + enddo + enddo + !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + deallocate(self_scalar1) + deallocate(ksi1) + deallocate(cosp1) + deallocate(sinp1) + + do k = 1, nsigmas + + ! kernel_scratch(:,:,k) = kernel_scratch(:,:,k) & + ! & + matmul(transpose(kernel_MA(:,:,k)),kernel_MA(:,:,k)) + + ! y(:,k) = y(:,k) + matmul(transpose(kernel_MA(:,:,k)), energies(:)) + + if (verbose) write (*,"(A,I12)", advance="no") " DSYRK() sigma =", k + t_start = omp_get_wtime() + + call dsyrk("U", "T", na1, nm1, 1.0d0, kernel_MA(:,:,k), nm1, & + & 1.0d0, kernel_scratch(:,:,k), na1) + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + if (verbose) write (*,"(A,I12)", advance="no") " DGEMV() sigma =", k + t_start = omp_get_wtime() + + call dgemv("T", nm1, na1, 1.0d0, kernel_ma(:,:,k), nm1, & + & energies(:), 1, 1.0d0, y(:,k), 1) + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + enddo + + deallocate(kernel_ma) + + ! Add regularization + do k = 1, nsigmas + do i = 1, na1 + kernel_scratch(i,i,k) = kernel_scratch(i,i,k) + llambda + enddo + enddo + + alphas = 0.0d0 + + ! Solve alphas + if (verbose) write (*,"(A)") "CHOLESKY DECOMPOSITION" + do k = 1, nsigmas + + if (verbose) write (*,"(A,I12)", advance="no") " DPOTRF() sigma =", k + t_start = omp_get_wtime() + + call dpotrf("U", na1, kernel_scratch(:,:,k), na1, info) + if (info > 0) then + write (*,*) "QML WARNING: Error in LAPACK Cholesky decomposition DPOTRF()." + write (*,*) "QML WARNING: The", info, "-th leading order is not positive definite." + else if (info < 0) then + write (*,*) "QML WARNING: Error in LAPACK Cholesky decomposition DPOTRF()." + write (*,*) "QML WARNING: The", -info, "-th argument had an illegal value." + endif + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + if (verbose) write (*,"(A,I12)", advance="no") " DPOTRS() sigma =", k + t_start = omp_get_wtime() + + call dpotrs("U", na1, 1, kernel_scratch(:,:,k), na1, y(:,k), na1, info) + if (info < 0) then + write (*,*) "QML WARNING: Error in LAPACK Cholesky solver DPOTRS()." + write (*,*) "QML WARNING: The", -info, "-th argument had an illegal value." + endif + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + alphas(k,:) = y(:,k) + enddo + + deallocate(y) + deallocate(kernel_scratch) + +end subroutine fget_force_alphas_fchl + + +subroutine fget_atomic_local_gradient_kernels_fchl(x1, x2, verbose, n1, n2, nneigh1, nneigh2, & + & nm1, nm2, na1, naq2, nsigmas, & + & t_width, d_width, cut_start, cut_distance, order, pd, & + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, dx, & + & kernel_idx, parameters, kernels) + + use ffchl_module, only: scalar, get_angular_norm2, & + & get_pmax, get_ksi, init_cosp_sinp, get_selfscalar, & + & get_pmax_displaced, get_ksi_displaced, init_cosp_sinp_displaced, get_selfscalar_displaced + + use ffchl_kernels, only: kernel + + use omp_lib, only: omp_get_wtime + + implicit none + + ! fchl descriptors for the training set, format (nm1,3,2,maxatoms,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x1 + double precision, dimension(:,:,:,:,:,:,:), intent(in) :: x2 + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh1 + integer, dimension(:,:,:,:,:), intent(in) :: nneigh2 + + ! Number of molecules + integer, intent(in) :: nm1 + integer, intent(in) :: nm2 + + integer, intent(in) :: na1 + integer, intent(in) :: naq2 + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: n1 + integer, dimension(:), intent(in) :: n2 + + ! Number of kernels + integer, intent(in) :: nsigmas + + ! Angular Gaussian width + double precision, intent(in) :: t_width + + ! Distance Gaussian width + double precision, intent(in) :: d_width + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Truncation order for Fourier terms + integer, intent(in) :: order + + ! Periodic table distance matrix + double precision, dimension(:,:), intent(in) :: pd + + ! Scaling for angular and distance terms + double precision, intent(in) :: distance_scale + double precision, intent(in) :: angular_scale + + ! Switch alchemy on or off + logical, intent(in) :: alchemy + + ! Decaying power laws for two- and three-body terms + double precision, intent(in) :: two_body_power + double precision, intent(in) :: three_body_power + + ! Displacement for numerical differentiation + double precision, intent(in) :: dx + + ! Kernel ID and corresponding parameters + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters + + ! Resulting alpha vector + double precision, dimension(nsigmas,na1,naq2), intent(out) :: kernels + + ! Internal counters + integer :: i2, j1, j2 + integer :: na, nb + integer :: a, b + + ! Temporary variables necessary for parallelization + double precision :: s12 + + ! Pre-computed terms in the full distance matrix + double precision, allocatable, dimension(:,:) :: self_scalar1 + double precision, allocatable, dimension(:,:,:,:,:) :: self_scalar2 + + ! Pre-computed two-body weights + double precision, allocatable, dimension(:,:,:) :: ksi1 + double precision, allocatable, dimension(:,:,:,:,:,:) :: ksi2 + + ! Pre-computed terms for the Fourier expansion of the three-body term + double precision, allocatable, dimension(:,:,:,:,:) :: sinp1 + double precision, allocatable, dimension(:,:,:,:,:) :: cosp1 + + ! Pre-computed terms for the Fourier expansion of the three-body term + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: sinp2 + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: cosp2 + + ! Indexes for numerical differentiation + integer :: xyz_pm2 + integer :: xyz2, pm2 + integer :: idx1, idx2 + integer :: idx1_start,idx1_end + integer :: idx2_start,idx2_end + + ! Max index in the periodic table + integer :: pmax1 + integer :: pmax2 + + ! Angular normalization constant + double precision :: ang_norm2 + + ! Max number of neighbors + integer :: maxneigh1 + integer :: maxneigh2 + + ! Variables to calculate time + double precision :: t_start, t_end + + if (verbose) write (*,*) "INIT, dx =", dx + + if (verbose) write (*,*) "CLEARING KERNEL MEM" + kernels = 0.0d0 + + ! Angular normalization constant + ang_norm2 = get_angular_norm2(t_width) + + ! Max number of neighbors in the representations + maxneigh1 = maxval(nneigh1) + maxneigh2 = maxval(nneigh2) + + ! pmax = max nuclear charge + pmax1 = get_pmax(x1, n1) + pmax2 = get_pmax_displaced(x2, n2) + + ! Get two-body weight function + ksi1 = get_ksi(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, verbose) + ksi2 = get_ksi_displaced(x2, n2, nneigh2, two_body_power, cut_start, cut_distance, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + allocate(sinp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1, sinp1, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp2(nm2, 3*2, maxval(n2), maxval(n2), pmax2, order, maxneigh2)) + allocate(sinp2(nm2, 3*2, maxval(n2), maxval(n2), pmax2, order, maxneigh2)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp_displaced(x2, n2, nneigh2, three_body_power, order, cut_start, & + & cut_distance, cosp2, sinp2, verbose) + + ! Pre-calculate self-scalar terms + self_scalar1 = get_selfscalar(x1, nm1, n1, nneigh1, ksi1, sinp1, cosp1, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + ! Pre-calculate self-scalar terms + self_scalar2 = get_selfscalar_displaced(x2, nm2, n2, nneigh2, ksi2, sinp2, cosp2, t_width, & + & d_width, cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL GRADIENT" + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(na,nb,xyz_pm2,s12),& + !$OMP& PRIVATE(idx1,idx2,idx1_start,idx1_end,idx2_start,idx2_end) + do a = 1, nm1 + na = n1(a) + + idx1_end = sum(n1(:a)) + idx1_start = idx1_end - na + 1 + + do j1 = 1, na + idx1 = idx1_start - 1 + j1 + + do b = 1, nm2 + nb = n2(b) + + idx2_end = sum(n2(:b)) + idx2_start = idx2_end - nb + 1 + + do xyz2 = 1, 3 + do pm2 = 1, 2 + xyz_pm2 = 2*xyz2 + pm2 - 2 + do i2 = 1, nb + + idx2 = (idx2_start-1)*3 + (i2-1)*3 + xyz2 + + do j2 = 1, nb + + s12 = scalar(x1(a,j1,:,:), x2(b,xyz2,pm2,i2,j2,:,:), & + & nneigh1(a,j1), nneigh2(b,xyz2,pm2,i2,j2), & + & ksi1(a,j1,:), ksi2(b,xyz2,pm2,i2,j2,:), & + & sinp1(a,j1,:,:,:), sinp2(b,xyz_pm2,i2,j2,:,:,:), & + & cosp1(a,j1,:,:,:), cosp2(b,xyz_pm2,i2,j2,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2,distance_scale, angular_scale, alchemy) + + if (pm2 == 2) then + + kernels(:,idx1,idx2) = kernels(:,idx1,idx2) & + & + kernel(self_scalar1(a,j1), self_scalar2(b,xyz2,pm2,i2,j2), s12,& + & kernel_idx, parameters) + else + kernels(:,idx1,idx2) = kernels(:,idx1,idx2) & + & - kernel(self_scalar1(a,j1), self_scalar2(b,xyz2,pm2,i2,j2), s12,& + & kernel_idx, parameters) + + end if + + enddo + enddo + enddo + enddo + enddo + enddo + enddo + !$OMP END PARALLEL do + + kernels = kernels / (2 * dx) + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end subroutine fget_atomic_local_gradient_kernels_fchl + + +subroutine fget_atomic_local_gradient_5point_kernels_fchl(x1, x2, verbose, n1, n2, nneigh1, nneigh2, & + & nm1, nm2, na1, naq2, nsigmas, & + & t_width, d_width, cut_start, cut_distance, order, pd, & + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, dx, & + & kernel_idx, parameters, kernels) + + use ffchl_module, only: scalar, get_angular_norm2, & + & get_pmax, get_ksi, init_cosp_sinp, get_selfscalar, & + & get_pmax_displaced, get_ksi_displaced, init_cosp_sinp_displaced, get_selfscalar_displaced + + use ffchl_kernels, only: kernel + + use omp_lib, only: omp_get_wtime + + implicit none + + ! fchl descriptors for the training set, format (nm1,3,2,maxatoms,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x1 + double precision, dimension(:,:,:,:,:,:,:), intent(in) :: x2 + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh1 + integer, dimension(:,:,:,:,:), intent(in) :: nneigh2 + + ! Number of molecules + integer, intent(in) :: nm1 + integer, intent(in) :: nm2 + + integer, intent(in) :: na1 + integer, intent(in) :: naq2 + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: n1 + integer, dimension(:), intent(in) :: n2 + + ! Number of kernels + integer, intent(in) :: nsigmas + + ! Angular Gaussian width + double precision, intent(in) :: t_width + + ! Distance Gaussian width + double precision, intent(in) :: d_width + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Truncation order for Fourier terms + integer, intent(in) :: order + + ! Periodic table distance matrix + double precision, dimension(:,:), intent(in) :: pd + + ! Scaling for angular and distance terms + double precision, intent(in) :: distance_scale + double precision, intent(in) :: angular_scale + + ! Switch alchemy on or off + logical, intent(in) :: alchemy + + ! Decaying power laws for two- and three-body terms + double precision, intent(in) :: two_body_power + double precision, intent(in) :: three_body_power + + ! Displacement for numerical differentiation + double precision, intent(in) :: dx + + ! Kernel ID and corresponding parameters + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters + + ! Resulting alpha vector + double precision, dimension(nsigmas,na1,naq2), intent(out) :: kernels + + ! Internal counters + integer :: i2, j1, j2 + integer :: na, nb + integer :: a, b + + ! Temporary variables necessary for parallelization + double precision :: s12 + + ! Pre-computed terms in the full distance matrix + double precision, allocatable, dimension(:,:) :: self_scalar1 + double precision, allocatable, dimension(:,:,:,:,:) :: self_scalar2 + + ! Pre-computed two-body weights + double precision, allocatable, dimension(:,:,:) :: ksi1 + double precision, allocatable, dimension(:,:,:,:,:,:) :: ksi2 + + ! Pre-computed terms for the Fourier expansion of the three-body term + double precision, allocatable, dimension(:,:,:,:,:) :: sinp1 + double precision, allocatable, dimension(:,:,:,:,:) :: cosp1 + + ! Pre-computed terms for the Fourier expansion of the three-body term + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: sinp2 + double precision, allocatable, dimension(:,:,:,:,:,:,:) :: cosp2 + + ! Indexes for numerical differentiation + integer :: xyz_pm2 + integer :: xyz2, pm2 + integer :: idx1, idx2 + integer :: idx1_start,idx1_end + integer :: idx2_start,idx2_end + + ! Max index in the periodic table + integer :: pmax1 + integer :: pmax2 + + ! Angular normalization constant + double precision :: ang_norm2 + + ! Max number of neighbors + integer :: maxneigh1 + integer :: maxneigh2 + + ! Variables to calculate time + double precision :: t_start, t_end + + ! For numerical differentiation + double precision, parameter, dimension(5) :: fact = (/ 1.0d0, -8.0d0, 0.0d0, 8.0d0, -1.0d0/) + + ! fact(1) = 1.0d0 + ! fact(2) = -8.0d0 + ! fact(3) = 0.0d0 + ! fact(4) = 8.0d0 + ! fact(5) = -1.0d0 + + if (verbose) write (*,*) "INIT, dx =", dx + + if (verbose) write (*,*) "CLEARING KERNEL MEM" + kernels = 0.0d0 + + ! Angular normalization constant + ang_norm2 = get_angular_norm2(t_width) + + ! Max number of neighbors in the representations + maxneigh1 = maxval(nneigh1) + maxneigh2 = maxval(nneigh2) + + ! pmax = max nuclear charge + pmax1 = get_pmax(x1, n1) + pmax2 = get_pmax_displaced(x2, n2) + + ! Get two-body weight function + ksi1 = get_ksi(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, verbose) + ksi2 = get_ksi_displaced(x2, n2, nneigh2, two_body_power, cut_start, cut_distance, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + allocate(sinp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1, sinp1, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp2(nm2, 3*5, maxval(n2), maxval(n2), pmax2, order, maxneigh2)) + allocate(sinp2(nm2, 3*5, maxval(n2), maxval(n2), pmax2, order, maxneigh2)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp_displaced(x2, n2, nneigh2, three_body_power, order, cut_start, & + & cut_distance, cosp2, sinp2, verbose) + + ! Pre-calculate self-scalar terms + self_scalar1 = get_selfscalar(x1, nm1, n1, nneigh1, ksi1, sinp1, cosp1, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + ! Pre-calculate self-scalar terms + self_scalar2 = get_selfscalar_displaced(x2, nm2, n2, nneigh2, ksi2, sinp2, cosp2, t_width, & + & d_width, cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL GRADIENT" + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(na,nb,xyz_pm2,s12),& + !$OMP& PRIVATE(idx1,idx2,idx1_start,idx1_end,idx2_start,idx2_end) + do a = 1, nm1 + na = n1(a) + + idx1_end = sum(n1(:a)) + idx1_start = idx1_end - na + 1 + + do j1 = 1, na + idx1 = idx1_start - 1 + j1 + + do b = 1, nm2 + nb = n2(b) + + idx2_end = sum(n2(:b)) + idx2_start = idx2_end - nb + 1 + + do xyz2 = 1, 3 + do pm2 = 1, 5 + + if (pm2 /= 3) then + + ! xyz_pm2 = 2*xyz2 + pm2 - 2 + xyz_pm2 = 5*(xyz2 - 1) + pm2 + + do i2 = 1, nb + idx2 = (idx2_start-1)*3 + (i2-1)*3 + xyz2 + + do j2 = 1, nb + + s12 = scalar(x1(a,j1,:,:), x2(b,xyz2,pm2,i2,j2,:,:), & + & nneigh1(a,j1), nneigh2(b,xyz2,pm2,i2,j2), & + & ksi1(a,j1,:), ksi2(b,xyz2,pm2,i2,j2,:), & + & sinp1(a,j1,:,:,:), sinp2(b,xyz_pm2,i2,j2,:,:,:), & + & cosp1(a,j1,:,:,:), cosp2(b,xyz_pm2,i2,j2,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2,distance_scale, angular_scale, alchemy) + + kernels(:,idx1,idx2) = kernels(:,idx1,idx2) & + & + kernel(self_scalar1(a,j1), self_scalar2(b,xyz2,pm2,i2,j2), s12,& + & kernel_idx, parameters) * fact(pm2) + + enddo + enddo + + endif + + enddo + enddo + enddo + enddo + enddo + !$OMP END PARALLEL do + + kernels = kernels / (12 * dx) + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end subroutine fget_atomic_local_gradient_5point_kernels_fchl diff --git a/qml/fchl/ffchl_kernels.f90 b/qml/fchl/ffchl_kernels.f90 new file mode 100644 index 000000000..a7699e39f --- /dev/null +++ b/qml/fchl/ffchl_kernels.f90 @@ -0,0 +1,342 @@ +! MIT License +! +! Copyright (c) 2018 Anders Steen Christensen and Felix A. Faber +! +! Permission is hereby granted, free of charge, to any person obtaining a copy +! of this software and associated documentation files (the "Software"), to deal +! in the Software without restriction, including without limitation the rights +! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +! copies of the Software, and to permit persons to whom the Software is +! furnished to do so, subject to the following conditions: +! +! The above copyright notice and this permission notice shall be included in all +! copies or substantial portions of the Software. +! +! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +! SOFTWARE. + +! Inspiration from: +! http://crsouza.com/2010/03/17/kernel-functions-for-machine-learning-applications/#kernel_functions +module ffchl_kernels + + implicit none + + public :: kernel + +contains + + +subroutine gaussian_kernel(s11, s22, s12, parameters, k) + + implicit none + + double precision, intent(in) :: s11 + double precision, intent(in) :: s22 + double precision, intent(in) :: s12 + double precision, intent(in), dimension(:,:) :: parameters + + double precision, intent(out), dimension(:) :: k + + integer :: i + double precision :: l2 + + l2 = s11 + s22 - 2.0d0*s12 + + do i = 1, size(k) + k(i) = exp(l2 * parameters(i,1)) + enddo + +end subroutine gaussian_kernel + + +subroutine linear_kernel(s12, parameters, k) + + implicit none + + double precision, intent(in) :: s12 + double precision, intent(in), dimension(:,:) :: parameters + double precision, intent(out), dimension(:) :: k + + integer :: i + + do i = 1, size(k) + k(i) = s12 + parameters(i,1) + enddo + +end subroutine linear_kernel + + +subroutine polynomial_kernel(s12, parameters, k) + + implicit none + + double precision, intent(in) :: s12 + double precision, intent(in), dimension(:,:) :: parameters + + double precision, intent(out), dimension(:) :: k + + integer :: i + + do i = 1, size(k) + k(i) = (parameters(i,1) * s12 + parameters(i,2))**parameters(i,3) + enddo + +end subroutine polynomial_kernel + + +subroutine sigmoid_kernel(s12, parameters, k) + + implicit none + double precision, intent(in) :: s12 + double precision, intent(in), dimension(:,:) :: parameters + + double precision, intent(out), dimension(:) :: k + + integer :: i + + do i = 1, size(k) + k(i) = tanh(parameters(i,1) * s12 + parameters(i,2)) + enddo + +end subroutine sigmoid_kernel + + +subroutine multiquadratic_kernel(s11, s22, s12, parameters, k) + + implicit none + + double precision, intent(in) :: s11 + double precision, intent(in) :: s22 + double precision, intent(in) :: s12 + double precision, intent(in), dimension(:,:) :: parameters + + double precision, intent(out), dimension(:) :: k + + integer :: i + double precision :: l2 + + l2 = s11 + s22 - 2.0d0*s12 + + do i = 1, size(k) + k(i) = sqrt(l2 + parameters(i,1)**2) + enddo + +end subroutine multiquadratic_kernel + + +subroutine inverse_multiquadratic_kernel(s11, s22, s12, parameters, k) + + implicit none + + double precision, intent(in) :: s11 + double precision, intent(in) :: s22 + double precision, intent(in) :: s12 + double precision, intent(in), dimension(:,:) :: parameters + + double precision, intent(out), dimension(:) :: k + + integer :: i + double precision :: l2 + + l2 = s11 + s22 - 2.0d0*s12 + + do i = 1, size(k) + k(i) = 1.0d0 / sqrt(l2 + parameters(i,1)**2) + enddo + +end subroutine inverse_multiquadratic_kernel + + +subroutine bessel_kernel(s12, parameters, k) + + implicit none + + double precision, intent(in) :: s12 + double precision, intent(in), dimension(:,:) :: parameters + + double precision, intent(out), dimension(:) :: k + + integer :: i + + do i = 1, size(k) + k(i) = BESSEL_JN(int(parameters(i,2)), parameters(i,1) *s12) & + & / (s12**(-parameters(i,3)*(parameters(i,2) + 1))) + enddo + +end subroutine bessel_kernel + +subroutine l2_kernel(s11, s22, s12, parameters, k) + + implicit none + + double precision, intent(in) :: s11 + double precision, intent(in) :: s22 + double precision, intent(in) :: s12 + double precision, intent(in), dimension(:,:) :: parameters + + double precision, intent(out), dimension(:) :: k + + integer :: i + double precision :: l2 + + l2 = s11 + s22 - 2.0d0*s12 + + do i = 1, size(k) + k(i) = l2*parameters(i,1) + parameters(i,2) + enddo + +end subroutine l2_kernel + + +subroutine matern_kernel(s11, s22, s12, parameters, kernel) + + implicit none + + double precision, intent(in) :: s11 + double precision, intent(in) :: s22 + double precision, intent(in) :: s12 + double precision, intent(in), dimension(:,:) :: parameters + + double precision, intent(out), dimension(:) :: kernel + + double precision :: l2 + + double precision :: rho + + integer :: i, k, n + double precision:: v, fact + + l2 = sqrt(s11 + s22 - 2.0d0*s12) + + kernel(:) = 0.0d0 + + do i = 1, size(kernel) + n = int(parameters(i,2)) + v = n + 0.5d0 + + rho = 2.0d0 * sqrt(2.0d0 * v) * l2 / parameters(i,1) + + do k = 0, n + + fact = parameters(i,3+k) + + kernel(i) = kernel(i) + exp(-0.5d0 * rho) * fact * rho**(n-k) + + enddo + enddo + +end subroutine matern_kernel + + +subroutine cauchy_kernel(s11, s22, s12, parameters, k) + + implicit none + + double precision, intent(in) :: s11 + double precision, intent(in) :: s22 + double precision, intent(in) :: s12 + double precision, intent(in), dimension(:,:) :: parameters + + double precision, intent(out), dimension(:) :: k + + integer :: i + double precision :: l2 + + l2 = s11 + s22 - 2.0d0*s12 + + do i = 1, size(k) + k(i) = 1.0d0 /(1.0d0 + l2/parameters(i,1)**2) + enddo + +end subroutine cauchy_kernel + +subroutine polynomial2_kernel(s12, parameters, k) + + implicit none + + double precision, intent(in) :: s12 + double precision, intent(in), dimension(:,:) :: parameters + + double precision, intent(out), dimension(:) :: k + + integer :: i + + do i = 1, size(k) + k(i) = parameters(i, 1) & + & + parameters(i, 2) * s12 & + & + parameters(i, 3) * s12**2 & + & + parameters(i, 4) * s12**3 & + & + parameters(i, 5) * s12**4 & + & + parameters(i, 6) * s12**5 & + & + parameters(i, 7) * s12**6 & + & + parameters(i, 8) * s12**7 & + & + parameters(i, 9) * s12**8 & + & + parameters(i, 10) * s12**9 + enddo + + +end subroutine polynomial2_kernel + +function kernel(s11, s22, s12, kernel_idx, parameters) result(k) + + implicit none + + double precision, intent(in) :: s11 + double precision, intent(in) :: s22 + double precision, intent(in) :: s12 + integer, intent(in) :: kernel_idx + double precision, intent(in), dimension(:,:) :: parameters + + integer :: n + double precision, allocatable, dimension(:) :: k + + n = size(parameters, dim=1) + allocate(k(n)) + + if (kernel_idx == 1) then + call gaussian_kernel(s11, s22, s12, parameters, k) + + else if (kernel_idx == 2) then + call linear_kernel(s12, parameters, k) + + else if (kernel_idx == 3) then + call polynomial_kernel(s12, parameters, k) + + else if (kernel_idx == 4) then + call sigmoid_kernel(s12, parameters, k) + + else if (kernel_idx == 5) then + call multiquadratic_kernel(s11, s22, s12, parameters, k) + + else if (kernel_idx == 6) then + call inverse_multiquadratic_kernel(s11, s22, s12, parameters, k) + + else if (kernel_idx == 7) then + call bessel_kernel(s12, parameters, k) + + else if (kernel_idx == 8) then + call l2_kernel(s11, s22, s12, parameters, k) + + else if (kernel_idx == 9) then + call matern_kernel(s11, s22, s12, parameters, k) + + else if (kernel_idx == 10) then + call cauchy_kernel(s11, s22, s12, parameters, k) + + else if (kernel_idx == 11) then + call polynomial2_kernel(s12, parameters, k) + + else + write (*,*) "QML ERROR: Unknown kernel function requested:", kernel_idx + stop + endif + + +end function kernel + +end module ffchl_kernels diff --git a/qml/fchl/ffchl_module.f90 b/qml/fchl/ffchl_module.f90 index 765c73175..add6c7278 100644 --- a/qml/fchl/ffchl_module.f90 +++ b/qml/fchl/ffchl_module.f90 @@ -1,31 +1,117 @@ -! MIT License -! -! Copyright (c) 2018 Anders Steen Christensen -! -! Permission is hereby granted, free of charge, to any person obtaining a copy -! of this software and associated documentation files (the "Software"), to deal -! in the Software without restriction, including without limitation the rights -! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -! copies of the Software, and to permit persons to whom the Software is -! furnished to do so, subject to the following conditions: -! -! The above copyright notice and this permission notice shall be included in all -! copies or substantial portions of the Software. -! -! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -! SOFTWARE. - module ffchl_module implicit none + ! For polarization + ! double precision, parameter, dimension(19, 3) :: field_displacement = reshape( & + ! & (/ -1, -1, 0, & ! 1 x- y- + ! & -1, 0, -1, & ! 2 x- z- + ! & -1, 0, 0, & ! 3 x- + ! & -1, 0, 1, & ! 4 x- z+ + ! & -1, 1, 0, & ! 5 x- y+ + ! & 0, -1, -1, & ! 6 y- z- + ! & 0, -1, 0, & ! 7 y- + ! & 0, -1, 1, & ! 8 y- z+ + ! & 0, 0, -1, & ! 9 z- + ! & 0, 0, 0, & ! 10 unperturbed = 10 + ! & 0, 0, 1, & ! 11 z+ + ! & 0, 1, -1, & ! 12 y+ z- + ! & 0, 1, 0, & ! 13 y+ + ! & 0, 1, 1, & ! 14 y+ z+ + ! & 1, -1, 0, & ! 15 x+ y- + ! & 1, 0, -1, & ! 16 x+ z- + ! & 1, 0, 0, & ! 17 x+ + ! & 1, 0, 1, & ! 18 x+ z+ + ! & 1, 1, 0 & ! 19 x+ y+ + ! &/), (/ 19, 3 /)) + + + contains + +function get_pmax(x, na) result(pmax) + + implicit none + + ! FCHL descriptors for the set, format (nm1,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: na + + ! Internal counter + integer :: a + + ! Max nuclear charge + integer :: pmax + + pmax = 0 + + do a = 1, size(na, dim=1) + pmax = max(pmax, int(maxval(x(a,1,2,:na(a))))) + enddo + +end function get_pmax + + +function get_pmax_atomic(x, nneigh) result(pmax) + + implicit none + + ! FCHL descriptors for the set, format (nm1,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:), intent(in) :: x + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: nneigh + + ! Internal counter + integer :: a + + ! Max nuclear charge + integer :: pmax + + pmax = 0 + + do a = 1, size(nneigh, dim=1) + pmax = max(pmax, int(maxval(x(a,2,:nneigh(a))))) + enddo + +end function get_pmax_atomic + + +function get_pmax_displaced(x, na) result(pmax) + + implicit none + + ! FCHL descriptors for the training set, format (nm1,3,2,maxatoms,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:,:,:,:), intent(in) :: x + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: na + + ! Internal counter + integer :: a, b, i, xyz, pm + + ! Max nuclear charge + integer :: pmax + + pmax = 0 + + do a = 1, size(na, dim=1) + b = na(a) + do xyz = 1, 3 + do pm = 1, size(x, dim=3) + do i = 1, b + pmax = max(pmax, int(maxval(x(a,xyz,pm,i,1,2,:na(a))))) + enddo + enddo + enddo + enddo + +end function get_pmax_displaced + + pure function cut_function(r, cut_start, cut_distance) result(f) implicit none @@ -97,6 +183,7 @@ pure function get_angular_norm2(t_width) result(ang_norm2) end function + function get_twobody_weights(x, neighbors, power, cut_start, cut_distance, dim1) result(ksi) implicit none @@ -629,4 +716,1394 @@ pure function scalar_alchemy(X1, X2, N1, N2, ksi1, ksi2, sin1, sin2, cos1, cos2, end function scalar_alchemy -end module ffchl_module +function get_ksi(x, na, nneigh, two_body_power, cut_start, cut_distance, verbose) result(ksi) + + use omp_lib, only: omp_get_wtime + + implicit none + + ! FCHL descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: na + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh + + ! Decaying powerlaws for two-body term + double precision, intent(in) :: two_body_power + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + ! Pre-computed two-body weights + double precision, allocatable, dimension(:,:,:) :: ksi + + ! Internal counters + integer :: maxneigh, maxatoms, nm, a, ni, i + + double precision :: t_start, t_end + + + maxneigh = maxval(nneigh) + maxatoms = maxval(na) + nm = size(x, dim=1) + + allocate(ksi(nm, maxatoms, maxneigh)) + + ksi = 0.0d0 + + if (verbose) write (*,"(A)", advance="no") "TWO-BODY TERMS" + t_start = omp_get_wtime() + + !$OMP PARALLEL DO PRIVATE(ni) + do a = 1, nm + ni = na(a) + do i = 1, ni + ksi(a, i, :) = get_twobody_weights(x(a,i,:,:), nneigh(a, i), & + & two_body_power, cut_start, cut_distance, maxneigh) + enddo + enddo + !$OMP END PARALLEL do + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end function get_ksi + + +function get_ksi_displaced(x, na, nneigh, two_body_power, cut_start, cut_distance, verbose) result(ksi) + + use omp_lib, only: omp_get_wtime + + implicit none + + ! FCHL descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:,:,:,:), intent(in) :: x + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: na + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:,:,:,:), intent(in) :: nneigh + + ! Decaying powerlaws for two-body term + double precision, intent(in) :: two_body_power + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + ! Pre-computed two-body weights + double precision, allocatable, dimension(:,:,:,:,:,:) :: ksi + + ! Internal counters + integer :: maxneigh, maxatoms, nm, a, ni, i, j, pm, xyz, ndisp + + double precision :: t_start, t_end + + if (verbose) write (*,"(A)", advance="no") "TWO-BODY GRADIENT" + t_start = omp_get_wtime() + + maxneigh = maxval(nneigh) + maxatoms = maxval(na) + nm = size(x, dim=1) + ndisp = size(x, dim=3) + + allocate(ksi(nm,3,ndisp,maxatoms,maxatoms,maxneigh)) + + ksi = 0.0d0 + + !$OMP PARALLEL DO PRIVATE(ni) + do a = 1, nm + ni = na(a) + do xyz = 1, 3 + do pm = 1, ndisp + do i = 1, ni + do j = 1, ni + ksi(a, xyz, pm, i, j, :) = get_twobody_weights( & + & x(a,xyz,pm,i,j,:,:), nneigh(a, xyz, pm, i, j), & + & two_body_power, cut_start, cut_distance, maxneigh) + enddo + enddo + enddo + enddo + enddo + !$OMP END PARALLEL do + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end function get_ksi_displaced + + +function get_ksi_atomic(x, na, nneigh, two_body_power, cut_start, cut_distance, verbose) result(ksi) + + use omp_lib, only: omp_get_wtime + + implicit none + + ! FCHL descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:), intent(in) :: x + + ! List of numbers of atoms + integer, intent(in) :: na + + ! Number of neighbors for each atom in each compound + integer, dimension(:), intent(in) :: nneigh + + ! Decaying powerlaws for two-body term + double precision, intent(in) :: two_body_power + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + ! Pre-computed two-body weights + double precision, allocatable, dimension(:,:) :: ksi + + ! Internal counters + integer :: maxneigh, nm, i + + double precision :: t_start, t_end + + maxneigh = maxval(nneigh) + nm = size(x, dim=1) + + allocate(ksi(na, maxneigh)) + + ksi = 0.0d0 + + if (verbose) write (*,"(A)", advance="no") "TWO-BODY TERMS" + t_start = omp_get_wtime() + + !$OMP PARALLEL DO + do i = 1, na + ksi(i, :) = get_twobody_weights(x(i,:,:), nneigh(i), & + & two_body_power, cut_start, cut_distance, maxneigh) + enddo + !$OMP END PARALLEL do + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end function get_ksi_atomic + + +subroutine init_cosp_sinp(x, na, nneigh, three_body_power, order, cut_start, cut_distance, cosp, sinp, verbose) + + use omp_lib, only: omp_get_wtime + + implicit none + + ! FCHL descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: na + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh + + ! Decaying powerlaws for two-body term + double precision, intent(in) :: three_body_power + + integer, intent(in) :: order + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + ! Cosine and sine terms for each atomtype + double precision, dimension(:,:,:,:,:), intent(out) :: cosp + double precision, dimension(:,:,:,:,:), intent(out) :: sinp + + ! Internal counters + integer :: maxneigh, maxatoms, pmax, nm, a, ni, i + + double precision, allocatable, dimension(:,:,:,:) :: fourier + + double precision :: t_start, t_end + + + maxneigh = maxval(nneigh) + maxatoms = maxval(na) + nm = size(x, dim=1) + + pmax = get_pmax(x, na) + + cosp = 0.0d0 + sinp = 0.0d0 + + if (verbose) write (*,"(A)", advance="no") "THREE-BODY TERMS" + t_start = omp_get_wtime() + + !$OMP PARALLEL DO PRIVATE(ni, fourier) schedule(dynamic) + do a = 1, nm + ni = na(a) + do i = 1, ni + + fourier = get_threebody_fourier(x(a,i,:,:), & + & nneigh(a, i), order, three_body_power, cut_start, cut_distance, pmax, order, maxneigh) + + cosp(a,i,:,:,:) = fourier(1,:,:,:) + sinp(a,i,:,:,:) = fourier(2,:,:,:) + + enddo + enddo + !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end subroutine init_cosp_sinp + + +subroutine init_cosp_sinp_displaced(x, na, nneigh, three_body_power, order, cut_start, cut_distance, cosp, sinp, verbose) + + use omp_lib, only: omp_get_wtime + + implicit none + + ! FCHL descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:,:,:,:), intent(in) :: x + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: na + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:,:,:,:), intent(in) :: nneigh + + ! Decaying powerlaws for two-body term + double precision, intent(in) :: three_body_power + + integer, intent(in) :: order + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + ! Cosine and sine terms for each atomtype + double precision, dimension(:,:,:,:,:,:,:) :: sinp + double precision, dimension(:,:,:,:,:,:,:) :: cosp + + ! Internal counters + integer :: maxneigh, maxatoms, pmax, nm, a, ni, i, j, xyz, pm, xyz_pm + + double precision, allocatable, dimension(:,:,:,:) :: fourier + + double precision :: t_start, t_end + + integer :: ndisp + + + if (verbose) write (*,"(A)", advance="no") "THREE-BODY GRADIENT" + t_start = omp_get_wtime() + + maxneigh = maxval(nneigh) + maxatoms = maxval(na) + nm = size(x, dim=1) + + ndisp = size(x, dim=3) + + pmax = get_pmax_displaced(x, na) + + cosp = 0.0d0 + sinp = 0.0d0 + + !$OMP PARALLEL DO PRIVATE(ni, fourier, xyz_pm) schedule(dynamic) + do a = 1, nm + ni = na(a) + do xyz = 1, 3 + do pm = 1, ndisp + do i = 1, ni + do j = 1, ni + + xyz_pm = ndisp*(xyz - 1) + pm + + fourier = get_threebody_fourier(x(a,xyz,pm,i,j,:,:), & + & nneigh(a, xyz, pm, i, j), & + & order, three_body_power, cut_start, cut_distance, & + & pmax, order, maxneigh) + + cosp(a,xyz_pm,i,j,:,:,:) = fourier(1,:,:,:) + sinp(a,xyz_pm,i,j,:,:,:) = fourier(2,:,:,:) + + enddo + enddo + enddo + enddo + enddo + !$OMP END PARALLEL do + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end subroutine init_cosp_sinp_displaced + + +subroutine init_cosp_sinp_atomic(x, na, nneigh, three_body_power, order, cut_start, cut_distance, cosp, sinp, verbose) + + use omp_lib, only: omp_get_wtime + + implicit none + + ! FCHL descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:), intent(in) :: x + + ! List of numbers of atom + integer, intent(in) :: na + + ! Number of neighbors for each atom + integer, dimension(:), intent(in) :: nneigh + + ! Decaying powerlaws for two-body term + double precision, intent(in) :: three_body_power + + ! Fourier truncation order + integer, intent(in) :: order + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + ! Cosine and sine terms for each atomtype + double precision, dimension(:,:,:,:), intent(out) :: cosp + double precision, dimension(:,:,:,:), intent(out) :: sinp + + ! Internal counters + integer :: maxneigh, pmax, nm, i + + ! Internal temporary variable + double precision, allocatable, dimension(:,:,:,:) :: fourier + + double precision :: t_start, t_end + + maxneigh = maxval(nneigh) + nm = size(x, dim=1) + + pmax = get_pmax_atomic(x, nneigh) + + cosp = 0.0d0 + sinp = 0.0d0 + + if (verbose) write (*,"(A)", advance="no") "THREE-BODY TERMS" + t_start = omp_get_wtime() + + !$OMP PARALLEL DO PRIVATE(fourier) + do i = 1, na + + fourier = get_threebody_fourier(x(i,:,:), & + & nneigh(i), order, three_body_power, cut_start, cut_distance, pmax, order, maxneigh) + + cosp(i,:,:,:) = fourier(1,:,:,:) + sinp(i,:,:,:) = fourier(2,:,:,:) + + enddo + !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end subroutine init_cosp_sinp_atomic + + +function get_selfscalar(x, nm, na, nneigh, ksi, sinp, cosp, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) result(self_scalar) + + use omp_lib, only: omp_get_wtime + + implicit none + + ! FCHL descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x + + ! Number of molecules molecule + integer, intent(in) :: nm + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: na + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh + + ! Pre-computed two-body weights + double precision, dimension(:,:,:), intent(in) :: ksi + + ! Cosine and sine terms for each atomtype + double precision, dimension(:,:,:,:,:), intent(in) :: sinp + double precision, dimension(:,:,:,:,:), intent(in) :: cosp + + ! Angular Gaussian width + double precision, intent(in) :: t_width + + ! Distance Gaussian width + double precision, intent(in) :: d_width + + double precision, intent(in) :: cut_distance + + ! Truncation order for Fourier terms + integer, intent(in) :: order + + ! Periodic table distance matrix + double precision, dimension(:,:), intent(in) :: pd + + ! Angular normalization constant + double precision, intent(in) :: ang_norm2 + + ! Scaling for angular and distance terms + double precision, intent(in) :: distance_scale + double precision, intent(in) :: angular_scale + + ! Switch alchemy on or off + logical, intent(in) :: alchemy + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + double precision, allocatable, dimension(:,:) :: self_scalar + + ! Internal counters + integer :: a, ni, i + + double precision :: t_start, t_end + + if (verbose) write (*,"(A)", advance="no") "SELF-SCALAR TERMS" + t_start = omp_get_wtime() + + allocate(self_scalar(nm, maxval(na))) + + !$OMP PARALLEL DO PRIVATE(ni) + do a = 1, nm + ni = na(a) + do i = 1, ni + self_scalar(a,i) = scalar(x(a,i,:,:), x(a,i,:,:), & + & nneigh(a,i), nneigh(a,i), ksi(a,i,:), ksi(a,i,:), & + & sinp(a,i,:,:,:), sinp(a,i,:,:,:), & + & cosp(a,i,:,:,:), cosp(a,i,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2,distance_scale, angular_scale, alchemy) + enddo + enddo + !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end function get_selfscalar + + +function get_selfscalar_displaced(x, nm, na, nneigh, ksi, sinp, cosp, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) result(self_scalar) + + use omp_lib, only: omp_get_wtime + + implicit none + + ! FCHL descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:,:,:,:), intent(in) :: x + + ! Number of molecules molecule + integer, intent(in) :: nm + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: na + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:,:,:,:), intent(in) :: nneigh + + ! Pre-computed two-body weights + double precision, dimension(:,:,:,:,:,:), intent(in) :: ksi + + ! Cosine and sine terms for each atomtype + double precision, dimension(:,:,:,:,:,:,:), intent(in) :: sinp + double precision, dimension(:,:,:,:,:,:,:), intent(in) :: cosp + + ! Angular Gaussian width + double precision, intent(in) :: t_width + + ! Distance Gaussian width + double precision, intent(in) :: d_width + + double precision, intent(in) :: cut_distance + + ! Truncation order for Fourier terms + integer, intent(in) :: order + + ! Periodic table distance matrix + double precision, dimension(:,:), intent(in) :: pd + + ! Angular normalization constant + double precision, intent(in) :: ang_norm2 + + ! Scaling for angular and distance terms + double precision, intent(in) :: distance_scale + double precision, intent(in) :: angular_scale + + ! Switch alchemy on or off + logical, intent(in) :: alchemy + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + double precision, allocatable, dimension(:,:,:,:,:) :: self_scalar + + ! Internal counters + integer :: a, ni, i, j, pm, xyz, xyz_pm, ndisp + + double precision :: t_start, t_end + + if (verbose) write (*,"(A)", advance="no") "SELF-SCALAR GRADIENT" + t_start = omp_get_wtime() + + ndisp = size(x, dim=3) + allocate(self_scalar(nm, 3, ndisp, maxval(na), maxval(na))) + self_scalar = 0.0d0 + + !$OMP PARALLEL DO PRIVATE(ni, xyz_pm) schedule(dynamic) + do a = 1, nm + ni = na(a) + do xyz = 1, 3 + do pm = 1, ndisp + do i = 1, ni + do j = 1, ni + + ! z_pm = 2*xyz + pm - 2 + xyz_pm = ndisp*(xyz - 1) + pm + + self_scalar(a,xyz,pm,i,j) = scalar(x(a,xyz,pm,i,j,:,:), x(a,xyz,pm,i,j,:,:), & + & nneigh(a,xyz,pm,i,j), nneigh(a,xyz,pm,i,j), & + & ksi(a,xyz,pm,i,j,:), ksi(a,xyz,pm,i,j,:), & + & sinp(a,xyz_pm,i,j,:,:,:), sinp(a,xyz_pm,i,j,:,:,:), & + & cosp(a,xyz_pm,i,j,:,:,:), cosp(a,xyz_pm,i,j,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2,distance_scale, angular_scale, alchemy) + enddo + enddo + enddo + enddo + enddo + !$OMP END PARALLEL do + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end function get_selfscalar_displaced + + + +function get_ksi_ef(x, na, nneigh, two_body_power, cut_start, cut_distance, ef_scale, df, verbose) result(ksi) + + use omp_lib, only: omp_get_wtime + + implicit none + + ! FCHL descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: na + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh + + ! Decaying powerlaws for two-body term + double precision, intent(in) :: two_body_power + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Electric field displacement + double precision, intent(in) :: ef_scale + double precision, intent(in) :: df + + ! Display output + logical, intent(in) :: verbose + + ! Pre-computed two-body weights + double precision, allocatable, dimension(:,:,:,:,:) :: ksi + + ! Internal counters + integer :: maxneigh, maxatoms, nm, a, ni, i, xyz, pm + + ! Electric field + double precision, dimension(3) :: field + + double precision :: t_start, t_end + + if (verbose) write (*,"(A)", advance="no") "TWO-BODY TERMS" + t_start = omp_get_wtime() + + maxneigh = maxval(nneigh) + maxatoms = maxval(na) + nm = size(x, dim=1) + + allocate(ksi(nm, 3, 2, maxatoms, maxneigh)) + + ksi = 0.0d0 + + !$OMP PARALLEL DO PRIVATE(ni, field) + do a = 1, nm + ni = na(a) + do xyz = 1, 3 + do pm = 1, 2 + + field = 0.0d0 + field(xyz) = (pm - 1.5d0)*2.0d0 * df + + ! write(*,*) xyz, pm, field + + do i = 1, ni + + ksi(a, xyz, pm, i, :) = get_twobody_weights_ef(x(a,i,:,:), field, nneigh(a, i), & + & two_body_power, cut_start, cut_distance, maxneigh, ef_scale) + + enddo + enddo + enddo + enddo + !$OMP END PARALLEL do + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end function get_ksi_ef + + +function get_ksi_ef_field(x, na, nneigh, two_body_power, cut_start, cut_distance, fields, ef_scale, verbose) result(ksi) + + use omp_lib, only: omp_get_wtime + + implicit none + + ! FCHL descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: na + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh + + ! Decaying powerlaws for two-body term + double precision, intent(in) :: two_body_power + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + + ! Electric fields for each representation + double precision, dimension(:,:), intent(in) :: fields + + ! Display output + logical, intent(in) :: verbose + + ! Pre-computed two-body weights + double precision, allocatable, dimension(:,:,:) :: ksi + + ! Internal counters + integer :: maxneigh, maxatoms, nm, a, ni, i + + double precision :: t_start, t_end + + double precision, intent(in) :: ef_scale + + ! Electric field displacement + ! double precision, intent(in) :: df + + if (verbose) write (*,"(A)", advance="no") "TWO-BODY TERMS" + t_start = omp_get_wtime() + + maxneigh = maxval(nneigh) + maxatoms = maxval(na) + nm = size(x, dim=1) + + allocate(ksi(nm, maxatoms, maxneigh)) + + ksi = 0.0d0 + + !$OMP PARALLEL DO PRIVATE(ni) + do a = 1, nm + ni = na(a) + + do i = 1, ni + + ksi(a, i, :) = get_twobody_weights_ef(x(a,i,:,:), fields(a,:), nneigh(a, i), & + & two_body_power, cut_start, cut_distance, maxneigh, ef_scale) + enddo + + enddo + !$OMP END PARALLEL do + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end function get_ksi_ef_field + +subroutine init_cosp_sinp_ef(x, na, nneigh, three_body_power, order, cut_start, cut_distance, & + & cosp, sinp, ef_scale, df, verbose) + + use omp_lib, only: omp_get_wtime + + implicit none + + ! FCHL descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: na + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh + + ! Decaying powerlaws for two-body term + double precision, intent(in) :: three_body_power + + integer, intent(in) :: order + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + double precision, intent(in) :: ef_scale + + ! Electric field displacement + double precision, intent(in) :: df + + ! Cosine and sine terms for each atomtype + double precision, dimension(:,:,:,:,:,:,:), intent(out) :: cosp + double precision, dimension(:,:,:,:,:,:,:), intent(out) :: sinp + + ! Display output + logical, intent(in) :: verbose + + ! Internal counters + integer :: maxneigh, maxatoms, pmax, nm, a, ni, i + + double precision, allocatable, dimension(:,:,:,:) :: fourier + + double precision :: t_start, t_end + + ! Internal counters + integer :: xyz, pm + + ! Electric field + double precision, dimension(3) :: field + + if (verbose) write (*,"(A)", advance="no") "THREE-BODY TERMS" + t_start = omp_get_wtime() + + maxneigh = maxval(nneigh) + maxatoms = maxval(na) + nm = size(x, dim=1) + + pmax = get_pmax(x, na) + + cosp = 0.0d0 + sinp = 0.0d0 + + !$OMP PARALLEL DO PRIVATE(ni, fourier, field) schedule(dynamic) + do a = 1, nm + ni = na(a) + do xyz = 1, 3 + do pm = 1, 2 + + field = 0.0d0 + field(xyz) = (pm - 1.5d0) * 2.0d0 * df + + do i = 1, ni + + fourier = get_threebody_fourier_ef(x(a,i,:,:), field, & + & nneigh(a, i), order, three_body_power, cut_start, cut_distance, pmax, order, maxneigh, ef_scale) + + cosp(a,xyz,pm,i,:,:,:) = fourier(1,:,:,:) + sinp(a,xyz,pm,i,:,:,:) = fourier(2,:,:,:) + + enddo + enddo + enddo + enddo + !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end subroutine init_cosp_sinp_ef + + +subroutine init_cosp_sinp_ef_field(x, na, nneigh, three_body_power, & + & order, cut_start, cut_distance, cosp, sinp, fields, ef_scale, verbose) + + use omp_lib, only: omp_get_wtime + + implicit none + + ! FCHL descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: na + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh + + ! Decaying powerlaws for two-body term + double precision, intent(in) :: three_body_power + + integer, intent(in) :: order + + ! Fraction of cut_distance at which cut-off starts + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + double precision, intent(in) :: ef_scale + + ! Electric field displacement + ! double precision, intent(in) :: df + + ! Display output + logical, intent(in) :: verbose + + ! Cosine and sine terms for each atomtype + double precision, dimension(:,:,:,:,:), intent(out) :: cosp + double precision, dimension(:,:,:,:,:), intent(out) :: sinp + + ! Electric fields for each representation + double precision, dimension(:,:), intent(in) :: fields + + ! Internal counters + integer :: maxneigh, maxatoms, pmax, nm, a, ni, i + + double precision, allocatable, dimension(:,:,:,:) :: fourier + + double precision :: t_start, t_end + + if (verbose) write (*,"(A)", advance="no") "THREE-BODY TERMS" + t_start = omp_get_wtime() + + maxneigh = maxval(nneigh) + maxatoms = maxval(na) + nm = size(x, dim=1) + + pmax = get_pmax(x, na) + + cosp = 0.0d0 + sinp = 0.0d0 + + !$OMP PARALLEL DO PRIVATE(ni, fourier) schedule(dynamic) + do a = 1, nm + + ni = na(a) + + do i = 1, ni + + fourier = get_threebody_fourier_ef(x(a,i,:,:), fields(a,:), & + & nneigh(a, i), order, three_body_power, cut_start, cut_distance, pmax, order, maxneigh, ef_scale) + + cosp(a,i,:,:,:) = fourier(1,:,:,:) + sinp(a,i,:,:,:) = fourier(2,:,:,:) + enddo + + enddo + !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + +end subroutine init_cosp_sinp_ef_field + + +! Calculate the Fourier terms for the FCHL three-body expansion +function get_threebody_fourier_ef(x, field, neighbors, order, power, cut_start, cut_distance, & + & dim1, dim2, dim3, ef_scale) result(fourier) + + implicit none + + ! Input representation, dimension=(5,n). + double precision, dimension(:,:), intent(in) :: x + + double precision, dimension(3), intent(in) :: field + + ! Number of neighboring atoms to iterate over. + integer, intent(in) :: neighbors + + ! Fourier-expansion order. + integer, intent(in) :: order + + ! Power law + double precision, intent(in) :: power + + ! Lower limit of damping function + double precision, intent(in) :: cut_start + + ! Upper limit of damping function + double precision, intent(in) :: cut_distance + + double precision, intent(in) :: ef_scale + + ! Dimensions or the output array. + integer, intent(in) :: dim1, dim2, dim3 + + ! dim(1,:,:,:) are cos terms, dim(2,:,:,:) are sine terms. + double precision, dimension(2,dim1,dim2,dim3) :: fourier + + ! Pi at double precision. + double precision, parameter :: pi = 4.0d0 * atan(1.0d0) + + ! Internal counters. + integer :: j, k, m + + ! Indexes for the periodic-table distance matrix. + integer :: pj, pk + + ! Angle between atoms for the three-body term. + double precision :: theta + + ! Three-body weight + double precision :: ksi3 + + ! Temporary variables for cos and sine Fourier terms. + double precision :: cos_m, sin_m + + fourier = 0.0d0 + + do j = 2, neighbors + do k = j+1, neighbors + + ksi3 = calc_ksi3_ef(X(:,:), field, j, k, neighbors, power, cut_start, cut_distance, ef_scale) + theta = calc_angle(x(3:5, j), x(3:5, 1), x(3:5, k)) + + pj = int(x(2,k)) + pk = int(x(2,j)) + + do m = 1, order + + cos_m = (cos(m * theta) - cos((theta + pi) * m))*ksi3 + sin_m = (sin(m * theta) - sin((theta + pi) * m))*ksi3 + + fourier(1, pj, m, j) = fourier(1, pj, m, j) + cos_m + fourier(2, pj, m, j) = fourier(2, pj, m, j) + sin_m + + fourier(1, pk, m, k) = fourier(1, pk, m, k) + cos_m + fourier(2, pk, m, k) = fourier(2, pk, m, k) + sin_m + + enddo + + enddo + enddo + + return + +end function get_threebody_fourier_ef + + +function calc_ksi3_ef(X, field, j, k, neighbors, power, cut_start, cut_distance, ef_scale) result(ksi3) + + implicit none + + double precision, dimension(:,:), intent(in) :: X + + + double precision, dimension(3), intent(in) :: field + + integer, intent(in) :: j + integer, intent(in) :: k + + ! Number of neighboring atoms to iterate over. + integer, intent(in) :: neighbors + + double precision, intent(in) :: power + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + double precision, intent(in) :: ef_scale + + double precision :: cos_i, cos_j, cos_k + double precision :: di, dj, dk + + double precision :: ksi3 + double precision :: cut + + ! Center of nuclear charge + double precision, dimension(3) :: coz + double precision, dimension(3) :: dipole + + double precision :: total_charge + + integer :: i + ! coz + ! dipole + + cos_i = calc_cos_angle(x(3:5, k), x(3:5, 1), x(3:5, j)) + cos_j = calc_cos_angle(x(3:5, j), x(3:5, k), x(3:5, 1)) + cos_k = calc_cos_angle(x(3:5, 1), x(3:5, j), x(3:5, k)) + + dk = x(1, j) + dj = x(1, k) + di = norm2(x(3:5, j) - x(3:5, k)) + + + cut = cut_function(dk, cut_start, cut_distance) * & + & cut_function(dj, cut_start, cut_distance) * & + & cut_function(di, cut_start, cut_distance) + + + total_charge = 0.0d0 + + coz = 0.0d0 + + do i = 1, neighbors + coz = coz + x(3:5, i) * x(2, i) + total_charge = total_charge + x(2, i) + enddo + + coz = coz / total_charge + + dipole = (x(3:5, 1) - coz) * x(6, 1) + (x(3:5, j) - coz) * x(6, j) & + & + (x(3:5, k) - coz) * x(6, k) + + ! ksi3 = cut * (1.0d0 + 3.0d0 * cos_i*cos_j*cos_k) / (di * dj * dk)**power + + ksi3 = cut * ( (1.0d0 + 3.0d0 * cos_i*cos_j*cos_k) / (di * dj * dk)**power & + & + ef_scale * dot_product(dipole, field) ) + + ! ksi3 = cut * dot_product(dipole, field) + +end function calc_ksi3_ef + +function get_twobody_weights_ef(x, field, neighbors, power, cut_start, cut_distance, dim1, ef_scale) result(ksi) + + implicit none + + double precision, dimension(:,:), intent(in) :: x + double precision, dimension(3), intent(in) :: field + integer, intent(in) :: neighbors + double precision, intent(in) :: power + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + double precision, intent(in) :: ef_scale + integer, intent(in) :: dim1 + + double precision, dimension(dim1) :: ksi + + ! Electric field displacement + ! double precision, intent(in) :: df + + integer :: i + + double precision, dimension(3) :: dipole + + ! Center of nuclear charge + double precision, dimension(3) :: coz + double precision :: total_charge + + ksi = 0.0d0 + + coz = 0.0d0 + total_charge = 0.0d0 + do i = 1, neighbors + coz = coz + x(3:5, i) * x(2, i) + total_charge = total_charge + x(2, i) + enddo + + coz = coz / total_charge + + do i = 2, neighbors + + dipole = (x(3:5, 1) - coz) * x(6, 1) + (x(3:5, i) - coz) * x(6, i) + + ksi(i) = cut_function(x(1, i), cut_start, cut_distance) & + & * (1.0d0/ x(1, i)**power + ef_scale * dot_product(dipole, field)) + + ! ksi(i) = cut_function(x(1, i), cut_start, cut_distance) & + ! & * (dot_product(dipole, field)) + + enddo + +end function get_twobody_weights_ef + + +! Code for polarization +! function get_twobody_weights_pol(x, field, neighbors, power, cut_start, cut_distance, dim1, ef_scale, qidx, df) result(ksi) +! +! implicit none +! +! double precision, dimension(:,:), intent(in) :: x +! double precision, dimension(3), intent(in) :: field +! integer, intent(in) :: neighbors +! double precision, intent(in) :: power +! double precision, intent(in) :: cut_start +! double precision, intent(in) :: cut_distance +! double precision, intent(in) :: ef_scale +! integer, intent(in) :: dim1 +! +! integer, intent(in) :: qidx +! +! ! Electric field displacement +! double precision, intent(in) :: df +! +! double precision, dimension(dim1) :: ksi +! +! +! integer :: i +! +! double precision, dimension(3) :: dipole +! +! ! Center of nuclear charge +! double precision, dimension(3) :: coz +! double precision :: total_charge +! +! ksi = 0.0d0 +! +! coz = 0.0d0 +! total_charge = 0.0d0 +! do i = 1, neighbors +! coz = coz + x(3:5, i) * x(2, i) +! total_charge = total_charge + x(2, i) +! enddo +! +! coz = coz / total_charge +! +! write(*,*) "SHAPE", size(x, dim=1), size(x, dim=2) +! do i = 2, neighbors +! +! write(*,*) i, qidx, x(qidx + 5, 1), x(qidx + 5, i), sum(x(qidx + 5, :)) +! +! dipole = (x(3:5, 1) - coz) * x(qidx + 5, 1) + (x(3:5, i) - coz) * x(qidx + 5, i) +! +! ! ksi(i) = cut_function(x(1, i), cut_start, cut_distance) & +! ! & * (1.0d0/ x(1, i)**power + ef_scale * dot_product(dipole, field)) +! +! ksi(i) = cut_function(x(1, i), cut_start, cut_distance) & +! & * (dot_product(dipole, field)) +! +! enddo +! +! end function get_twobody_weights_pol +! +! function get_ksi_pol(x, na, nneigh, two_body_power, cut_start, cut_distance, ef_scale, df) result(ksi) +! +! use omp_lib, only: omp_get_wtime +! +! implicit none +! +! ! FCHL descriptors for the training set, format (i,maxatoms,5,maxneighbors) +! double precision, dimension(:,:,:,:), intent(in) :: x +! +! ! List of numbers of atoms in each molecule +! integer, dimension(:), intent(in) :: na +! +! ! Number of neighbors for each atom in each compound +! integer, dimension(:,:), intent(in) :: nneigh +! +! ! Decaying powerlaws for two-body term +! double precision, intent(in) :: two_body_power +! +! ! Fraction of cut_distance at which cut-off starts +! double precision, intent(in) :: cut_start +! double precision, intent(in) :: cut_distance +! +! ! Electric field displacement +! double precision, intent(in) :: ef_scale +! double precision, intent(in) :: df +! +! ! Pre-computed two-body weights +! double precision, allocatable, dimension(:,:,:,:) :: ksi +! +! ! Internal counters +! integer :: maxneigh, maxatoms, nm, a, ni, i, xyz +! +! ! Electric field +! double precision, dimension(3) :: field +! +! double precision :: t_start, t_end +! +! write (*,"(A)", advance="no") "TWO-BODY TERMS POLARIZABLE" +! t_start = omp_get_wtime() +! +! maxneigh = maxval(nneigh) +! maxatoms = maxval(na) +! nm = size(x, dim=1) +! +! allocate(ksi(nm, 19, maxatoms, maxneigh)) +! +! ksi = 0.0d0 +! +! ! !$OMP PARALLEL DO PRIVATE(ni, field) +! do a = 1, nm +! ni = na(a) +! do xyz = 1, 19 +! +! field(1) = field_displacement(xyz,1) * df +! field(2) = field_displacement(xyz,2) * df +! field(3) = field_displacement(xyz,3) * df +! +! do i = 1, ni +! +! ksi(a, xyz, i, :) = get_twobody_weights_pol(x(a,i,:,:), field, nneigh(a, i), & +! & two_body_power, cut_start, cut_distance, maxneigh, ef_scale, xyz, df) +! +! enddo +! enddo +! enddo +! ! !$OMP END PARALLEL do +! +! t_end = omp_get_wtime() +! write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" +! +! end function get_ksi_pol +! +! subroutine init_cosp_sinp_pol(x, na, nneigh, three_body_power, order, cut_start, cut_distance, cosp, sinp, ef_scale, df) +! +! use omp_lib, only: omp_get_wtime +! +! implicit none +! +! ! FCHL descriptors for the training set, format (i,maxatoms,5,maxneighbors) +! double precision, dimension(:,:,:,:), intent(in) :: x +! +! ! List of numbers of atoms in each molecule +! integer, dimension(:), intent(in) :: na +! +! ! Number of neighbors for each atom in each compound +! integer, dimension(:,:), intent(in) :: nneigh +! +! ! Decaying powerlaws for two-body term +! double precision, intent(in) :: three_body_power +! +! integer, intent(in) :: order +! +! ! Fraction of cut_distance at which cut-off starts +! double precision, intent(in) :: cut_start +! double precision, intent(in) :: cut_distance +! double precision, intent(in) :: ef_scale +! +! ! Electric field displacement +! double precision, intent(in) :: df +! +! ! Cosine and sine terms for each atomtype +! double precision, dimension(:,:,:,:,:,:), intent(out) :: cosp +! double precision, dimension(:,:,:,:,:,:), intent(out) :: sinp +! +! +! ! Internal counters +! integer :: maxneigh, maxatoms, pmax, nm, a, ni, i +! +! double precision, allocatable, dimension(:,:,:,:) :: fourier +! +! double precision :: t_start, t_end +! +! ! Internal counters +! integer :: xyz +! +! ! Electric field +! double precision, dimension(3) :: field +! +! write (*,"(A)", advance="no") "THREE-BODY TERMS POLARIZABLE" +! t_start = omp_get_wtime() +! +! maxneigh = maxval(nneigh) +! maxatoms = maxval(na) +! nm = size(x, dim=1) +! +! pmax = get_pmax(x, na) +! +! cosp = 0.0d0 +! sinp = 0.0d0 +! +! ! !$OMP PARALLEL DO PRIVATE(ni, fourier, field) schedule(dynamic) +! do a = 1, nm +! ni = na(a) +! do xyz = 1, 19 +! +! field(1) = field_displacement(xyz,1) * df +! field(2) = field_displacement(xyz,2) * df +! field(3) = field_displacement(xyz,3) * df +! ! write(*,*) xyz, field_displacement(xyz,:), field +! +! do i = 1, ni +! +! fourier = get_threebody_fourier_ef(x(a,i,:,:), field, & +! & nneigh(a, i), order, three_body_power, cut_start, cut_distance, pmax, order, maxneigh, ef_scale) +! +! cosp(a,xyz,i,:,:,:) = fourier(1,:,:,:) +! sinp(a,xyz,i,:,:,:) = fourier(2,:,:,:) +! +! enddo +! enddo +! enddo +! ! !$OMP END PARALLEL DO +! +! t_end = omp_get_wtime() +! write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" +! +! end subroutine init_cosp_sinp_pol + +function get_twobody_weights_ef_field(x, field, neighbors, power, cut_start, cut_distance, dim1, ef_scale) result(ksi) + + implicit none + + double precision, dimension(:,:), intent(in) :: x + double precision, dimension(3), intent(in) :: field + integer, intent(in) :: neighbors + double precision, intent(in) :: power + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + double precision, intent(in) :: ef_scale + integer, intent(in) :: dim1 + + double precision, dimension(dim1) :: ksi + + ! Electric field displacement + ! double precision, intent(in) :: df + + integer :: i + + double precision, dimension(3) :: dipole + + ! Center of nuclear charge + double precision, dimension(3) :: coz + double precision :: total_charge + + ksi = 0.0d0 + + coz = 0.0d0 + total_charge = 0.0d0 + do i = 1, neighbors + coz = coz + x(3:5, i) * x(2, i) + total_charge = total_charge + x(2, i) + enddo + + coz = coz / total_charge + + do i = 2, neighbors + + dipole = (x(3:5, 1) - coz) * x(6, 1) + (x(3:5, i) - coz) * x(6, i) + + ksi(i) = cut_function(x(1, i), cut_start, cut_distance) & + & * (1.0d0/ x(1, i)**power + ef_scale * dot_product(dipole, field)) + + enddo + +end function get_twobody_weights_ef_field + +end module ffchl_module + diff --git a/qml/fchl/ffchl_scalar_kernels.f90 b/qml/fchl/ffchl_scalar_kernels.f90 index d7bbf09cf..cdae58b17 100644 --- a/qml/fchl/ffchl_scalar_kernels.f90 +++ b/qml/fchl/ffchl_scalar_kernels.f90 @@ -1,52 +1,27 @@ -! MIT License -! -! Copyright (c) 2018 Anders Steen Christensen -! -! Permission is hereby granted, free of charge, to any person obtaining a copy -! of this software and associated documentation files (the "Software"), to deal -! in the Software without restriction, including without limitation the rights -! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -! copies of the Software, and to permit persons to whom the Software is -! furnished to do so, subject to the following conditions: -! -! The above copyright notice and this permission notice shall be included in all -! copies or substantial portions of the Software. -! -! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -! SOFTWARE. - - -subroutine fget_kernels_fchl(x1, x2, n1, n2, nneigh1, nneigh2, & - & sigmas, nm1, nm2, nsigmas, & +subroutine fget_kernels_fchl(x1, x2, verbose, n1, n2, nneigh1, nneigh2, nm1, nm2, nsigmas, & & t_width, d_width, cut_start, cut_distance, order, pd, & - & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, kernels) + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, & + & kernel_idx, parameters, kernels) - use ffchl_module, only: scalar, get_threebody_fourier, get_twobody_weights + use ffchl_module, only: scalar, get_angular_norm2, get_pmax, get_ksi, init_cosp_sinp, get_selfscalar + + use ffchl_kernels, only: kernel - implicit none + use omp_lib, only: omp_get_wtime - double precision, allocatable, dimension(:,:,:,:) :: fourier + implicit none ! fchl descriptors for the training set, format (i,maxatoms,5,maxneighbors) double precision, dimension(:,:,:,:), intent(in) :: x1 double precision, dimension(:,:,:,:), intent(in) :: x2 + ! Whether to be verbose with output + logical, intent(in) :: verbose + ! List of numbers of atoms in each molecule integer, dimension(:), intent(in) :: n1 integer, dimension(:), intent(in) :: n2 - - ! Number of neighbors for each atom in each compound - integer, dimension(:,:), intent(in) :: nneigh1 - integer, dimension(:,:), intent(in) :: nneigh2 - - ! Sigma in the Gaussian kernel - double precision, dimension(:), intent(in) :: sigmas - + ! Number of molecules integer, intent(in) :: nm1 integer, intent(in) :: nm2 @@ -54,227 +29,155 @@ subroutine fget_kernels_fchl(x1, x2, n1, n2, nneigh1, nneigh2, & ! Number of sigmas integer, intent(in) :: nsigmas - double precision, intent(in) :: two_body_power - double precision, intent(in) :: three_body_power + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh1 + integer, dimension(:,:), intent(in) :: nneigh2 + ! Angular Gaussian width double precision, intent(in) :: t_width + + ! Distance Gaussian width double precision, intent(in) :: d_width + + ! Fraction of cut_distance at which cut-off starts double precision, intent(in) :: cut_start double precision, intent(in) :: cut_distance + + ! Truncation order for Fourier terms integer, intent(in) :: order + + ! Periodic table distance matrix + double precision, dimension(:,:), intent(in) :: pd + + ! Scaling for angular and distance terms double precision, intent(in) :: distance_scale double precision, intent(in) :: angular_scale - ! -1.0 / sigma^2 for use in the kernel - double precision, dimension(nsigmas) :: inv_sigma2 + ! Switch alchemy on or off + logical, intent(in) :: alchemy - double precision, dimension(:,:), intent(in) :: pd + ! Decaying power laws for two- and three-body terms + double precision, intent(in) :: two_body_power + double precision, intent(in) :: three_body_power + + ! Kernel ID and corresponding parameters + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters ! Resulting alpha vector double precision, dimension(nsigmas,nm1,nm2), intent(out) :: kernels ! Internal counters - integer :: i, j, k! , l + integer :: i, j integer :: ni, nj - integer :: a, b, n + integer :: a, b ! Temporary variables necessary for parallelization - double precision :: l2dist - double precision, allocatable, dimension(:,:) :: atomic_distance + double precision :: s12 ! Pre-computed terms in the full distance matrix double precision, allocatable, dimension(:,:) :: self_scalar1 double precision, allocatable, dimension(:,:) :: self_scalar2 - ! Pre-computed terms + ! Pre-computed two-body weights double precision, allocatable, dimension(:,:,:) :: ksi1 double precision, allocatable, dimension(:,:,:) :: ksi2 + ! Pre-computed terms for the Fourier expansion of the three-body term double precision, allocatable, dimension(:,:,:,:,:) :: sinp1 double precision, allocatable, dimension(:,:,:,:,:) :: sinp2 double precision, allocatable, dimension(:,:,:,:,:) :: cosp1 double precision, allocatable, dimension(:,:,:,:,:) :: cosp2 - logical, intent(in) :: alchemy - - ! Value of PI at full FORTRAN precision. - double precision, parameter :: pi = 4.0d0 * atan(1.0d0) - - ! counter for periodic distance + ! Max index in the periodic table integer :: pmax1 integer :: pmax2 - ! integer :: nneighi + ! Angular normalization constant double precision :: ang_norm2 - + + ! Max number of neighbors integer :: maxneigh1 integer :: maxneigh2 + ! Variables to calculate time + double precision :: t_start, t_end + + if (verbose) write (*,*) "CLEARING KERNEL MEM" + kernels(:,:,:) = 0.0d0 + + ! Get max number of neighbors maxneigh1 = maxval(nneigh1) maxneigh2 = maxval(nneigh2) - ang_norm2 = 0.0d0 - - do n = -10000, 10000 - ang_norm2 = ang_norm2 + exp(-((t_width * n)**2)) & - & * (2.0d0 - 2.0d0 * cos(n * pi)) - end do - - ang_norm2 = sqrt(ang_norm2 * pi) * 2.0d0 - - pmax1 = 0 - pmax2 = 0 - - do a = 1, nm1 - pmax1 = max(pmax1, int(maxval(x1(a,1,2,:n1(a))))) - enddo - do a = 1, nm2 - pmax2 = max(pmax2, int(maxval(x2(a,1,2,:n2(a))))) - enddo - - inv_sigma2(:) = -0.5d0 / (sigmas(:))**2 - - allocate(ksi1(nm1, maxval(n1), maxneigh1)) - allocate(ksi2(nm2, maxval(n2), maxneigh2)) - - ksi1 = 0.0d0 - ksi2 = 0.0d0 - - - !$OMP PARALLEL DO PRIVATE(ni) - do a = 1, nm1 - ni = n1(a) - do i = 1, ni - ksi1(a, i, :) = get_twobody_weights(x1(a,i,:,:), nneigh1(a, i), & - & two_body_power, cut_start, cut_distance, maxneigh1) - enddo - enddo - !$OMP END PARALLEL do - - !$OMP PARALLEL DO PRIVATE(ni) - do a = 1, nm2 - ni = n2(a) - do i = 1, ni - ksi2(a, i, :) = get_twobody_weights(x2(a,i,:,:), nneigh2(a, i), & - & two_body_power, cut_start, cut_distance, maxneigh2) - enddo - enddo - !$OMP END PARALLEL do - + ! Calculate angular normalization constant + ang_norm2 = get_angular_norm2(t_width) - allocate(cosp1(nm1, maxval(n1), pmax1, order, maxval(nneigh1))) - allocate(sinp1(nm1, maxval(n1), pmax1, order, maxval(nneigh1))) + ! pmax = max nuclear charge + pmax1 = get_pmax(x1, n1) + pmax2 = get_pmax(x2, n2) - cosp1 = 0.0d0 - sinp1 = 0.0d0 + ! Get two-body weight function + ksi1 = get_ksi(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, verbose) + ksi2 = get_ksi(x2, n2, nneigh2, two_body_power, cut_start, cut_distance, verbose) - !$OMP PARALLEL DO PRIVATE(ni, fourier) schedule(dynamic) - do a = 1, nm1 - ni = n1(a) - do i = 1, ni - - fourier = get_threebody_fourier(x1(a,i,:,:), & - & nneigh1(a, i), order, three_body_power, cut_start, cut_distance, pmax1, order, maxneigh1) - - cosp1(a,i,:,:,:) = fourier(1,:,:,:) - sinp1(a,i,:,:,:) = fourier(2,:,:,:) - - enddo - enddo - !$OMP END PARALLEL DO - - allocate(cosp2(nm2, maxval(n2), pmax2, order, maxval(nneigh2))) - allocate(sinp2(nm2, maxval(n2), pmax2, order, maxval(nneigh2))) - - cosp2 = 0.0d0 - sinp2 = 0.0d0 - - !$OMP PARALLEL DO PRIVATE(ni, fourier) schedule(dynamic) - do a = 1, nm2 - ni = n2(a) - do i = 1, ni - - fourier = get_threebody_fourier(x2(a,i,:,:), & - & nneigh2(a, i), order, three_body_power, cut_start, cut_distance, pmax2, order, maxneigh2) - - cosp2(a,i,:,:,:) = fourier(1,:,:,:) - sinp2(a,i,:,:,:) = fourier(2,:,:,:) - - enddo - enddo - !$OMP END PARALLEL DO - - allocate(self_scalar1(nm1, maxval(n1))) - allocate(self_scalar2(nm2, maxval(n2))) - - !$OMP PARALLEL DO PRIVATE(ni) - do a = 1, nm1 - ni = n1(a) - do i = 1, ni - self_scalar1(a,i) = scalar(x1(a,i,:,:), x1(a,i,:,:), & - & nneigh1(a,i), nneigh1(a,i), ksi1(a,i,:), ksi1(a,i,:), & - & sinp1(a,i,:,:,:), sinp1(a,i,:,:,:), & - & cosp1(a,i,:,:,:), cosp1(a,i,:,:,:), & - & t_width, d_width, cut_distance, order, & - & pd, ang_norm2,distance_scale, angular_scale, alchemy) - enddo - enddo - !$OMP END PARALLEL DO - - !$OMP PARALLEL DO PRIVATE(ni) - do a = 1, nm2 - ni = n2(a) - do i = 1, ni - self_scalar2(a,i) = scalar(x2(a,i,:,:), x2(a,i,:,:), & - & nneigh2(a,i), nneigh2(a,i), ksi2(a,i,:), ksi2(a,i,:), & - & sinp2(a,i,:,:,:), sinp2(a,i,:,:,:), & - & cosp2(a,i,:,:,:), cosp2(a,i,:,:,:), & - & t_width, d_width, cut_distance, order, & - & pd, ang_norm2, distance_scale, angular_scale, alchemy) - enddo - enddo - !$OMP END PARALLEL DO - - - allocate(atomic_distance(maxval(n1), maxval(n2))) - - kernels(:,:,:) = 0.0d0 - atomic_distance(:,:) = 0.0d0 + ! Allocate three-body Fourier terms + allocate(cosp1(nm1, maxval(n1), pmax1, order, maxneigh1)) + allocate(sinp1(nm1, maxval(n1), pmax1, order, maxneigh1)) - !$OMP PARALLEL DO schedule(dynamic) PRIVATE(l2dist,atomic_distance,ni,nj) + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1,sinp1, verbose) + + ! Allocate three-body Fourier terms + allocate(cosp2(nm2, maxval(n2), pmax2, order, maxneigh2)) + allocate(sinp2(nm2, maxval(n2), pmax2, order, maxneigh2)) + + ! Initialize and pre-calculate three-body Fourier terms + call init_cosp_sinp(x2, n2, nneigh2, three_body_power, order, cut_start, cut_distance, & + & cosp2,sinp2, verbose) + + ! Pre-calculate self-scalar terms + self_scalar1 = get_selfscalar(x1, nm1, n1, nneigh1, ksi1, sinp1, cosp1, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + ! Pre-calculate self-scalar terms + self_scalar2 = get_selfscalar(x2, nm2, n2, nneigh2, ksi2, sinp2, cosp2, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL" + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(s12,ni,nj) do b = 1, nm2 nj = n2(b) do a = 1, nm1 ni = n1(a) - atomic_distance(:,:) = 0.0d0 - do i = 1, ni do j = 1, nj - l2dist = scalar(x1(a,i,:,:), x2(b,j,:,:), & + s12 = scalar(x1(a,i,:,:), x2(b,j,:,:), & & nneigh1(a,i), nneigh2(b,j), ksi1(a,i,:), ksi2(b,j,:), & & sinp1(a,i,:,:,:), sinp2(b,j,:,:,:), & & cosp1(a,i,:,:,:), cosp2(b,j,:,:,:), & & t_width, d_width, cut_distance, order, & & pd, ang_norm2, distance_scale, angular_scale, alchemy) - l2dist = self_scalar1(a,i) + self_scalar2(b,j) - 2.0d0 * l2dist - atomic_distance(i,j) = l2dist - + kernels(:, a, b) = kernels(:, a, b) & + & + kernel(self_scalar1(a,i), self_scalar2(b,j), s12, & + & kernel_idx, parameters) + enddo enddo - do k = 1, nsigmas - kernels(k, a, b) = sum(exp(atomic_distance(:ni,:nj) & - & * inv_sigma2(k))) - enddo - enddo enddo !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" - deallocate(atomic_distance) deallocate(self_scalar1) deallocate(self_scalar2) deallocate(ksi1) @@ -287,28 +190,31 @@ subroutine fget_kernels_fchl(x1, x2, n1, n2, nneigh1, nneigh2, & end subroutine fget_kernels_fchl -subroutine fget_symmetric_kernels_fchl(x1, n1, nneigh1, sigmas, nm1, nsigmas, & +subroutine fget_symmetric_kernels_fchl(x1, verbose, n1, nneigh1, nm1, nsigmas, & & t_width, d_width, cut_start, cut_distance, order, pd, & - & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, kernels) + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, & + & kernel_idx, parameters, kernels) - use ffchl_module, only: scalar, get_threebody_fourier, get_twobody_weights + use ffchl_module, only: scalar, get_angular_norm2, get_pmax, get_ksi, init_cosp_sinp, get_selfscalar + + use ffchl_kernels, only: kernel - implicit none + use omp_lib, only: omp_get_wtime - double precision, allocatable, dimension(:,:,:,:) :: fourier + implicit none ! FCHL descriptors for the training set, format (i,j_1,5,m_1) double precision, dimension(:,:,:,:), intent(in) :: x1 + ! Whether to be verbose with output + logical, intent(in) :: verbose + ! List of numbers of atoms in each molecule integer, dimension(:), intent(in) :: n1 ! Number of neighbors for each atom in each compound integer, dimension(:,:), intent(in) :: nneigh1 - ! Sigma in the Gaussian kernel - double precision, dimension(:), intent(in) :: sigmas - ! Number of molecules integer, intent(in) :: nm1 @@ -327,21 +233,17 @@ subroutine fget_symmetric_kernels_fchl(x1, n1, nneigh1, sigmas, nm1, nsigmas, & double precision, intent(in) :: angular_scale logical, intent(in) :: alchemy - ! -1.0 / sigma^2 for use in the kernel - double precision, dimension(nsigmas) :: inv_sigma2 - double precision, dimension(:,:), intent(in) :: pd ! Resulting alpha vector double precision, dimension(nsigmas,nm1,nm1), intent(out) :: kernels ! Internal counters - integer :: i, j, k, ni, nj - integer :: a, b, n + integer :: i, j, ni, nj + integer :: a, b ! Temporary variables necessary for parallelization - double precision :: l2dist - double precision, allocatable, dimension(:,:) :: atomic_distance + double precision :: s12 ! Pre-computed terms in the full distance matrix double precision, allocatable, dimension(:,:) :: self_scalar1 @@ -352,127 +254,73 @@ subroutine fget_symmetric_kernels_fchl(x1, n1, nneigh1, sigmas, nm1, nsigmas, & double precision, allocatable, dimension(:,:,:,:,:) :: sinp1 double precision, allocatable, dimension(:,:,:,:,:) :: cosp1 - ! Value of PI at full FORTRAN precision. - double precision, parameter :: pi = 4.0d0 * atan(1.0d0) + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters ! counter for periodic distance integer :: pmax1 ! integer :: nneighi + double precision :: t_start, t_end + double precision :: ang_norm2 integer :: maxneigh1 + + if (verbose) write (*,*) "CLEARING KERNEL MEM" + kernels(:,:,:) = 0.0d0 - maxneigh1 = maxval(nneigh1) - - ang_norm2 = 0.0d0 - - do n = -10000, 10000 - ang_norm2 = ang_norm2 + exp(-((t_width * n)**2)) & - & * (2.0d0 - 2.0d0 * cos(n * pi)) - end do - - ang_norm2 = sqrt(ang_norm2 * pi) * 2.0d0 - - pmax1 = 0 - - do a = 1, nm1 - pmax1 = max(pmax1, int(maxval(x1(a,1,2,:n1(a))))) - enddo - - inv_sigma2(:) = -0.5d0 / (sigmas(:))**2 - - allocate(ksi1(nm1, maxval(n1), maxval(nneigh1))) + ang_norm2 = get_angular_norm2(t_width) - ksi1 = 0.0d0 + maxneigh1 = maxval(nneigh1) + pmax1 = get_pmax(x1, n1) - !$OMP PARALLEL DO PRIVATE(ni) - do a = 1, nm1 - ni = n1(a) - do i = 1, ni - ksi1(a, i, :) = get_twobody_weights(x1(a,i,:,:), nneigh1(a, i), & - & two_body_power, cut_start, cut_distance, maxneigh1) - enddo - enddo - !$OMP END PARALLEL do + ksi1 = get_ksi(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, verbose) allocate(cosp1(nm1, maxval(n1), pmax1, order, maxval(nneigh1))) allocate(sinp1(nm1, maxval(n1), pmax1, order, maxval(nneigh1))) - cosp1 = 0.0d0 - sinp1 = 0.0d0 + call init_cosp_sinp(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1,sinp1, verbose) - !$OMP PARALLEL DO PRIVATE(ni, fourier) - do a = 1, nm1 - ni = n1(a) - do i = 1, ni - - fourier = get_threebody_fourier(x1(a,i,:,:), & - & nneigh1(a, i), order, three_body_power, cut_start, cut_distance, pmax1, order, maxval(nneigh1)) - - cosp1(a,i,:,:,:) = fourier(1,:,:,:) - sinp1(a,i,:,:,:) = fourier(2,:,:,:) - - enddo - enddo - !$OMP END PARALLEL DO - - allocate(self_scalar1(nm1, maxval(n1))) - - !$OMP PARALLEL DO PRIVATE(ni) - do a = 1, nm1 - ni = n1(a) - do i = 1, ni - self_scalar1(a,i) = scalar(x1(a,i,:,:), x1(a,i,:,:), & - & nneigh1(a,i), nneigh1(a,i), ksi1(a,i,:), ksi1(a,i,:), & - & sinp1(a,i,:,:,:), sinp1(a,i,:,:,:), & - & cosp1(a,i,:,:,:), cosp1(a,i,:,:,:), & - & t_width, d_width, cut_distance, order, & - & pd, ang_norm2,distance_scale, angular_scale, alchemy) - enddo - enddo - !$OMP END PARALLEL DO + self_scalar1 = get_selfscalar(x1, nm1, n1, nneigh1, ksi1, sinp1, cosp1, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) - allocate(atomic_distance(maxval(n1), maxval(n1))) + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL" - kernels(:,:,:) = 0.0d0 - atomic_distance(:,:) = 0.0d0 - - !$OMP PARALLEL DO schedule(dynamic) PRIVATE(l2dist,atomic_distance,ni,nj) + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(s12,ni,nj) do b = 1, nm1 nj = n1(b) do a = b, nm1 ni = n1(a) - atomic_distance(:,:) = 0.0d0 - do i = 1, ni do j = 1, nj - l2dist = scalar(x1(a,i,:,:), x1(b,j,:,:), & + s12 = scalar(x1(a,i,:,:), x1(b,j,:,:), & & nneigh1(a,i), nneigh1(b,j), ksi1(a,i,:), ksi1(b,j,:), & & sinp1(a,i,:,:,:), sinp1(b,j,:,:,:), & & cosp1(a,i,:,:,:), cosp1(b,j,:,:,:), & & t_width, d_width, cut_distance, order, & & pd, ang_norm2, distance_scale, angular_scale, alchemy) - l2dist = self_scalar1(a,i) + self_scalar1(b,j) - 2.0d0 * l2dist - atomic_distance(i,j) = l2dist + kernels(:, a, b) = kernels(:, a, b) & + & + kernel(self_scalar1(a,i), self_scalar1(b,j), s12, & + & kernel_idx, parameters) + + kernels(:, b, a) = kernels(:, a, b) enddo enddo - do k = 1, nsigmas - kernels(k, a, b) = sum(exp(atomic_distance(:ni,:nj) & - & * inv_sigma2(k))) - kernels(k, b, a) = kernels(k, a, b) - enddo - enddo enddo !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" - deallocate(atomic_distance) deallocate(self_scalar1) deallocate(ksi1) deallocate(cosp1) @@ -481,28 +329,29 @@ subroutine fget_symmetric_kernels_fchl(x1, n1, nneigh1, sigmas, nm1, nsigmas, & end subroutine fget_symmetric_kernels_fchl -subroutine fget_global_symmetric_kernels_fchl(x1, n1, nneigh1, sigmas, nm1, nsigmas, & +subroutine fget_global_symmetric_kernels_fchl(x1, verbose, n1, nneigh1, nm1, nsigmas, & & t_width, d_width, cut_start, cut_distance, order, pd, & - & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, kernels) + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, & + & kernel_idx, parameters, kernels) - use ffchl_module, only: scalar, get_threebody_fourier, get_twobody_weights + use ffchl_module, only: scalar, get_angular_norm2, get_pmax, get_ksi, init_cosp_sinp + use ffchl_kernels, only: kernel + use omp_lib, only: omp_get_wtime implicit none - double precision, allocatable, dimension(:,:,:,:) :: fourier - ! FCHL descriptors for the training set, format (i,j_1,5,m_1) double precision, dimension(:,:,:,:), intent(in) :: x1 + ! Whether to be verbose with output + logical, intent(in) :: verbose + ! List of numbers of atoms in each molecule integer, dimension(:), intent(in) :: n1 ! Number of neighbors for each atom in each compound integer, dimension(:,:), intent(in) :: nneigh1 - ! Sigma in the Gaussian kernel - double precision, dimension(:), intent(in) :: sigmas - ! Number of molecules integer, intent(in) :: nm1 @@ -521,21 +370,17 @@ subroutine fget_global_symmetric_kernels_fchl(x1, n1, nneigh1, sigmas, nm1, nsig double precision, intent(in) :: angular_scale logical, intent(in) :: alchemy - ! -1.0 / sigma^2 for use in the kernel - double precision, dimension(nsigmas) :: inv_sigma2 - double precision, dimension(:,:), intent(in) :: pd ! Resulting alpha vector double precision, dimension(nsigmas,nm1,nm1), intent(out) :: kernels ! Internal counters - integer :: i, j, k, ni, nj - integer :: a, b, n + integer :: i, j, ni, nj + integer :: a, b ! Temporary variables necessary for parallelization - double precision :: l2dist - double precision, allocatable, dimension(:,:) :: atomic_distance + double precision :: s12 ! Pre-computed terms in the full distance matrix double precision, allocatable, dimension(:) :: self_scalar1 @@ -546,76 +391,41 @@ subroutine fget_global_symmetric_kernels_fchl(x1, n1, nneigh1, sigmas, nm1, nsig double precision, allocatable, dimension(:,:,:,:,:) :: sinp1 double precision, allocatable, dimension(:,:,:,:,:) :: cosp1 - ! Value of PI at full FORTRAN precision. - double precision, parameter :: pi = 4.0d0 * atan(1.0d0) + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters ! counter for periodic distance integer :: pmax1 - ! integer :: nneighi double precision :: ang_norm2 double precision :: mol_dist + ! Variables to calculate time + double precision :: t_start, t_end + integer :: maxneigh1 maxneigh1 = maxval(nneigh1) - ang_norm2 = 0.0d0 + ang_norm2 = get_angular_norm2(t_width) - do n = -10000, 10000 - ang_norm2 = ang_norm2 + exp(-((t_width * n)**2)) & - & * (2.0d0 - 2.0d0 * cos(n * pi)) - end do + pmax1 = get_pmax(x1, n1) - ang_norm2 = sqrt(ang_norm2 * pi) * 2.0d0 - - pmax1 = 0 - - do a = 1, nm1 - pmax1 = max(pmax1, int(maxval(x1(a,1,2,:n1(a))))) - enddo + ksi1 = get_ksi(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, verbose) - inv_sigma2(:) = -0.5d0 / (sigmas(:))**2 - - allocate(ksi1(nm1, maxval(n1), maxval(nneigh1))) - - ksi1 = 0.0d0 - - !$OMP PARALLEL DO PRIVATE(ni) - do a = 1, nm1 - ni = n1(a) - do i = 1, ni - ksi1(a, i, :) = get_twobody_weights(x1(a,i,:,:), nneigh1(a, i), & - & two_body_power, cut_start, cut_distance, maxneigh1) - enddo - enddo - !$OMP END PARALLEL do - - allocate(cosp1(nm1, maxval(n1), pmax1, order, maxneigh1)) - allocate(sinp1(nm1, maxval(n1), pmax1, order, maxneigh1)) - - cosp1 = 0.0d0 - sinp1 = 0.0d0 - - !$OMP PARALLEL DO PRIVATE(ni, fourier) - do a = 1, nm1 - ni = n1(a) - do i = 1, ni - - fourier = get_threebody_fourier(x1(a,i,:,:), & - & nneigh1(a, i), order, three_body_power, cut_start, cut_distance, pmax1, order, maxneigh1) + allocate(cosp1(nm1, maxval(n1), pmax1, order, maxval(nneigh1))) + allocate(sinp1(nm1, maxval(n1), pmax1, order, maxval(nneigh1))) - cosp1(a,i,:,:,:) = fourier(1,:,:,:) - sinp1(a,i,:,:,:) = fourier(2,:,:,:) - - enddo - enddo - !$OMP END PARALLEL DO + call init_cosp_sinp(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1,sinp1, verbose) allocate(self_scalar1(nm1)) self_scalar1 = 0.0d0 + + if (verbose) write (*,"(A)", advance="no") "TWO-BODY TERMS" + t_start = omp_get_wtime() !$OMP PARALLEL DO PRIVATE(ni) REDUCTION(+:self_scalar1) do a = 1, nm1 @@ -634,45 +444,50 @@ subroutine fget_global_symmetric_kernels_fchl(x1, n1, nneigh1, sigmas, nm1, nsig enddo !$OMP END PARALLEL DO - allocate(atomic_distance(maxval(n1), maxval(n1))) + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + if (verbose) write (*,*) "CLEARING KERNEL MEM" kernels(:,:,:) = 0.0d0 - atomic_distance(:,:) = 0.0d0 + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL" - !$OMP PARALLEL DO schedule(dynamic) PRIVATE(l2dist,atomic_distance,ni,nj) + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(s12,ni,nj,mol_dist) do b = 1, nm1 nj = n1(b) do a = b, nm1 ni = n1(a) - atomic_distance(:,:) = 0.0d0 + mol_dist = 0.0d0 do i = 1, ni do j = 1, nj - l2dist = scalar(x1(a,i,:,:), x1(b,j,:,:), & + s12 = scalar(x1(a,i,:,:), x1(b,j,:,:), & & nneigh1(a,i), nneigh1(b,j), ksi1(a,i,:), ksi1(b,j,:), & & sinp1(a,i,:,:,:), sinp1(b,j,:,:,:), & & cosp1(a,i,:,:,:), cosp1(b,j,:,:,:), & & t_width, d_width, cut_distance, order, & & pd, ang_norm2, distance_scale, angular_scale, alchemy) - atomic_distance(i,j) = l2dist + mol_dist = mol_dist + s12 enddo enddo - mol_dist = self_scalar1(a) + self_scalar1(b) - 2.0d0 * sum(atomic_distance(:ni,:nj)) + kernels(:, a, b) = kernel(self_scalar1(a), self_scalar1(b), mol_dist, & + & kernel_idx, parameters) + + kernels(:, b, a) = kernels(:, a, b) - do k = 1, nsigmas - kernels(k, a, b) = exp(mol_dist * inv_sigma2(k)) - kernels(k, b, a) = kernels(k, a, b) - enddo enddo enddo !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" - deallocate(atomic_distance) deallocate(self_scalar1) deallocate(ksi1) deallocate(cosp1) @@ -681,21 +496,25 @@ subroutine fget_global_symmetric_kernels_fchl(x1, n1, nneigh1, sigmas, nm1, nsig end subroutine fget_global_symmetric_kernels_fchl -subroutine fget_global_kernels_fchl(x1, x2, n1, n2, nneigh1, nneigh2, & - & sigmas, nm1, nm2, nsigmas, & +subroutine fget_global_kernels_fchl(x1, x2, verbose, n1, n2, nneigh1, nneigh2, & + & nm1, nm2, nsigmas, & & t_width, d_width, cut_start, cut_distance, order, pd, & - & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, kernels) + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, & + & kernel_idx, parameters, kernels) - use ffchl_module, only: scalar, get_threebody_fourier, get_twobody_weights + use ffchl_module, only: scalar, get_angular_norm2, get_pmax, get_ksi, init_cosp_sinp + use ffchl_kernels, only: kernel + use omp_lib, only: omp_get_wtime implicit none - double precision, allocatable, dimension(:,:,:,:) :: fourier - ! fchl descriptors for the training set, format (i,maxatoms,5,maxneighbors) double precision, dimension(:,:,:,:), intent(in) :: x1 double precision, dimension(:,:,:,:), intent(in) :: x2 + ! Whether to be verbose with output + logical, intent(in) :: verbose + ! List of numbers of atoms in each molecule integer, dimension(:), intent(in) :: n1 integer, dimension(:), intent(in) :: n2 @@ -704,9 +523,6 @@ subroutine fget_global_kernels_fchl(x1, x2, n1, n2, nneigh1, nneigh2, & integer, dimension(:,:), intent(in) :: nneigh1 integer, dimension(:,:), intent(in) :: nneigh2 - ! Sigma in the Gaussian kernel - double precision, dimension(:), intent(in) :: sigmas - ! Number of molecules integer, intent(in) :: nm1 integer, intent(in) :: nm2 @@ -726,22 +542,19 @@ subroutine fget_global_kernels_fchl(x1, x2, n1, n2, nneigh1, nneigh2, & double precision, intent(in) :: angular_scale logical, intent(in) :: alchemy - ! -1.0 / sigma^2 for use in the kernel - double precision, dimension(nsigmas) :: inv_sigma2 - double precision, dimension(:,:), intent(in) :: pd ! Resulting alpha vector double precision, dimension(nsigmas,nm1,nm2), intent(out) :: kernels ! Internal counters - integer :: i, j, k + integer :: i, j integer :: ni, nj - integer :: a, b, n + integer :: a, b ! Temporary variables necessary for parallelization - double precision :: l2dist - double precision, allocatable, dimension(:,:) :: atomic_distance + double precision :: s12 + ! double precision, allocatable, dimension(:,:) :: atomic_distance ! Pre-computed terms in the full distance matrix double precision, allocatable, dimension(:) :: self_scalar1 @@ -756,8 +569,8 @@ subroutine fget_global_kernels_fchl(x1, x2, n1, n2, nneigh1, nneigh2, & double precision, allocatable, dimension(:,:,:,:,:) :: cosp1 double precision, allocatable, dimension(:,:,:,:,:) :: cosp2 - ! Value of PI at full FORTRAN precision. - double precision, parameter :: pi = 4.0d0 * atan(1.0d0) + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters ! counter for periodic distance integer :: pmax1 @@ -767,106 +580,47 @@ subroutine fget_global_kernels_fchl(x1, x2, n1, n2, nneigh1, nneigh2, & double precision :: mol_dist + ! Variables to calculate time + double precision :: t_start, t_end + integer :: maxneigh1 integer :: maxneigh2 maxneigh1 = maxval(nneigh1) maxneigh2 = maxval(nneigh2) - ang_norm2 = 0.0d0 - - do n = -10000, 10000 - ang_norm2 = ang_norm2 + exp(-((t_width * n)**2)) & - & * (2.0d0 - 2.0d0 * cos(n * pi)) - end do - - ang_norm2 = sqrt(ang_norm2 * pi) * 2.0d0 - - pmax1 = 0 - pmax2 = 0 - - do a = 1, nm1 - pmax1 = max(pmax1, int(maxval(x1(a,1,2,:n1(a))))) - enddo - do a = 1, nm2 - pmax2 = max(pmax2, int(maxval(x2(a,1,2,:n2(a))))) - enddo - - inv_sigma2(:) = -0.5d0 / (sigmas(:))**2 - - allocate(ksi1(nm1, maxval(n1), maxval(nneigh1))) - allocate(ksi2(nm2, maxval(n2), maxval(nneigh2))) - - ksi1 = 0.0d0 - ksi2 = 0.0d0 - - !$OMP PARALLEL DO PRIVATE(ni) - do a = 1, nm1 - ni = n1(a) - do i = 1, ni - ksi1(a, i, :) = get_twobody_weights(x1(a,i,:,:), nneigh1(a, i), & - & two_body_power, cut_start, cut_distance, maxneigh1) - enddo - enddo - !$OMP END PARALLEL do - - !$OMP PARALLEL DO PRIVATE(ni) - do a = 1, nm2 - ni = n2(a) - do i = 1, ni - ksi2(a, i, :) = get_twobody_weights(x2(a,i,:,:), nneigh2(a, i), & - & two_body_power, cut_start, cut_distance, maxneigh2) - enddo - enddo - !$OMP END PARALLEL do + ang_norm2 = get_angular_norm2(t_width) + pmax1 = get_pmax(x1, n1) + pmax2 = get_pmax(x2, n2) + + ksi1 = get_ksi(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, verbose) + ksi2 = get_ksi(x2, n2, nneigh2, two_body_power, cut_start, cut_distance, verbose) + + allocate(cosp1(nm1, maxval(n1), pmax1, order, maxval(nneigh1))) allocate(sinp1(nm1, maxval(n1), pmax1, order, maxval(nneigh1))) - cosp1 = 0.0d0 - sinp1 = 0.0d0 - - !$OMP PARALLEL DO PRIVATE(ni, fourier) - do a = 1, nm1 - ni = n1(a) - do i = 1, ni - - fourier = get_threebody_fourier(x1(a,i,:,:), & - & nneigh1(a, i), order, three_body_power, cut_start, cut_distance, pmax1, order, maxneigh1) - - cosp1(a,i,:,:,:) = fourier(1,:,:,:) - sinp1(a,i,:,:,:) = fourier(2,:,:,:) - - enddo - enddo - !$OMP END PARALLEL DO + call init_cosp_sinp(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1,sinp1, verbose) allocate(cosp2(nm2, maxval(n2), pmax2, order, maxval(nneigh2))) allocate(sinp2(nm2, maxval(n2), pmax2, order, maxval(nneigh2))) + + call init_cosp_sinp(x2, n2, nneigh2, three_body_power, order, cut_start, cut_distance, & + & cosp2,sinp2, verbose) - cosp2 = 0.0d0 - sinp2 = 0.0d0 - - !$OMP PARALLEL DO PRIVATE(ni, fourier) - do a = 1, nm2 - ni = n2(a) - do i = 1, ni - - fourier = get_threebody_fourier(x2(a,i,:,:), & - & nneigh2(a, i), order, three_body_power, cut_start, cut_distance, pmax2, order, maxval(nneigh2)) - - cosp2(a,i,:,:,:) = fourier(1,:,:,:) - sinp2(a,i,:,:,:) = fourier(2,:,:,:) - - enddo - enddo - !$OMP END PARALLEL DO + ! Global self-scalar have their own summation and are not a general function allocate(self_scalar1(nm1)) allocate(self_scalar2(nm2)) self_scalar1 = 0.0d0 self_scalar2 = 0.0d0 + + + if (verbose) write (*,"(A)", advance="no") "TWO-BODY TERMS" + t_start = omp_get_wtime() !$OMP PARALLEL DO PRIVATE(ni) REDUCTION(+:self_scalar1) do a = 1, nm1 @@ -884,6 +638,13 @@ subroutine fget_global_kernels_fchl(x1, x2, n1, n2, nneigh1, nneigh2, & enddo enddo !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + + if (verbose) write (*,"(A)", advance="no") "TWO-BODY TERMS" + t_start = omp_get_wtime() !$OMP PARALLEL DO PRIVATE(ni) REDUCTION(+:self_scalar2) do a = 1, nm2 @@ -900,47 +661,48 @@ subroutine fget_global_kernels_fchl(x1, x2, n1, n2, nneigh1, nneigh2, & enddo enddo !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" - - allocate(atomic_distance(maxval(n1), maxval(n2))) - + if (verbose) write (*,*) "CLEARING KERNEL MEM" kernels(:,:,:) = 0.0d0 - atomic_distance(:,:) = 0.0d0 + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL" - !$OMP PARALLEL DO schedule(dynamic) PRIVATE(l2dist,atomic_distance,ni,nj) + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(s12,ni,nj,mol_dist) do b = 1, nm2 nj = n2(b) do a = 1, nm1 ni = n1(a) - atomic_distance(:,:) = 0.0d0 + mol_dist = 0.0d0 do i = 1, ni do j = 1, nj - l2dist = scalar(x1(a,i,:,:), x2(b,j,:,:), & + s12 = scalar(x1(a,i,:,:), x2(b,j,:,:), & & nneigh1(a,i), nneigh2(b,j), ksi1(a,i,:), ksi2(b,j,:), & & sinp1(a,i,:,:,:), sinp2(b,j,:,:,:), & & cosp1(a,i,:,:,:), cosp2(b,j,:,:,:), & & t_width, d_width, cut_distance, order, & & pd, ang_norm2, distance_scale, angular_scale, alchemy) - atomic_distance(i,j) = l2dist + mol_dist = mol_dist + s12 enddo enddo - - mol_dist = self_scalar1(a) + self_scalar2(b) - 2.0d0 * sum(atomic_distance(:ni,:nj)) - - do k = 1, nsigmas - kernels(k, a, b) = exp(mol_dist * inv_sigma2(k)) - enddo + kernels(:, a, b) = kernel(self_scalar1(a), self_scalar2(b), mol_dist, & + & kernel_idx, parameters) enddo enddo !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" - deallocate(atomic_distance) deallocate(self_scalar1) deallocate(self_scalar2) deallocate(ksi1) @@ -953,28 +715,32 @@ subroutine fget_global_kernels_fchl(x1, x2, n1, n2, nneigh1, nneigh2, & end subroutine fget_global_kernels_fchl -subroutine fget_atomic_kernels_fchl(x1, x2, nneigh1, nneigh2, & - & sigmas, na1, na2, nsigmas, & +subroutine fget_atomic_kernels_fchl(x1, x2, verbose, nneigh1, nneigh2, & + & na1, na2, nsigmas, & & t_width, d_width, cut_start, cut_distance, order, pd, & - & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, kernels) + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, & + & kernel_idx, parameters, kernels) - use ffchl_module, only: scalar, get_threebody_fourier, get_twobody_weights + use ffchl_module, only: scalar, get_angular_norm2, & + & get_pmax_atomic, get_ksi_atomic, init_cosp_sinp_atomic + + use ffchl_kernels, only: kernel + + use omp_lib, only: omp_get_wtime implicit none - double precision, allocatable, dimension(:,:,:,:) :: fourier - ! fchl descriptors for the training set, format (i,maxatoms,5,maxneighbors) double precision, dimension(:,:,:), intent(in) :: x1 double precision, dimension(:,:,:), intent(in) :: x2 + ! Whether to be verbose with output + logical, intent(in) :: verbose + ! Number of neighbors for each atom in each compound integer, dimension(:), intent(in) :: nneigh1 integer, dimension(:), intent(in) :: nneigh2 - ! Sigma in the Gaussian kernel - double precision, dimension(:), intent(in) :: sigmas - ! Number of molecules integer, intent(in) :: na1 integer, intent(in) :: na2 @@ -994,9 +760,6 @@ subroutine fget_atomic_kernels_fchl(x1, x2, nneigh1, nneigh2, & double precision, intent(in) :: angular_scale logical, intent(in) :: alchemy - ! -1.0 / sigma^2 for use in the kernel - double precision, dimension(nsigmas) :: inv_sigma2 - double precision, dimension(:,:), intent(in) :: pd ! Resulting alpha vector @@ -1004,11 +767,9 @@ subroutine fget_atomic_kernels_fchl(x1, x2, nneigh1, nneigh2, & ! Internal counters integer :: i, j - ! integer :: ni, nj - integer :: a, n ! Temporary variables necessary for parallelization - double precision :: l2dist + double precision :: s12 ! Pre-computed terms in the full distance matrix double precision, allocatable, dimension(:) :: self_scalar1 @@ -1022,100 +783,44 @@ subroutine fget_atomic_kernels_fchl(x1, x2, nneigh1, nneigh2, & double precision, allocatable, dimension(:,:,:,:) :: sinp2 double precision, allocatable, dimension(:,:,:,:) :: cosp1 double precision, allocatable, dimension(:,:,:,:) :: cosp2 - - ! Value of PI at full FORTRAN precision. - double precision, parameter :: pi = 4.0d0 * atan(1.0d0) + + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters ! counter for periodic distance integer :: pmax1 integer :: pmax2 - ! integer :: nneighi double precision :: ang_norm2 - ! double precision :: mol_dist - + ! Variables to calculate time + double precision :: t_start, t_end + integer :: maxneigh1 integer :: maxneigh2 maxneigh1 = maxval(nneigh1) maxneigh2 = maxval(nneigh2) - ang_norm2 = 0.0d0 + ang_norm2 = get_angular_norm2(t_width) - do n = -10000, 10000 - ang_norm2 = ang_norm2 + exp(-((t_width * n)**2)) & - & * (2.0d0 - 2.0d0 * cos(n * pi)) - end do + pmax1 = get_pmax_atomic(x1, nneigh1) + pmax2 = get_pmax_atomic(x2, nneigh2) + + ksi1 = get_ksi_atomic(x1, na1, nneigh1, two_body_power, cut_start, cut_distance, verbose) + ksi2 = get_ksi_atomic(x2, na2, nneigh2, two_body_power, cut_start, cut_distance, verbose) - ang_norm2 = sqrt(ang_norm2 * pi) * 2.0d0 - - pmax1 = 0 - pmax2 = 0 - - do a = 1, na1 - pmax1 = max(pmax1, int(maxval(x1(a,2,:nneigh1(a))))) - enddo - do a = 1, na2 - pmax2 = max(pmax2, int(maxval(x2(a,2,:nneigh2(a))))) - enddo - - inv_sigma2(:) = -0.5d0 / (sigmas(:))**2 - - allocate(ksi1(na1, maxval(nneigh1))) - allocate(ksi2(na2, maxval(nneigh2))) - - ksi1 = 0.0d0 - ksi2 = 0.0d0 - - !$OMP PARALLEL DO - do i = 1, na1 - ksi1(i, :) = get_twobody_weights(x1(i,:,:), nneigh1(i), & - & two_body_power, cut_start, cut_distance, maxneigh1) - enddo - !$OMP END PARALLEL do - - !$OMP PARALLEL DO - do i = 1, na2 - ksi2(i, :) = get_twobody_weights(x2(i,:,:), nneigh2(i), & - & two_body_power, cut_start, cut_distance, maxneigh2) - enddo - !$OMP END PARALLEL do allocate(cosp1(na1, pmax1, order, maxneigh1)) allocate(sinp1(na1, pmax1, order, maxneigh1)) - cosp1 = 0.0d0 - sinp1 = 0.0d0 - - !$OMP PARALLEL DO PRIVATE(fourier) - do i = 1, na1 - - fourier = get_threebody_fourier(x1(i,:,:), & - & nneigh1(i), order, three_body_power, cut_start, cut_distance, pmax1, order, maxneigh1) - - cosp1(i,:,:,:) = fourier(1,:,:,:) - sinp1(i,:,:,:) = fourier(2,:,:,:) - - enddo - !$OMP END PARALLEL DO - + call init_cosp_sinp_atomic(x1, na1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1,sinp1, verbose) + allocate(cosp2(na2, pmax2, order, maxneigh2)) allocate(sinp2(na2, pmax2, order, maxneigh2)) - cosp2 = 0.0d0 - sinp2 = 0.0d0 - - !$OMP PARALLEL DO PRIVATE(fourier) - do i = 1, na2 - - fourier = get_threebody_fourier(x2(i,:,:), & - & nneigh2(i), order, three_body_power, cut_start, cut_distance, pmax2, order, maxneigh2) - - cosp2(i,:,:,:) = fourier(1,:,:,:) - sinp2(i,:,:,:) = fourier(2,:,:,:) - - enddo - !$OMP END PARALLEL DO + call init_cosp_sinp_atomic(x2, na2, nneigh2, three_body_power, order, cut_start, cut_distance, & + & cosp2,sinp2, verbose) allocate(self_scalar1(na1)) allocate(self_scalar2(na2)) @@ -1145,24 +850,32 @@ subroutine fget_atomic_kernels_fchl(x1, x2, nneigh1, nneigh2, & enddo !$OMP END PARALLEL DO + if (verbose) write (*,*) "CLEARING KERNEL MEM" kernels(:,:,:) = 0.0d0 + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL" - !$OMP PARALLEL DO schedule(dynamic) PRIVATE(l2dist) + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(s12) do i = 1, na1 do j = 1, na2 - l2dist = self_scalar1(i) + self_scalar2(j) - 2.0d0 * scalar(x1(i,:,:), x2(j,:,:), & + s12 = scalar(x1(i,:,:), x2(j,:,:), & & nneigh1(i), nneigh2(j), ksi1(i,:), ksi2(j,:), & & sinp1(i,:,:,:), sinp2(j,:,:,:), & & cosp1(i,:,:,:), cosp2(j,:,:,:), & & t_width, d_width, cut_distance, order, & & pd, ang_norm2, distance_scale, angular_scale, alchemy) - kernels(:, i, j) = exp(l2dist * inv_sigma2(:)) + kernels(:, i, j) = kernel(self_scalar1(i), self_scalar2(j), s12, & + & kernel_idx, parameters) enddo enddo !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" deallocate(self_scalar1) deallocate(self_scalar2) @@ -1176,26 +889,28 @@ subroutine fget_atomic_kernels_fchl(x1, x2, nneigh1, nneigh2, & end subroutine fget_atomic_kernels_fchl -subroutine fget_atomic_symmetric_kernels_fchl(x1, nneigh1, & - & sigmas, na1, nsigmas, & +subroutine fget_atomic_symmetric_kernels_fchl(x1, verbose, nneigh1, na1, nsigmas, & & t_width, d_width, cut_start, cut_distance, order, pd, & - & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, kernels) + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, & + & kernel_idx, parameters, kernels) - use ffchl_module, only: scalar, get_threebody_fourier, get_twobody_weights + use ffchl_module, only: scalar, get_angular_norm2, & + & get_pmax_atomic, get_ksi_atomic, init_cosp_sinp_atomic + use ffchl_kernels, only: kernel + + use omp_lib, only: omp_get_wtime implicit none - double precision, allocatable, dimension(:,:,:,:) :: fourier - ! fchl descriptors for the training set, format (i,maxatoms,5,maxneighbors) double precision, dimension(:,:,:), intent(in) :: x1 + ! Whether to be verbose with output + logical, intent(in) :: verbose + ! Number of neighbors for each atom in each compound integer, dimension(:), intent(in) :: nneigh1 - ! Sigma in the Gaussian kernel - double precision, dimension(:), intent(in) :: sigmas - ! Number of molecules integer, intent(in) :: na1 @@ -1214,21 +929,19 @@ subroutine fget_atomic_symmetric_kernels_fchl(x1, nneigh1, & double precision, intent(in) :: angular_scale logical, intent(in) :: alchemy - ! -1.0 / sigma^2 for use in the kernel - double precision, dimension(nsigmas) :: inv_sigma2 - double precision, dimension(:,:), intent(in) :: pd + + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters ! Resulting alpha vector double precision, dimension(nsigmas,na1,na1), intent(out) :: kernels ! Internal counters integer :: i, j - ! integer :: ni, nj - integer :: a, n ! Temporary variables necessary for parallelization - double precision :: l2dist + double precision :: s12 ! Pre-computed terms in the full distance matrix double precision, allocatable, dimension(:) :: self_scalar1 @@ -1239,67 +952,35 @@ subroutine fget_atomic_symmetric_kernels_fchl(x1, nneigh1, & double precision, allocatable, dimension(:,:,:,:) :: sinp1 double precision, allocatable, dimension(:,:,:,:) :: cosp1 - ! Value of PI at full FORTRAN precision. - double precision, parameter :: pi = 4.0d0 * atan(1.0d0) - ! counter for periodic distance integer :: pmax1 - ! integer :: nneighi double precision :: ang_norm2 + ! Variables to calculate time + double precision :: t_start, t_end + integer :: maxneigh1 maxneigh1 = maxval(nneigh1) - ang_norm2 = 0.0d0 + ang_norm2 = get_angular_norm2(t_width) - do n = -10000, 10000 - ang_norm2 = ang_norm2 + exp(-((t_width * n)**2)) & - & * (2.0d0 - 2.0d0 * cos(n * pi)) - end do + pmax1 = get_pmax_atomic(x1, nneigh1) - ang_norm2 = sqrt(ang_norm2 * pi) * 2.0d0 - - pmax1 = 0 - - do a = 1, na1 - pmax1 = max(pmax1, int(maxval(x1(a,2,:nneigh1(a))))) - enddo - - inv_sigma2(:) = -0.5d0 / (sigmas(:))**2 - - allocate(ksi1(na1, maxval(nneigh1))) - - ksi1 = 0.0d0 - - !$OMP PARALLEL DO - do i = 1, na1 - ksi1(i, :) = get_twobody_weights(x1(i,:,:), nneigh1(i), & - & two_body_power, cut_start, cut_distance, maxneigh1) - enddo - !$OMP END PARALLEL do + ksi1 = get_ksi_atomic(x1, na1, nneigh1, two_body_power, cut_start, cut_distance, verbose) allocate(cosp1(na1, pmax1, order, maxneigh1)) allocate(sinp1(na1, pmax1, order, maxneigh1)) - cosp1 = 0.0d0 - sinp1 = 0.0d0 - - !$OMP PARALLEL DO PRIVATE(fourier) - do i = 1, na1 - - fourier = get_threebody_fourier(x1(i,:,:), & - & nneigh1(i), order, three_body_power, cut_start, cut_distance, pmax1, order, maxneigh1) - - cosp1(i,:,:,:) = fourier(1,:,:,:) - sinp1(i,:,:,:) = fourier(2,:,:,:) - - enddo - !$OMP END PARALLEL DO + call init_cosp_sinp_atomic(x1, na1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1,sinp1, verbose) allocate(self_scalar1(na1)) self_scalar1 = 0.0d0 + + if (verbose) write (*,"(A)", advance="no") "TWO-BODY TERMS" + t_start = omp_get_wtime() !$OMP PARALLEL DO do i = 1, na1 @@ -1311,25 +992,37 @@ subroutine fget_atomic_symmetric_kernels_fchl(x1, nneigh1, & & pd, ang_norm2,distance_scale, angular_scale, alchemy) enddo !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + if (verbose) write (*,*) "CLEARING KERNEL MEM" kernels(:,:,:) = 0.0d0 + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL" - !$OMP PARALLEL DO schedule(dynamic) PRIVATE(l2dist) + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(s12) do i = 1, na1 - do j = 1, na1 + do j = i, na1 - l2dist = self_scalar1(i) + self_scalar1(j) - 2.0d0 * scalar(x1(i,:,:), x1(j,:,:), & + s12 = scalar(x1(i,:,:), x1(j,:,:), & & nneigh1(i), nneigh1(j), ksi1(i,:), ksi1(j,:), & & sinp1(i,:,:,:), sinp1(j,:,:,:), & & cosp1(i,:,:,:), cosp1(j,:,:,:), & & t_width, d_width, cut_distance, order, & & pd, ang_norm2, distance_scale, angular_scale, alchemy) - kernels(:, i, j) = exp(l2dist * inv_sigma2(:)) + kernels(:, i, j) = kernel(self_scalar1(i), self_scalar1(j), s12, & + & kernel_idx, parameters) + kernels(:, j, i) = kernels(:, i, j) enddo enddo !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" deallocate(self_scalar1) deallocate(ksi1) @@ -1337,3 +1030,184 @@ subroutine fget_atomic_symmetric_kernels_fchl(x1, nneigh1, & deallocate(sinp1) end subroutine fget_atomic_symmetric_kernels_fchl + + +subroutine fget_atomic_local_kernels_fchl(x1, x2, verbose, n1, n2, nneigh1, nneigh2, & + & nm1, nm2, na1, nsigmas, & + & t_width, d_width, cut_start, cut_distance, order, pd, & + & distance_scale, angular_scale, alchemy, two_body_power, three_body_power, & + & kernel_idx, parameters, kernels) + + use ffchl_module, only: scalar, get_threebody_fourier, get_twobody_weights, & + & get_angular_norm2, get_pmax, get_ksi, init_cosp_sinp, get_selfscalar + use ffchl_kernels, only: kernel + + use omp_lib, only: omp_get_wtime + + implicit none + + ! fchl descriptors for the training set, format (i,maxatoms,5,maxneighbors) + double precision, dimension(:,:,:,:), intent(in) :: x1 + double precision, dimension(:,:,:,:), intent(in) :: x2 + + ! Whether to be verbose with output + logical, intent(in) :: verbose + + ! List of numbers of atoms in each molecule + integer, dimension(:), intent(in) :: n1 + integer, dimension(:), intent(in) :: n2 + + ! Number of neighbors for each atom in each compound + integer, dimension(:,:), intent(in) :: nneigh1 + integer, dimension(:,:), intent(in) :: nneigh2 + + ! Number of molecules + integer, intent(in) :: nm1 + integer, intent(in) :: nm2 + + integer, intent(in) :: na1 + + ! Number of sigmas + integer, intent(in) :: nsigmas + + double precision, intent(in) :: two_body_power + double precision, intent(in) :: three_body_power + + double precision, intent(in) :: t_width + double precision, intent(in) :: d_width + double precision, intent(in) :: cut_start + double precision, intent(in) :: cut_distance + integer, intent(in) :: order + double precision, intent(in) :: distance_scale + double precision, intent(in) :: angular_scale + + ! -1.0 / sigma^2 for use in the kernel + + double precision, dimension(:,:), intent(in) :: pd + + integer, intent(in) :: kernel_idx + double precision, dimension(:,:), intent(in) :: parameters + + ! Resulting alpha vector + double precision, dimension(nsigmas,na1,nm2), intent(out) :: kernels + + integer :: idx1 + + ! Internal counters + integer :: i, j + integer :: ni, nj + integer :: a, b + + ! Temporary variables necessary for parallelization + double precision :: s12 + + ! Pre-computed terms in the full distance matrix + double precision, allocatable, dimension(:,:) :: self_scalar1 + double precision, allocatable, dimension(:,:) :: self_scalar2 + + ! Pre-computed terms + double precision, allocatable, dimension(:,:,:) :: ksi1 + double precision, allocatable, dimension(:,:,:) :: ksi2 + + double precision, allocatable, dimension(:,:,:,:,:) :: sinp1 + double precision, allocatable, dimension(:,:,:,:,:) :: sinp2 + double precision, allocatable, dimension(:,:,:,:,:) :: cosp1 + double precision, allocatable, dimension(:,:,:,:,:) :: cosp2 + + logical, intent(in) :: alchemy + + ! Value of PI at full FORTRAN precision. + double precision, parameter :: pi = 4.0d0 * atan(1.0d0) + + ! counter for periodic distance + integer :: pmax1 + integer :: pmax2 + ! integer :: nneighi + + double precision :: ang_norm2 + + integer :: maxneigh1 + integer :: maxneigh2 + + ! Variables to calculate time + double precision :: t_start, t_end + + maxneigh1 = maxval(nneigh1) + maxneigh2 = maxval(nneigh2) + + ang_norm2 = get_angular_norm2(t_width) + + pmax1 = get_pmax(x1, n1) + pmax2 = get_pmax(x2, n2) + + ksi1 = get_ksi(x1, n1, nneigh1, two_body_power, cut_start, cut_distance, verbose) + ksi2 = get_ksi(x2, n2, nneigh2, two_body_power, cut_start, cut_distance, verbose) + + allocate(cosp1(nm1, maxval(n1), pmax1, order, maxval(nneigh1))) + allocate(sinp1(nm1, maxval(n1), pmax1, order, maxval(nneigh1))) + + call init_cosp_sinp(x1, n1, nneigh1, three_body_power, order, cut_start, cut_distance, & + & cosp1,sinp1, verbose) + + allocate(cosp2(nm2, maxval(n2), pmax2, order, maxval(nneigh2))) + allocate(sinp2(nm2, maxval(n2), pmax2, order, maxval(nneigh2))) + + call init_cosp_sinp(x2, n2, nneigh2, three_body_power, order, cut_start, cut_distance, & + & cosp2,sinp2, verbose) + + ! Pre-calculate self-scalar terms + self_scalar1 = get_selfscalar(x1, nm1, n1, nneigh1, ksi1, sinp1, cosp1, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + ! Pre-calculate self-scalar terms + self_scalar2 = get_selfscalar(x2, nm2, n2, nneigh2, ksi2, sinp2, cosp2, t_width, d_width, & + & cut_distance, order, pd, ang_norm2,distance_scale, angular_scale, alchemy, verbose) + + if (verbose) write (*,*) "CLEARING KERNEL MEM" + kernels(:,:,:) = 0.0d0 + + t_start = omp_get_wtime() + if (verbose) write (*,"(A)", advance="no") "KERNEL" + + !$OMP PARALLEL DO schedule(dynamic) PRIVATE(ni,nj,idx1,s12) + do a = 1, nm1 + ni = n1(a) + do i = 1, ni + + idx1 = sum(n1(:a)) - ni + i + + do b = 1, nm2 + nj = n2(b) + do j = 1, nj + + s12 = scalar(x1(a,i,:,:), x2(b,j,:,:), & + & nneigh1(a,i), nneigh2(b,j), ksi1(a,i,:), ksi2(b,j,:), & + & sinp1(a,i,:,:,:), sinp2(b,j,:,:,:), & + & cosp1(a,i,:,:,:), cosp2(b,j,:,:,:), & + & t_width, d_width, cut_distance, order, & + & pd, ang_norm2, distance_scale, angular_scale, alchemy) + + kernels(:, idx1, b) = kernels(:, idx1, b) & + & + kernel(self_scalar1(a,i), self_scalar2(b,j), s12, & + & kernel_idx, parameters) + + enddo + enddo + + enddo + enddo + !$OMP END PARALLEL DO + + t_end = omp_get_wtime() + if (verbose) write (*,"(A,F12.4,A)") " Time = ", t_end - t_start, " s" + + deallocate(self_scalar1) + deallocate(self_scalar2) + deallocate(ksi1) + deallocate(ksi2) + deallocate(cosp1) + deallocate(cosp2) + deallocate(sinp1) + deallocate(sinp2) + +end subroutine fget_atomic_local_kernels_fchl diff --git a/qml/representations/representations.py b/qml/representations/representations.py index da2556ff5..bd6416857 100644 --- a/qml/representations/representations.py +++ b/qml/representations/representations.py @@ -32,7 +32,7 @@ from .frepresentations import fgenerate_eigenvalue_coulomb_matrix from .frepresentations import fgenerate_bob -from .alchemy import NUCLEAR_CHARGE +from qml.data.alchemy import NUCLEAR_CHARGE from .slatm import get_boa from .slatm import get_sbop diff --git a/setup.py b/setup.py index dd525b605..a1d4e1ff9 100755 --- a/setup.py +++ b/setup.py @@ -56,7 +56,10 @@ ext_ffchl_module = Extension(name = '.fchl.ffchl_module', sources = [ 'qml/fchl/ffchl_module.f90', - 'qml/fchl/ffchl_scalar_kernels.f90' + 'qml/fchl/ffchl_kernels.f90', + 'qml/fchl/ffchl_scalar_kernels.f90', + 'qml/fchl/ffchl_force_kernels.f90', + 'qml/fchl/ffchl_electric_field_kernels.f90', ], extra_f90_compile_args = COMPILER_FLAGS, extra_f77_compile_args = COMPILER_FLAGS, diff --git a/test/data/amons_small.csv b/test/data/amons_small.csv new file mode 100644 index 000000000..1b872ad42 --- /dev/null +++ b/test/data/amons_small.csv @@ -0,0 +1,20 @@ +0;['C', 'H', 'H', 'H', 'H'];[[-1.142261, 3.74778, 0.000218], [-0.091473, 3.769291, 0.012461], [-1.471253, 4.220009, 0.918147], [-1.536477, 2.779198, -0.065153], [-1.51182, 4.243516, -0.867945]] ;[[12.31639, -16.5458, -8.42406], [-25.05334, 5.04539, 3.60296], [5.95414, -2.29557, 2.74966], [3.71385, 30.71658, -5.62516], [3.06897, -16.9206, 7.6966]] ;5;[6, 1, 1, 1, 1];-17.41095 +10;['O', 'H', 'H'];[[0.0, 0.0, 0.0], [1.01036, 0.0, 0.0], [-0.09966, 0.94856, 0.0]];[[-38.03661, 11.73501, 0.0], [ 19.75309, 14.57073, 0.0], [ 18.28352, -26.30575, 0.0]];3;[8, 1, 1];-54.91445 +11;['O', 'H', 'H'];[[0.0, 0.0, 0.0], [0.9669, 0.0, 0.0], [-0.17404, 0.97786, 0.0]];[[ 8.73669, -16.35122, 0.0], [-13.01658, 6.16219, 0.0], [ 4.2799, 10.18903, 0.0]];3;[8, 1, 1];-56.17048 +12;['O', 'H', 'H'];[[0.0, 0.0, 0.0], [0.98375, 0.0, 0.0], [-0.12994, 0.95397, 0.0]];[[-14.37658, 4.57996, 0.0], [ -0.0436, 11.81784, 0.0], [ 14.42018, -16.39781, 0.0]];3;[8, 1, 1];-55.79254 +13;['O', 'H', 'H'];[[0.0, 0.0, 0.0], [0.99677, 0.0, 0.0], [-0.14281, 0.95686, 0.0]];[[-22.38253, 2.99758, 0.0], [ 10.89681, 9.26853, 0.0], [ 11.48572, -12.26612, 0.0]];3;[8, 1, 1];-55.92965 +14;['O', 'H', 'H'];[[0.0, 0.0, 0.0], [0.98478, 0.0, 0.0], [-0.24464, 0.94805, 0.0]];[[ -1.79987, 3.7986, 0.0], [ 5.2571, -3.42156, 0.0], [ -3.45723, -0.37705, 0.0]];3;[8, 1, 1];-56.40381 +15;['O', 'H', 'H'];[[0.0, 0.0, 0.0], [0.96522, 0.0, 0.0], [ 0.00994, 0.95367, 0.0]];[[-10.40166, 0.3692, 0.0], [-23.07692, 33.42608, 0.0], [ 33.47859, -33.79527, 0.0]];3;[8, 1, 1];-52.17501 +1;['C', 'H', 'H', 'H', 'H'];[[-1.145291, 3.748982, -0.001953], [-0.052407, 3.70869, -0.035593], [-1.507193, 4.245377, 0.953864], [-1.509456, 2.726615, -0.02718], [-1.505883, 4.317023, -0.867742]] ;[[4.03971, -12.99821, -18.36794], [5.35709, -4.62622, -3.98289], [-13.18157, 13.73367, 28.69642], [4.28603, -1.09022, 1.13394], [-0.50127, 4.98099, -7.47953]] ;5;[6, 1, 1, 1, 1];-18.12132 +2;['C', 'H', 'H', 'H', 'H'];[[-1.150367, 3.755189, 0.006624], [-0.038244, 3.665887, -0.084111], [-1.415765, 4.203094, 0.932876], [-1.557013, 2.725295, -0.010064], [-1.503494, 4.32953, -0.917456]] ;[[-17.29101, 15.09716, 45.82458], [19.94683, -4.35662, -4.52482], [5.04596, -13.86967, -8.53807], [-1.50502, -13.79653, 3.50636], [-6.19676, 16.92565, -36.26806]];5;[6, 1, 1, 1, 1];-16.79721 +3;['C', 'H', 'H', 'H', 'H'];[[-1.146577, 3.752861, 0.002066], [-0.043352, 3.655431, -0.069965], [-1.408828, 4.167584, 0.968024], [-1.646346, 2.741265, -0.047364], [-1.461108, 4.387249, -0.875193]];[[-0.79102, 15.76379, 21.77518], [15.95724, 3.41324, -3.01424], [1.30564, -9.10471, 3.25693], [-15.39283, -24.57766, 4.99845], [-1.07902, 14.50535, -27.01631]] ;5;[6, 1, 1, 1, 1];-17.13339 +4;['C', 'H', 'H', 'H', 'H'];[[-1.136855, 3.748848, -0.004344], [-0.046392, 3.684816, 0.013016], [-1.517335, 4.199258, 0.90577], [-1.701714, 2.775324, -0.047668], [-1.409929, 4.339906, -0.819305]] ;[[14.06245, 29.54622, -26.01491], [10.32047, 11.28602, -3.62872], [-7.0523, -6.91168, -4.29632], [-27.45504, -16.02441, 5.86328], [10.12443, -17.89615, 28.07668]];5;[6, 1, 1, 1, 1];-16.44065 +5;['C', 'H', 'H', 'H', 'H'];[[-1.136155, 3.751492, -0.008185], [-0.060515, 3.633781, 0.087948], [-1.596641, 4.235901, 0.863249], [-1.65188, 2.727233, -0.013223], [-1.374666, 4.370913, -0.84043]] ;[[27.13531, 32.14324, -28.86276], [4.71495, 0.09429, -2.23009], [-13.10114, -1.101, -0.88264], [-21.6547, -32.62248, 13.1804], [2.90556, 1.48595, 18.79509]] ;5;[6, 1, 1, 1, 1];-16.35225 +6;['C', 'H', 'H', 'H', 'H'];[[-1.139078, 3.754139, -0.018689], [-0.04984, 3.634609, 0.358476], [-1.776175, 4.205711, 0.780148], [-1.606859, 2.652945, -0.083165], [-1.216035, 4.443051, -0.832872]] ;[[3.85021, 51.9263, -59.37932], [43.44114, -6.89629, 17.98258], [-15.88094, 3.6347, 3.50761], [-28.82146, -60.57108, 17.3304], [-2.58895, 11.90637, 20.55873]] ;5;[6, 1, 1, 1, 1];-9.61101 +7;['C', 'H', 'H', 'H', 'H'];[[-1.138433, 3.749592, 0.007393], [-0.095085, 3.796576, 0.473013], [-2.024611, 4.067766, 0.604023], [-1.347119, 2.700413, -0.261476], [-1.189778, 4.425692, -0.903478]] ;[[1.1065, -9.27126, 16.11296], [43.72398, -5.32003, 7.92354], [-27.64331, 1.38724, -7.18198], [-7.17646, -9.39317, 3.70894], [-10.0107, 22.59722, -20.56346]] ;5;[6, 1, 1, 1, 1];-14.43862 +8;['C', 'H', 'H', 'H', 'H'];[[-1.144944, 3.74557, 0.003876], [-0.080401, 3.977165, 0.316366], [-1.916658, 4.06914, 0.724085], [-1.246703, 2.686999, -0.159416], [-1.33531, 4.305019, -0.927083]] ;[[-13.17685, -22.926, 1.55439], [34.10899, 5.55194, 0.72765], [-17.44518, 2.5906, 1.24556], [-0.84791, 6.54178, 2.31052], [-2.63904, 8.24169, -5.83812]] ;5;[6, 1, 1, 1, 1];-17.55999 +9;['C', 'H', 'H', 'H', 'H'];[[-1.151221, 3.751386, -0.009791], [-0.160999, 3.860515, 0.425698], [-1.892196, 4.121994, 0.701988], [-1.181337, 2.705389, -0.074506], [-1.269812, 4.281183, -0.936524]];[[-10.8834, -16.03448, -27.82381], [-3.39093, -20.25127, 2.87439], [-2.3823, 3.18732, 4.42661], [19.86561, 29.47805, 15.63681], [-3.20898, 3.62037, 4.886]] ;5;[6, 1, 1, 1, 1];-16.25566 +16;['O', 'H', 'H'];[[0.0, 0.0, 0.0], [0.98561, 0.0, 0.0], [-0.19585, 0.99218, 0.0]];[[ -0.58371, -25.86982, 0.0], [ 3.01227, 2.24872, 0.0], [ -2.42856, 23.6211, 0.0]];3;[8, 1, 1];-56.0399 +17;['O', 'H', 'H'];[[0.0, 0.0, 0.0], [0.97863, 0.0, 0.0], [-0.16562, 0.94643, 0.0]];[[ -7.97602, 9.14113, 0.0], [ -2.52552, 7.36301, 0.0], [ 10.50154, -16.50414, 0.0]];3;[8, 1, 1];-56.10667 +18;['O', 'H', 'H'];[[0.0, 0.0, 0.0], [0.98389, 0.0, 0.0], [-0.19203, 0.97981, 0.0]];[[ -2.04495, -17.03578, 0.0], [ 1.80384, 2.98355, 0.0], [ 0.24111, 14.05223, 0.0]];3;[8, 1, 1];-56.28162 +19;['O', 'H', 'H'];[[0.0, 0.0, 0.0], [0.98668, 0.0, 0.0], [-0.23103, 0.91126, 0.0]];[[-14.55418, 34.10783, 0.0], [ 7.73518, -1.36791, 0.0], [ 6.819, -32.73992, 0.0]];3;[8, 1, 1];-55.77099 diff --git a/test/data/fchl_ef_rep.txt b/test/data/fchl_ef_rep.txt new file mode 100644 index 000000000..fd5473f75 --- /dev/null +++ b/test/data/fchl_ef_rep.txt @@ -0,0 +1,54 @@ +0.000000000000000000e+00 +9.570167187672324882e-01 +9.578110460837251372e-01 +8.000000000000000000e+00 +1.000000000000000000e+00 +1.000000000000000000e+00 +0.000000000000000000e+00 +-5.859999999999999654e-01 +8.549999999999999822e-01 +0.000000000000000000e+00 +5.110000000000000098e-01 +4.189999999999999281e-01 +0.000000000000000000e+00 +-5.580000000000000515e-01 +-1.040000000000000924e-01 +-4.104664900000000172e-01 +2.052332400000000112e-01 +2.052332400000000112e-01 +0.000000000000000000e+00 +9.570167187672324882e-01 +1.513625118713348705e+00 +1.000000000000000000e+00 +8.000000000000000000e+00 +1.000000000000000000e+00 +0.000000000000000000e+00 +5.859999999999999654e-01 +1.440999999999999837e+00 +0.000000000000000000e+00 +-5.110000000000000098e-01 +-9.200000000000008171e-02 +0.000000000000000000e+00 +5.580000000000000515e-01 +4.539999999999999591e-01 +2.052332400000000112e-01 +-4.104664900000000172e-01 +2.052332400000000112e-01 +0.000000000000000000e+00 +9.578110460837251372e-01 +1.513625118713348705e+00 +1.000000000000000000e+00 +8.000000000000000000e+00 +1.000000000000000000e+00 +0.000000000000000000e+00 +-8.549999999999999822e-01 +-1.440999999999999837e+00 +0.000000000000000000e+00 +-4.189999999999999281e-01 +9.200000000000008171e-02 +0.000000000000000000e+00 +1.040000000000000924e-01 +-4.539999999999999591e-01 +2.052332400000000112e-01 +-4.104664900000000172e-01 +2.052332400000000112e-01 diff --git a/test/test_fchl_electric_field.py b/test/test_fchl_electric_field.py new file mode 100644 index 000000000..1d896aa4e --- /dev/null +++ b/test/test_fchl_electric_field.py @@ -0,0 +1,473 @@ +from __future__ import print_function + +import os + +import csv +import ast + +import numpy as np +from copy import deepcopy + +import scipy +from scipy.linalg import lstsq + +import qml + +import qml.fchl +from qml.fchl import generate_representation +from qml.fchl import generate_representation_electric_field +from qml.fchl import generate_displaced_representations + +from qml.fchl import get_atomic_local_gradient_kernels +from qml.fchl import get_atomic_local_kernels +from qml.fchl import get_atomic_local_electric_field_gradient_kernels +from qml.fchl import get_gaussian_process_electric_field_kernels + +from qml.math import cho_solve + +DEBYE_TO_EAA = 0.20819434 +DEBYE_TO_AU = 0.393456 +DEBYE_TO_EAA = 0.20819434 +HARTREE_TO_KCAL_MOL = 627.509474 +KCAL_MOL_TO_EV = 1.0 / 23.06035 + +HARTREE_TO_EV = HARTREE_TO_KCAL_MOL * KCAL_MOL_TO_EV +BOHR_TO_ANGS = 0.529177249 + +CUT_DISTANCE = 1e6 +MAX_SIZE = 5 + +REP_ARGS = { + "cut_distance": CUT_DISTANCE, + "max_size": 5 + } + +DX = 0.01 +DF = 0.01 +EF_SCALING = 0.01 + +SIGMAS = [0.64] + +KERNEL_ARGS = { + "kernel": "gaussian", + "kernel_args": { + "sigma": SIGMAS, + }, + "cut_distance": CUT_DISTANCE, + "alchemy": "off", + } + +def parse_energy(filename): + + f = open(filename) + lines = f.readlines() + f.close() + + energy = dict() + + for line in lines: + + tokens = line.split() + e = (float(tokens[1]))# - -99.624524268 - -0.499821176) + angle = ang2ang(float(tokens[0])) + + energy[angle] = e + + e = np.array([energy[key] for key in sorted(energy.keys())]) + offset = 0.0#np.amin(e) + + for key in sorted(energy.keys()): + energy[key] = (energy[key] - offset)# * HARTREE_TO_KCAL_MOL * KCAL_MOL_TO_EV + + return energy + + +def ang2ang(angle): + + out = angle - 90.0 + + if out < -180.0: + out += 360 + + return out + +def parse_dipole(filename): + + f = open(filename) + lines = f.readlines() + f.close() + + dipole = dict() + + for line in lines: + + tokens = line.split() + + mu = np.array([float(tokens[-3]), float(tokens[-2]),float(tokens[-1])]) + angle = ang2ang(float(tokens[0])) + + dipole[angle] = mu# * DEBYE_TO_EAA + + return dipole + + + +def parse_csv(filename): + + X = [] + X_gradient = [] + X_dipole = [] + + E = [] + G = [] + D = [] + + with open(filename, 'r') as csvfile: + + csvlines = csv.reader(csvfile, delimiter=";") + + for i, row in enumerate(csvlines): + + nuclear_charges = np.array(ast.literal_eval(row[6]), dtype=np.int32) + + # Gradients (from force in hartree/borh to gradients in eV/angstrom) + gradient = np.array(ast.literal_eval(row[5])) * HARTREE_TO_EV/BOHR_TO_ANGS * -1 + + # SCF energy (eV) + energy = float(row[4]) * HARTREE_TO_EV + + # Dipole moment (Debye -> eV/Angs) + dipole = np.array(ast.literal_eval(row[3])) * DEBYE_TO_EAA + + # Coordinates (Angstrom) + coords = np.array(ast.literal_eval(row[2])) + + rep = generate_representation( coords, nuclear_charges, **REP_ARGS) + rep_gradient = generate_displaced_representations(coords, nuclear_charges, dx=DX, **REP_ARGS) + rep_dipole = generate_representation_electric_field( coords, nuclear_charges, + fictitious_charges="Gasteiger", **REP_ARGS) + + E.append(energy) + D.append(dipole) + G.append(gradient) + + X.append(rep) + X_gradient.append(rep_gradient) + X_dipole.append(rep_dipole) + + X = np.array(X) + X_gradient = np.array(X_gradient) + X_dipole = np.array(X_dipole) + + E = np.array(E) + G = np.array(G) + D = np.array(D) + + return X, X_gradient, X_dipole, E, G, D + + +def test_multiple_operators(): + + + try: + import pybel + import openbabel + except ImportError: + return + + + test_dir = os.path.dirname(os.path.realpath(__file__)) + + X, X_gradient, X_dipole, E, G, D = parse_csv(test_dir + "/data/dichloromethane_mp2_test.csv") + + K = get_atomic_local_kernels(X, X, **KERNEL_ARGS)[0] + K_gradient = get_atomic_local_gradient_kernels(X, X_gradient, dx=DX, **KERNEL_ARGS)[0] + K_dipole = get_atomic_local_electric_field_gradient_kernels(X, X_dipole, df=DF, ef_scaling=EF_SCALING, **KERNEL_ARGS)[0] + + Xs, Xs_gradient, Xs_dipole, Es, Gs, Ds = parse_csv(test_dir + "/data/dichloromethane_mp2_train.csv") + + Ks = get_atomic_local_kernels(X, Xs, **KERNEL_ARGS)[0] + Ks_gradient = get_atomic_local_gradient_kernels(X, Xs_gradient, dx=DX, **KERNEL_ARGS)[0] + Ks_dipole = get_atomic_local_electric_field_gradient_kernels(X, Xs_dipole, df=DF, ef_scaling=EF_SCALING, **KERNEL_ARGS)[0] + + offset = E.mean() + E -= offset + Es -= offset + + Y = np.concatenate((E, G.flatten(), D.flatten())) + C = np.concatenate((K.T , K_gradient.T, K_dipole.T)) + + + alpha, residuals, rank, sing = lstsq(C, Y, cond=-1, lapack_driver="gelsd") + + Et = np.dot(K.T, alpha) + Gt = np.dot(K_gradient.T, alpha) + Dt = np.dot(K_dipole.T, alpha) + + + mae = np.mean(np.abs(Et - E)) / KCAL_MOL_TO_EV + mae_gradient = np.mean(np.abs(Gt - G.flatten())) / KCAL_MOL_TO_EV * BOHR_TO_ANGS + mae_dipole = np.mean(np.abs(Dt - D.flatten())) / DEBYE_TO_EAA + + assert mae < 0.8, "Error in multiple operator training energy" + assert mae_gradient < 0.1, "Error in multiple operator training energy" + assert mae_dipole < 0.01, "Error in multiple operator training dipole" + + # print(mae) + # print(mae_gradient) + # print(mae_dipole) + + Ess = np.dot(Ks.T, alpha) + Gss = np.dot(Ks_gradient.T, alpha) + Dss = np.dot(Ks_dipole.T, alpha) + + mae = np.mean(np.abs(Ess - Es)) / KCAL_MOL_TO_EV + mae_gradient = np.mean(np.abs(Gss - Gs.flatten())) / KCAL_MOL_TO_EV * BOHR_TO_ANGS + mae_dipole = np.mean(np.abs(Dss - Ds.flatten())) / DEBYE_TO_EAA + + assert mae < 0.8, "Error in multiple operator test energy" + assert mae_gradient < 0.1, "Error in multiple operator test force" + assert mae_dipole < 0.02, "Error in multiple operator test dipole" + + # print(mae) + # print(mae_gradient) + # print(mae_dipole) + +def test_generate_representation(): + + + coords = np.array([[1.464, 0.707, 1.056], + [0.878, 1.218, 0.498], + [2.319, 1.126, 0.952]]) + + nuclear_charges = np.array([8, 1, 1], dtype=np.int32) + + rep_ref = np.loadtxt("data/fchl_ef_rep.txt").reshape((3, 6, 3)) + + # Test with fictitious charges from a numpy array + fic_charges1 = np.array([-0.41046649, 0.20523324, 0.20523324]) + + rep1 = generate_representation_electric_field( coords, nuclear_charges, + fictitious_charges=fic_charges1, max_size=3) + + assert np.allclose(rep1, rep_ref), "Error generating representation for electric fields" + + + # Test with fictitious charges from a list + fic_charges2 = [-0.41046649, 0.20523324, 0.20523324] + + rep2 = generate_representation_electric_field( coords, nuclear_charges, + fictitious_charges=fic_charges2, max_size=3) + + assert np.allclose(rep2, rep_ref), "Error generating representation for electric fields" + + + # Test with fictitious charges from Open Babel (Gasteiger). + # Skip test if there is no pybel/openbabel + try: + import pybel + import openbabel + except ImportError: + return + + rep3 = generate_representation_electric_field( coords, nuclear_charges, + fictitious_charges="Gasteiger", max_size=3) + + assert np.allclose(rep3, rep_ref), "Error assigning partial charges: Check Openbabel/Pybel installation" + + +def test_gaussian_process(): + + + try: + import pybel + import openbabel + except ImportError: + return + + + test_dir = os.path.dirname(os.path.realpath(__file__)) + + X, X_gradient, X_dipole, E, G, D = parse_csv(test_dir + "/data/dichloromethane_mp2_test.csv") + + K = get_gaussian_process_electric_field_kernels(X_dipole, X_dipole, **KERNEL_ARGS)[0] + + Xs, Xs_gradient, Xs_dipole, Es, Gs, Ds = parse_csv(test_dir + "/data/dichloromethane_mp2_train.csv") + + Ks = get_gaussian_process_electric_field_kernels(X_dipole, Xs_dipole, **KERNEL_ARGS)[0] + + offset = E.mean() + E -= offset + Es -= offset + + Y = np.concatenate((E, D.flatten())) + Ys = np.concatenate((Es, Ds.flatten())) + + C = deepcopy(K); + C[np.diag_indices_from(C)] += 1e-9 + alpha = cho_solve(C, Y) + + Yt = np.dot(K.T, alpha) + Yss = np.dot(Ks.T, alpha) + + n = len(E) + + mae = np.mean(np.abs(Yt[:n] - E)) / KCAL_MOL_TO_EV + mae_dipole = np.mean(np.abs(Yt[n:] - D.flatten())) / DEBYE_TO_EAA + + print(mae) + print(mae_dipole) + + assert mae < 0.002, "Error in multiple operator training energy" + assert mae_dipole < 0.001, "Error in multiple operator training dipole" + + mae = np.mean(np.abs(Yss[:n] - Es)) / KCAL_MOL_TO_EV + mae_dipole = np.mean(np.abs(Yss[n:] - Ds.flatten())) / DEBYE_TO_EAA + + print(mae) + print(mae_dipole) + + assert mae < 0.3, "Error in multiple operator test energy" + assert mae_dipole < 0.02, "Error in multiple operator test dipole" + +def test_gaussian_process_field_dependent(): + + + try: + import pybel + import openbabel + except ImportError: + return + + + test_dir = os.path.dirname(os.path.realpath(__file__)) + + dipole = parse_dipole(test_dir + "/data/hf_dipole.txt") + energy = parse_energy(test_dir + "/data/hf_energy.txt") + + # Get energies, dipole moments and angles from datafiles + e = np.array([energy[key] for key in sorted(energy.keys())]) + mu = np.array([dipole[key] for key in sorted(dipole.keys())]) + a = np.array([key for key in sorted(dipole.keys())]) + + # Generate dummy coordinates + coordinates = np.array([[0.0,0.0,0.0],[1.0,0.0,0.0]]) + nuclear_charges = np.array([9,1],dtype=np.int32) + + # Which angle to put in the training set + train_angles = np.array([135]) + + reps = [] + fields = [] + eY = [] + dY = [] + + # Make training set + for ang in train_angles: + + ang_rad = ang/180.0*np.pi + + field = np.array([np.cos(ang_rad), np.sin(ang_rad), 0.0]) * 0.001 + rep = generate_representation_electric_field(coordinates, nuclear_charges, max_size=2, + neighbors=2, cut_distance=1e6) + fields.append(field) + reps.append(rep) + + eY.append(energy[ang]) + + dY.append(dipole[ang]) + + F = [fields,fields] + X = np.array(reps) + eY = np.array(eY) + dY = np.concatenate(dY) + + eYs = [] + dYs = [] + + reps = [] + fields_test = [] + + # Make test set + test_angles = range(-180,180,20) + for ang in test_angles: + + ang_rad = ang/180.0*np.pi + + field = np.array([np.cos(ang_rad), np.sin(ang_rad), 0.0]) * 0.001 + + rep = generate_representation_electric_field(coordinates, nuclear_charges, max_size=2, + neighbors=2, cut_distance=1e6) + + fields_test.append(field) + reps.append(rep) + + eYs.append(energy[ang]) + dYs.append(dipole[ang]) + + + Fs = [fields,fields_test] + Xs = np.array(reps) + eYs = np.array(eYs) + dYs = np.concatenate(dYs) + + Y = np.concatenate((eY, dY)) + + sigmas = [2.5] + kernel_args = { + "kernel": "gaussian", + "kernel_args": { + "sigma": sigmas, + }, + "cut_distance": 1e6, + "alchemy": "off", + } + + # Get Gaussian Process kernels for training and test + K = get_gaussian_process_electric_field_kernels(X, X, fields=F, **kernel_args)[0] + Ks = get_gaussian_process_electric_field_kernels(X, Xs, fields=Fs, **kernel_args)[0] + + n_train = len(eY) + n_test = len(eYs) + + + # Solve + C = deepcopy(K) + C[np.diag_indices_from(C)] += 1e-14 + alpha = cho_solve(C,Y) + + Cs = deepcopy(Ks) + + Yt = np.dot(K.T, alpha) + + eYt = Yt[:n_train] + dYt = Yt[n_train:] + + # get predictions + Yss = np.dot(Cs.T, alpha) + + eYss = Yss[:n_test] + dYss = Yss[n_test:] + + dtmae = np.mean(np.abs(dYt - dY)) + etmae = np.mean(np.abs(eYt - eY)) + + dmae = np.mean(np.abs(dYs - dYss)) + emae = np.mean(np.abs(eYs - eYss)) + + # print("%7.2f %10.4f %14.4f %14.4f %14.4f" % (sigmas[0], dmae, emae,dtmae,etmae)) + + assert dmae < 0.005, "Error in test dipole" + assert dtmae < 0.002, "Error in training dipole" + assert emae < 0.0001, "Error in test energy" + assert etmae < 0.0001, "Error in training energy" + + +if __name__ == "__main__": + + test_multiple_operators() + test_generate_representation() + test_gaussian_process() + test_gaussian_process_field_dependent() + test_explicit_electric_field() + diff --git a/test/test_fchl_force.py b/test/test_fchl_force.py new file mode 100644 index 000000000..0fb74b1b2 --- /dev/null +++ b/test/test_fchl_force.py @@ -0,0 +1,408 @@ +from __future__ import print_function + +import ast +import time + +import scipy +import scipy.stats +from scipy.linalg import lstsq + +from copy import deepcopy + +import numpy as np +from numpy.linalg import norm, inv + +import csv + +import qml +from qml.math import cho_solve +from qml.fchl import generate_representation +from qml.fchl import generate_displaced_representations +from qml.fchl import generate_displaced_representations_5point + +from qml.fchl import get_local_kernels +from qml.fchl import get_local_symmetric_kernels +from qml.fchl import get_local_gradient_kernels +from qml.fchl import get_local_hessian_kernels +from qml.fchl import get_local_symmetric_hessian_kernels +from qml.fchl import get_gaussian_process_kernels +from qml.fchl import get_force_alphas +from qml.fchl import get_atomic_local_gradient_kernels +from qml.fchl import get_atomic_local_gradient_5point_kernels +from qml.fchl import get_atomic_local_kernels + +FORCE_KEY = "forces" +ENERGY_KEY = "om2_energy" +CSV_FILE = "data/amons_small.csv" +SIGMAS = [0.64] + +TRAINING = 13 +TEST = 7 + +DX = 0.005 +CUT_DISTANCE = 1e6 +KERNEL_ARGS = { + "verbose": False, + "cut_distance": CUT_DISTANCE, + "kernel": "gaussian", + "kernel_args": { + "sigma": SIGMAS, + }, +} + +LLAMBDA_ENERGY = 1e-7 +LLAMBDA_FORCE = 1e-7 + +def mae(a, b): + + return np.mean(np.abs(a.flatten() - b.flatten())) + + +def csv_to_molecular_reps(csv_filename, force_key="orca_forces", energy_key="orca_energy"): + + np.random.seed(667) + + + x = [] + f = [] + e = [] + distance = [] + + disp_x = [] + disp_x5 = [] + + max_atoms = 5 + + with open(csv_filename, 'r') as csvfile: + + df = csv.reader(csvfile, delimiter=";", quotechar='#') + + for row in df: + + coordinates = np.array(ast.literal_eval(row[2])) + nuclear_charges = ast.literal_eval(row[5]) + atomtypes = ast.literal_eval(row[1]) + force = np.array(ast.literal_eval(row[3])) + energy = float(row[6]) + + rep = generate_representation(coordinates, nuclear_charges, + max_size=max_atoms, cut_distance=CUT_DISTANCE) + + disp_rep = generate_displaced_representations(coordinates, nuclear_charges, + max_size=max_atoms, cut_distance=CUT_DISTANCE, dx=DX) + + disp_rep5 = generate_displaced_representations_5point(coordinates, nuclear_charges, + max_size=max_atoms, cut_distance=CUT_DISTANCE, dx=DX) + + x.append(rep) + f.append(force) + e.append(energy) + + disp_x.append(disp_rep) + disp_x5.append(disp_rep5) + + return np.array(x), f, e, np.array(disp_x), np.array(disp_x5) + + +def test_gaussian_process_derivative(): + + Xall, Fall, Eall, dXall, dXall5 = csv_to_molecular_reps(CSV_FILE, + force_key=FORCE_KEY, energy_key=ENERGY_KEY) + + Eall = np.array(Eall) + Fall = np.array(Fall) + + X = Xall[:TRAINING] + dX = dXall[:TRAINING] + F = Fall[:TRAINING] + E = Eall[:TRAINING] + + Xs = Xall[-TEST:] + dXs = dXall[-TEST:] + Fs = Fall[-TEST:] + Es = Eall[-TEST:] + + K = get_gaussian_process_kernels(X, dX, dx=DX, **KERNEL_ARGS) + Kt = K[:,TRAINING:,TRAINING:] + Kt_local = K[:,:TRAINING,:TRAINING] + Kt_energy = K[:,:TRAINING,TRAINING:] + + Kt_grad2 = get_local_gradient_kernels( X, dX, dx=DX, **KERNEL_ARGS) + + Ks = get_local_hessian_kernels( dX, dXs, dx=DX, **KERNEL_ARGS) + Ks_energy = get_local_gradient_kernels( X, dXs, dx=DX, **KERNEL_ARGS) + + Ks_energy2 = get_local_gradient_kernels( Xs, dX, dx=DX, **KERNEL_ARGS) + Ks_local = get_local_kernels( X, Xs, **KERNEL_ARGS) + + F = np.concatenate(F) + Fs = np.concatenate(Fs) + + Y = np.array(F.flatten()) + Y = np.concatenate((E, Y)) + + for i, sigma in enumerate(SIGMAS): + + C = deepcopy(K[i]) + + for j in range(TRAINING): + C[j,j] += LLAMBDA_ENERGY + + for j in range(TRAINING,K.shape[2]): + C[j,j] += LLAMBDA_FORCE + + alpha = cho_solve(C, Y) + beta = alpha[:TRAINING] + gamma = alpha[TRAINING:] + + Fss = np.dot(np.transpose(Ks[i]), gamma) + np.dot(np.transpose(Ks_energy[i]), beta) + Ft = np.dot(np.transpose(Kt[i]), gamma) + np.dot(np.transpose(Kt_energy[i]), beta) + + Ess = np.dot(Ks_energy2[i], gamma) + np.dot(Ks_local[i].T, beta) + Et = np.dot(Kt_energy [i], gamma) + np.dot(Kt_local[i].T, beta) + + assert mae(Ess, Es) < 0.1, "Error in Gaussian Process test energy" + assert mae(Et, E) < 0.001, "Error in Gaussian Process training energy" + + assert mae(Fss, Fs) < 1.0, "Error in Gaussian Process test force" + assert mae(Ft, F) < 0.001, "Error in Gaussian Process training force" + + +def test_gdml_derivative(): + + Xall, Fall, Eall, dXall, dXall5 = csv_to_molecular_reps(CSV_FILE, + force_key=FORCE_KEY, energy_key=ENERGY_KEY) + + Eall = np.array(Eall) + Fall = np.array(Fall) + + X = Xall[:TRAINING] + dX = dXall[:TRAINING] + F = Fall[:TRAINING] + E = Eall[:TRAINING] + + Xs = Xall[-TEST:] + dXs = dXall[-TEST:] + Fs = Fall[-TEST:] + Es = Eall[-TEST:] + + K = get_local_symmetric_hessian_kernels(dX, dx=DX, **KERNEL_ARGS) + Ks = get_local_hessian_kernels(dXs, dX, dx=DX, **KERNEL_ARGS) + + Kt_energy = get_local_gradient_kernels( X, dX, dx=DX, **KERNEL_ARGS) + Ks_energy = get_local_gradient_kernels( Xs, dX, dx=DX, **KERNEL_ARGS) + + F = np.concatenate(F) + Fs = np.concatenate(Fs) + + Y = np.array(F.flatten()) + # Y = np.concatenate((E, Y)) + + for i, sigma in enumerate(SIGMAS): + + C = deepcopy(K[i]) + for j in range(K.shape[2]): + C[j,j] += LLAMBDA_FORCE + + alpha = cho_solve(C, Y) + Fss = np.dot(Ks[i], alpha) + Ft = np.dot(K[i], alpha) + + Ess = np.dot(Ks_energy[i], alpha) + Et = np.dot(Kt_energy[i], alpha) + + slope, intercept, r_value, p_value, std_err = scipy.stats.linregress(E.flatten(), Et.flatten()) + + Ess -= intercept + Et -= intercept + + # This test will only work for molecules of same type + # assert mae(Ess, Es) < 0.1, "Error in Gaussian Process test energy" + # assert mae(Et, E) < 0.001, "Error in Gaussian Process training energy" + + assert mae(Fss, Fs) < 1.0, "Error in GDML test force" + assert mae(Ft, F) < 0.001, "Error in GDML training force" + + +def test_normal_equation_derivative(): + + Xall, Fall, Eall, dXall, dXall5 = csv_to_molecular_reps(CSV_FILE, + force_key=FORCE_KEY, energy_key=ENERGY_KEY) + + Eall = np.array(Eall) + Fall = np.array(Fall) + + X = Xall[:TRAINING] + dX = dXall[:TRAINING] + dX5 = dXall5[:TRAINING] + F = Fall[:TRAINING] + E = Eall[:TRAINING] + + Xs = Xall[-TEST:] + dXs = dXall[-TEST:] + dXs5 = dXall5[-TEST:] + Fs = Fall[-TEST:] + Es = Eall[-TEST:] + + Ftrain = np.concatenate(F) + Etrain = np.array(E) + alphas = get_force_alphas(X, dX, Ftrain, energy=Etrain, dx=DX, + regularization=LLAMBDA_FORCE, **KERNEL_ARGS) + + Kt_force = get_atomic_local_gradient_kernels(X, dX, dx=DX, **KERNEL_ARGS) + Ks_force = get_atomic_local_gradient_kernels(X, dXs, dx=DX, **KERNEL_ARGS) + + Kt_force5 = get_atomic_local_gradient_5point_kernels(X, dX5, dx=DX, **KERNEL_ARGS) + Ks_force5 = get_atomic_local_gradient_5point_kernels(X, dXs5, dx=DX, **KERNEL_ARGS) + + Kt_energy = get_atomic_local_kernels(X, X, **KERNEL_ARGS) + Ks_energy = get_atomic_local_kernels(X, Xs, **KERNEL_ARGS) + + F = np.concatenate(F) + Fs = np.concatenate(Fs) + Y = np.array(F.flatten()) + + for i, sigma in enumerate(SIGMAS): + + Ft = np.zeros((Kt_force[i,:,:].shape[1]//3,3)) + Fss = np.zeros((Ks_force[i,:,:].shape[1]//3,3)) + + Ft5 = np.zeros((Kt_force5[i,:,:].shape[1]//3,3)) + Fss5 = np.zeros((Ks_force5[i,:,:].shape[1]//3,3)) + + for xyz in range(3): + + Ft[:,xyz] = np.dot(Kt_force[i,:,xyz::3].T, alphas[i]) + Fss[:,xyz] = np.dot(Ks_force[i,:,xyz::3].T, alphas[i]) + + Ft5[:,xyz] = np.dot(Kt_force5[i,:,xyz::3].T, alphas[i]) + Fss5[:,xyz] = np.dot(Ks_force5[i,:,xyz::3].T, alphas[i]) + + Ess = np.dot(Ks_energy[i].T, alphas[i]) + Et = np.dot(Kt_energy[i].T, alphas[i]) + + assert mae(Ess, Es) < 0.3, "Error in normal equation test energy" + assert mae(Et, E) < 0.08, "Error in normal equation training energy" + + assert mae(Fss, Fs) < 3.2, "Error in normal equation test force" + assert mae(Ft, F) < 0.5, "Error in normal equation training force" + + assert mae(Fss5, Fs) < 3.2, "Error in normal equation 5-point test force" + assert mae(Ft5, F) < 0.5, "Error in normal equation 5-point training force" + + assert mae(Fss5, Fss) < 0.01, "Error in normal equation 5-point or 2-point test force" + assert mae(Ft5, Ft) < 0.01, "Error in normal equation 5-point or 2-point training force" + + +def test_operator_derivative(): + + Xall, Fall, Eall, dXall, dXall5 = csv_to_molecular_reps(CSV_FILE, + force_key=FORCE_KEY, energy_key=ENERGY_KEY) + + Eall = np.array(Eall) + Fall = np.array(Fall) + + X = Xall[:TRAINING] + dX = dXall[:TRAINING] + dX5 = dXall5[:TRAINING] + F = Fall[:TRAINING] + E = Eall[:TRAINING] + + Xs = Xall[-TEST:] + dXs = dXall[-TEST:] + dXs5 = dXall5[-TEST:] + Fs = Fall[-TEST:] + Es = Eall[-TEST:] + + Ftrain = np.concatenate(F) + Etrain = np.array(E) + + Kt_energy = get_atomic_local_kernels(X, X, **KERNEL_ARGS) + Ks_energy = get_atomic_local_kernels(X, Xs, **KERNEL_ARGS) + + Kt_force = get_atomic_local_gradient_kernels(X, dX, dx=DX, **KERNEL_ARGS) + Ks_force = get_atomic_local_gradient_kernels(X, dXs, dx=DX, **KERNEL_ARGS) + + F = np.concatenate(F) + Fs = np.concatenate(Fs) + Y = np.array(F.flatten()) + + for i, sigma in enumerate(SIGMAS): + + Y = np.concatenate((E, F.flatten())) + + C = np.concatenate((Kt_energy[i].T, Kt_force[i].T)) + + alphas, residuals, singular_values, rank = lstsq(C, Y, cond=1e-9, lapack_driver="gelsd") + + Ess = np.dot(Ks_energy[i].T, alphas) + Et = np.dot(Kt_energy[i].T, alphas) + + Fss = np.dot(Ks_force[i].T, alphas) + Ft = np.dot(Kt_force[i].T, alphas) + + assert mae(Ess, Es) < 0.08, "Error in operator test energy" + assert mae(Et, E) < 0.04, "Error in operator training energy" + + assert mae(Fss, Fs.flatten()) < 1.1, "Error in operator test force" + assert mae(Ft, F.flatten()) < 0.1, "Error in operator training force" + +def test_krr_derivative(): + + Xall, Fall, Eall, dXall, dXall5 = csv_to_molecular_reps(CSV_FILE, + force_key=FORCE_KEY, energy_key=ENERGY_KEY) + + Eall = np.array(Eall) + Fall = np.array(Fall) + + X = Xall[:TRAINING] + dX = dXall[:TRAINING] + F = Fall[:TRAINING] + E = Eall[:TRAINING] + + Xs = Xall[-TEST:] + dXs = dXall[-TEST:] + Fs = Fall[-TEST:] + Es = Eall[-TEST:] + + K = get_local_symmetric_kernels(X, **KERNEL_ARGS) + Ks = get_local_kernels(Xs, X, **KERNEL_ARGS) + + Kt_force = get_local_gradient_kernels(X, dX, dx=DX, **KERNEL_ARGS) + Ks_force = get_local_gradient_kernels(X, dXs, dx=DX, **KERNEL_ARGS) + + F = np.concatenate(F) + Fs = np.concatenate(Fs) + + Y = np.array(E) + + for i, sigma in enumerate(SIGMAS): + + C = deepcopy(K[i]) + for j in range(K.shape[2]): + C[j,j] += LLAMBDA_ENERGY + + alpha = cho_solve(C, Y) + + Fss = np.dot(Ks_force[i].T, alpha) + Ft = np.dot(Kt_force[i].T, alpha) + + Ess = np.dot(Ks[i], alpha) + Et = np.dot(K[i], alpha) + + slope, intercept, r_value, p_value, std_err = scipy.stats.linregress(E.flatten(), Et.flatten()) + + assert mae(Ess, Es) < 0.7, "Error in KRR test energy" + assert mae(Et, E) < 0.02, "Error in KRR training energy" + + assert mae(Fss, Fs) < 5.6, "Error in KRR test force" + assert mae(Ft, F) < 4.3, "Error in KRR training force" + + +if __name__ == "__main__": + + test_gaussian_process_derivative() + test_gdml_derivative() + test_normal_equation_derivative() + test_operator_derivative() + test_krr_derivative() diff --git a/test/test_fchl.py b/test/test_fchl_scalar.py similarity index 51% rename from test/test_fchl.py rename to test/test_fchl_scalar.py index c9a9cb9cf..7b58d9321 100644 --- a/test/test_fchl.py +++ b/test/test_fchl_scalar.py @@ -26,6 +26,11 @@ import os import numpy as np +import scipy +from scipy.special import jn +from scipy.special import binom +from scipy.misc import factorial + from qml.data import Compound from qml.math import cho_solve @@ -74,6 +79,10 @@ def test_krr_fchl_local(): "alchemy_period_width": 1.0, "alchemy_group_width": 1.0, "fourier_order": 2, + "kernel": "gaussian", + "kernel_args": { + "sigma": [2.5], + }, } test_dir = os.path.dirname(os.path.realpath(__file__)) @@ -116,11 +125,10 @@ def test_krr_fchl_local(): Ys = np.array([mol.properties for mol in test]) # Set hyper-parameters - sigma = 2.5 llambda = 1e-8 - K_symmetric = get_local_symmetric_kernels(X, [sigma], **kernel_args)[0] - K = get_local_kernels(X, X, [sigma], **kernel_args)[0] + K_symmetric = get_local_symmetric_kernels(X, **kernel_args)[0] + K = get_local_kernels(X, X, **kernel_args)[0] assert np.allclose(K, K_symmetric), "Error in FCHL symmetric local kernels" assert np.invert(np.all(np.isnan(K_symmetric))), "FCHL local symmetric kernel contains NaN" @@ -131,7 +139,7 @@ def test_krr_fchl_local(): alpha = cho_solve(K,Y) # Calculate prediction kernel - Ks = get_local_kernels(Xs, X , [sigma], **kernel_args)[0] + Ks = get_local_kernels(Xs, X, **kernel_args)[0] assert np.invert(np.all(np.isnan(Ks))), "FCHL local testkernel contains NaN" Yss = np.dot(Ks, alpha) @@ -142,6 +150,13 @@ def test_krr_fchl_local(): def test_krr_fchl_global(): + # Test that all kernel arguments work + kernel_args = { + "kernel": "gaussian", + "kernel_args": { + "sigma": [100.0], + }, + } test_dir = os.path.dirname(os.path.realpath(__file__)) # Parse file containing PBE0/def2-TZVP heats of formation and xyz filenames @@ -183,11 +198,11 @@ def test_krr_fchl_global(): Ys = np.array([mol.properties for mol in test]) # Set hyper-parameters - sigma = 100.0 + # sigma = 100.0 llambda = 1e-8 - K_symmetric = get_global_symmetric_kernels(X, [sigma])[0] - K = get_global_kernels(X, X, [sigma])[0] + K_symmetric = get_global_symmetric_kernels(X, **kernel_args)[0] + K = get_global_kernels(X, X, **kernel_args)[0] assert np.allclose(K, K_symmetric), "Error in FCHL symmetric global kernels" assert np.invert(np.all(np.isnan(K_symmetric))), "FCHL global symmetric kernel contains NaN" @@ -197,18 +212,26 @@ def test_krr_fchl_global(): K[np.diag_indices_from(K)] += llambda alpha = cho_solve(K,Y) - # # Calculate prediction kernel - Ks = get_global_kernels(Xs, X , [sigma])[0] + Ks = get_global_kernels(Xs, X, **kernel_args)[0] assert np.invert(np.all(np.isnan(Ks))), "FCHL global testkernel contains NaN" Yss = np.dot(Ks, alpha) + print(Ys, Yss) + mae = np.mean(np.abs(Ys - Yss)) assert abs(2 - mae) < 1.0, "Error in FCHL global kernel-ridge regression" def test_krr_fchl_atomic(): + kernel_args = { + "kernel": "gaussian", + "kernel_args": { + "sigma": [2.5], + }, + } + test_dir = os.path.dirname(os.path.realpath(__file__)) # Parse file containing PBE0/def2-TZVP heats of formation and xyz filenames @@ -232,10 +255,7 @@ def test_krr_fchl_atomic(): X = np.array([mol.representation for mol in mols]) - # Set hyper-parameters - sigma = 2.5 - - K = get_local_symmetric_kernels(X, [sigma])[0] + K = get_local_symmetric_kernels(X, **kernel_args)[0] K_test = np.zeros((len(mols),len(mols))) @@ -243,19 +263,27 @@ def test_krr_fchl_atomic(): for j, Xj in enumerate(X): - K_atomic = get_atomic_kernels(Xi[:mols[i].natoms], Xj[:mols[j].natoms], [sigma])[0] + K_atomic = get_atomic_kernels(Xi[:mols[i].natoms], Xj[:mols[j].natoms], **kernel_args)[0] K_test[i,j] = np.sum(K_atomic) assert np.invert(np.all(np.isnan(K_atomic))), "FCHL atomic kernel contains NaN" if (i == j): - K_atomic_symmetric = get_atomic_symmetric_kernels(Xi[:mols[i].natoms], [sigma])[0] + K_atomic_symmetric = get_atomic_symmetric_kernels(Xi[:mols[i].natoms], **kernel_args)[0] assert np.allclose(K_atomic, K_atomic_symmetric), "Error in FCHL symmetric atomic kernels" assert np.invert(np.all(np.isnan(K_atomic_symmetric))), "FCHL atomic symmetric kernel contains NaN" assert np.allclose(K, K_test), "Error in FCHL atomic kernels" def test_fchl_local_periodic(): + kernel_args = { + "cut_distance": 7.0, + "cut_start": 0.7, + "kernel": "gaussian", + "kernel_args": { + "sigma": [2.5], + }, + } nuclear_charges = [ np.array([13, 13, 58, 58, 58, 58, 58, 58, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 23, 23]), @@ -390,8 +418,7 @@ def test_fchl_local_periodic(): cell=cells[i], max_size=36, neighbors=200, cut_distance=7.0) for i in range(5)]) - sigmas = [2.5] - K = get_local_symmetric_kernels(X, sigmas, cut_distance=7.0, cut_start=0.7) + K = get_local_symmetric_kernels(X, **kernel_args) K_ref = np.array( [[ 530.03184304, 435.65196293, 198.61245535, 782.49428327, 263.53562172], @@ -433,8 +460,6 @@ def test_krr_fchl_alchemy(): X = np.array([mol.representation for mol in mols]) - sigma = 2.5 - np.set_printoptions(edgeitems = 16, linewidth=6666) overlap = np.array([[ 1. , 0.00835282, 0.90696062, 0.82257756, 0.61368025, 0.37660345, 0.19010927, 0.07894037, 0.02696323, 0.00757568, 0.67663385, 0.61368025, 0.45783336, 0.28096329, 0.14183016, 0.05889311, 0.02011579, 0.0056518 , 0.41523683, 0.37660345], [ 0.00835282, 1. , 0.00757568, 0.02696323, 0.07894037, 0.19010927, 0.37660345, 0.61368025, 0.82257756, 0.90696062, 0.0056518 , 0.02011579, 0.05889311, 0.14183016, 0.28096329, 0.45783336, 0.61368025, 0.67663385, 0.0034684 , 0.01234467], @@ -457,20 +482,644 @@ def test_krr_fchl_alchemy(): [ 0.41523683, 0.0034684 , 0.67663385, 0.61368025, 0.45783336, 0.28096329, 0.14183016, 0.05889311, 0.02011579, 0.0056518 , 0.90696062, 0.82257756, 0.61368025, 0.37660345, 0.19010927, 0.07894037, 0.02696323, 0.00757568, 1. , 0.90696062], [ 0.37660345, 0.01234467, 0.61368025, 0.67663385, 0.61368025, 0.45783336, 0.28096329, 0.14183016, 0.05889311, 0.02011579, 0.82257756, 0.90696062, 0.82257756, 0.61368025, 0.37660345, 0.19010927, 0.07894037, 0.02696323, 0.90696062, 1. ]]) - K_alchemy = get_local_symmetric_kernels(X, [sigma], alchemy="periodic-table")[0] - K_custom = get_local_symmetric_kernels(X, [sigma], alchemy=overlap)[0] + kernel_args = { + "alchemy": "periodic-table", + "kernel": "gaussian", + "kernel_args": { + "sigma": [2.5], + }, + } + K_alchemy = get_local_symmetric_kernels(X, **kernel_args)[0] + + kernel_args = { + "alchemy": overlap, + "kernel": "gaussian", + "kernel_args": { + "sigma": [2.5], + }, + } + + K_custom = get_local_symmetric_kernels(X, **kernel_args)[0] assert np.allclose(K_alchemy, K_custom), "Error in alchemy" nooverlap = np.eye(100) - K_noalchemy = get_local_symmetric_kernels(X, [sigma], alchemy="off")[0] - K_custom = get_local_symmetric_kernels(X, [sigma], alchemy=nooverlap)[0] + + kernel_args = { + "alchemy": "off", + "kernel": "gaussian", + "kernel_args": { + "sigma": [2.5], + }, + } + + K_noalchemy = get_local_symmetric_kernels(X, **kernel_args)[0] + + kernel_args = { + "alchemy": nooverlap, + "kernel": "gaussian", + "kernel_args": { + "sigma": [2.5], + }, + } + K_custom = get_local_symmetric_kernels(X, **kernel_args)[0] assert np.allclose(K_noalchemy, K_custom), "Error in no-alchemy" + +def test_fchl_linear(): + + test_dir = os.path.dirname(os.path.realpath(__file__)) + + # Parse file containing PBE0/def2-TZVP heats of formation and xyz filenames + data = get_energies(test_dir + "/data/hof_qm7.txt") + + # Generate a list of qml.Compound() objects" + mols = [] + + for xyz_file in sorted(data.keys())[:5]: + + # Initialize the qml.Compound() objects + mol = Compound(xyz=test_dir + "/qm7/" + xyz_file) + + # This is a Molecular Coulomb matrix sorted by row norm + mol.representation = generate_representation(mol.coordinates, \ + mol.nuclear_charges, cut_distance=1e6) + mols.append(mol) + + X = np.array([mol.representation for mol in mols]) + + K = get_local_symmetric_kernels(X)[0] + + K_test = np.zeros((len(mols),len(mols))) + + kernel_args = { + "kernel": "linear", + "kernel_args": {"c": [1.0],}, + } + + for i, Xi in enumerate(X): + Sii = get_atomic_kernels(Xi[:mols[i].natoms], Xi[:mols[i].natoms], **kernel_args)[0] + for j, Xj in enumerate(X): + + + Sjj = get_atomic_kernels(Xj[:mols[j].natoms], Xj[:mols[j].natoms], **kernel_args)[0] + + Sij = get_atomic_kernels(Xi[:mols[i].natoms], Xj[:mols[j].natoms], **kernel_args)[0] + + for ii in range(Sii.shape[0]): + for jj in range(Sjj.shape[0]): + + l2 = Sii[ii,ii] + Sjj[jj,jj] - 2 * Sij[ii,jj] + K_test[i,j] += np.exp(- l2 / (2*(2.5**2))) + + assert np.allclose(K, K_test), "Error in FCHL linear kernels" + + +def test_fchl_polynomial(): + + test_dir = os.path.dirname(os.path.realpath(__file__)) + + # Parse file containing PBE0/def2-TZVP heats of formation and xyz filenames + data = get_energies(test_dir + "/data/hof_qm7.txt") + + # Generate a list of qml.Compound() objects" + mols = [] + + for xyz_file in sorted(data.keys())[:5]: + + # Initialize the qml.Compound() objects + mol = Compound(xyz=test_dir + "/qm7/" + xyz_file) + + # This is a Molecular Coulomb matrix sorted by row norm + mol.representation = generate_representation(mol.coordinates, \ + mol.nuclear_charges, cut_distance=1e6) + mols.append(mol) + + X = np.array([mol.representation for mol in mols]) + + polynomial_kernel_args = { + "kernel": "polynomial", + "kernel_args": { + "alpha": [2.0], + "c": [3.0], + "d": [4.0], + }, + } + + linear_kernel_args = { + "kernel": "linear", + "kernel_args": { + "c": [0.0], + }, + } + + + K = get_local_symmetric_kernels(X, **polynomial_kernel_args)[0] + + K_test = np.zeros((len(mols),len(mols))) + + for i, Xi in enumerate(X): + for j, Xj in enumerate(X): + + Sij = get_atomic_kernels(Xi[:mols[i].natoms], Xj[:mols[j].natoms], **linear_kernel_args)[0] + + for ii in range(Sij.shape[0]): + for jj in range(Sij.shape[1]): + + K_test[i,j] += (2.0 * Sij[ii,jj] + 3.0)**4.0 + + assert np.allclose(K, K_test), "Error in FCHL polynomial kernels" + + +def test_fchl_sigmoid(): + + test_dir = os.path.dirname(os.path.realpath(__file__)) + + # Parse file containing PBE0/def2-TZVP heats of formation and xyz filenames + data = get_energies(test_dir + "/data/hof_qm7.txt") + + # Generate a list of qml.Compound() objects" + mols = [] + + for xyz_file in sorted(data.keys())[:5]: + + # Initialize the qml.Compound() objects + mol = Compound(xyz=test_dir + "/qm7/" + xyz_file) + + # This is a Molecular Coulomb matrix sorted by row norm + mol.representation = generate_representation(mol.coordinates, \ + mol.nuclear_charges, cut_distance=1e6) + mols.append(mol) + + X = np.array([mol.representation for mol in mols]) + + sigmoid_kernel_args = { + "kernel": "sigmoid", + "kernel_args": { + "alpha": [2.0], + "c": [3.0], + }, + } + + linear_kernel_args = { + "kernel": "linear", + "kernel_args": { + "c": [0.0], + }, + } + + + K = get_local_symmetric_kernels(X, **sigmoid_kernel_args)[0] + + K_test = np.zeros((len(mols),len(mols))) + + for i, Xi in enumerate(X): + for j, Xj in enumerate(X): + + Sij = get_atomic_kernels(Xi[:mols[i].natoms], Xj[:mols[j].natoms], **linear_kernel_args)[0] + + for ii in range(Sij.shape[0]): + for jj in range(Sij.shape[1]): + + # K_test[i,j] += (2.0 * Sij[ii,jj] + 3.0)**4.0 + K_test[i,j] += np.tanh(2.0 * Sij[ii,jj] + 3.0) + + assert np.allclose(K, K_test), "Error in FCHL sigmoid kernels" + + +def test_fchl_multiquadratic(): + + test_dir = os.path.dirname(os.path.realpath(__file__)) + + # Parse file containing PBE0/def2-TZVP heats of formation and xyz filenames + data = get_energies(test_dir + "/data/hof_qm7.txt") + + # Generate a list of qml.Compound() objects" + mols = [] + + for xyz_file in sorted(data.keys())[:5]: + + # Initialize the qml.Compound() objects + mol = Compound(xyz=test_dir + "/qm7/" + xyz_file) + + # This is a Molecular Coulomb matrix sorted by row norm + mol.representation = generate_representation(mol.coordinates, \ + mol.nuclear_charges, cut_distance=1e6) + mols.append(mol) + + X = np.array([mol.representation for mol in mols]) + + kernel_args = { + "kernel": "multiquadratic", + "kernel_args": { + "c": [2.0], + }, + } + + linear_kernel_args = { + "kernel": "linear", + "kernel_args": { + "c": [0.0], + }, + } + + + K = get_local_symmetric_kernels(X, **kernel_args)[0] + + K_test = np.zeros((len(mols),len(mols))) + + for i, Xi in enumerate(X): + Sii = get_atomic_kernels(Xi[:mols[i].natoms], Xi[:mols[i].natoms], **linear_kernel_args)[0] + for j, Xj in enumerate(X): + + Sjj = get_atomic_kernels(Xj[:mols[j].natoms], Xj[:mols[j].natoms], **linear_kernel_args)[0] + Sij = get_atomic_kernels(Xi[:mols[i].natoms], Xj[:mols[j].natoms], **linear_kernel_args)[0] + + for ii in range(Sii.shape[0]): + for jj in range(Sjj.shape[0]): + + l2 = Sii[ii,ii] + Sjj[jj,jj] - 2 * Sij[ii,jj] + K_test[i,j] += np.sqrt(l2 + 4.0) + + assert np.allclose(K, K_test), "Error in FCHL multiquadratic kernels" + + +def test_fchl_inverse_multiquadratic(): + + test_dir = os.path.dirname(os.path.realpath(__file__)) + + # Parse file containing PBE0/def2-TZVP heats of formation and xyz filenames + data = get_energies(test_dir + "/data/hof_qm7.txt") + + # Generate a list of qml.Compound() objects" + mols = [] + + for xyz_file in sorted(data.keys())[:5]: + + # Initialize the qml.Compound() objects + mol = Compound(xyz=test_dir + "/qm7/" + xyz_file) + + # This is a Molecular Coulomb matrix sorted by row norm + mol.representation = generate_representation(mol.coordinates, \ + mol.nuclear_charges, cut_distance=1e6) + mols.append(mol) + + X = np.array([mol.representation for mol in mols]) + + kernel_args = { + "kernel": "inverse-multiquadratic", + "kernel_args": { + "c": [2.0], + }, + } + + linear_kernel_args = { + "kernel": "linear", + "kernel_args": { + "c": [0.0], + }, + } + + + K = get_local_symmetric_kernels(X, **kernel_args)[0] + + K_test = np.zeros((len(mols),len(mols))) + + for i, Xi in enumerate(X): + Sii = get_atomic_kernels(Xi[:mols[i].natoms], Xi[:mols[i].natoms], **linear_kernel_args)[0] + for j, Xj in enumerate(X): + + Sjj = get_atomic_kernels(Xj[:mols[j].natoms], Xj[:mols[j].natoms], **linear_kernel_args)[0] + Sij = get_atomic_kernels(Xi[:mols[i].natoms], Xj[:mols[j].natoms], **linear_kernel_args)[0] + + for ii in range(Sii.shape[0]): + for jj in range(Sjj.shape[0]): + + l2 = Sii[ii,ii] + Sjj[jj,jj] - 2 * Sij[ii,jj] + K_test[i,j] += 1.0 / np.sqrt(l2 + 4.0) + assert np.allclose(K, K_test), "Error in FCHL inverse multiquadratic kernels" + + +def test_fchl_bessel(): + + test_dir = os.path.dirname(os.path.realpath(__file__)) + + # Parse file containing PBE0/def2-TZVP heats of formation and xyz filenames + data = get_energies(test_dir + "/data/hof_qm7.txt") + + # Generate a list of qml.Compound() objects" + mols = [] + + for xyz_file in sorted(data.keys())[:5]: + + # Initialize the qml.Compound() objects + mol = Compound(xyz=test_dir + "/qm7/" + xyz_file) + + # This is a Molecular Coulomb matrix sorted by row norm + mol.representation = generate_representation(mol.coordinates, \ + mol.nuclear_charges, cut_distance=1e6) + mols.append(mol) + + X = np.array([mol.representation for mol in mols]) + + kernel_args = { + "kernel": "bessel", + "kernel_args": { + "sigma": [2.0], + "v": [3.0], + "n": [2.0], + }, + } + + linear_kernel_args = { + "kernel": "linear", + "kernel_args": { + "c": [0.0], + }, + } + + + K = get_local_symmetric_kernels(X, **kernel_args)[0] + + K_test = np.zeros((len(mols),len(mols))) + + sigma = 2.0 + v = 3 + n = 2 + + + for i, Xi in enumerate(X): + Sii = get_atomic_kernels(Xi[:mols[i].natoms], Xi[:mols[i].natoms], **linear_kernel_args)[0] + for j, Xj in enumerate(X): + + Sjj = get_atomic_kernels(Xj[:mols[j].natoms], Xj[:mols[j].natoms], **linear_kernel_args)[0] + Sij = get_atomic_kernels(Xi[:mols[i].natoms], Xj[:mols[j].natoms], **linear_kernel_args)[0] + + for ii in range(Sii.shape[0]): + for jj in range(Sjj.shape[0]): + + l2 = np.sqrt(Sii[ii,ii] + Sjj[jj,jj] - 2 * Sij[ii,jj]) + + K_test[i,j] += jn(v, sigma * Sij[ii,jj])/ Sij[ii,jj]**(-n*(v+1)) + + assert np.allclose(K, K_test), "Error in FCHL inverse bessel kernels" + + +def test_fchl_l2(): + + test_dir = os.path.dirname(os.path.realpath(__file__)) + + # Parse file containing PBE0/def2-TZVP heats of formation and xyz filenames + data = get_energies(test_dir + "/data/hof_qm7.txt") + + # Generate a list of qml.Compound() objects" + mols = [] + + for xyz_file in sorted(data.keys())[:5]: + + # Initialize the qml.Compound() objects + mol = Compound(xyz=test_dir + "/qm7/" + xyz_file) + + # This is a Molecular Coulomb matrix sorted by row norm + mol.representation = generate_representation(mol.coordinates, \ + mol.nuclear_charges, cut_distance=1e6) + mols.append(mol) + + X = np.array([mol.representation for mol in mols]) + + l2_kernel_args = { + "kernel": "l2", + "kernel_args": { + "alpha": [1.0], + "c": [0.0], + }, + } + + + K = get_local_symmetric_kernels(X)[0] + + K_test = np.zeros((len(mols),len(mols))) + + sigma = 2.0 + v = 3 + n = 2 + + inv_sigma = -1.0/ (2.0*2.5**2) + + for i, Xi in enumerate(X): + for j, Xj in enumerate(X): + + Sij = get_atomic_kernels(Xi[:mols[i].natoms], Xj[:mols[j].natoms], + **l2_kernel_args)[0] + + for ii in range(Sij.shape[0]): + for jj in range(Sij.shape[1]): + + + K_test[i,j] += np.exp(Sij[ii,jj] * inv_sigma) + + assert np.allclose(K, K_test), "Error in FCHL l2 kernels" + + +def test_fchl_matern(): + + test_dir = os.path.dirname(os.path.realpath(__file__)) + + # Parse file containing PBE0/def2-TZVP heats of formation and xyz filenames + data = get_energies(test_dir + "/data/hof_qm7.txt") + + # Generate a list of qml.Compound() objects" + mols = [] + + for xyz_file in sorted(data.keys())[:5]: + + # Initialize the qml.Compound() objects + mol = Compound(xyz=test_dir + "/qm7/" + xyz_file) + + # This is a Molecular Coulomb matrix sorted by row norm + mol.representation = generate_representation(mol.coordinates, \ + mol.nuclear_charges, cut_distance=1e6) + mols.append(mol) + + X = np.array([mol.representation for mol in mols]) + + kernel_args = { + "kernel": "matern", + "kernel_args": { + "sigma": [5.0], + "n": [2.0], + }, + } + + linear_kernel_args = { + "kernel": "linear", + "kernel_args": { + "c": [0.0], + }, + } + + + K = get_local_symmetric_kernels(X, **kernel_args)[0] + + K_test = np.zeros((len(mols),len(mols))) + + sigma = 5.0 + n = 2 + v = n + 0.5 + + + for i, Xi in enumerate(X): + Sii = get_atomic_kernels(Xi[:mols[i].natoms], Xi[:mols[i].natoms], **linear_kernel_args)[0] + for j, Xj in enumerate(X): + + Sjj = get_atomic_kernels(Xj[:mols[j].natoms], Xj[:mols[j].natoms], **linear_kernel_args)[0] + Sij = get_atomic_kernels(Xi[:mols[i].natoms], Xj[:mols[j].natoms], **linear_kernel_args)[0] + + for ii in range(Sii.shape[0]): + for jj in range(Sjj.shape[0]): + + l2 = np.sqrt(Sii[ii,ii] + Sjj[jj,jj] - 2 * Sij[ii,jj]) + + rho = (2*np.sqrt(2*v)*l2/sigma) + + for k in range(0, n+1): + fact = float(factorial(n+k)) / factorial(2*n) * binom(n,k) + K_test[i,j] += np.exp(-0.5 * rho) * fact * rho**(n-k) + + + assert np.allclose(K, K_test), "Error in FCHL matern kernels" + + +def test_fchl_cauchy(): + + test_dir = os.path.dirname(os.path.realpath(__file__)) + + # Parse file containing PBE0/def2-TZVP heats of formation and xyz filenames + data = get_energies(test_dir + "/data/hof_qm7.txt") + + # Generate a list of qml.Compound() objects" + mols = [] + + for xyz_file in sorted(data.keys())[:5]: + + # Initialize the qml.Compound() objects + mol = Compound(xyz=test_dir + "/qm7/" + xyz_file) + + # This is a Molecular Coulomb matrix sorted by row norm + mol.representation = generate_representation(mol.coordinates, \ + mol.nuclear_charges, cut_distance=1e6) + mols.append(mol) + + X = np.array([mol.representation for mol in mols]) + + kernel_args = { + "kernel": "cauchy", + "kernel_args": { + "sigma": [2.0], + }, + } + + linear_kernel_args = { + "kernel": "linear", + "kernel_args": { + "c": [0.0], + }, + } + + + K = get_local_symmetric_kernels(X, **kernel_args)[0] + + K_test = np.zeros((len(mols),len(mols))) + + for i, Xi in enumerate(X): + Sii = get_atomic_kernels(Xi[:mols[i].natoms], Xi[:mols[i].natoms], **linear_kernel_args)[0] + for j, Xj in enumerate(X): + + Sjj = get_atomic_kernels(Xj[:mols[j].natoms], Xj[:mols[j].natoms], **linear_kernel_args)[0] + Sij = get_atomic_kernels(Xi[:mols[i].natoms], Xj[:mols[j].natoms], **linear_kernel_args)[0] + + for ii in range(Sii.shape[0]): + for jj in range(Sjj.shape[0]): + + l2 = Sii[ii,ii] + Sjj[jj,jj] - 2 * Sij[ii,jj] + K_test[i,j] += 1.0 / (1.0 + l2/2.0**2) + + assert np.allclose(K, K_test), "Error in FCHL cauchy kernels" + + +def test_fchl_polynomial2(): + + test_dir = os.path.dirname(os.path.realpath(__file__)) + + # Parse file containing PBE0/def2-TZVP heats of formation and xyz filenames + data = get_energies(test_dir + "/data/hof_qm7.txt") + + # Generate a list of qml.Compound() objects" + mols = [] + + for xyz_file in sorted(data.keys())[:5]: + + # Initialize the qml.Compound() objects + mol = Compound(xyz=test_dir + "/qm7/" + xyz_file) + + # This is a Molecular Coulomb matrix sorted by row norm + mol.representation = generate_representation(mol.coordinates, \ + mol.nuclear_charges, cut_distance=1e6) + mols.append(mol) + + X = np.array([mol.representation for mol in mols]) + + kernel_args = { + "kernel": "polynomial2", + "kernel_args": { + "coeff": [[1.0, 2.0, 3.0]], + }, + } + + linear_kernel_args = { + "kernel": "linear", + "kernel_args": { + "c": [0.0], + }, + } + + + K = get_local_symmetric_kernels(X, **kernel_args)[0] + + K_test = np.zeros((len(mols),len(mols))) + + for i, Xi in enumerate(X): + Sii = get_atomic_kernels(Xi[:mols[i].natoms], Xi[:mols[i].natoms], **linear_kernel_args)[0] + for j, Xj in enumerate(X): + + Sjj = get_atomic_kernels(Xj[:mols[j].natoms], Xj[:mols[j].natoms], **linear_kernel_args)[0] + Sij = get_atomic_kernels(Xi[:mols[i].natoms], Xj[:mols[j].natoms], **linear_kernel_args)[0] + + for ii in range(Sii.shape[0]): + for jj in range(Sjj.shape[0]): + + K_test[i,j] += 1.0 + 2.0 * Sij[ii,jj] + 3.0 * Sij[ii,jj]**2 + + assert np.allclose(K, K_test), "Error in FCHL polynomial2 kernels" + if __name__ == "__main__": test_krr_fchl_local() test_krr_fchl_global() test_krr_fchl_atomic() test_fchl_local_periodic() + test_krr_fchl_alchemy() + + test_fchl_local_periodic() + test_fchl_alchemy() + test_fchl_linear() + test_fchl_polynomial() + test_fchl_sigmoid() + test_fchl_multiquadratic() + test_fchl_inverse_multiquadratic() + test_fchl_bessel() + test_fchl_l2() + test_fchl_matern() + test_fchl_cauchy() + test_fchl_polynomial2()