From b6a434e31c11f87f6bac5a9eefa323ae6f5201e5 Mon Sep 17 00:00:00 2001 From: Kirill Zinovjev Date: Thu, 20 Jun 2024 19:04:08 +0200 Subject: [PATCH 001/134] Add emle_features argument to EMLECalculator --- emle/calculator.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/emle/calculator.py b/emle/calculator.py index 75e8f11..050839a 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -370,6 +370,7 @@ class EMLECalculator: def __init__( self, model=None, + emle_features='aev', method="electrostatic", backend="torchani", external_backend=None, @@ -403,6 +404,9 @@ def __init__( Path to the EMLE embedding model parameter file. If None, then a default model will be used. + emle_features: 'aev' or 'soap' + Type of features used to train the EMLE model + method: str The desired embedding method. Options are: "electrostatic": @@ -601,6 +605,12 @@ def __init__( else: self._model = self._default_model + if emle_features not in ['soap', 'aev']: + msg = "'emle_features' must be either 'soap' or 'aev'" + _logger.error(msg) + raise TypeError(msg) + self._emle_features = emle_features + if method is None: method = "electrostatic" From b940100c10ad8bcb2e0927a040a7312cfacf7ba6 Mon Sep 17 00:00:00 2001 From: Kirill Zinovjev Date: Thu, 20 Jun 2024 19:19:47 +0200 Subject: [PATCH 002/134] Create and store aev_computer from ANI-2x when AEV features are used --- emle/calculator.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 050839a..f3b1304 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -605,12 +605,6 @@ def __init__( else: self._model = self._default_model - if emle_features not in ['soap', 'aev']: - msg = "'emle_features' must be either 'soap' or 'aev'" - _logger.error(msg) - raise TypeError(msg) - self._emle_features = emle_features - if method is None: method = "electrostatic" @@ -1081,6 +1075,19 @@ def __init__( "cuda" if _torch.cuda.is_available() else "cpu" ) + if emle_features not in ['soap', 'aev']: + msg = "'emle_features' must be either 'soap' or 'aev'" + _logger.error(msg) + raise TypeError(msg) + self._emle_features = emle_features + + if self._emle_features == 'aev': + import torchani as _torchani + + # Create the TorchANI model. + ani2x = _torchani.models.ANI2x(periodic_table_index=True).to(self._device) + self._aev_computer = ani2x.aev_computer + if energy_frequency is None: energy_frequency = 0 From 7ffbeb57ed16b920e68c3ebbae3523150e76d455 Mon Sep 17 00:00:00 2001 From: Kirill Zinovjev Date: Fri, 21 Jun 2024 23:36:11 +0200 Subject: [PATCH 003/134] Proof of concept replacing SOAP with AEV --- emle/calculator.py | 89 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/emle/calculator.py b/emle/calculator.py index f3b1304..4432209 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -201,6 +201,89 @@ def get_Kinv(cls, ref_soap, sigma): return _np.linalg.inv(K + sigma**2 * _np.eye(n, dtype=_np.float32)) +class _AEVCalculator: + """ + Calculates AEV feature vectors for a given system + """ + + + def __init__(self, aev_computer, device): + """ + Constructor + + Parameters + ---------- + + aev_computer: torchani.aev.AEVComputer + Computer for AEV features + + device: torch device + """ + self._aev_computer = aev_computer + self._device = device + + z = (1, 6, 7, 8, 16) + zid_map = _np.zeros(max(z) + 1, dtype=int) + for i, z_i in enumerate(z): + zid_map[z_i] = i + zid_map[0] = -1 + self.zid_map = zid_map + + + def __call__(self, z, xyz, gradient=False): + """ + Calculates the AEV feature vectors and their gradients for a + given molecule. + + Parameters + ---------- + + z: numpy.array (N_ATOMS) + Chemical species (element) for each atom. + + xyz: numpy.array (N_ATOMS, 3) + Atomic positions. + + gradient: bool + Whether the gradient should be calculated. + + Returns + ------- + + aev: numpy.array (N_ATOMS, N_AEV) + AEV feature vectors for each atom. + + gradient: numpy.array (N_ATOMS, N_AEV, N_ATOMS, 3) + gradients of the AEV feature vectors w.r.t. atomic positions + """ + coords = _torch.tensor( + _np.float32(xyz.reshape(1, *xyz.shape)), + requires_grad=True, + device=self._device, + ) + + # Convert the atomic numbers to a Torch tensor. + zid = self.zid_map[z] + atomic_numbers = _torch.tensor( + zid.reshape(1, *zid.shape), + device=self._device, + ) + # _logger.error(f'{atomic_numbers}') + + def get_aev(coords): + aev = self._aev_computer.forward((atomic_numbers, coords))[1][0] + return aev / _torch.linalg.norm(aev, axis=1, keepdims=True) + + aev = get_aev(coords).cpu().detach().numpy() + + if not gradient: + return aev + + from torch.autograd.functional import jacobian + grad = jacobian(get_aev, coords, vectorize=True, strategy="forward-mode") + grad = grad.reshape((*aev.shape, -1, 3)).cpu().detach().numpy() + return aev, grad + class _SOAPCalculatorSpinv: """ Calculates Smooth Overlap of Atomic Positions (SOAP) feature vectors for @@ -1167,7 +1250,11 @@ def __init__( for id in self._hypers["global_species"]: self._supported_elements.append(_ase.Atom(id).symbol) - self._get_soap = _SOAPCalculatorSpinv(self._hypers) + if self._emle_features == 'soap': + self._get_soap = _SOAPCalculatorSpinv(self._hypers) + else: + self._get_soap = _AEVCalculator(self._aev_computer, self._device) + self._q_core = _torch.tensor( self._params["q_core"], dtype=_torch.float32, device=self._device ) From e0a297e8feb55a5cfd5c81442a9634672205b9d9 Mon Sep 17 00:00:00 2001 From: Kirill Zinovjev Date: Sat, 22 Jun 2024 09:58:08 +0200 Subject: [PATCH 004/134] Write 'emle-features' value to emle_settings.yaml --- emle/calculator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/emle/calculator.py b/emle/calculator.py index 4432209..11c7e49 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1332,6 +1332,7 @@ def __init__( # Store the settings as a dictionary. self._settings = { "model": None if model is None else self._model, + "emle_features": self._emle_features, "method": self._method, "backend": self._backend, "external_backend": None if external_backend is None else external_backend, From 1061879feb3e4bf87ee9a194781e6858cd5db159 Mon Sep 17 00:00:00 2001 From: Kirill Zinovjev Date: Sat, 22 Jun 2024 09:58:24 +0200 Subject: [PATCH 005/134] Keep SOAP features as default for now --- emle/calculator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emle/calculator.py b/emle/calculator.py index 11c7e49..931068f 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -453,7 +453,7 @@ class EMLECalculator: def __init__( self, model=None, - emle_features='aev', + emle_features='soap', method="electrostatic", backend="torchani", external_backend=None, From c66442899fec50ee75a3eb0a274d5715ba473bb3 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 24 Jun 2024 10:08:28 +0100 Subject: [PATCH 006/134] Blacken. --- emle/calculator.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 931068f..ea1ec19 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -206,7 +206,6 @@ class _AEVCalculator: Calculates AEV feature vectors for a given system """ - def __init__(self, aev_computer, device): """ Constructor @@ -229,7 +228,6 @@ def __init__(self, aev_computer, device): zid_map[0] = -1 self.zid_map = zid_map - def __call__(self, z, xyz, gradient=False): """ Calculates the AEV feature vectors and their gradients for a @@ -280,10 +278,12 @@ def get_aev(coords): return aev from torch.autograd.functional import jacobian + grad = jacobian(get_aev, coords, vectorize=True, strategy="forward-mode") grad = grad.reshape((*aev.shape, -1, 3)).cpu().detach().numpy() return aev, grad + class _SOAPCalculatorSpinv: """ Calculates Smooth Overlap of Atomic Positions (SOAP) feature vectors for @@ -453,7 +453,7 @@ class EMLECalculator: def __init__( self, model=None, - emle_features='soap', + emle_features="soap", method="electrostatic", backend="torchani", external_backend=None, @@ -1158,13 +1158,13 @@ def __init__( "cuda" if _torch.cuda.is_available() else "cpu" ) - if emle_features not in ['soap', 'aev']: + if emle_features not in ["soap", "aev"]: msg = "'emle_features' must be either 'soap' or 'aev'" _logger.error(msg) raise TypeError(msg) self._emle_features = emle_features - if self._emle_features == 'aev': + if self._emle_features == "aev": import torchani as _torchani # Create the TorchANI model. @@ -1250,7 +1250,7 @@ def __init__( for id in self._hypers["global_species"]: self._supported_elements.append(_ase.Atom(id).symbol) - if self._emle_features == 'soap': + if self._emle_features == "soap": self._get_soap = _SOAPCalculatorSpinv(self._hypers) else: self._get_soap = _AEVCalculator(self._aev_computer, self._device) From f62a107d24944d68c0839c37a876e466cf75ac30 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 24 Jun 2024 10:14:45 +0100 Subject: [PATCH 007/134] Valiate 'features' kwarg and apply minor formatting tweaks. --- emle/calculator.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index ea1ec19..36003ca 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -214,9 +214,10 @@ def __init__(self, aev_computer, device): ---------- aev_computer: torchani.aev.AEVComputer - Computer for AEV features + Computer for AEV features. device: torch device + The PyTorch device to use for calculations. """ self._aev_computer = aev_computer self._device = device @@ -266,7 +267,6 @@ def __call__(self, z, xyz, gradient=False): zid.reshape(1, *zid.shape), device=self._device, ) - # _logger.error(f'{atomic_numbers}') def get_aev(coords): aev = self._aev_computer.forward((atomic_numbers, coords))[1][0] @@ -453,7 +453,7 @@ class EMLECalculator: def __init__( self, model=None, - emle_features="soap", + features="soap", method="electrostatic", backend="torchani", external_backend=None, @@ -487,8 +487,8 @@ def __init__( Path to the EMLE embedding model parameter file. If None, then a default model will be used. - emle_features: 'aev' or 'soap' - Type of features used to train the EMLE model + features: 'aev' or 'soap' + Type of features used to train the EMLE model. method: str The desired embedding method. Options are: @@ -1158,13 +1158,21 @@ def __init__( "cuda" if _torch.cuda.is_available() else "cpu" ) - if emle_features not in ["soap", "aev"]: + if not isinstance(features, str): + msg = "'emle_features' must be of type 'str'" + _logger.error(msg) + raise TypeError(msg) + + # Strip whitespace and convert to lower case. + features = features.lower().replace(" ", "") + + if features not in ["soap", "aev"]: msg = "'emle_features' must be either 'soap' or 'aev'" _logger.error(msg) raise TypeError(msg) - self._emle_features = emle_features + self._features = features - if self._emle_features == "aev": + if self._features == "aev": import torchani as _torchani # Create the TorchANI model. @@ -1250,7 +1258,7 @@ def __init__( for id in self._hypers["global_species"]: self._supported_elements.append(_ase.Atom(id).symbol) - if self._emle_features == "soap": + if self._features == "soap": self._get_soap = _SOAPCalculatorSpinv(self._hypers) else: self._get_soap = _AEVCalculator(self._aev_computer, self._device) @@ -1332,7 +1340,7 @@ def __init__( # Store the settings as a dictionary. self._settings = { "model": None if model is None else self._model, - "emle_features": self._emle_features, + "features": self._features, "method": self._method, "backend": self._backend, "external_backend": None if external_backend is None else external_backend, From 7942e5ead4e5458874612608e5503a4e5e9a4ec9 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 24 Jun 2024 10:15:10 +0100 Subject: [PATCH 008/134] Expose 'features' in CLI and environment variable. --- bin/emle-server | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bin/emle-server b/bin/emle-server index 82a66d0..d724ee8 100755 --- a/bin/emle-server +++ b/bin/emle-server @@ -58,6 +58,7 @@ try: except: port = None model = os.getenv("EMLE_MODEL") +features = os.getenv("EMLE_FEATURES") method = os.getenv("EMLE_METHOD") mm_charges = os.getenv("EMLE_MM_CHARGES") try: @@ -124,6 +125,7 @@ env = { "host": host, "port": port, "model": model, + "features": features, "method": method, "mm_charges": mm_charges, "num_clients": num_clients, @@ -177,6 +179,12 @@ parser.add_argument("--port", type=str, help="the port number", required=False) parser.add_argument( "--model", type=str, help="path to an EMLE model file", required=False ) +parser.add_argument( + "--features", + type=str, + help="the type of feature used by the model ('soap' or 'aev')", + required=False, +) parser.add_argument( "--method", type=str, From cd630bd06acb05725fe27b385aeb2fa56de761e5 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 24 Jun 2024 11:29:30 +0100 Subject: [PATCH 009/134] Update kwarg in error message. --- emle/calculator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 36003ca..055d856 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1159,7 +1159,7 @@ def __init__( ) if not isinstance(features, str): - msg = "'emle_features' must be of type 'str'" + msg = "'features' must be of type 'str'" _logger.error(msg) raise TypeError(msg) @@ -1167,7 +1167,7 @@ def __init__( features = features.lower().replace(" ", "") if features not in ["soap", "aev"]: - msg = "'emle_features' must be either 'soap' or 'aev'" + msg = "'features' must be either 'soap' or 'aev'" _logger.error(msg) raise TypeError(msg) self._features = features From e32fa42c8a716cbc3dacff6287a468d322ed4e09 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 24 Jun 2024 11:32:31 +0100 Subject: [PATCH 010/134] Handle NoneType feature kwarg from emle-server. --- bin/emle-server | 1 + emle/calculator.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/bin/emle-server b/bin/emle-server index d724ee8..e6fb8e2 100755 --- a/bin/emle-server +++ b/bin/emle-server @@ -183,6 +183,7 @@ parser.add_argument( "--features", type=str, help="the type of feature used by the model ('soap' or 'aev')", + choices=["soap", "aev"], required=False, ) parser.add_argument( diff --git a/emle/calculator.py b/emle/calculator.py index 055d856..e22bba7 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1158,6 +1158,8 @@ def __init__( "cuda" if _torch.cuda.is_available() else "cpu" ) + if features is None: + features = "soap" if not isinstance(features, str): msg = "'features' must be of type 'str'" _logger.error(msg) From 9fa510d8cccd33142f958504c48f574b086e5649 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 24 Jun 2024 19:45:52 +0100 Subject: [PATCH 011/134] Formatting tweaks. [ci skip] --- emle/calculator.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/emle/calculator.py b/emle/calculator.py index e22bba7..90d9464 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -255,6 +255,7 @@ def __call__(self, z, xyz, gradient=False): gradient: numpy.array (N_ATOMS, N_AEV, N_ATOMS, 3) gradients of the AEV feature vectors w.r.t. atomic positions """ + # Convert the coordinates to a Torch tensor. coords = _torch.tensor( _np.float32(xyz.reshape(1, *xyz.shape)), requires_grad=True, @@ -272,6 +273,7 @@ def get_aev(coords): aev = self._aev_computer.forward((atomic_numbers, coords))[1][0] return aev / _torch.linalg.norm(aev, axis=1, keepdims=True) + # Store the AEV feature vectors. aev = get_aev(coords).cpu().detach().numpy() if not gradient: From 7d3e5e52b37ccf49e7eac1a7da935ffd8bdfae47 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 24 Jun 2024 20:38:47 +0100 Subject: [PATCH 012/134] Add support for NNPOps and re-use existing AEVComputer. --- emle/calculator.py | 124 ++++++++++++++++++++++++++++----------------- 1 file changed, 77 insertions(+), 47 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 90d9464..a2c3b46 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1167,22 +1167,6 @@ def __init__( _logger.error(msg) raise TypeError(msg) - # Strip whitespace and convert to lower case. - features = features.lower().replace(" ", "") - - if features not in ["soap", "aev"]: - msg = "'features' must be either 'soap' or 'aev'" - _logger.error(msg) - raise TypeError(msg) - self._features = features - - if self._features == "aev": - import torchani as _torchani - - # Create the TorchANI model. - ani2x = _torchani.models.ANI2x(periodic_table_index=True).to(self._device) - self._aev_computer = ani2x.aev_computer - if energy_frequency is None: energy_frequency = 0 @@ -1262,6 +1246,67 @@ def __init__( for id in self._hypers["global_species"]: self._supported_elements.append(_ase.Atom(id).symbol) + # Initialise TorchANI backend attributes. + if self._backend == "torchani": + import torchani as _torchani + + # Create the TorchANI model. + self._torchani_model = _torchani.models.ANI2x(periodic_table_index=True).to( + self._device + ) + + try: + import NNPOps as _NNPOps + + self._has_nnpops = True + self._nnpops_active = False + except: + self._has_nnpops = False + + # If the backend is ORCA, then try to find the executable. + elif self._backend == "orca": + if orca_path is None: + msg = "'orca_path' must be specified when using the ORCA backend" + _logger.error(msg) + raise ValueError(msg) + + if not isinstance(orca_path, str): + msg = "'orca_path' must be of type 'str'" + _logger.error(msg) + raise TypeError(msg) + + # Convert to an absolute path. + abs_orca_path = _os.path.abspath(orca_path) + + if not _os.path.isfile(abs_orca_path): + msg = f"Unable to locate ORCA executable: '{orca_path}'" + _logger.error(msg) + raise IOError(msg) + + self._orca_path = abs_orca_path + + # Strip whitespace and convert to lower case. + features = features.lower().replace(" ", "") + + if features not in ["soap", "aev"]: + msg = "'features' must be either 'soap' or 'aev'" + _logger.error(msg) + raise TypeError(msg) + self._features = features + + if self._features == "aev": + # Re-use the existing AEV computer from TorchANI. + if self._backend == "torchani": + self._aev_computer = self._torchani_model.aev_computer + # Create a new AEV computer. + else: + import torchani as _torchani + + ani2x = _torchani.models.ANI2x(periodic_table_index=True).to( + self._device + ) + self._aev_computer = ani2x.aev_computer + if self._features == "soap": self._get_soap = _SOAPCalculatorSpinv(self._hypers) else: @@ -1298,37 +1343,6 @@ def __init__( ) self._get_E_with_grad = _grad_and_value(self._get_E, argnums=(1, 2, 3, 4)) - # Initialise TorchANI backend attributes. - if self._backend == "torchani": - import torchani as _torchani - - # Create the TorchANI model. - self._torchani_model = _torchani.models.ANI2x(periodic_table_index=True).to( - self._device - ) - - # If the backend is ORCA, then try to find the executable. - elif self._backend == "orca": - if orca_path is None: - msg = "'orca_path' must be specified when using the ORCA backend" - _logger.error(msg) - raise ValueError(msg) - - if not isinstance(orca_path, str): - msg = "'orca_path' must be of type 'str'" - _logger.error(msg) - raise TypeError(msg) - - # Convert to an absolute path. - abs_orca_path = _os.path.abspath(orca_path) - - if not _os.path.isfile(abs_orca_path): - msg = f"Unable to locate ORCA executable: '{orca_path}'" - _logger.error(msg) - raise IOError(msg) - - self._orca_path = abs_orca_path - # Initialise the maximum number of MM atom that have been seen. self._max_mm_atoms = 0 @@ -2871,6 +2885,22 @@ def _run_torchani(self, xyz, atomic_numbers): device=self._device, ) + # Check for NNPOps and optimise the model. + if self._has_nnpops and not self._nnpops_active: + from NNPOps import OptimizedTorchANI as _OptimizedTorchANI + + # Optimise the TorchANI model. + self._torchani_model = _OptimizedTorchANI( + self._torchani_model, atomic_numbers + ).to(self._device) + + # Flag that NNPOps is active. + self._nnpops_active = True + + # Applyt the optimised AEV symmetry functions. + if self._features == "aev": + self._aev_computer = self._torchani_model.aev_computer + # Compute the energy and gradient. energy = self._torchani_model((atomic_numbers, coords)).energies gradient = _torch.autograd.grad(energy.sum(), coords)[0] * _BOHR_TO_ANGSTROM From 316c70fe0a3bd8dfdd7467508d75109c12b81741 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Wed, 26 Jun 2024 13:05:33 +0100 Subject: [PATCH 013/134] Switch to using autograd.grad directly. --- emle/calculator.py | 105 ++++++++++++++++++++++++--------------------- 1 file changed, 56 insertions(+), 49 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index a2c3b46..b03ec88 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -48,12 +48,6 @@ import torch as _torch -try: - from torch.func import grad_and_value as _grad_and_value -except: - from func_torch import grad_and_value as _grad_and_value - - _ANGSTROM_TO_BOHR = 1.0 / _ase.units.Bohr _NANOMETER_TO_BOHR = 10.0 / _ase.units.Bohr _BOHR_TO_ANGSTROM = _ase.units.Bohr @@ -1341,7 +1335,6 @@ def __init__( self._params["n_ref"], 1e-3, ) - self._get_E_with_grad = _grad_and_value(self._get_E, argnums=(1, 2, 3, 4)) # Initialise the maximum number of MM atom that have been seen. self._max_mm_atoms = 0 @@ -1577,26 +1570,37 @@ def run(self, path=None): # Convert inputs to Torch tensors. xyz_qm_bohr = _torch.tensor( - xyz_qm_bohr, dtype=_torch.float32, device=self._device + xyz_qm_bohr, dtype=_torch.float32, device=self._device, requires_grad=True ) xyz_mm_bohr = _torch.tensor( - xyz_mm_bohr, dtype=_torch.float32, device=self._device + xyz_mm_bohr, dtype=_torch.float32, device=self._device, requires_grad=True ) charges_mm = _torch.tensor( charges_mm, dtype=_torch.float32, device=self._device ) - s = _torch.tensor(s, dtype=_torch.float32, device=self._device) - chi = _torch.tensor(chi, dtype=_torch.float32, device=self._device) - - # Compute gradients and energy. - grads, E = self._get_E_with_grad(charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi) - dE_dxyz_qm_bohr_part, dE_dxyz_mm_bohr, dE_ds, dE_dchi = grads - dE_dxyz_qm_bohr = ( - dE_dxyz_qm_bohr_part.cpu().numpy() - + dE_ds.cpu().numpy() @ ds_dxyz_qm_bohr.swapaxes(0, 1) - + dE_dchi.cpu().numpy() @ dchi_dxyz_qm_bohr.swapaxes(0, 1) + s = _torch.tensor( + s, dtype=_torch.float32, device=self._device, requires_grad=True + ) + chi = _torch.tensor( + chi, dtype=_torch.float32, device=self._device, requires_grad=True ) + # Compute energy and gradients. + E = self._get_E(charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi) + if self._method == "mm": + dE_dxyz_qm_bohr, dE_dxyz_mm_bohr = _torch.autograd.grad( + E, (xyz_qm_bohr, xyz_mm_bohr) + ) + dE_dxyz_mm_bohr = dE_dxyz_mm_bohr.cpu().numpy() + else: + grads = _torch.autograd.grad(E, (xyz_qm_bohr, xyz_mm_bohr, s, chi)) + dE_dxyz_qm_bohr_part, dE_dxyz_mm_bohr, dE_ds, dE_dchi = grads + dE_dxyz_qm_bohr = ( + dE_dxyz_qm_bohr_part.cpu().numpy() + + dE_ds.cpu().numpy() @ ds_dxyz_qm_bohr.swapaxes(0, 1) + + dE_dchi.cpu().numpy() @ dchi_dxyz_qm_bohr.swapaxes(0, 1) + ) + # Compute the total energy and gradients. E_tot = E + E_vac grad_qm = dE_dxyz_qm_bohr + grad_vac @@ -1620,16 +1624,12 @@ def run(self, path=None): method = self._method self._method = "mm" - # Recompute the gradients and energy. - grads, E = self._get_E_with_grad( - charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi - ) - dE_dxyz_qm_bohr_part, dE_dxyz_mm_bohr, dE_ds, dE_dchi = grads - dE_dxyz_qm_bohr = ( - dE_dxyz_qm_bohr_part.cpu().numpy() - + dE_ds.cpu().numpy() @ ds_dxyz_qm_bohr.swapaxes(0, 1) - + dE_dchi.cpu().numpy() @ dchi_dxyz_qm_bohr.swapaxes(0, 1) + # Recompute the energy and gradients. + E = self._get_E(charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi) + dE_dxyz_qm_bohr, dE_dxyz_mm_bohr = _torch.autograd.grad( + E, (xyz_qm_bohr, xyz_mm_bohr) ) + dE_dxyz_qm_bohr = dE_dxyz_qm_bohr.cpu().numpy() dE_dxyz_mm_bohr = dE_dxyz_mm_bohr.cpu().numpy() # Restore the method. @@ -1983,26 +1983,37 @@ def _sire_callback(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): # Convert inputs to Torch tensors. xyz_qm_bohr = _torch.tensor( - xyz_qm_bohr, dtype=_torch.float32, device=self._device + xyz_qm_bohr, dtype=_torch.float32, device=self._device, requires_grad=True ) xyz_mm_bohr = _torch.tensor( - xyz_mm_bohr, dtype=_torch.float32, device=self._device + xyz_mm_bohr, dtype=_torch.float32, device=self._device, requires_grad=True ) charges_mm = _torch.tensor( charges_mm, dtype=_torch.float32, device=self._device ) - s = _torch.tensor(s, dtype=_torch.float32, device=self._device) - chi = _torch.tensor(chi, dtype=_torch.float32, device=self._device) - - # Compute gradients and energy. - grads, E = self._get_E_with_grad(charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi) - dE_dxyz_qm_bohr_part, dE_dxyz_mm_bohr, dE_ds, dE_dchi = grads - dE_dxyz_qm_bohr = ( - dE_dxyz_qm_bohr_part.cpu().numpy() - + dE_ds.cpu().numpy() @ ds_dxyz_qm_bohr.swapaxes(0, 1) - + dE_dchi.cpu().numpy() @ dchi_dxyz_qm_bohr.swapaxes(0, 1) + s = _torch.tensor( + s, dtype=_torch.float32, device=self._device, requires_grad=True + ) + chi = _torch.tensor( + chi, dtype=_torch.float32, device=self._device, requires_grad=True ) + # Compute energy and gradients. + E = self._get_E(charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi) + if self._method == "mm": + dE_dxyz_qm_bohr, dE_dxyz_mm_bohr = _torch.autograd.grad( + E, (xyz_qm_bohr, xyz_mm_bohr) + ) + dE_dxyz_mm_bohr = dE_dxyz_mm_bohr.cpu().numpy() + else: + grads = _torch.autograd.grad(E, (xyz_qm_bohr, xyz_mm_bohr, s, chi)) + dE_dxyz_qm_bohr_part, dE_dxyz_mm_bohr, dE_ds, dE_dchi = grads + dE_dxyz_qm_bohr = ( + dE_dxyz_qm_bohr_part.cpu().numpy() + + dE_ds.cpu().numpy() @ ds_dxyz_qm_bohr.swapaxes(0, 1) + + dE_dchi.cpu().numpy() @ dchi_dxyz_qm_bohr.swapaxes(0, 1) + ) + # Compute the total energy and gradients. E_tot = E + E_vac grad_qm = dE_dxyz_qm_bohr + grad_vac @@ -2030,16 +2041,12 @@ def _sire_callback(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): method = self._method self._method = "mm" - # Recompute the gradients and energy. - grads, E = self._get_E_with_grad( - charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi - ) - dE_dxyz_qm_bohr_part, dE_dxyz_mm_bohr, dE_ds, dE_dchi = grads - dE_dxyz_qm_bohr = ( - dE_dxyz_qm_bohr_part.cpu().numpy() - + dE_ds.cpu().numpy() @ ds_dxyz_qm_bohr.swapaxes(0, 1) - + dE_dchi.cpu().numpy() @ dchi_dxyz_qm_bohr.swapaxes(0, 1) + # Recompute the energy and gradients. + E = self._get_E(charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi) + dE_dxyz_qm_bohr, dE_dxyz_mm_bohr = _torch.autograd.grad( + E, (xyz_qm_bohr, xyz_mm_bohr) ) + dE_dxyz_qm_bohr = dE_dxyz_qm_bohr.cpu().numpy() dE_dxyz_mm_bohr = dE_dxyz_mm_bohr.cpu().numpy() # Restore the method. From 6d37a762ec53c5266c0b421063660c57900fd6f7 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Wed, 26 Jun 2024 19:38:03 +0100 Subject: [PATCH 014/134] Generalise feature naming. --- emle/calculator.py | 106 +++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index b03ec88..5e65133 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -72,7 +72,7 @@ class _GPRCalculator: """Predicts an atomic property for a molecule with Gaussian Process Regression (GPR).""" - def __init__(self, ref_values, ref_soap, n_ref, sigma): + def __init__(self, ref_values, ref_feat, n_ref, sigma): """ Constructor @@ -82,7 +82,7 @@ def __init__(self, ref_values, ref_soap, n_ref, sigma): ref_values: numpy.array (N_Z, N_REF) The property values corresponding to the basis vectors for each species. - ref_soap: numpy.array (N_Z, N_REF, N_SOAP) + ref_feat: numpy.array (N_Z, N_REF, N_FEAT) The basis feature vectors for each species. n_ref: (N_Z,) @@ -91,21 +91,21 @@ def __init__(self, ref_values, ref_soap, n_ref, sigma): sigma: float The uncertainty of the observations (regularizer). """ - self.ref_soap = ref_soap - Kinv = self.get_Kinv(ref_soap, sigma) + self.ref_feat = ref_feat + Kinv = self.get_Kinv(ref_feat, sigma) self.n_ref = n_ref self.n_z = len(n_ref) self.ref_mean = _np.sum(ref_values, axis=1) / n_ref ref_shifted = ref_values - self.ref_mean[:, None] self.c = (Kinv @ ref_shifted[:, :, None]).squeeze() - def __call__(self, mol_soap, zid, gradient=False): + def __call__(self, mol_feat, zid, gradient=False): """ Parameters ---------- - mol_soap: numpy.array (N_ATOMS, N_SOAP) + mol_feat: numpy.array (N_ATOMS, N_FEAT) The feature vectors for each atom. zid: numpy.array (N_ATOMS,) @@ -118,34 +118,34 @@ def __call__(self, mol_soap, zid, gradient=False): ------- result: numpy.array (N_ATOMS) - The values of the predicted property for each atom + The values of the predicted property for each atom. - gradient: numpy.array (N_ATOMS, N_SOAP) - The gradients of the property w.r.t. the SOAP features + gradient: numpy.array (N_ATOMS, N_FEAT) + The gradients of the property w.r.t. the features. """ result = _np.zeros(len(zid), dtype=_np.float32) for i in range(self.n_z): n_ref = self.n_ref[i] - ref_soap_z = self.ref_soap[i, :n_ref] - mol_soap_z = mol_soap[zid == i, :, None] + ref_feat_z = self.ref_feat[i, :n_ref] + mol_feat_z = mol_feat[zid == i, :, None] - K_mol_ref2 = (ref_soap_z @ mol_soap_z) ** 2 + K_mol_ref2 = (ref_feat_z @ mol_feat_z) ** 2 K_mol_ref2 = K_mol_ref2.reshape(K_mol_ref2.shape[:-1]) result[zid == i] = K_mol_ref2 @ self.c[i, :n_ref] + self.ref_mean[i] if not gradient: return result - return result, self.get_gradient(mol_soap, zid) + return result, self.get_gradient(mol_feat, zid) - def get_gradient(self, mol_soap, zid): + def get_gradient(self, mol_feat, zid): """ Returns the gradient of the predicted property with respect to - SOAP features. + the features. Parameters ---------- - mol_soap: numpy.array (N_ATOMS, N_SOAP) + mol_feat: numpy.array (N_ATOMS, N_FEAT) The feature vectors for each atom. zid: numpy.array (N_ATOMS,) @@ -154,31 +154,31 @@ def get_gradient(self, mol_soap, zid): Returns ------- - result: numpy.array (N_ATOMS, N_SOAP) + result: numpy.array (N_ATOMS, N_FEAT) The gradients of the property with respect to the soap features. """ - n_at, n_soap = mol_soap.shape - df_dsoap = _np.zeros((n_at, n_soap), dtype=_np.float32) + n_at, n_feat = mol_feat.shape + df_dfeat = _np.zeros((n_at, n_feat), dtype=_np.float32) for i in range(self.n_z): n_ref = self.n_ref[i] - ref_soap_z = self.ref_soap[i, :n_ref] - mol_soap_z = mol_soap[zid == i, :, None] - K_mol_ref = ref_soap_z @ mol_soap_z + ref_feat_z = self.ref_feat[i, :n_ref] + mol_feat_z = mol_feat[zid == i, :, None] + K_mol_ref = ref_feat_z @ mol_feat_z K_mol_ref = K_mol_ref.reshape(K_mol_ref.shape[:-1]) c = self.c[i, :n_ref] - df_dsoap[zid == i] = (K_mol_ref[:, None, :] * ref_soap_z.T) @ c * 2 - return df_dsoap + df_dfeat[zid == i] = (K_mol_ref[:, None, :] * ref_feat_z.T) @ c * 2 + return df_dfeat @classmethod - def get_Kinv(cls, ref_soap, sigma): + def get_Kinv(cls, ref_feat, sigma): """ Internal function to compute the inverse of the K matrix for GPR. Parameters ---------- - ref_soap: numpy.array (N_Z, MAX_N_REF, N_SOAP) + ref_feat: numpy.array (N_Z, MAX_N_REF, N_FEAT) The basis feature vectors for each species. sigma: float @@ -190,8 +190,8 @@ def get_Kinv(cls, ref_soap, sigma): result: numpy.array (MAX_N_REF, MAX_N_REF) The inverse of the K matrix. """ - n = ref_soap.shape[1] - K = (ref_soap @ ref_soap.swapaxes(1, 2)) ** 2 + n = ref_feat.shape[1] + K = (ref_feat @ ref_feat.swapaxes(1, 2)) ** 2 return _np.linalg.inv(K + sigma**2 * _np.eye(n, dtype=_np.float32)) @@ -319,10 +319,10 @@ def __call__(self, z, xyz, gradient=False): Returns ------- - soap: numpy.array (N_ATOMS, N_SOAP) + soap: numpy.array (N_ATOMS, N_FEAT) SOAP feature vectors for each atom. - gradient: numpy.array (N_ATOMS, N_SOAP, N_ATOMS, 3) + gradient: numpy.array (N_ATOMS, N_FEAT, N_ATOMS, 3) gradients of the soap feature vectors w.r.t. atomic positions """ mol = self.get_mol(z, xyz) @@ -371,10 +371,10 @@ def get_soap(atoms, spinv, gradient=False): Returns ------- - soap: numpy.array (N_ATOMS, N_SOAP) + soap: numpy.array (N_ATOMS, N_FEAT) SOAP feature vectors for each atom. - gradient: numpy.array (N_ATOMS, N_SOAP, N_ATOMS, 3) + gradient: numpy.array (N_ATOMS, N_FEAT, N_ATOMS, 3) Gradients of the SOAP feature vectors with respect to atomic positions. """ managers = spinv.transform(atoms) @@ -1302,9 +1302,9 @@ def __init__( self._aev_computer = ani2x.aev_computer if self._features == "soap": - self._get_soap = _SOAPCalculatorSpinv(self._hypers) + self._get_features = _SOAPCalculatorSpinv(self._hypers) else: - self._get_soap = _AEVCalculator(self._aev_computer, self._device) + self._get_features = _AEVCalculator(self._aev_computer, self._device) self._q_core = _torch.tensor( self._params["q_core"], dtype=_torch.float32, device=self._device @@ -1560,13 +1560,15 @@ def run(self, path=None): xyz_qm_bohr = xyz_qm * _ANGSTROM_TO_BOHR xyz_mm_bohr = xyz_mm * _ANGSTROM_TO_BOHR - mol_soap, dsoap_dxyz = self._get_soap(atomic_numbers, xyz_qm, gradient=True) - dsoap_dxyz_qm_bohr = dsoap_dxyz * _BOHR_TO_ANGSTROM + # Get the features and their gradients. + mol_feat, dfeat_dxyz = self._get_features(atomic_numbers, xyz_qm, gradient=True) + dfeat_dxyz_qm_bohr = dfeat_dxyz * _BOHR_TO_ANGSTROM - s, ds_dsoap = self._get_s(mol_soap, self._species_id, gradient=True) - chi, dchi_dsoap = self._get_chi(mol_soap, self._species_id, gradient=True) - ds_dxyz_qm_bohr = self._get_df_dxyz(ds_dsoap, dsoap_dxyz_qm_bohr) - dchi_dxyz_qm_bohr = self._get_df_dxyz(dchi_dsoap, dsoap_dxyz_qm_bohr) + # Compute the s and chi values and their gradients. + s, ds_dfeat = self._get_s(mol_feat, self._species_id, gradient=True) + chi, dchi_dfeat = self._get_chi(mol_feat, self._species_id, gradient=True) + ds_dxyz_qm_bohr = self._get_df_dxyz(ds_dfeat, dfeat_dxyz_qm_bohr) + dchi_dxyz_qm_bohr = self._get_df_dxyz(dchi_dfeat, dfeat_dxyz_qm_bohr) # Convert inputs to Torch tensors. xyz_qm_bohr = _torch.tensor( @@ -1973,13 +1975,15 @@ def _sire_callback(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): xyz_qm_bohr = xyz_qm * _ANGSTROM_TO_BOHR xyz_mm_bohr = xyz_mm * _ANGSTROM_TO_BOHR - mol_soap, dsoap_dxyz = self._get_soap(atomic_numbers, xyz_qm, gradient=True) - dsoap_dxyz_qm_bohr = dsoap_dxyz * _BOHR_TO_ANGSTROM + # Get the features and their gradients. + mol_feat, dfeat_dxyz = self._get_features(atomic_numbers, xyz_qm, gradient=True) + dfeat_dxyz_qm_bohr = dfeat_dxyz * _BOHR_TO_ANGSTROM - s, ds_dsoap = self._get_s(mol_soap, self._species_id, gradient=True) - chi, dchi_dsoap = self._get_chi(mol_soap, self._species_id, gradient=True) - ds_dxyz_qm_bohr = self._get_df_dxyz(ds_dsoap, dsoap_dxyz_qm_bohr) - dchi_dxyz_qm_bohr = self._get_df_dxyz(dchi_dsoap, dsoap_dxyz_qm_bohr) + # Compute the s and chi values and their gradients. + s, ds_dfeat = self._get_s(mol_feat, self._species_id, gradient=True) + chi, dchi_dfeat = self._get_chi(mol_feat, self._species_id, gradient=True) + ds_dxyz_qm_bohr = self._get_df_dxyz(ds_dfeat, dfeat_dxyz_qm_bohr) + dchi_dxyz_qm_bohr = self._get_df_dxyz(dchi_dfeat, dfeat_dxyz_qm_bohr) # Convert inputs to Torch tensors. xyz_qm_bohr = _torch.tensor( @@ -2363,24 +2367,24 @@ def _get_A_thole(self, r_data, s, q_val, k_Z): return A @staticmethod - def _get_df_dxyz(df_dsoap, dsoap_dxyz): + def _get_df_dxyz(df_dfeat, dfeat_dxyz): """ Internal method to calculate the gradient of some property with respect to - xyz coordinates based on gradient with respect to SOAP and SOAP gradients. + xyz coordinates based on gradient with respect to feature and feature gradients. Parameters ---------- - df_dsoap: numpy.array (N_ATOMS, N_SOAP) + df_dfeat: numpy.array (N_ATOMS, N_FEAT) - dsoap_dxyz: numpy.array (N_ATOMS, N_SOAP, N_ATOMS, 3) + dfeat_dxyz: numpy.array (N_ATOMS, N_FEAT, N_ATOMS, 3) Returns ------- result: numpy.array (N_ATOMS, N_ATOMS, 3) """ - return _np.einsum("ij,ijkl->ikl", df_dsoap, dsoap_dxyz) + return _np.einsum("ij,ijkl->ikl", df_dfeat, dfeat_dxyz) @staticmethod def _get_vpot_q(q, T0): From 9338c603a06a76036a806a18e2278a5a2fd950e7 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Wed, 26 Jun 2024 19:49:30 +0100 Subject: [PATCH 015/134] Fix gradient tensor use with MM embedding. --- emle/calculator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 5e65133..7119b65 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1593,7 +1593,7 @@ def run(self, path=None): dE_dxyz_qm_bohr, dE_dxyz_mm_bohr = _torch.autograd.grad( E, (xyz_qm_bohr, xyz_mm_bohr) ) - dE_dxyz_mm_bohr = dE_dxyz_mm_bohr.cpu().numpy() + dE_dxyz_qm_bohr = dE_dxyz_qm_bohr.cpu().numpy() else: grads = _torch.autograd.grad(E, (xyz_qm_bohr, xyz_mm_bohr, s, chi)) dE_dxyz_qm_bohr_part, dE_dxyz_mm_bohr, dE_ds, dE_dchi = grads @@ -2008,7 +2008,7 @@ def _sire_callback(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): dE_dxyz_qm_bohr, dE_dxyz_mm_bohr = _torch.autograd.grad( E, (xyz_qm_bohr, xyz_mm_bohr) ) - dE_dxyz_mm_bohr = dE_dxyz_mm_bohr.cpu().numpy() + dE_dxyz_qm_bohr = dE_dxyz_qm_bohr.cpu().numpy() else: grads = _torch.autograd.grad(E, (xyz_qm_bohr, xyz_mm_bohr, s, chi)) dE_dxyz_qm_bohr_part, dE_dxyz_mm_bohr, dE_ds, dE_dchi = grads From ca2d4da168e072dcdd615c24586a243fc564f166 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Thu, 27 Jun 2024 09:10:57 +0100 Subject: [PATCH 016/134] Refactor features validation. [ci skip] --- emle/calculator.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 7119b65..9294097 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1154,13 +1154,6 @@ def __init__( "cuda" if _torch.cuda.is_available() else "cpu" ) - if features is None: - features = "soap" - if not isinstance(features, str): - msg = "'features' must be of type 'str'" - _logger.error(msg) - raise TypeError(msg) - if energy_frequency is None: energy_frequency = 0 @@ -1279,6 +1272,13 @@ def __init__( self._orca_path = abs_orca_path + if features is None: + features = "soap" + if not isinstance(features, str): + msg = "'features' must be of type 'str'" + _logger.error(msg) + raise TypeError(msg) + # Strip whitespace and convert to lower case. features = features.lower().replace(" ", "") From b3ba1fdf8d45ffbb1313f455ee2400d7a6569943 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Thu, 27 Jun 2024 09:58:27 +0100 Subject: [PATCH 017/134] Extract AEVs from tuple by name. [ci skip] --- emle/calculator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emle/calculator.py b/emle/calculator.py index 9294097..49c2cc1 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -264,7 +264,7 @@ def __call__(self, z, xyz, gradient=False): ) def get_aev(coords): - aev = self._aev_computer.forward((atomic_numbers, coords))[1][0] + aev = self._aev_computer.forward((atomic_numbers, coords)).aevs[0] return aev / _torch.linalg.norm(aev, axis=1, keepdims=True) # Store the AEV feature vectors. From 9798741f84cf40ca51da42225649c11a000f80b0 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Thu, 27 Jun 2024 10:07:13 +0100 Subject: [PATCH 018/134] Generalise GPRCalculator docstring. --- emle/calculator.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 49c2cc1..6fdf58b 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -155,8 +155,7 @@ def get_gradient(self, mol_feat, zid): ------- result: numpy.array (N_ATOMS, N_FEAT) - The gradients of the property with respect to - the soap features. + The gradients of the property with respect to the features. """ n_at, n_feat = mol_feat.shape df_dfeat = _np.zeros((n_at, n_feat), dtype=_np.float32) From 14962464d90bbca797637750ee5c54de25044f3f Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Thu, 27 Jun 2024 15:45:41 +0100 Subject: [PATCH 019/134] Full AEV implementation with features computed in energy function. This fixes the autograd backpropagation by moving the calculation of the features and derived properties (s, chi) into the energy function. There is now no need to manually compute feature derivatives and apply their contribution to the QM gradient via the chain rule. [ci skip] --- bin/emle-server | 9 - emle/calculator.py | 520 +++++++++++---------------------------------- 2 files changed, 127 insertions(+), 402 deletions(-) diff --git a/bin/emle-server b/bin/emle-server index e6fb8e2..82a66d0 100755 --- a/bin/emle-server +++ b/bin/emle-server @@ -58,7 +58,6 @@ try: except: port = None model = os.getenv("EMLE_MODEL") -features = os.getenv("EMLE_FEATURES") method = os.getenv("EMLE_METHOD") mm_charges = os.getenv("EMLE_MM_CHARGES") try: @@ -125,7 +124,6 @@ env = { "host": host, "port": port, "model": model, - "features": features, "method": method, "mm_charges": mm_charges, "num_clients": num_clients, @@ -179,13 +177,6 @@ parser.add_argument("--port", type=str, help="the port number", required=False) parser.add_argument( "--model", type=str, help="path to an EMLE model file", required=False ) -parser.add_argument( - "--features", - type=str, - help="the type of feature used by the model ('soap' or 'aev')", - choices=["soap", "aev"], - required=False, -) parser.add_argument( "--method", type=str, diff --git a/emle/calculator.py b/emle/calculator.py index 6fdf58b..64602cf 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -44,8 +44,6 @@ import ase as _ase import ase.io as _ase_io -from rascal.representations import SphericalInvariants as _SphericalInvariants - import torch as _torch _ANGSTROM_TO_BOHR = 1.0 / _ase.units.Bohr @@ -69,131 +67,6 @@ } -class _GPRCalculator: - """Predicts an atomic property for a molecule with Gaussian Process Regression (GPR).""" - - def __init__(self, ref_values, ref_feat, n_ref, sigma): - """ - Constructor - - Parameters - ---------- - - ref_values: numpy.array (N_Z, N_REF) - The property values corresponding to the basis vectors for each species. - - ref_feat: numpy.array (N_Z, N_REF, N_FEAT) - The basis feature vectors for each species. - - n_ref: (N_Z,) - Number of supported species. - - sigma: float - The uncertainty of the observations (regularizer). - """ - self.ref_feat = ref_feat - Kinv = self.get_Kinv(ref_feat, sigma) - self.n_ref = n_ref - self.n_z = len(n_ref) - self.ref_mean = _np.sum(ref_values, axis=1) / n_ref - ref_shifted = ref_values - self.ref_mean[:, None] - self.c = (Kinv @ ref_shifted[:, :, None]).squeeze() - - def __call__(self, mol_feat, zid, gradient=False): - """ - - Parameters - ---------- - - mol_feat: numpy.array (N_ATOMS, N_FEAT) - The feature vectors for each atom. - - zid: numpy.array (N_ATOMS,) - The species identity value of each atom. - - gradient: bool - Whether the gradient should be calculated. - - Returns - ------- - - result: numpy.array (N_ATOMS) - The values of the predicted property for each atom. - - gradient: numpy.array (N_ATOMS, N_FEAT) - The gradients of the property w.r.t. the features. - """ - - result = _np.zeros(len(zid), dtype=_np.float32) - for i in range(self.n_z): - n_ref = self.n_ref[i] - ref_feat_z = self.ref_feat[i, :n_ref] - mol_feat_z = mol_feat[zid == i, :, None] - - K_mol_ref2 = (ref_feat_z @ mol_feat_z) ** 2 - K_mol_ref2 = K_mol_ref2.reshape(K_mol_ref2.shape[:-1]) - result[zid == i] = K_mol_ref2 @ self.c[i, :n_ref] + self.ref_mean[i] - if not gradient: - return result - return result, self.get_gradient(mol_feat, zid) - - def get_gradient(self, mol_feat, zid): - """ - Returns the gradient of the predicted property with respect to - the features. - - Parameters - ---------- - - mol_feat: numpy.array (N_ATOMS, N_FEAT) - The feature vectors for each atom. - - zid: numpy.array (N_ATOMS,) - The species identity value of each atom. - - Returns - ------- - - result: numpy.array (N_ATOMS, N_FEAT) - The gradients of the property with respect to the features. - """ - n_at, n_feat = mol_feat.shape - df_dfeat = _np.zeros((n_at, n_feat), dtype=_np.float32) - for i in range(self.n_z): - n_ref = self.n_ref[i] - ref_feat_z = self.ref_feat[i, :n_ref] - mol_feat_z = mol_feat[zid == i, :, None] - K_mol_ref = ref_feat_z @ mol_feat_z - K_mol_ref = K_mol_ref.reshape(K_mol_ref.shape[:-1]) - c = self.c[i, :n_ref] - df_dfeat[zid == i] = (K_mol_ref[:, None, :] * ref_feat_z.T) @ c * 2 - return df_dfeat - - @classmethod - def get_Kinv(cls, ref_feat, sigma): - """ - Internal function to compute the inverse of the K matrix for GPR. - - Parameters - ---------- - - ref_feat: numpy.array (N_Z, MAX_N_REF, N_FEAT) - The basis feature vectors for each species. - - sigma: float - The uncertainty of the observations (regularizer). - - Returns - ------- - - result: numpy.array (MAX_N_REF, MAX_N_REF) - The inverse of the K matrix. - """ - n = ref_feat.shape[1] - K = (ref_feat @ ref_feat.swapaxes(1, 2)) ** 2 - return _np.linalg.inv(K + sigma**2 * _np.eye(n, dtype=_np.float32)) - - class _AEVCalculator: """ Calculates AEV feature vectors for a given system @@ -215,179 +88,140 @@ def __init__(self, aev_computer, device): self._aev_computer = aev_computer self._device = device - z = (1, 6, 7, 8, 16) - zid_map = _np.zeros(max(z) + 1, dtype=int) - for i, z_i in enumerate(z): - zid_map[z_i] = i - zid_map[0] = -1 - self.zid_map = zid_map - - def __call__(self, z, xyz, gradient=False): + def __call__(self, zid, xyz): """ - Calculates the AEV feature vectors and their gradients for a - given molecule. + Calculates the AEV feature vectors for a given molecule. Parameters ---------- - z: numpy.array (N_ATOMS) - Chemical species (element) for each atom. - - xyz: numpy.array (N_ATOMS, 3) - Atomic positions. + zid: numpy.array (N_ATOMS) + Chemical species indices for each atom. - gradient: bool - Whether the gradient should be calculated. + xyz: torch.tensor (N_ATOMS, 3) + Atomic positions in Bohr. Returns ------- - aev: numpy.array (N_ATOMS, N_AEV) + aev: torch.tensor (N_ATOMS, N_AEV) AEV feature vectors for each atom. - - gradient: numpy.array (N_ATOMS, N_AEV, N_ATOMS, 3) - gradients of the AEV feature vectors w.r.t. atomic positions """ - # Convert the coordinates to a Torch tensor. - coords = _torch.tensor( - _np.float32(xyz.reshape(1, *xyz.shape)), - requires_grad=True, - device=self._device, - ) - - # Convert the atomic numbers to a Torch tensor. - zid = self.zid_map[z] - atomic_numbers = _torch.tensor( - zid.reshape(1, *zid.shape), - device=self._device, - ) - - def get_aev(coords): - aev = self._aev_computer.forward((atomic_numbers, coords)).aevs[0] - return aev / _torch.linalg.norm(aev, axis=1, keepdims=True) - # Store the AEV feature vectors. - aev = get_aev(coords).cpu().detach().numpy() + # Reshape the species indices. + zid = zid.reshape(1, *zid.shape) - if not gradient: - return aev + # Reshape the atomic positions and convert to Angstrom. + xyz = xyz.reshape(1, *xyz.shape) * _BOHR_TO_ANGSTROM - from torch.autograd.functional import jacobian + # Compute the AEVs. + aev = self._aev_computer((zid, xyz)).aevs[0] + return aev / _torch.linalg.norm(aev, axis=1, keepdims=True) - grad = jacobian(get_aev, coords, vectorize=True, strategy="forward-mode") - grad = grad.reshape((*aev.shape, -1, 3)).cpu().detach().numpy() - return aev, grad +class _GPRCalculator: + """Predicts an atomic property for a molecule with Gaussian Process Regression (GPR).""" -class _SOAPCalculatorSpinv: - """ - Calculates Smooth Overlap of Atomic Positions (SOAP) feature vectors for - a given system from spherical invariants. - """ - - def __init__(self, hypers): + def __init__(self, ref_values, ref_features, n_ref, sigma, device): """ Constructor Parameters ---------- - hypers: dict - Hyperparameters for rascal SphericalInvariants. - - """ - self.spinv = _SphericalInvariants(**hypers) - - def __call__(self, z, xyz, gradient=False): - """ - Calculates the SOAP feature vectors and their gradients for a - given molecule. - - Parameters - ---------- + ref_values: numpy.array (N_Z, N_REF) + The property values corresponding to the basis vectors for each species. - z: numpy.array (N_ATOMS) - Chemical species (element) for each atom. + ref_features: numpy.array (N_Z, N_REF, N_FEAT) + The basis feature vectors for each species. - xyz: numpy.array (N_ATOMS, 3) - Atomic positions. + n_ref: (N_Z,) + Number of supported species. - gradient: bool - Whether the gradient should be calculated. + sigma: float + The uncertainty of the observations (regularizer). - Returns - ------- + device: torch device + The PyTorch device to use for calculations. + """ + # Store the device and reference features. + self._device = device + self._ref_features = ref_features - soap: numpy.array (N_ATOMS, N_FEAT) - SOAP feature vectors for each atom. + # Compute the inverse of the K matrix. + Kinv = _torch.tensor( + self._get_Kinv(ref_features, sigma), + dtype=_torch.float32, + device=self._device, + ) - gradient: numpy.array (N_ATOMS, N_FEAT, N_ATOMS, 3) - gradients of the soap feature vectors w.r.t. atomic positions - """ - mol = self.get_mol(z, xyz) - return self.get_soap(mol, self.spinv, gradient) + # Store additional attributes for the GPR model. + self._n_ref = n_ref + self._n_z = len(n_ref) + self._ref_mean = _np.sum(ref_values, axis=1) / n_ref + ref_shifted = _torch.tensor( + ref_values - self._ref_mean[:, None], + dtype=_torch.float32, + device=self._device, + ) + self._c = (Kinv @ ref_shifted[:, :, None]).squeeze() - @staticmethod - def get_mol(z, xyz): + def __call__(self, mol_features, zid): """ - Creates Atomic Simulation Environment (ASE) Atoms object from atomic - species and positions. Parameters ---------- - z: numpy.array (N_ATOMS) - Chemical species (element) for each atom. + mol_features: numpy.array (N_ATOMS, N_FEAT) + The feature vectors for each atom. - xyz: numpy.array (N_ATOMS, 3) - Atomic positions. + zid: torch.tensor (N_ATOMS,) + The species identity value of each atom. Returns ------- - result: ase.Atoms - ASE atoms object. + result: torch.tensor, numpy.array (N_ATOMS) + The values of the predicted property for each atom. """ - xyz_min = _np.min(xyz, axis=0) - xyz_max = _np.max(xyz, axis=0) - xyz_range = xyz_max - xyz_min - return _ase.Atoms(z, positions=xyz - xyz_min, cell=xyz_range, pbc=0) - @staticmethod - def get_soap(atoms, spinv, gradient=False): + result = _torch.zeros(len(zid), dtype=_torch.float32, device=self._device) + for i in range(self._n_z): + n_ref = self._n_ref[i] + ref_features_z = _torch.tensor( + self._ref_features[i, :n_ref], dtype=_torch.float32, device=self._device + ) + mol_features_z = mol_features[zid == i, :, None] + + K_mol_ref2 = (ref_features_z @ mol_features_z) ** 2 + K_mol_ref2 = K_mol_ref2.reshape(K_mol_ref2.shape[:-1]) + result[zid == i] = K_mol_ref2 @ self._c[i, :n_ref] + self._ref_mean[i] + + return result + + @classmethod + def _get_Kinv(cls, ref_features, sigma): """ - Calculates the SOAP feature vectors and their gradients for ASE atoms. + Internal function to compute the inverse of the K matrix for GPR. Parameters ---------- - atoms: ase.Atoms - ASE atoms object. + ref_features: numpy.array (N_Z, MAX_N_REF, N_FEAT) + The basis feature vectors for each species. - spinv: rascal.representations.SphericalInvariants - SphericalInvariants object to calculate SOAP features. + sigma: float + The uncertainty of the observations (regularizer). Returns ------- - soap: numpy.array (N_ATOMS, N_FEAT) - SOAP feature vectors for each atom. - - gradient: numpy.array (N_ATOMS, N_FEAT, N_ATOMS, 3) - Gradients of the SOAP feature vectors with respect to atomic positions. + result: numpy.array (MAX_N_REF, MAX_N_REF) + The inverse of the K matrix. """ - managers = spinv.transform(atoms) - soap = managers.get_features(spinv) - if not gradient: - return soap - grad = managers.get_features_gradient(spinv) - meta = managers.get_gradients_info() - n_at, n_soap = soap.shape - dsoap_dxyz = _np.zeros((n_at, n_soap, n_at, 3)) - dsoap_dxyz[meta[:, 1], :, meta[:, 2], :] = grad.reshape( - (-1, 3, n_soap) - ).swapaxes(2, 1) - return soap, dsoap_dxyz + n = ref_features.shape[1] + K = (ref_features @ ref_features.swapaxes(1, 2)) ** 2 + return _np.linalg.inv(K + sigma**2 * _np.eye(n, dtype=_np.float32)) class EMLECalculator: @@ -396,10 +230,6 @@ class EMLECalculator: embedding. Requires the use of a QM (or ML) engine to compute in vacuo energies forces, to which those from the EMLE model are added. Supported backends are listed in the _supported_backends attribute below. - - WARNING: This class is assumed to be static for the purposes of working with - PyTorch, i.e. all attributes assigned in the constructor and used within the - _get_E method are immutable. """ # Class attributes. @@ -408,7 +238,7 @@ class EMLECalculator: _module_dir = _os.path.dirname(_os.path.abspath(__file__)) # Create the name of the default model file. - _default_model = _os.path.join(_module_dir, "emle_spinv.mat") + _default_model = _os.path.join(_module_dir, "emle_qm7_aev.mat") # Default ML model parameters. These will be overwritten by values in the # embedding model file. @@ -448,7 +278,6 @@ class EMLECalculator: def __init__( self, model=None, - features="soap", method="electrostatic", backend="torchani", external_backend=None, @@ -482,9 +311,6 @@ def __init__( Path to the EMLE embedding model parameter file. If None, then a default model will be used. - features: 'aev' or 'soap' - Type of features used to train the EMLE model. - method: str The desired embedding method. Options are: "electrostatic": @@ -1244,6 +1070,8 @@ def __init__( try: import NNPOps as _NNPOps + # Note that NNPOps sometimes leaves a dangling pt_main_thread + # process which keeps the emle-server alive. self._has_nnpops = True self._nnpops_active = False except: @@ -1271,39 +1099,17 @@ def __init__( self._orca_path = abs_orca_path - if features is None: - features = "soap" - if not isinstance(features, str): - msg = "'features' must be of type 'str'" - _logger.error(msg) - raise TypeError(msg) - - # Strip whitespace and convert to lower case. - features = features.lower().replace(" ", "") - - if features not in ["soap", "aev"]: - msg = "'features' must be either 'soap' or 'aev'" - _logger.error(msg) - raise TypeError(msg) - self._features = features - - if self._features == "aev": - # Re-use the existing AEV computer from TorchANI. - if self._backend == "torchani": - self._aev_computer = self._torchani_model.aev_computer - # Create a new AEV computer. - else: - import torchani as _torchani + # Re-use the existing AEV computer from TorchANI. + if self._backend == "torchani": + self._aev_computer = self._torchani_model.aev_computer + # Create a new AEV computer. + else: + import torchani as _torchani - ani2x = _torchani.models.ANI2x(periodic_table_index=True).to( - self._device - ) - self._aev_computer = ani2x.aev_computer + ani2x = _torchani.models.ANI2x(periodic_table_index=True).to(self._device) + self._aev_computer = ani2x.aev_computer - if self._features == "soap": - self._get_features = _SOAPCalculatorSpinv(self._hypers) - else: - self._get_features = _AEVCalculator(self._aev_computer, self._device) + self._get_features = _AEVCalculator(self._aev_computer, self._device) self._q_core = _torch.tensor( self._params["q_core"], dtype=_torch.float32, device=self._device @@ -1327,12 +1133,14 @@ def __init__( self._params["ref_soap"], self._params["n_ref"], 1e-3, + self._device, ) self._get_chi = _GPRCalculator( self._params["chi_ref"], self._params["ref_soap"], self._params["n_ref"], 1e-3, + self._device, ) # Initialise the maximum number of MM atom that have been seen. @@ -1350,7 +1158,6 @@ def __init__( # Store the settings as a dictionary. self._settings = { "model": None if model is None else self._model, - "features": self._features, "method": self._method, "backend": self._backend, "external_backend": None if external_backend is None else external_backend, @@ -1458,7 +1265,7 @@ def run(self, path=None): ) _logger.error(msg) raise ValueError(msg) - self._species_id = _np.array(species_id) + self._species_id = _torch.tensor(_np.array(species_id), device=self._device) # First try to use the specified backend to compute in vacuo # energies and (optionally) gradients. @@ -1559,16 +1366,6 @@ def run(self, path=None): xyz_qm_bohr = xyz_qm * _ANGSTROM_TO_BOHR xyz_mm_bohr = xyz_mm * _ANGSTROM_TO_BOHR - # Get the features and their gradients. - mol_feat, dfeat_dxyz = self._get_features(atomic_numbers, xyz_qm, gradient=True) - dfeat_dxyz_qm_bohr = dfeat_dxyz * _BOHR_TO_ANGSTROM - - # Compute the s and chi values and their gradients. - s, ds_dfeat = self._get_s(mol_feat, self._species_id, gradient=True) - chi, dchi_dfeat = self._get_chi(mol_feat, self._species_id, gradient=True) - ds_dxyz_qm_bohr = self._get_df_dxyz(ds_dfeat, dfeat_dxyz_qm_bohr) - dchi_dxyz_qm_bohr = self._get_df_dxyz(dchi_dfeat, dfeat_dxyz_qm_bohr) - # Convert inputs to Torch tensors. xyz_qm_bohr = _torch.tensor( xyz_qm_bohr, dtype=_torch.float32, device=self._device, requires_grad=True @@ -1579,33 +1376,19 @@ def run(self, path=None): charges_mm = _torch.tensor( charges_mm, dtype=_torch.float32, device=self._device ) - s = _torch.tensor( - s, dtype=_torch.float32, device=self._device, requires_grad=True - ) - chi = _torch.tensor( - chi, dtype=_torch.float32, device=self._device, requires_grad=True - ) # Compute energy and gradients. - E = self._get_E(charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi) - if self._method == "mm": - dE_dxyz_qm_bohr, dE_dxyz_mm_bohr = _torch.autograd.grad( - E, (xyz_qm_bohr, xyz_mm_bohr) - ) - dE_dxyz_qm_bohr = dE_dxyz_qm_bohr.cpu().numpy() - else: - grads = _torch.autograd.grad(E, (xyz_qm_bohr, xyz_mm_bohr, s, chi)) - dE_dxyz_qm_bohr_part, dE_dxyz_mm_bohr, dE_ds, dE_dchi = grads - dE_dxyz_qm_bohr = ( - dE_dxyz_qm_bohr_part.cpu().numpy() - + dE_ds.cpu().numpy() @ ds_dxyz_qm_bohr.swapaxes(0, 1) - + dE_dchi.cpu().numpy() @ dchi_dxyz_qm_bohr.swapaxes(0, 1) - ) + E = self._get_E(charges_mm, xyz_qm_bohr, xyz_mm_bohr) + dE_dxyz_qm_bohr, dE_dxyz_mm_bohr = _torch.autograd.grad( + E, (xyz_qm_bohr, xyz_mm_bohr) + ) + dE_dxyz_qm_bohr = dE_dxyz_qm_bohr.cpu().numpy() + dE_dxyz_mm_bohr = dE_dxyz_mm_bohr.cpu().numpy() # Compute the total energy and gradients. E_tot = E + E_vac grad_qm = dE_dxyz_qm_bohr + grad_vac - grad_mm = dE_dxyz_mm_bohr.cpu().numpy() + grad_mm = dE_dxyz_mm_bohr # Interpolate between the MM and ML/MM potential. if self._is_interpolate: @@ -1626,7 +1409,7 @@ def run(self, path=None): self._method = "mm" # Recompute the energy and gradients. - E = self._get_E(charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi) + E = self._get_E(charges_mm, xyz_qm_bohr, xyz_mm_bohr) dE_dxyz_qm_bohr, dE_dxyz_mm_bohr = _torch.autograd.grad( E, (xyz_qm_bohr, xyz_mm_bohr) ) @@ -1860,7 +1643,7 @@ def _sire_callback(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): ) _logger.error(msg) raise ValueError(msg) - self._species_id = _np.array(species_id) + self._species_id = _torch.tensor(_np.array(species_id), device=self._device) # First try to use the specified backend to compute in vacuo # energies and (optionally) gradients. @@ -1974,16 +1757,6 @@ def _sire_callback(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): xyz_qm_bohr = xyz_qm * _ANGSTROM_TO_BOHR xyz_mm_bohr = xyz_mm * _ANGSTROM_TO_BOHR - # Get the features and their gradients. - mol_feat, dfeat_dxyz = self._get_features(atomic_numbers, xyz_qm, gradient=True) - dfeat_dxyz_qm_bohr = dfeat_dxyz * _BOHR_TO_ANGSTROM - - # Compute the s and chi values and their gradients. - s, ds_dfeat = self._get_s(mol_feat, self._species_id, gradient=True) - chi, dchi_dfeat = self._get_chi(mol_feat, self._species_id, gradient=True) - ds_dxyz_qm_bohr = self._get_df_dxyz(ds_dfeat, dfeat_dxyz_qm_bohr) - dchi_dxyz_qm_bohr = self._get_df_dxyz(dchi_dfeat, dfeat_dxyz_qm_bohr) - # Convert inputs to Torch tensors. xyz_qm_bohr = _torch.tensor( xyz_qm_bohr, dtype=_torch.float32, device=self._device, requires_grad=True @@ -1994,33 +1767,19 @@ def _sire_callback(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): charges_mm = _torch.tensor( charges_mm, dtype=_torch.float32, device=self._device ) - s = _torch.tensor( - s, dtype=_torch.float32, device=self._device, requires_grad=True - ) - chi = _torch.tensor( - chi, dtype=_torch.float32, device=self._device, requires_grad=True - ) # Compute energy and gradients. - E = self._get_E(charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi) - if self._method == "mm": - dE_dxyz_qm_bohr, dE_dxyz_mm_bohr = _torch.autograd.grad( - E, (xyz_qm_bohr, xyz_mm_bohr) - ) - dE_dxyz_qm_bohr = dE_dxyz_qm_bohr.cpu().numpy() - else: - grads = _torch.autograd.grad(E, (xyz_qm_bohr, xyz_mm_bohr, s, chi)) - dE_dxyz_qm_bohr_part, dE_dxyz_mm_bohr, dE_ds, dE_dchi = grads - dE_dxyz_qm_bohr = ( - dE_dxyz_qm_bohr_part.cpu().numpy() - + dE_ds.cpu().numpy() @ ds_dxyz_qm_bohr.swapaxes(0, 1) - + dE_dchi.cpu().numpy() @ dchi_dxyz_qm_bohr.swapaxes(0, 1) - ) + E = self._get_E(charges_mm, xyz_qm_bohr, xyz_mm_bohr) + dE_dxyz_qm_bohr, dE_dxyz_mm_bohr = _torch.autograd.grad( + E, (xyz_qm_bohr, xyz_mm_bohr) + ) + dE_dxyz_qm_bohr = dE_dxyz_qm_bohr.cpu().numpy() + dE_dxyz_mm_bohr = dE_dxyz_mm_bohr.cpu().numpy() # Compute the total energy and gradients. E_tot = E + E_vac grad_qm = dE_dxyz_qm_bohr + grad_vac - grad_mm = dE_dxyz_mm_bohr.cpu().numpy() + grad_mm = dE_dxyz_mm_bohr # Interpolate between the MM and ML/MM potential. if self._is_interpolate: @@ -2045,7 +1804,7 @@ def _sire_callback(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): self._method = "mm" # Recompute the energy and gradients. - E = self._get_E(charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi) + E = self._get_E(charges_mm, xyz_qm_bohr, xyz_mm_bohr) dE_dxyz_qm_bohr, dE_dxyz_mm_bohr = _torch.autograd.grad( E, (xyz_qm_bohr, xyz_mm_bohr) ) @@ -2115,7 +1874,7 @@ def _sire_callback(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): ).tolist(), ) - def _get_E(self, charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi): + def _get_E(self, charges_mm, xyz_qm_bohr, xyz_mm_bohr): """ Computes total EMLE embedding energy (sum of static and induced). @@ -2132,23 +1891,15 @@ def _get_E(self, charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi): Positions of MM atoms (in bohr units), padded to max_mm_atoms with zeros. - s: torch.tensor (N_ATOMS,) - MBIS valence shell widths. - - chi: torch.tensor (N_ATOMS,) - Electronegativities. - Returns ------- result: torch.tensor (1,) Total EMLE embedding energy. """ - return _torch.sum( - self._get_E_components(charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi) - ) + return _torch.sum(self._get_E_components(charges_mm, xyz_qm_bohr, xyz_mm_bohr)) - def _get_E_components(self, charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi): + def _get_E_components(self, charges_mm, xyz_qm_bohr, xyz_mm_bohr): """ Computes EMLE energy components @@ -2165,18 +1916,22 @@ def _get_E_components(self, charges_mm, xyz_qm_bohr, xyz_mm_bohr, s, chi): Positions of MM atoms (in bohr units), padded to max_mm_atoms with zeros - s: torch.tensor (N_ATOMS,) - MBIS valence shell widths. - - chi: torch.tensor (N_ATOMS,) - Electronegativities. - Returns ------- result: torch.tensor (2,) Values of static and induced EMLE energy components. """ + + # Get the features. + mol_features = self._get_features(self._species_id, xyz_qm_bohr) + + # Compute the MBIS valence shell widths. + s = self._get_s(mol_features, self._species_id) + + # Compute the electronegativities. + chi = self._get_chi(mol_features, self._species_id) + if self._method != "mm": q_core = self._q_core[self._species_id] else: @@ -2365,26 +2120,6 @@ def _get_A_thole(self, r_data, s, q_val, k_Z): return A - @staticmethod - def _get_df_dxyz(df_dfeat, dfeat_dxyz): - """ - Internal method to calculate the gradient of some property with respect to - xyz coordinates based on gradient with respect to feature and feature gradients. - - Parameters - ---------- - - df_dfeat: numpy.array (N_ATOMS, N_FEAT) - - dfeat_dxyz: numpy.array (N_ATOMS, N_FEAT, N_ATOMS, 3) - - Returns - ------- - - result: numpy.array (N_ATOMS, N_ATOMS, 3) - """ - return _np.einsum("ij,ijkl->ikl", df_dfeat, dfeat_dxyz) - @staticmethod def _get_vpot_q(q, T0): """ @@ -2907,9 +2642,8 @@ def _run_torchani(self, xyz, atomic_numbers): # Flag that NNPOps is active. self._nnpops_active = True - # Applyt the optimised AEV symmetry functions. - if self._features == "aev": - self._aev_computer = self._torchani_model.aev_computer + # Apply the optimised AEV symmetry functions. + self._aev_computer = self._torchani_model.aev_computer # Compute the energy and gradient. energy = self._torchani_model((atomic_numbers, coords)).energies From 59d11a058bc545318a93fc964b46fd6d4ec8127e Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Fri, 28 Jun 2024 14:23:29 +0100 Subject: [PATCH 020/134] AEVCalculator doesn't need device argument. [ci skip] --- emle/calculator.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 64602cf..64b2fc2 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -72,7 +72,7 @@ class _AEVCalculator: Calculates AEV feature vectors for a given system """ - def __init__(self, aev_computer, device): + def __init__(self, aev_computer): """ Constructor @@ -86,7 +86,6 @@ def __init__(self, aev_computer, device): The PyTorch device to use for calculations. """ self._aev_computer = aev_computer - self._device = device def __call__(self, zid, xyz): """ @@ -1109,7 +1108,7 @@ def __init__( ani2x = _torchani.models.ANI2x(periodic_table_index=True).to(self._device) self._aev_computer = ani2x.aev_computer - self._get_features = _AEVCalculator(self._aev_computer, self._device) + self._get_features = _AEVCalculator(self._aev_computer) self._q_core = _torch.tensor( self._params["q_core"], dtype=_torch.float32, device=self._device From e5346b4e1f65fecc0bdc390d3785ade680528af4 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Fri, 28 Jun 2024 14:32:52 +0100 Subject: [PATCH 021/134] Remove comment about pt_main_thread issue. [ci skip] --- emle/calculator.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 64b2fc2..934441c 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1069,8 +1069,6 @@ def __init__( try: import NNPOps as _NNPOps - # Note that NNPOps sometimes leaves a dangling pt_main_thread - # process which keeps the emle-server alive. self._has_nnpops = True self._nnpops_active = False except: From 48d83ebc9a347b3dc0d798accc8329a929756017 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Fri, 28 Jun 2024 15:14:13 +0100 Subject: [PATCH 022/134] Remove redundant global. [ci skip] --- emle/calculator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/emle/calculator.py b/emle/calculator.py index 934441c..38bbc9d 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -56,7 +56,6 @@ # Settings for the default model. For system specific models, these will be # overwritten by values in the model file. _SPECIES = (1, 6, 7, 8, 16) -_SIGMA = 1e-3 _SPHERICAL_EXPANSION_HYPERS_COMMON = { "gaussian_sigma_constant": 0.5, "gaussian_sigma_type": "Constant", From 47edb09ddb111b61e33e551cb69c46cc1b4db6d5 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Fri, 28 Jun 2024 15:17:20 +0100 Subject: [PATCH 023/134] Docstring tweaks. [ci skip] --- emle/calculator.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 38bbc9d..53f3d5b 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1878,14 +1878,13 @@ def _get_E(self, charges_mm, xyz_qm_bohr, xyz_mm_bohr): ---------- charges_mm: torch.tensor (max_mm_atoms,) - MM point charges, padded to max_mm_atoms with zeros. + MM point charges in atomic units, padded to max_mm_atoms with zeros. xyz_qm_bohr: torch.tensor (N_ATOMS, 3) - Positions of QM atoms (in bohr units). + Positions of QM atoms (in Bohr). xyz_mm_bohr: torch.tensor (max_mm_atoms, 3) - Positions of MM atoms (in bohr units), - padded to max_mm_atoms with zeros. + Positions of MM atoms (in Bohr), padded to max_mm_atoms with zeros. Returns ------- @@ -1903,14 +1902,13 @@ def _get_E_components(self, charges_mm, xyz_qm_bohr, xyz_mm_bohr): ---------- charges_mm: torch.tensor (max_mm_atoms,) - MM point charges, padded to max_mm_atoms with zeros. + MM point charges in atomic units, padded to max_mm_atoms with zeros. xyz_qm_bohr: torch.tensor (N_ATOMS, 3) - Positions of QM atoms (in bohr units). + Positions of QM atoms (in Bohr). xyz_mm_bohr: torch.tensor (max_mm_atoms, 3) - Positions of MM atoms (in bohr units), - padded to max_mm_atoms with zeros + Positions of MM atoms (in Bohr), padded to max_mm_atoms with zeros. Returns ------- From 82502e0b049aa6c37de2f41a2a426c39f4048de6 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Fri, 28 Jun 2024 15:24:07 +0100 Subject: [PATCH 024/134] Add initial AEV based EMLE torch.nn.Module model. [ci skip] --- emle/_models.py | 773 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 773 insertions(+) create mode 100644 emle/_models.py diff --git a/emle/_models.py b/emle/_models.py new file mode 100644 index 0000000..b86e4b0 --- /dev/null +++ b/emle/_models.py @@ -0,0 +1,773 @@ +####################################################################### +# EMLE-Engine: https://github.com/chemle/emle-engine +# +# Copyright: 2023-2024 +# +# Authors: Lester Hedges +# Kirill Zinovjev +# +# EMLE-Engine is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# EMLE-Engine is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with EMLE-Engine If not, see . +##################################################################### + +"""EMLE model implementation.""" + +__author__ = "Lester Hedges" +__email__ = "lester.hedges@gmail.com" + +__all__ = ["EMLE"] + +import ase as _ase +import numpy as _np +import os as _os +import scipy.io as _scipy_io +import torch as _torch +import torchani as _torchani + +_ANGSTROM_TO_BOHR = 1.0 / _ase.units.Bohr +_BOHR_TO_ANGSTROM = _ase.units.Bohr + +# Settings for the default model. For system specific models, these will be +# overwritten by values in the model file. +_SPECIES = (1, 6, 7, 8, 16) +_SIGMA = 1e-3 +_SPHERICAL_EXPANSION_HYPERS_COMMON = { + "gaussian_sigma_constant": 0.5, + "gaussian_sigma_type": "Constant", + "cutoff_smooth_width": 0.5, + "radial_basis": "GTO", + "expansion_by_species_method": "user defined", + "global_species": _SPECIES, +} + + +class _AEVCalculator: + """ + Calculates AEV feature vectors for a given system + """ + + def __init__(self, device): + """ + Constructor + + Parameters + ---------- + + device: torch device + The PyTorch device to use for calculations. + """ + self._device = device + + # Create the AEV computer. + ani2x = _torchani.models.ANI2x(periodic_table_index=True).to(self._device) + self._aev_computer = ani2x.aev_computer + + def __call__(self, zid, xyz): + """ + Calculates the AEV feature vectors for a given molecule. + + Parameters + ---------- + + zid: numpy.array (N_ATOMS) + Chemical species indices for each atom. + + xyz: torch.tensor (N_ATOMS, 3) + Atomic positions in Bohr. + + Returns + ------- + + aev: torch.tensor (N_ATOMS, N_AEV) + AEV feature vectors for each atom. + """ + + # Reshape the species indices. + zid = zid.reshape(1, *zid.shape) + + # Reshape the atomic positions and convert to Angstrom. + xyz = xyz.reshape(1, *xyz.shape) * _BOHR_TO_ANGSTROM + + # Compute the AEVs. + aev = self._aev_computer((zid, xyz)).aevs[0] + return aev / _torch.linalg.norm(aev, axis=1, keepdims=True) + + +class _GPRCalculator: + """Predicts an atomic property for a molecule with Gaussian Process Regression (GPR).""" + + def __init__(self, ref_values, ref_features, n_ref, sigma, device): + """ + Constructor + + Parameters + ---------- + + ref_values: numpy.array (N_Z, N_REF) + The property values corresponding to the basis vectors for each species. + + ref_features: numpy.array (N_Z, N_REF, N_FEAT) + The basis feature vectors for each species. + + n_ref: (N_Z,) + Number of supported species. + + sigma: float + The uncertainty of the observations (regularizer). + + device: torch device + The PyTorch device to use for calculations. + """ + # Store the device and reference features. + self._device = device + self._ref_features = ref_features + + # Compute the inverse of the K matrix. + Kinv = _torch.tensor( + self._get_Kinv(ref_features, sigma), + dtype=_torch.float32, + device=self._device, + ) + + # Store additional attributes for the GPR model. + self._n_ref = n_ref + self._n_z = len(n_ref) + self._ref_mean = _np.sum(ref_values, axis=1) / n_ref + ref_shifted = _torch.tensor( + ref_values - self._ref_mean[:, None], + dtype=_torch.float32, + device=self._device, + ) + self._c = (Kinv @ ref_shifted[:, :, None]).squeeze() + + def __call__(self, mol_features, zid): + """ + + Parameters + ---------- + + mol_features: numpy.array (N_ATOMS, N_FEAT) + The feature vectors for each atom. + + zid: torch.tensor (N_ATOMS,) + The species identity value of each atom. + + Returns + ------- + + result: torch.tensor, numpy.array (N_ATOMS) + The values of the predicted property for each atom. + """ + + result = _torch.zeros(len(zid), dtype=_torch.float32, device=self._device) + for i in range(self._n_z): + n_ref = self._n_ref[i] + ref_features_z = _torch.tensor( + self._ref_features[i, :n_ref], dtype=_torch.float32, device=self._device + ) + mol_features_z = mol_features[zid == i, :, None] + + K_mol_ref2 = (ref_features_z @ mol_features_z) ** 2 + K_mol_ref2 = K_mol_ref2.reshape(K_mol_ref2.shape[:-1]) + result[zid == i] = K_mol_ref2 @ self._c[i, :n_ref] + self._ref_mean[i] + + return result + + @classmethod + def _get_Kinv(cls, ref_features, sigma): + """ + Internal function to compute the inverse of the K matrix for GPR. + + Parameters + ---------- + + ref_features: numpy.array (N_Z, MAX_N_REF, N_FEAT) + The basis feature vectors for each species. + + sigma: float + The uncertainty of the observations (regularizer). + + Returns + ------- + + result: numpy.array (MAX_N_REF, MAX_N_REF) + The inverse of the K matrix. + """ + n = ref_features.shape[1] + K = (ref_features @ ref_features.swapaxes(1, 2)) ** 2 + return _np.linalg.inv(K + sigma**2 * _np.eye(n, dtype=_np.float32)) + + +class EMLE(_torch.nn.Module): + """ + Predicts EMLE energies and gradients allowing QM/MM with ML electrostatic + embedding. + """ + + # Class attributes. + + # Get the directory of this module file. + _module_dir = _os.path.dirname(_os.path.abspath(__file__)) + + # Create the name of the default model file. + _model = _os.path.join(_module_dir, "emle_qm7_aev.mat") + + # Default ML model parameters. These will be overwritten by values in the + # embedding model file. + + # Model hyper-parameters. + _hypers = { + "interaction_cutoff": 3.0, + "max_radial": 4, + "max_angular": 4, + "compute_gradients": True, + **_SPHERICAL_EXPANSION_HYPERS_COMMON, + } + + def __init__(self, device): + """ + Constructor + + Parameters + ---------- + + device: torch device + The PyTorch device to use for calculations. + """ + + if not isinstance(device, _torch.device): + raise TypeError("'device' must be of type 'torch.device'") + self._device = device + + # Create an AEV calculator to perform the feature calculations. + self._get_features = _AEVCalculator(self._device) + + # Load the model parameters. + try: + self._params = _scipy_io.loadmat(self._model, squeeze_me=True) + except: + raise IOError(f"Unable to load model parameters from: '{self._model}'") + + # Store model parameters as tensors. + self._q_core = _torch.tensor( + self._params["q_core"], dtype=_torch.float32, device=self._device + ) + self._a_QEq = self._params["a_QEq"] + self._a_Thole = self._params["a_Thole"] + self._k_Z = _torch.tensor( + self._params["k_Z"], dtype=_torch.float32, device=self._device + ) + self._q_total = _torch.tensor( + self._params.get("total_charge", 0), + dtype=_torch.float32, + device=self._device, + ) + self._get_s = _GPRCalculator( + self._params["s_ref"], + self._params["ref_soap"], + self._params["n_ref"], + 1e-3, + self._device, + ) + self._get_chi = _GPRCalculator( + self._params["chi_ref"], + self._params["ref_soap"], + self._params["n_ref"], + 1e-3, + self._device, + ) + + # Initialise EMLE embedding model attributes. + hypers_keys = ( + "gaussian_sigma_constant", + "global_species", + "interaction_cutoff", + "max_radial", + "max_angular", + ) + for key in hypers_keys: + if key in self._params: + try: + self._hypers[key] = tuple(self._params[key].tolist()) + except: + self._hypers[key] = self._params[key] + + def forward(self, atomic_numbers, charges_mm, xyz_qm_bohr, xyz_mm_bohr): + """ + Computes the static and induced EMLE energy components. + + Parameters + ---------- + + atomic_numbers: torch.tensor (N_QM_ATOMS,) + Atomic numbers of QM atoms. + + charges_mm: torch.tensor (max_mm_atoms,) + MM point charges in atomic units. + + xyz_qm_bohr: torch.tensor (N_QM_ATOMS, 3) + Positions of QM atoms in Bohr. + + xyz_mm_bohr: torch.tensor (N_MM_ATOMS, 3) + Positions of MM atoms in Bohr. + + Returns + ------- + + result: torch.tensor (2,) + Values of static and induced EMLE energy components. + """ + + # Convert the QM atomic numbers to elements and species IDs. + species_id = [] + for id in atomic_numbers: + try: + species_id.append(self._hypers["global_species"].index(id)) + except: + msg = f"Unsupported element index '{id}'." + raise ValueError(msg) + species_id = _torch.tensor(_np.array(species_id), device=self._device) + + # Get the features. + mol_features = self._get_features(species_id, xyz_qm_bohr) + + # Compute the MBIS valence shell widths. + s = self._get_s(mol_features, species_id) + + # Compute the electronegativities. + chi = self._get_chi(mol_features, species_id) + + q_core = self._q_core[species_id] + k_Z = self._k_Z[species_id] + r_data = self._get_r_data(xyz_qm_bohr, self._device) + mesh_data = self._get_mesh_data(xyz_qm_bohr, xyz_mm_bohr, s) + q = self._get_q(r_data, s, chi) + q_val = q - q_core + mu_ind = self._get_mu_ind(r_data, mesh_data, charges_mm, s, q_val, k_Z) + vpot_q_core = self._get_vpot_q(q_core, mesh_data["T0_mesh"]) + vpot_q_val = self._get_vpot_q(q_val, mesh_data["T0_mesh_slater"]) + vpot_static = vpot_q_core + vpot_q_val + E_static = _torch.sum(vpot_static @ charges_mm) + + vpot_ind = self._get_vpot_mu(mu_ind, mesh_data["T1_mesh"]) + E_ind = _torch.sum(vpot_ind @ charges_mm) * 0.5 + + return _torch.stack([E_static, E_ind]) + + def _get_q(self, r_data, s, chi): + """ + Internal method that predicts MBIS charges + (Eq. 16 in 10.1021/acs.jctc.2c00914) + + Parameters + ---------- + + r_data: r_data object (output of self._get_r_data) + + s: torch.tensor (N_ATOMS,) + MBIS valence shell widths. + + chi: torch.tensor (N_ATOMS,) + Electronegativities. + + Returns + ------- + + result: torch.tensor (N_ATOMS,) + Predicted MBIS charges. + """ + A = self._get_A_QEq(r_data, s) + b = _torch.hstack([-chi, self._q_total]) + return _torch.linalg.solve(A, b)[:-1] + + def _get_A_QEq(self, r_data, s): + """ + Internal method, generates A matrix for charge prediction + (Eq. 16 in 10.1021/acs.jctc.2c00914) + + Parameters + ---------- + + r_data: r_data object (output of self._get_r_data) + + s: torch.tensor (N_ATOMS,) + MBIS valence shell widths. + + Returns + ------- + + result: torch.tensor (N_ATOMS + 1, N_ATOMS + 1) + """ + s_gauss = s * self._a_QEq + s2 = s_gauss**2 + s_mat = _torch.sqrt(s2[:, None] + s2[None, :]) + + A = self._get_T0_gaussian(r_data["T01"], r_data["r_mat"], s_mat) + + new_diag = _torch.ones_like( + A.diagonal(), dtype=_torch.float32, device=self._device + ) * (1.0 / (s_gauss * _np.sqrt(_np.pi))) + mask = _torch.diag( + _torch.ones_like(new_diag, dtype=_torch.float32, device=self._device) + ) + A = mask * _torch.diag(new_diag) + (1.0 - mask) * A + + # Store the dimensions of A. + x, y = A.shape + + # Create an tensor of ones with one more row and column than A. + B = _torch.ones(x + 1, y + 1, dtype=_torch.float32, device=self._device) + + # Copy A into B. + B[:x, :y] = A + + # Set the final entry on the diagonal to zero. + B[-1, -1] = 0.0 + + return B + + def _get_mu_ind(self, r_data, mesh_data, q, s, q_val, k_Z): + """ + Internal method, calculates induced atomic dipoles + (Eq. 20 in 10.1021/acs.jctc.2c00914) + + Parameters + ---------- + + r_data: r_data object (output of self._get_r_data) + + mesh_data: mesh_data object (output of self._get_mesh_data) + + q: torch.tensor (N_MM_ATOMS,) + MM point charges. + + s: torch.tensor (N_QM_ATOMS,) + MBIS valence shell widths. + + q_val: torch.tensor (N_QM_ATOMS,) + MBIS valence charges. + + k_Z: torch.tensor (N_Z) + Scaling factors for polarizabilities. + + Returns + ------- + + result: torch.tensor (N_ATOMS, 3) + Array of induced dipoles + """ + A = self._get_A_thole(r_data, s, q_val, k_Z) + + r = 1.0 / mesh_data["T0_mesh"] + f1 = self._get_f1_slater(r, s[:, None] * 2.0) + fields = _torch.sum( + mesh_data["T1_mesh"] * f1[:, :, None] * q[:, None], axis=1 + ).flatten() + + mu_ind = _torch.linalg.solve(A, fields) + E_ind = mu_ind @ fields * 0.5 + return mu_ind.reshape((-1, 3)) + + def _get_A_thole(self, r_data, s, q_val, k_Z): + """ + Internal method, generates A matrix for induced dipoles prediction + (Eq. 20 in 10.1021/acs.jctc.2c00914) + + Parameters + ---------- + + r_data: r_data object (output of self._get_r_data) + + s: torch.tensor (N_ATOMS,) + MBIS valence shell widths. + + q_val: torch.tensor (N_ATOMS,) + MBIS charges. + + k_Z: torch.tensor (N_Z) + Scaling factors for polarizabilities. + + Returns + ------- + + result: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + The A matrix for induced dipoles prediction. + """ + v = -60 * q_val * s**3 + alpha = v * k_Z + + alphap = alpha * self._a_Thole + alphap_mat = alphap[:, None] * alphap[None, :] + + au3 = r_data["r_mat"] ** 3 / _torch.sqrt(alphap_mat) + au31 = au3.repeat_interleave(3, dim=1) + au32 = au31.repeat_interleave(3, dim=0) + + A = -self._get_T2_thole(r_data["T21"], r_data["T22"], au32) + + new_diag = 1.0 / alpha.repeat_interleave(3) + mask = _torch.diag( + _torch.ones_like(new_diag, dtype=_torch.float32, device=self._device) + ) + A = mask * _torch.diag(new_diag) + (1.0 - mask) * A + + return A + + @staticmethod + def _get_vpot_q(q, T0): + """ + Internal method to calculate the electrostatic potential. + + Parameters + ---------- + + q: torch.tensor (N_MM_ATOMS,) + MM point charges. + + T0: torch.tensor (N_QM_ATOMS, max_mm_atoms) + T0 tensor for QM atoms over MM atom positions. + + Returns + ------- + + result: torch.tensor (max_mm_atoms) + Electrostatic potential over MM atoms. + """ + return _torch.sum(T0 * q[:, None], axis=0) + + @staticmethod + def _get_vpot_mu(mu, T1): + """ + Internal method to calculate the electrostatic potential generated + by atomic dipoles. + + Parameters + ---------- + + mu: torch.tensor (N_ATOMS, 3) + Atomic dipoles. + + T1: torch.tensor (N_ATOMS, max_mm_atoms, 3) + T1 tensor for QM atoms over MM atom positions. + + Returns + ------- + + result: torch.tensor (max_mm_atoms) + Electrostatic potential over MM atoms. + """ + return -_torch.tensordot(T1, mu, ((0, 2), (0, 1))) + + @classmethod + def _get_r_data(cls, xyz, device): + """ + Internal method to calculate r_data object. + + Parameters + ---------- + + xyz: torch.tensor (N_ATOMS, 3) + Atomic positions. + + device: torch.device + The PyTorch device to use. + + Returns + ------- + + result: r_data object + """ + n_atoms = len(xyz) + + rr_mat = xyz[:, None, :] - xyz[None, :, :] + r_mat = _torch.cdist(xyz, xyz) + r_inv = _torch.where(r_mat == 0.0, 0.0, 1.0 / r_mat) + + r_inv1 = r_inv.repeat_interleave(3, dim=1) + r_inv2 = r_inv1.repeat_interleave(3, dim=0) + + # Get a stacked matrix of outer products over the rr_mat tensors. + outer = _torch.einsum("bik,bij->bjik", rr_mat, rr_mat).reshape( + (n_atoms * 3, n_atoms * 3) + ) + + id2 = _torch.tile( + _torch.tile( + _torch.eye(3, dtype=_torch.float32, device=device).T, (1, n_atoms) + ).T, + (1, n_atoms), + ) + + t01 = r_inv + t11 = -rr_mat.reshape(n_atoms, n_atoms * 3) * r_inv1**3 + t21 = -id2 * r_inv2**3 + t22 = 3 * outer * r_inv2**5 + + return {"r_mat": r_mat, "T01": t01, "T11": t11, "T21": t21, "T22": t22} + + @classmethod + def _get_mesh_data(cls, xyz, xyz_mesh, s): + """ + Internal method, calculates mesh_data object. + + Parameters + ---------- + + xyz: torch.tensor (N_ATOMS, 3) + Atomic positions. + + xyz_mesh: torch.tensor (max_mm_atoms, 3) + MM positions. + + s: torch.tensor (N_ATOMS,) + MBIS valence widths. + """ + rr = xyz_mesh[None, :, :] - xyz[:, None, :] + r = _torch.linalg.norm(rr, axis=2) + + return { + "T0_mesh": 1.0 / r, + "T0_mesh_slater": cls._get_T0_slater(r, s[:, None]), + "T1_mesh": -rr / r[:, :, None] ** 3, + } + + @classmethod + def _get_f1_slater(cls, r, s): + """ + Internal method, calculates damping factors for Slater densities. + + Parameters + ---------- + + r: torch.tensor (N_ATOMS, max_mm_atoms) + Distances from QM to MM atoms. + + s: torch.tensor (N_ATOMS,) + MBIS valence widths. + + Returns + ------- + + result: torch.tensor (N_ATOMS, max_mm_atoms) + """ + return ( + cls._get_T0_slater(r, s) * r + - _torch.exp(-r / s) / s * (0.5 + r / (s * 2)) * r + ) + + @staticmethod + def _get_T0_slater(r, s): + """ + Internal method, calculates T0 tensor for Slater densities. + + Parameters + ---------- + + r: torch.tensor (N_ATOMS, max_mm_atoms) + Distances from QM to MM atoms. + + s: torch.tensor (N_ATOMS,) + MBIS valence widths. + + Returns + ------- + + results: torch.tensor (N_ATOMS, max_mm_atoms) + """ + return (1 - (1 + r / (s * 2)) * _torch.exp(-r / s)) / r + + @staticmethod + def _get_T0_gaussian(t01, r, s_mat): + """ + Internal method, calculates T0 tensor for Gaussian densities (for QEq). + + Parameters + ---------- + + t01: torch.tensor (N_ATOMS, N_ATOMS) + T0 tensor for QM atoms. + + r: torch.tensor (N_ATOMS, N_ATOMS) + Distance matrix for QM atoms. + + s_mat: torch.tensor (N_ATOMS, N_ATOMS) + Matrix of Gaussian sigmas for QM atoms. + + Returns + ------- + + results: torch.tensor (N_ATOMS, N_ATOMS) + """ + return t01 * _torch.erf(r / (s_mat * _np.sqrt(2))) + + @classmethod + def _get_T2_thole(cls, tr21, tr22, au3): + """ + Internal method, calculates T2 tensor with Thole damping. + + Parameters + ---------- + + tr21: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + r_data["T21"] + + tr21: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + r_data["T22"] + + au3: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + Scaled distance matrix (see _get_A_thole). + + Returns + ------- + + result: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + """ + return cls._lambda3(au3) * tr21 + cls._lambda5(au3) * tr22 + + @staticmethod + def _lambda3(au3): + """ + Internal method, calculates r^3 component of T2 tensor with Thole + damping. + + Parameters + ---------- + + au3: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + Scaled distance matrix (see _get_A_thole). + + Returns + ------- + + result: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + """ + return 1 - _torch.exp(-au3) + + @staticmethod + def _lambda5(au3): + """ + Internal method, calculates r^5 component of T2 tensor with Thole + damping. + + Parameters + ---------- + + au3: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + Scaled distance matrix (see _get_A_thole). + + Returns + ------- + + result: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + """ + return 1 - (1 + au3) * _torch.exp(-au3) From 9a7bc1ab18b71c5b97da4920f5bc317fb6d3688d Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Fri, 28 Jun 2024 16:10:40 +0100 Subject: [PATCH 025/134] Call base class constructor to ensure all attributes are initialised. [ci skip] --- emle/_models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/emle/_models.py b/emle/_models.py index b86e4b0..148f116 100644 --- a/emle/_models.py +++ b/emle/_models.py @@ -245,6 +245,9 @@ def __init__(self, device): The PyTorch device to use for calculations. """ + # Call the base class constructor. + super().__init__() + if not isinstance(device, _torch.device): raise TypeError("'device' must be of type 'torch.device'") self._device = device From bb7799e9f9296210058b94c2e6ea20d9df69f702 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Fri, 28 Jun 2024 16:51:00 +0100 Subject: [PATCH 026/134] Add combined ANI2x-EMLE model. [ci skip] --- emle/_models.py | 115 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 3 deletions(-) diff --git a/emle/_models.py b/emle/_models.py index 148f116..f81add6 100644 --- a/emle/_models.py +++ b/emle/_models.py @@ -25,7 +25,7 @@ __author__ = "Lester Hedges" __email__ = "lester.hedges@gmail.com" -__all__ = ["EMLE"] +__all__ = ["EMLE", "ANI2xEMLE"] import ase as _ase import numpy as _np @@ -234,7 +234,7 @@ class EMLE(_torch.nn.Module): **_SPHERICAL_EXPANSION_HYPERS_COMMON, } - def __init__(self, device): + def __init__(self, device, create_aev_calculator=True): """ Constructor @@ -243,6 +243,9 @@ def __init__(self, device): device: torch device The PyTorch device to use for calculations. + + create_aev_calculator: bool + Whether to create an AEV calculator instance. """ # Call the base class constructor. @@ -252,8 +255,12 @@ def __init__(self, device): raise TypeError("'device' must be of type 'torch.device'") self._device = device + if not isinstance(create_aev_calculator, bool): + raise TypeError("'create_aev_calculator' must be of type 'bool'") + # Create an AEV calculator to perform the feature calculations. - self._get_features = _AEVCalculator(self._device) + if create_aev_calculator: + self._get_features = _AEVCalculator(self._device) # Load the model parameters. try: @@ -350,6 +357,7 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm_bohr, xyz_mm_bohr): # Compute the electronegativities. chi = self._get_chi(mol_features, species_id) + # Compute the static energy. q_core = self._q_core[species_id] k_Z = self._k_Z[species_id] r_data = self._get_r_data(xyz_qm_bohr, self._device) @@ -362,6 +370,7 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm_bohr, xyz_mm_bohr): vpot_static = vpot_q_core + vpot_q_val E_static = _torch.sum(vpot_static @ charges_mm) + # Compute the induced energy. vpot_ind = self._get_vpot_mu(mu_ind, mesh_data["T1_mesh"]) E_ind = _torch.sum(vpot_ind @ charges_mm) * 0.5 @@ -774,3 +783,103 @@ def _lambda5(au3): result: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) """ return 1 - (1 + au3) * _torch.exp(-au3) + + +class ANI2xEMLE(EMLE): + def __init__(self, device): + """ + Constructor + + Parameters + ---------- + + device: torch device + The PyTorch device to use for calculations. + """ + super().__init__(device, create_aev_calculator=False) + + # Create the ANI2x model. + self._ani2x = _torchani.models.ANI2x(periodic_table_index=True).to(self._device) + + # Hook the forward pass of the ANI2x model to get the AEV features. + def hook_wrapper(): + def hook(module, input, output): + self._aevs = output.aevs[0] + + return hook + + # Register the hook. + self._aev_hook = self._ani2x.aev_computer.register_forward_hook(hook_wrapper()) + + def forward(self, atomic_numbers, charges_mm, xyz_qm_bohr, xyz_mm_bohr): + """ + Computes the static and induced EMLE energy components. + + Parameters + ---------- + + atomic_numbers: torch.tensor (N_QM_ATOMS,) + Atomic numbers of QM atoms. + + charges_mm: torch.tensor (max_mm_atoms,) + MM point charges in atomic units. + + xyz_qm_bohr: torch.tensor (N_QM_ATOMS, 3) + Positions of QM atoms in Bohr. + + xyz_mm_bohr: torch.tensor (N_MM_ATOMS, 3) + Positions of MM atoms in Bohr. + + Returns + ------- + + result: torch.tensor (2,) + Values of static and induced EMLE energy components. + """ + + # Convert the QM atomic numbers to elements and species IDs. + species_id = [] + for id in atomic_numbers: + try: + species_id.append(self._hypers["global_species"].index(id)) + except: + msg = f"Unsupported element index '{id}'." + raise ValueError(msg) + species_id = _torch.tensor(_np.array(species_id), device=self._device) + + # Reshape the atomic numbers. + atomic_numbers = atomic_numbers.reshape(1, *atomic_numbers.shape) + + # Convert coordinates to Angstrom and reshape. + xyz_qm = xyz_qm_bohr.reshape(1, *xyz_qm_bohr.shape) * _BOHR_TO_ANGSTROM + + # Get the in vacuo energy. + E_vac = self._ani2x((atomic_numbers, xyz_qm)).energies[0] + + # Normalise the AEVs. + self._aevs = self._aevs / _torch.linalg.norm(self._aevs, axis=1, keepdims=True) + + # Compute the MBIS valence shell widths. + s = self._get_s(self._aevs, species_id) + + # Compute the electronegativities. + chi = self._get_chi(self._aevs, species_id) + + # Compute the static energy. + q_core = self._q_core[species_id] + k_Z = self._k_Z[species_id] + r_data = self._get_r_data(xyz_qm_bohr, self._device) + mesh_data = self._get_mesh_data(xyz_qm_bohr, xyz_mm_bohr, s) + q = self._get_q(r_data, s, chi) + q_val = q - q_core + mu_ind = self._get_mu_ind(r_data, mesh_data, charges_mm, s, q_val, k_Z) + vpot_q_core = self._get_vpot_q(q_core, mesh_data["T0_mesh"]) + vpot_q_val = self._get_vpot_q(q_val, mesh_data["T0_mesh_slater"]) + vpot_static = vpot_q_core + vpot_q_val + E_static = _torch.sum(vpot_static @ charges_mm) + + # Compute the induced energy. + vpot_ind = self._get_vpot_mu(mu_ind, mesh_data["T1_mesh"]) + E_ind = _torch.sum(vpot_ind @ charges_mm) * 0.5 + + return _torch.stack([E_vac, E_static, E_ind]) From 80f3e357b79a70e20592a4c656e189f930fdfd84 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Fri, 28 Jun 2024 17:10:56 +0100 Subject: [PATCH 027/134] Rename models module. [ci skip] --- emle/{_models.py => models.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename emle/{_models.py => models.py} (100%) diff --git a/emle/_models.py b/emle/models.py similarity index 100% rename from emle/_models.py rename to emle/models.py From e16a348a61f746642a2d49f36cc07ffd46139c6d Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Sun, 30 Jun 2024 10:58:35 +0100 Subject: [PATCH 028/134] Handle situations where there are no point charges. --- emle/models.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/emle/models.py b/emle/models.py index f81add6..31f0b48 100644 --- a/emle/models.py +++ b/emle/models.py @@ -34,7 +34,6 @@ import torch as _torch import torchani as _torchani -_ANGSTROM_TO_BOHR = 1.0 / _ase.units.Bohr _BOHR_TO_ANGSTROM = _ase.units.Bohr # Settings for the default model. For system specific models, these will be @@ -338,6 +337,10 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm_bohr, xyz_mm_bohr): Values of static and induced EMLE energy components. """ + # If there are no point charges, return zeros. + if len(xyz_mm_bohr) == 0: + return _torch.zeros(2, dtype=_torch.float32, device=self._device) + # Convert the QM atomic numbers to elements and species IDs. species_id = [] for id in atomic_numbers: @@ -856,6 +859,12 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm_bohr, xyz_mm_bohr): # Get the in vacuo energy. E_vac = self._ani2x((atomic_numbers, xyz_qm)).energies[0] + # If there are no point charges, return the in vacuo energy and zeros + # for the static and induced terms. + if len(xyz_mm_bohr) == 0: + zero = _torch.tensor(0.0, dtype=_torch.float32, device=self._device) + return _torch.stack([E_vac, zero, zero]) + # Normalise the AEVs. self._aevs = self._aevs / _torch.linalg.norm(self._aevs, axis=1, keepdims=True) From 74d5f38a22b935086e7aaf1248d4e35892151eea Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Sun, 30 Jun 2024 11:14:14 +0100 Subject: [PATCH 029/134] Use same unit system as ANI2x, i.e. Angstrom and Hartree. --- emle/models.py | 57 ++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/emle/models.py b/emle/models.py index 31f0b48..f309992 100644 --- a/emle/models.py +++ b/emle/models.py @@ -17,7 +17,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with EMLE-Engine If not, see . +# along with EMLE-Engine. If not, see . ##################################################################### """EMLE model implementation.""" @@ -34,12 +34,11 @@ import torch as _torch import torchani as _torchani -_BOHR_TO_ANGSTROM = _ase.units.Bohr +_ANGSTROM_TO_BOHR = 1.0 / _ase.units.Bohr # Settings for the default model. For system specific models, these will be # overwritten by values in the model file. _SPECIES = (1, 6, 7, 8, 16) -_SIGMA = 1e-3 _SPHERICAL_EXPANSION_HYPERS_COMMON = { "gaussian_sigma_constant": 0.5, "gaussian_sigma_type": "Constant", @@ -82,7 +81,7 @@ def __call__(self, zid, xyz): Chemical species indices for each atom. xyz: torch.tensor (N_ATOMS, 3) - Atomic positions in Bohr. + Atomic positions in Angstrom. Returns ------- @@ -94,8 +93,8 @@ def __call__(self, zid, xyz): # Reshape the species indices. zid = zid.reshape(1, *zid.shape) - # Reshape the atomic positions and convert to Angstrom. - xyz = xyz.reshape(1, *xyz.shape) * _BOHR_TO_ANGSTROM + # Reshape the atomic positions. + xyz = xyz.reshape(1, *xyz.shape) # Compute the AEVs. aev = self._aev_computer((zid, xyz)).aevs[0] @@ -311,7 +310,7 @@ def __init__(self, device, create_aev_calculator=True): except: self._hypers[key] = self._params[key] - def forward(self, atomic_numbers, charges_mm, xyz_qm_bohr, xyz_mm_bohr): + def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): """ Computes the static and induced EMLE energy components. @@ -324,21 +323,21 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm_bohr, xyz_mm_bohr): charges_mm: torch.tensor (max_mm_atoms,) MM point charges in atomic units. - xyz_qm_bohr: torch.tensor (N_QM_ATOMS, 3) - Positions of QM atoms in Bohr. + xyz_qm: torch.tensor (N_QM_ATOMS, 3) + Positions of QM atoms in Angstrom. - xyz_mm_bohr: torch.tensor (N_MM_ATOMS, 3) - Positions of MM atoms in Bohr. + xyz_mm: torch.tensor (N_MM_ATOMS, 3) + Positions of MM atoms in Angstrom. Returns ------- result: torch.tensor (2,) - Values of static and induced EMLE energy components. + The static and induced EMLE energy components in Hartree. """ # If there are no point charges, return zeros. - if len(xyz_mm_bohr) == 0: + if len(xyz_mm) == 0: return _torch.zeros(2, dtype=_torch.float32, device=self._device) # Convert the QM atomic numbers to elements and species IDs. @@ -352,7 +351,7 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm_bohr, xyz_mm_bohr): species_id = _torch.tensor(_np.array(species_id), device=self._device) # Get the features. - mol_features = self._get_features(species_id, xyz_qm_bohr) + mol_features = self._get_features(species_id, xyz_qm) # Compute the MBIS valence shell widths. s = self._get_s(mol_features, species_id) @@ -360,6 +359,10 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm_bohr, xyz_mm_bohr): # Compute the electronegativities. chi = self._get_chi(mol_features, species_id) + # Convert coordinates to Bohr. + xyz_qm_bohr = xyz_qm * _ANGSTROM_TO_BOHR + xyz_mm_bohr = xyz_mm * _ANGSTROM_TO_BOHR + # Compute the static energy. q_core = self._q_core[species_id] k_Z = self._k_Z[species_id] @@ -814,7 +817,7 @@ def hook(module, input, output): # Register the hook. self._aev_hook = self._ani2x.aev_computer.register_forward_hook(hook_wrapper()) - def forward(self, atomic_numbers, charges_mm, xyz_qm_bohr, xyz_mm_bohr): + def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): """ Computes the static and induced EMLE energy components. @@ -827,17 +830,17 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm_bohr, xyz_mm_bohr): charges_mm: torch.tensor (max_mm_atoms,) MM point charges in atomic units. - xyz_qm_bohr: torch.tensor (N_QM_ATOMS, 3) - Positions of QM atoms in Bohr. + xyz_qm: torch.tensor (N_QM_ATOMS, 3) + Positions of QM atoms in Angstrom. - xyz_mm_bohr: torch.tensor (N_MM_ATOMS, 3) - Positions of MM atoms in Bohr. + xyz_mm: torch.tensor (N_MM_ATOMS, 3) + Positions of MM atoms in Angstrom. Returns ------- - result: torch.tensor (2,) - Values of static and induced EMLE energy components. + result: torch.tensor (3,) + The ANI2x and static and induced EMLE energy components in Hartree. """ # Convert the QM atomic numbers to elements and species IDs. @@ -853,15 +856,15 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm_bohr, xyz_mm_bohr): # Reshape the atomic numbers. atomic_numbers = atomic_numbers.reshape(1, *atomic_numbers.shape) - # Convert coordinates to Angstrom and reshape. - xyz_qm = xyz_qm_bohr.reshape(1, *xyz_qm_bohr.shape) * _BOHR_TO_ANGSTROM + # Reshape the coordinates, + xyz = xyz_qm.reshape(1, *xyz_qm.shape) # Get the in vacuo energy. - E_vac = self._ani2x((atomic_numbers, xyz_qm)).energies[0] + E_vac = self._ani2x((atomic_numbers, xyz)).energies[0] # If there are no point charges, return the in vacuo energy and zeros # for the static and induced terms. - if len(xyz_mm_bohr) == 0: + if len(xyz_mm) == 0: zero = _torch.tensor(0.0, dtype=_torch.float32, device=self._device) return _torch.stack([E_vac, zero, zero]) @@ -874,6 +877,10 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm_bohr, xyz_mm_bohr): # Compute the electronegativities. chi = self._get_chi(self._aevs, species_id) + # Convert coordinates to Bohr. + xyz_qm_bohr = xyz_qm * _ANGSTROM_TO_BOHR + xyz_mm_bohr = xyz_mm * _ANGSTROM_TO_BOHR + # Compute the static energy. q_core = self._q_core[species_id] k_Z = self._k_Z[species_id] From d3d30067acce5b5eea0825efe355b9a7509723f8 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Sun, 30 Jun 2024 11:14:55 +0100 Subject: [PATCH 030/134] Formatting tweak in license header. [ci skip] --- bin/emle-server | 2 +- bin/orca | 2 +- emle/_sander_calculator.py | 2 +- emle/_socket.py | 2 +- emle/calculator.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/emle-server b/bin/emle-server index 82a66d0..a459ba7 100755 --- a/bin/emle-server +++ b/bin/emle-server @@ -19,7 +19,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with EMLE-Engine If not, see . +# along with EMLE-Engine. If not, see . ##################################################################### import argparse diff --git a/bin/orca b/bin/orca index 6a59e9e..3dcb24f 100755 --- a/bin/orca +++ b/bin/orca @@ -19,7 +19,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with EMLE-Engine If not, see . +# along with EMLE-Engine. If not, see . ##################################################################### import os diff --git a/emle/_sander_calculator.py b/emle/_sander_calculator.py index bfcbb3f..6b37c50 100644 --- a/emle/_sander_calculator.py +++ b/emle/_sander_calculator.py @@ -17,7 +17,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with EMLE-Engine If not, see . +# along with EMLE-Engine. If not, see . ##################################################################### """ASE sander calculator implementation.""" diff --git a/emle/_socket.py b/emle/_socket.py index 9ad4659..6d864bd 100644 --- a/emle/_socket.py +++ b/emle/_socket.py @@ -17,7 +17,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with EMLE-Engine If not, see . +# along with EMLE-Engine. If not, see . ##################################################################### """Simple TCP socket-server implementation.""" diff --git a/emle/calculator.py b/emle/calculator.py index 53f3d5b..140069e 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -17,7 +17,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with EMLE-Engine If not, see . +# along with EMLE-Engine. If not, see . ##################################################################### """EMLE calculator implementation.""" From 7868f06f9fc949b7b0d9e6ce094253e4a986d6fc Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Sun, 30 Jun 2024 11:18:37 +0100 Subject: [PATCH 031/134] Clarification in comment. [ci skip] --- emle/calculator.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 140069e..c113dbc 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1185,6 +1185,9 @@ def __init__( with open("emle_settings.yaml", "w") as f: _yaml.dump(self._settings, f) + # Initialise a NULL internal model calculator. + self._ani2x_emle = None + def run(self, path=None): """ Calculate the energy and gradients. @@ -1222,7 +1225,7 @@ def run(self, path=None): xyz_file_qm, ) = self.parse_orca_input(orca_input) - # Make sure that the number of QM atoms matches the number of MM atoms + # Make sure that the number of QM atoms matches the number of MM charges # when using mm embedding. if self._method == "mm": if len(xyz_qm) != len(self._mm_charges): @@ -1600,7 +1603,7 @@ def _sire_callback(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): # Initialise a null ASE atoms object. atoms = None - # Make sure that the number of QM atoms matches the number of MM atoms + # Make sure that the number of QM atoms matches the number of MM charges # when using mm embedding. if self._method == "mm": if len(xyz_qm) != len(self._mm_charges): From eaa9b041fdb8c517f5af6596451650e23846e830 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Sun, 30 Jun 2024 11:38:39 +0100 Subject: [PATCH 032/134] Add an optimised Sire callback. [ci skip] --- emle/calculator.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/emle/calculator.py b/emle/calculator.py index c113dbc..06bf2a0 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -52,6 +52,7 @@ _EV_TO_HARTREE = 1.0 / _ase.units.Hartree _KCAL_MOL_TO_HARTREE = 1.0 / _ase.units.Hartree * _ase.units.kcal / _ase.units.mol _HARTREE_TO_KJ_MOL = _ase.units.Hartree / _ase.units.kJ * _ase.units.mol +_NANOMETER_TO_ANGSTROM = 10.0 # Settings for the default model. For system specific models, these will be # overwritten by values in the model file. @@ -1873,6 +1874,73 @@ def _sire_callback(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): ).tolist(), ) + def _sire_callback_optimised(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): + """ + A callback function to be used with Sire. + + Parameters + ---------- + + atomic_numbers: [float] + A list of atomic numbers for the QM region. + + charges_mm: [float] + The charges on the MM atoms. + + xyz_qm: [[float, float, float]] + The coordinates of the QM atoms in Angstrom. + + xyz_mm: [[float, float, float]] + The coordinates of the MM atoms in Angstrom. + + Returns + ------- + + energy: float + The energy in kJ/mol. + + force_qm: [[float, float, float]] + The forces on the QM atoms in kJ/mol/nanometer. + + force_mm: [[float, float, float]] + The forces on the MM atoms in kJ/mol/nanometer. + """ + + # For performance, we assume that the input is already validated. + + # Convert to numpy arrays then Torch tensors. + atomic_numbers = _torch.tensor(atomic_numbers, device=self._device) + charges_mm = _torch.tensor( + charges_mm, dtype=_torch.float32, device=self._device + ) + xyz_qm = _torch.tensor( + xyz_qm, dtype=_torch.float32, device=self._device, requires_grad=True + ) + xyz_mm = _torch.tensor( + xyz_mm, dtype=_torch.float32, device=self._device, requires_grad=True + ) + + # Create an internal ANI2xEMLE model if one doesn't already exist. + if self._ani2x_emle is None: + from .models import ANI2xEMLE as _ANI2xEMLE + + self._ani2x_emle = _ANI2xEMLE(self._device) + + # Compute the energy and gradients. + E = self._ani2x_emle(atomic_numbers, charges_mm, xyz_qm, xyz_mm) + dE_dxyz_qm, dE_dxyz_mm = _torch.autograd.grad(E.sum(), (xyz_qm, xyz_mm)) + + # Convert the energy and gradients to numpy arrays. + E = E.sum().item() * _HARTREE_TO_KJ_MOL + force_qm = ( + -dE_dxyz_qm.cpu().numpy() * _HARTREE_TO_KJ_MOL * _NANOMETER_TO_ANGSTROM + ).tolist() + force_mm = ( + -dE_dxyz_mm.cpu().numpy() * _HARTREE_TO_KJ_MOL * _NANOMETER_TO_ANGSTROM + ).tolist() + + return E, force_qm, force_mm + def _get_E(self, charges_mm, xyz_qm_bohr, xyz_mm_bohr): """ Computes total EMLE embedding energy (sum of static and induced). From 7d3457ba5d04eddeb78953bc45419612c0e0b4b7 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Sun, 30 Jun 2024 11:54:09 +0100 Subject: [PATCH 033/134] Allow use of NNPOps with ANI2xEMLE model. [ci skip] --- emle/calculator.py | 2 +- emle/models.py | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 06bf2a0..1eb221b 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1924,7 +1924,7 @@ def _sire_callback_optimised(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): if self._ani2x_emle is None: from .models import ANI2xEMLE as _ANI2xEMLE - self._ani2x_emle = _ANI2xEMLE(self._device) + self._ani2x_emle = _ANI2xEMLE(self._device, atomic_numbers=atomic_numbers) # Compute the energy and gradients. E = self._ani2x_emle(atomic_numbers, charges_mm, xyz_qm, xyz_mm) diff --git a/emle/models.py b/emle/models.py index f309992..3b33962 100644 --- a/emle/models.py +++ b/emle/models.py @@ -792,7 +792,7 @@ def _lambda5(au3): class ANI2xEMLE(EMLE): - def __init__(self, device): + def __init__(self, device, atomic_numbers=None): """ Constructor @@ -801,16 +801,31 @@ def __init__(self, device): device: torch device The PyTorch device to use for calculations. + + atomic_numbers: list + List of atomic numbers to use in the ANI2x model. If specified, + and NNPOps is available, then an optimised version of ANI2x will + be used. """ super().__init__(device, create_aev_calculator=False) # Create the ANI2x model. self._ani2x = _torchani.models.ANI2x(periodic_table_index=True).to(self._device) + # Optmised the ANI2x model if atomic_numbers is specified. + if atomic_numbers is not None: + try: + from NNPOps import OptimizedTorchANI as _OptimizedTorchANI + + species = atomic_numbers.reshape(1, *atomic_numbers.shape) + self._ani2x = _OptimizedTorchANI(self._ani2x, species).to(self._device) + except: + raise + # Hook the forward pass of the ANI2x model to get the AEV features. def hook_wrapper(): def hook(module, input, output): - self._aevs = output.aevs[0] + self._aevs = output[1][0] return hook From 71dfa1ab429e94d32aa06b3f8d0551f72c91e69d Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 1 Jul 2024 10:23:38 +0100 Subject: [PATCH 034/134] Allow model to be moved and used with existing ANI2x model. [ci skip] --- emle/calculator.py | 14 ++- emle/models.py | 224 +++++++++++++++++++++++++++++++++------------ 2 files changed, 177 insertions(+), 61 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 1eb221b..2bbef7f 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1922,9 +1922,21 @@ def _sire_callback_optimised(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): # Create an internal ANI2xEMLE model if one doesn't already exist. if self._ani2x_emle is None: + from NNPOps import OptimizedTorchANI as _OptimizedTorchANI from .models import ANI2xEMLE as _ANI2xEMLE - self._ani2x_emle = _ANI2xEMLE(self._device, atomic_numbers=atomic_numbers) + # Optimise the TorchANI model. + self._torchani_model = _OptimizedTorchANI( + self._torchani_model, atomic_numbers.reshape(-1, *atomic_numbers.shape) + ).to(self._device) + + # Flag that NNPOps is active. + self._nnpops_active = True + + # Create the model. + self._ani2x_emle = _ANI2xEMLE( + ani2x_model=self._torchani_model, device=self._device + ) # Compute the energy and gradients. E = self._ani2x_emle(atomic_numbers, charges_mm, xyz_qm, xyz_mm) diff --git a/emle/models.py b/emle/models.py index 3b33962..e8ef9fe 100644 --- a/emle/models.py +++ b/emle/models.py @@ -54,20 +54,22 @@ class _AEVCalculator: Calculates AEV feature vectors for a given system """ - def __init__(self, device): + def __init__(self, device=None): """ Constructor - Parameters - ---------- - - device: torch device - The PyTorch device to use for calculations. + device: torch.device + The device on which to run the model. """ - self._device = device + + if device is not None: + if not isinstance(device, _torch.device): + raise TypeError("'device' must be of type 'torch.device'") + else: + device = _torch.get_default_device() # Create the AEV computer. - ani2x = _torchani.models.ANI2x(periodic_table_index=True).to(self._device) + ani2x = _torchani.models.ANI2x(periodic_table_index=True).to(device) self._aev_computer = ani2x.aev_computer def __call__(self, zid, xyz): @@ -100,11 +102,20 @@ def __call__(self, zid, xyz): aev = self._aev_computer((zid, xyz)).aevs[0] return aev / _torch.linalg.norm(aev, axis=1, keepdims=True) + def to(self, device): + """ + Move the AEV calculator to a new device. + """ + if not isinstance(device, _torch.device): + raise TypeError("'device' must be of type 'torch.device'") + self._aev_computer = self._aev_computer.to(device) + return self + class _GPRCalculator: """Predicts an atomic property for a molecule with Gaussian Process Regression (GPR).""" - def __init__(self, ref_values, ref_features, n_ref, sigma, device): + def __init__(self, ref_values, ref_features, n_ref, sigma, device=None): """ Constructor @@ -123,18 +134,24 @@ def __init__(self, ref_values, ref_features, n_ref, sigma, device): sigma: float The uncertainty of the observations (regularizer). - device: torch device - The PyTorch device to use for calculations. + device: torch.device + The device on which to run the model. """ - # Store the device and reference features. - self._device = device + + if device is not None: + if not isinstance(device, _torch.device): + raise TypeError("'device' must be of type 'torch.device'") + else: + device = _torch.get_default_device() + + # Store the reference features. self._ref_features = ref_features # Compute the inverse of the K matrix. Kinv = _torch.tensor( self._get_Kinv(ref_features, sigma), dtype=_torch.float32, - device=self._device, + device=device, ) # Store additional attributes for the GPR model. @@ -144,7 +161,7 @@ def __init__(self, ref_values, ref_features, n_ref, sigma, device): ref_shifted = _torch.tensor( ref_values - self._ref_mean[:, None], dtype=_torch.float32, - device=self._device, + device=device, ) self._c = (Kinv @ ref_shifted[:, :, None]).squeeze() @@ -167,11 +184,15 @@ def __call__(self, mol_features, zid): The values of the predicted property for each atom. """ - result = _torch.zeros(len(zid), dtype=_torch.float32, device=self._device) + result = _torch.zeros( + len(zid), dtype=_torch.float32, device=mol_features.device + ) for i in range(self._n_z): n_ref = self._n_ref[i] ref_features_z = _torch.tensor( - self._ref_features[i, :n_ref], dtype=_torch.float32, device=self._device + self._ref_features[i, :n_ref], + dtype=_torch.float32, + device=mol_features.device, ) mol_features_z = mol_features[zid == i, :, None] @@ -205,6 +226,15 @@ def _get_Kinv(cls, ref_features, sigma): K = (ref_features @ ref_features.swapaxes(1, 2)) ** 2 return _np.linalg.inv(K + sigma**2 * _np.eye(n, dtype=_np.float32)) + def to(self, device): + """ + Move the GPR calculator to a new device. + """ + if not isinstance(device, _torch.device): + raise TypeError("'device' must be of type 'torch.device'") + self._c = self._c.to(device) + return self + class EMLE(_torch.nn.Module): """ @@ -232,16 +262,13 @@ class EMLE(_torch.nn.Module): **_SPHERICAL_EXPANSION_HYPERS_COMMON, } - def __init__(self, device, create_aev_calculator=True): + def __init__(self, device=None, create_aev_calculator=True): """ Constructor Parameters ---------- - device: torch device - The PyTorch device to use for calculations. - create_aev_calculator: bool Whether to create an AEV calculator instance. """ @@ -249,16 +276,20 @@ def __init__(self, device, create_aev_calculator=True): # Call the base class constructor. super().__init__() - if not isinstance(device, _torch.device): - raise TypeError("'device' must be of type 'torch.device'") - self._device = device + if device is not None: + if not isinstance(device, _torch.device): + raise TypeError("'device' must be of type 'torch.device'") + else: + device = _torch.get_default_device() if not isinstance(create_aev_calculator, bool): raise TypeError("'create_aev_calculator' must be of type 'bool'") # Create an AEV calculator to perform the feature calculations. if create_aev_calculator: - self._get_features = _AEVCalculator(self._device) + self._get_features = _AEVCalculator(device=device) + else: + self._get_features = None # Load the model parameters. try: @@ -268,31 +299,29 @@ def __init__(self, device, create_aev_calculator=True): # Store model parameters as tensors. self._q_core = _torch.tensor( - self._params["q_core"], dtype=_torch.float32, device=self._device + self._params["q_core"], dtype=_torch.float32, device=device ) self._a_QEq = self._params["a_QEq"] self._a_Thole = self._params["a_Thole"] self._k_Z = _torch.tensor( - self._params["k_Z"], dtype=_torch.float32, device=self._device + self._params["k_Z"], dtype=_torch.float32, device=device ) self._q_total = _torch.tensor( - self._params.get("total_charge", 0), - dtype=_torch.float32, - device=self._device, + self._params.get("total_charge", 0), dtype=_torch.float32, device=device ) self._get_s = _GPRCalculator( self._params["s_ref"], self._params["ref_soap"], self._params["n_ref"], 1e-3, - self._device, + device=device, ) self._get_chi = _GPRCalculator( self._params["chi_ref"], self._params["ref_soap"], self._params["n_ref"], 1e-3, - self._device, + device=device, ) # Initialise EMLE embedding model attributes. @@ -310,6 +339,21 @@ def __init__(self, device, create_aev_calculator=True): except: self._hypers[key] = self._params[key] + def to(self, device): + """ + Move the model to a new device. + """ + if not isinstance(device, _torch.device): + raise TypeError("'device' must be of type 'torch.device'") + if self._get_features is not None: + self._get_features = self._get_features.to(device) + self._q_core = self._q_core.to(device) + self._k_Z = self._k_Z.to(device) + self._q_total = self._q_total.to(device) + self._get_s = self._get_s.to(device) + self._get_chi = self._get_chi.to(device) + return self + def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): """ Computes the static and induced EMLE energy components. @@ -338,7 +382,7 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): # If there are no point charges, return zeros. if len(xyz_mm) == 0: - return _torch.zeros(2, dtype=_torch.float32, device=self._device) + return _torch.zeros(2, dtype=_torch.float32, device=xyz_qm.device) # Convert the QM atomic numbers to elements and species IDs. species_id = [] @@ -348,7 +392,7 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): except: msg = f"Unsupported element index '{id}'." raise ValueError(msg) - species_id = _torch.tensor(_np.array(species_id), device=self._device) + species_id = _torch.tensor(_np.array(species_id), device=xyz_qm.device) # Get the features. mol_features = self._get_features(species_id, xyz_qm) @@ -366,7 +410,7 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): # Compute the static energy. q_core = self._q_core[species_id] k_Z = self._k_Z[species_id] - r_data = self._get_r_data(xyz_qm_bohr, self._device) + r_data = self._get_r_data(xyz_qm_bohr) mesh_data = self._get_mesh_data(xyz_qm_bohr, xyz_mm_bohr, s) q = self._get_q(r_data, s, chi) q_val = q - q_core @@ -430,13 +474,15 @@ def _get_A_QEq(self, r_data, s): s2 = s_gauss**2 s_mat = _torch.sqrt(s2[:, None] + s2[None, :]) + device = r_data["r_mat"].device + A = self._get_T0_gaussian(r_data["T01"], r_data["r_mat"], s_mat) new_diag = _torch.ones_like( - A.diagonal(), dtype=_torch.float32, device=self._device + A.diagonal(), dtype=_torch.float32, device=device ) * (1.0 / (s_gauss * _np.sqrt(_np.pi))) mask = _torch.diag( - _torch.ones_like(new_diag, dtype=_torch.float32, device=self._device) + _torch.ones_like(new_diag, dtype=_torch.float32, device=device) ) A = mask * _torch.diag(new_diag) + (1.0 - mask) * A @@ -444,7 +490,7 @@ def _get_A_QEq(self, r_data, s): x, y = A.shape # Create an tensor of ones with one more row and column than A. - B = _torch.ones(x + 1, y + 1, dtype=_torch.float32, device=self._device) + B = _torch.ones(x + 1, y + 1, dtype=_torch.float32, device=device) # Copy A into B. B[:x, :y] = A @@ -535,7 +581,7 @@ def _get_A_thole(self, r_data, s, q_val, k_Z): new_diag = 1.0 / alpha.repeat_interleave(3) mask = _torch.diag( - _torch.ones_like(new_diag, dtype=_torch.float32, device=self._device) + _torch.ones_like(new_diag, dtype=_torch.float32, device=A.device) ) A = mask * _torch.diag(new_diag) + (1.0 - mask) * A @@ -587,7 +633,7 @@ def _get_vpot_mu(mu, T1): return -_torch.tensordot(T1, mu, ((0, 2), (0, 1))) @classmethod - def _get_r_data(cls, xyz, device): + def _get_r_data(cls, xyz): """ Internal method to calculate r_data object. @@ -597,9 +643,6 @@ def _get_r_data(cls, xyz, device): xyz: torch.tensor (N_ATOMS, 3) Atomic positions. - device: torch.device - The PyTorch device to use. - Returns ------- @@ -621,7 +664,7 @@ def _get_r_data(cls, xyz, device): id2 = _torch.tile( _torch.tile( - _torch.eye(3, dtype=_torch.float32, device=device).T, (1, n_atoms) + _torch.eye(3, dtype=_torch.float32, device=xyz.device).T, (1, n_atoms) ).T, (1, n_atoms), ) @@ -792,35 +835,86 @@ def _lambda5(au3): class ANI2xEMLE(EMLE): - def __init__(self, device, atomic_numbers=None): + def __init__(self, ani2x_model=None, atomic_numbers=None, device=None): """ Constructor Parameters ---------- - device: torch device - The PyTorch device to use for calculations. + ani2x_model: torchani.models.ANI2x, NNPOPS.OptimizedTorchANI + An existing ANI2x model to use. If None, a new ANI2x model will be + created. If using an OptimizedTorchANI model, please ensure that + the ANI2x model from which it derived was created using + periodic_table_index=True. - atomic_numbers: list + atomic_numbers: torch.tensor (N_ATOMS,) List of atomic numbers to use in the ANI2x model. If specified, and NNPOps is available, then an optimised version of ANI2x will be used. - """ - super().__init__(device, create_aev_calculator=False) - # Create the ANI2x model. - self._ani2x = _torchani.models.ANI2x(periodic_table_index=True).to(self._device) + device: torch.device + The device on which to run the model. + """ + if device is not None: + if not isinstance(device, _torch.device): + raise TypeError("'device' must be of type 'torch.device'") + else: + device = _torch.get_default_device() - # Optmised the ANI2x model if atomic_numbers is specified. if atomic_numbers is not None: + if not isinstance(atomic_numbers, _torch.Tensor): + raise TypeError("'atomic_numbers' must be of type 'torch.Tensor'") + # Check that they are integers. + if atomic_numbers.dtype != _torch.int64: + raise ValueError("'atomic_numbers' must be of dtype 'torch.int64'") + + # Call the base class constructor. + super().__init__(device=device, create_aev_calculator=False) + + if ani2x_model is not None: + # Add the base ANI2x model and ensemble. + allowed_types = [ + _torchani.models.BuiltinModel, + _torchani.models.BuiltinEnsemble, + ] + + # Add the optimised model if NNPOps is available. try: - from NNPOps import OptimizedTorchANI as _OptimizedTorchANI + import NNPOps as _NNPOps - species = atomic_numbers.reshape(1, *atomic_numbers.shape) - self._ani2x = _OptimizedTorchANI(self._ani2x, species).to(self._device) + allowed_types.append(_NNPOps.OptimizedTorchANI) except: - raise + pass + + if not isinstance(ani2x_model, tuple(allowed_types)): + raise TypeError(f"'ani2x_model' must be of type {allowed_types}") + + if ( + isinstance( + ani2x_model, + (_torchani.models.BuiltinModel, _torchani.models.BuiltinEnsemble), + ) + and not ani2x_model.periodic_table_index + ): + raise ValueError( + "The ANI2x model must be created with 'periodic_table_index=True'" + ) + + self._ani2x = ani2x_model.to(device) + else: + # Create the ANI2x model. + self._ani2x = _torchani.models.ANI2x(periodic_table_index=True).to(device) + + # Optmised the ANI2x model if atomic_numbers is specified. + if atomic_numbers is not None: + try: + from NNPOps import OptimizedTorchANI as _OptimizedTorchANI + + species = atomic_numbers.reshape(1, *atomic_numbers.shape) + self._ani2x = _OptimizedTorchANI(self._ani2x, species) + except: + pass # Hook the forward pass of the ANI2x model to get the AEV features. def hook_wrapper(): @@ -832,6 +926,16 @@ def hook(module, input, output): # Register the hook. self._aev_hook = self._ani2x.aev_computer.register_forward_hook(hook_wrapper()) + def to(self, device): + """ + Move the model to a new device. + """ + if not isinstance(device, _torch.device): + raise TypeError("'device' must be of type 'torch.device'") + module = super(ANI2xEMLE, self).to(device) + module._ani2x = module._ani2x.to(device) + return module + def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): """ Computes the static and induced EMLE energy components. @@ -866,7 +970,7 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): except: msg = f"Unsupported element index '{id}'." raise ValueError(msg) - species_id = _torch.tensor(_np.array(species_id), device=self._device) + species_id = _torch.tensor(_np.array(species_id), device=xyz_qm.device) # Reshape the atomic numbers. atomic_numbers = atomic_numbers.reshape(1, *atomic_numbers.shape) @@ -880,7 +984,7 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): # If there are no point charges, return the in vacuo energy and zeros # for the static and induced terms. if len(xyz_mm) == 0: - zero = _torch.tensor(0.0, dtype=_torch.float32, device=self._device) + zero = _torch.tensor(0.0, dtype=_torch.float32, device=xyz_qm.device) return _torch.stack([E_vac, zero, zero]) # Normalise the AEVs. @@ -899,7 +1003,7 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): # Compute the static energy. q_core = self._q_core[species_id] k_Z = self._k_Z[species_id] - r_data = self._get_r_data(xyz_qm_bohr, self._device) + r_data = self._get_r_data(xyz_qm_bohr) mesh_data = self._get_mesh_data(xyz_qm_bohr, xyz_mm_bohr, s) q = self._get_q(r_data, s, chi) q_val = q - q_core From 10da04c4f6977ab4943b13246bf1195674891cac Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 1 Jul 2024 10:49:52 +0100 Subject: [PATCH 035/134] Named tuple isn't available for OptimizedTorchANI. [ci skip] --- emle/calculator.py | 2 +- emle/models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 2bbef7f..475c04a 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -114,7 +114,7 @@ def __call__(self, zid, xyz): xyz = xyz.reshape(1, *xyz.shape) * _BOHR_TO_ANGSTROM # Compute the AEVs. - aev = self._aev_computer((zid, xyz)).aevs[0] + aev = self._aev_computer((zid, xyz))[1][0] return aev / _torch.linalg.norm(aev, axis=1, keepdims=True) diff --git a/emle/models.py b/emle/models.py index e8ef9fe..e6c8a63 100644 --- a/emle/models.py +++ b/emle/models.py @@ -99,7 +99,7 @@ def __call__(self, zid, xyz): xyz = xyz.reshape(1, *xyz.shape) # Compute the AEVs. - aev = self._aev_computer((zid, xyz)).aevs[0] + aev = self._aev_computer((zid, xyz))[1][0] return aev / _torch.linalg.norm(aev, axis=1, keepdims=True) def to(self, device): From 42ce48b7813407f023d23fd03e71c8eef84da708 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 1 Jul 2024 17:09:34 +0100 Subject: [PATCH 036/134] Make module torch scriptable. [ci skip] --- emle/calculator.py | 49 +---- emle/models.py | 517 ++++++++++++++++++--------------------------- 2 files changed, 212 insertions(+), 354 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 475c04a..20eccab 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -54,18 +54,6 @@ _HARTREE_TO_KJ_MOL = _ase.units.Hartree / _ase.units.kJ * _ase.units.mol _NANOMETER_TO_ANGSTROM = 10.0 -# Settings for the default model. For system specific models, these will be -# overwritten by values in the model file. -_SPECIES = (1, 6, 7, 8, 16) -_SPHERICAL_EXPANSION_HYPERS_COMMON = { - "gaussian_sigma_constant": 0.5, - "gaussian_sigma_type": "Constant", - "cutoff_smooth_width": 0.5, - "radial_basis": "GTO", - "expansion_by_species_method": "user defined", - "global_species": _SPECIES, -} - class _AEVCalculator: """ @@ -239,17 +227,8 @@ class EMLECalculator: # Create the name of the default model file. _default_model = _os.path.join(_module_dir, "emle_qm7_aev.mat") - # Default ML model parameters. These will be overwritten by values in the - # embedding model file. - - # Model hyper-parameters. - _hypers = { - "interaction_cutoff": 3.0, - "max_radial": 4, - "max_angular": 4, - "compute_gradients": True, - **_SPHERICAL_EXPANSION_HYPERS_COMMON, - } + # Store the list of supported species. + _species = [1, 6, 7, 8, 16] # List of supported backends. _supported_backends = [ @@ -1037,26 +1016,6 @@ def __init__( # Initialise a null SanderCalculator object. self._sander_calculator = None - # Initialise EMLE embedding model attributes. - hypers_keys = ( - "gaussian_sigma_constant", - "global_species", - "interaction_cutoff", - "max_radial", - "max_angular", - ) - for key in hypers_keys: - if key in self._params: - try: - self._hypers[key] = tuple(self._params[key].tolist()) - except: - self._hypers[key] = self._params[key] - - # Work out the supported elements. - self._supported_elements = [] - for id in self._hypers["global_species"]: - self._supported_elements.append(_ase.Atom(id).symbol) - # Initialise TorchANI backend attributes. if self._backend == "torchani": import torchani as _torchani @@ -1256,7 +1215,7 @@ def run(self, path=None): elements = [] for id in atomic_numbers: try: - species_id.append(self._hypers["global_species"].index(id)) + species_id.append(self._species.index(id)) elements.append(_ase.Atom(id).symbol) except: msg = ( @@ -1634,7 +1593,7 @@ def _sire_callback(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): elements = [] for id in atomic_numbers: try: - species_id.append(self._hypers["global_species"].index(id)) + species_id.append(self._species.index(id)) elements.append(_ase.Atom(id).symbol) except: msg = ( diff --git a/emle/models.py b/emle/models.py index e6c8a63..d4c4a01 100644 --- a/emle/models.py +++ b/emle/models.py @@ -34,206 +34,8 @@ import torch as _torch import torchani as _torchani -_ANGSTROM_TO_BOHR = 1.0 / _ase.units.Bohr - -# Settings for the default model. For system specific models, these will be -# overwritten by values in the model file. -_SPECIES = (1, 6, 7, 8, 16) -_SPHERICAL_EXPANSION_HYPERS_COMMON = { - "gaussian_sigma_constant": 0.5, - "gaussian_sigma_type": "Constant", - "cutoff_smooth_width": 0.5, - "radial_basis": "GTO", - "expansion_by_species_method": "user defined", - "global_species": _SPECIES, -} - - -class _AEVCalculator: - """ - Calculates AEV feature vectors for a given system - """ - - def __init__(self, device=None): - """ - Constructor - - device: torch.device - The device on which to run the model. - """ - - if device is not None: - if not isinstance(device, _torch.device): - raise TypeError("'device' must be of type 'torch.device'") - else: - device = _torch.get_default_device() - - # Create the AEV computer. - ani2x = _torchani.models.ANI2x(periodic_table_index=True).to(device) - self._aev_computer = ani2x.aev_computer - - def __call__(self, zid, xyz): - """ - Calculates the AEV feature vectors for a given molecule. - - Parameters - ---------- - - zid: numpy.array (N_ATOMS) - Chemical species indices for each atom. - - xyz: torch.tensor (N_ATOMS, 3) - Atomic positions in Angstrom. - - Returns - ------- - - aev: torch.tensor (N_ATOMS, N_AEV) - AEV feature vectors for each atom. - """ - - # Reshape the species indices. - zid = zid.reshape(1, *zid.shape) - - # Reshape the atomic positions. - xyz = xyz.reshape(1, *xyz.shape) - - # Compute the AEVs. - aev = self._aev_computer((zid, xyz))[1][0] - return aev / _torch.linalg.norm(aev, axis=1, keepdims=True) - - def to(self, device): - """ - Move the AEV calculator to a new device. - """ - if not isinstance(device, _torch.device): - raise TypeError("'device' must be of type 'torch.device'") - self._aev_computer = self._aev_computer.to(device) - return self - - -class _GPRCalculator: - """Predicts an atomic property for a molecule with Gaussian Process Regression (GPR).""" - - def __init__(self, ref_values, ref_features, n_ref, sigma, device=None): - """ - Constructor - - Parameters - ---------- - - ref_values: numpy.array (N_Z, N_REF) - The property values corresponding to the basis vectors for each species. - - ref_features: numpy.array (N_Z, N_REF, N_FEAT) - The basis feature vectors for each species. - - n_ref: (N_Z,) - Number of supported species. - - sigma: float - The uncertainty of the observations (regularizer). - - device: torch.device - The device on which to run the model. - """ - - if device is not None: - if not isinstance(device, _torch.device): - raise TypeError("'device' must be of type 'torch.device'") - else: - device = _torch.get_default_device() - - # Store the reference features. - self._ref_features = ref_features - - # Compute the inverse of the K matrix. - Kinv = _torch.tensor( - self._get_Kinv(ref_features, sigma), - dtype=_torch.float32, - device=device, - ) - - # Store additional attributes for the GPR model. - self._n_ref = n_ref - self._n_z = len(n_ref) - self._ref_mean = _np.sum(ref_values, axis=1) / n_ref - ref_shifted = _torch.tensor( - ref_values - self._ref_mean[:, None], - dtype=_torch.float32, - device=device, - ) - self._c = (Kinv @ ref_shifted[:, :, None]).squeeze() - - def __call__(self, mol_features, zid): - """ - - Parameters - ---------- - - mol_features: numpy.array (N_ATOMS, N_FEAT) - The feature vectors for each atom. - - zid: torch.tensor (N_ATOMS,) - The species identity value of each atom. - - Returns - ------- - - result: torch.tensor, numpy.array (N_ATOMS) - The values of the predicted property for each atom. - """ - - result = _torch.zeros( - len(zid), dtype=_torch.float32, device=mol_features.device - ) - for i in range(self._n_z): - n_ref = self._n_ref[i] - ref_features_z = _torch.tensor( - self._ref_features[i, :n_ref], - dtype=_torch.float32, - device=mol_features.device, - ) - mol_features_z = mol_features[zid == i, :, None] - - K_mol_ref2 = (ref_features_z @ mol_features_z) ** 2 - K_mol_ref2 = K_mol_ref2.reshape(K_mol_ref2.shape[:-1]) - result[zid == i] = K_mol_ref2 @ self._c[i, :n_ref] + self._ref_mean[i] - - return result - - @classmethod - def _get_Kinv(cls, ref_features, sigma): - """ - Internal function to compute the inverse of the K matrix for GPR. - - Parameters - ---------- - - ref_features: numpy.array (N_Z, MAX_N_REF, N_FEAT) - The basis feature vectors for each species. - - sigma: float - The uncertainty of the observations (regularizer). - - Returns - ------- - - result: numpy.array (MAX_N_REF, MAX_N_REF) - The inverse of the K matrix. - """ - n = ref_features.shape[1] - K = (ref_features @ ref_features.swapaxes(1, 2)) ** 2 - return _np.linalg.inv(K + sigma**2 * _np.eye(n, dtype=_np.float32)) - - def to(self, device): - """ - Move the GPR calculator to a new device. - """ - if not isinstance(device, _torch.device): - raise TypeError("'device' must be of type 'torch.device'") - self._c = self._c.to(device) - return self +from torch import Tensor +from typing import Optional, Tuple class EMLE(_torch.nn.Module): @@ -242,26 +44,6 @@ class EMLE(_torch.nn.Module): embedding. """ - # Class attributes. - - # Get the directory of this module file. - _module_dir = _os.path.dirname(_os.path.abspath(__file__)) - - # Create the name of the default model file. - _model = _os.path.join(_module_dir, "emle_qm7_aev.mat") - - # Default ML model parameters. These will be overwritten by values in the - # embedding model file. - - # Model hyper-parameters. - _hypers = { - "interaction_cutoff": 3.0, - "max_radial": 4, - "max_angular": 4, - "compute_gradients": True, - **_SPHERICAL_EXPANSION_HYPERS_COMMON, - } - def __init__(self, device=None, create_aev_calculator=True): """ Constructor @@ -273,6 +55,14 @@ def __init__(self, device=None, create_aev_calculator=True): Whether to create an AEV calculator instance. """ + # Class attributes. + + # Get the directory of this module file. + self._module_dir = _os.path.dirname(_os.path.abspath(__file__)) + + # Create the name of the default model file. + self._model = _os.path.join(self._module_dir, "emle_qm7_aev.mat") + # Call the base class constructor. super().__init__() @@ -287,9 +77,10 @@ def __init__(self, device=None, create_aev_calculator=True): # Create an AEV calculator to perform the feature calculations. if create_aev_calculator: - self._get_features = _AEVCalculator(device=device) + ani2x = _torchani.models.ANI2x(periodic_table_index=True).to(device) + self._aev_computer = ani2x.aev_computer else: - self._get_features = None + self._aev_computer = None # Load the model parameters. try: @@ -297,6 +88,9 @@ def __init__(self, device=None, create_aev_calculator=True): except: raise IOError(f"Unable to load model parameters from: '{self._model}'") + # Set the supported species. + self._species = [1, 6, 7, 8, 16] + # Store model parameters as tensors. self._q_core = _torch.tensor( self._params["q_core"], dtype=_torch.float32, device=device @@ -309,35 +103,38 @@ def __init__(self, device=None, create_aev_calculator=True): self._q_total = _torch.tensor( self._params.get("total_charge", 0), dtype=_torch.float32, device=device ) - self._get_s = _GPRCalculator( - self._params["s_ref"], - self._params["ref_soap"], - self._params["n_ref"], - 1e-3, - device=device, + + # Extract the reference features. + self._ref_features = _torch.tensor( + self._params["ref_soap"], dtype=_torch.float32, device=device ) - self._get_chi = _GPRCalculator( - self._params["chi_ref"], - self._params["ref_soap"], - self._params["n_ref"], - 1e-3, - device=device, + + # Extract the reference values for the MBIS valence shell widths. + self._ref_values_s = _torch.tensor( + self._params["s_ref"], dtype=_torch.float32, device=device ) - # Initialise EMLE embedding model attributes. - hypers_keys = ( - "gaussian_sigma_constant", - "global_species", - "interaction_cutoff", - "max_radial", - "max_angular", + # Compute the inverse of the K matrix. + Kinv = self._get_Kinv(self._ref_features, 1e-3) + + # Store additional attributes for the MBIS GPR model. + self._n_ref = _torch.tensor( + self._params["n_ref"], dtype=_torch.int64, device=device + ) + self._n_z = len(self._n_ref) + self._ref_mean_s = _torch.sum(self._ref_values_s, dim=1) / self._n_ref + ref_shifted = self._ref_values_s - self._ref_mean_s[:, None] + self._c_s = (Kinv @ ref_shifted[:, :, None]).squeeze() + + # Exctract the reference values for the electronegativities. + self._ref_values_chi = _torch.tensor( + self._params["chi_ref"], dtype=_torch.float32, device=device ) - for key in hypers_keys: - if key in self._params: - try: - self._hypers[key] = tuple(self._params[key].tolist()) - except: - self._hypers[key] = self._params[key] + + # Store additional attributes for the electronegativity GPR model. + self._ref_mean_chi = _torch.sum(self._ref_values_chi, dim=1) / self._n_ref + ref_shifted = self._ref_values_chi - self._ref_mean_chi[:, None] + self._c_chi = (Kinv @ ref_shifted[:, :, None]).squeeze() def to(self, device): """ @@ -345,13 +142,19 @@ def to(self, device): """ if not isinstance(device, _torch.device): raise TypeError("'device' must be of type 'torch.device'") - if self._get_features is not None: - self._get_features = self._get_features.to(device) + if self._aev_computer is not None: + self._aev_computer = self._aev_computer.to(device) self._q_core = self._q_core.to(device) self._k_Z = self._k_Z.to(device) self._q_total = self._q_total.to(device) - self._get_s = self._get_s.to(device) - self._get_chi = self._get_chi.to(device) + self._ref_features = self._ref_features.to(device) + self._n_ref = self._n_ref.to(device) + self._ref_values_s = self._ref_values_s.to(device) + self._ref_values_chi = self._ref_values_chi.to(device) + self._ref_mean_s = self._ref_mean_s.to(device) + self._ref_mean_chi = self._ref_mean_chi.to(device) + self._c_s = self._c_s.to(device) + self._c_chi = self._c_chi.to(device) return self def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): @@ -385,27 +188,35 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): return _torch.zeros(2, dtype=_torch.float32, device=xyz_qm.device) # Convert the QM atomic numbers to elements and species IDs. - species_id = [] + species_id = _torch.tensor([], dtype=_torch.int64, device=xyz_qm.device) for id in atomic_numbers: - try: - species_id.append(self._hypers["global_species"].index(id)) - except: - msg = f"Unsupported element index '{id}'." - raise ValueError(msg) - species_id = _torch.tensor(_np.array(species_id), device=xyz_qm.device) + species_id = _torch.cat( + ( + species_id, + _torch.tensor([self._species.index(id)], device=species_id.device), + ), + ) + + # Reshape the atomic numbers. + zid = species_id.unsqueeze(0) - # Get the features. - mol_features = self._get_features(species_id, xyz_qm) + # Reshape the atomic positions. + xyz = xyz_qm.unsqueeze(0) + + # Compute the AEVs. + aev = self._aev_computer((zid, xyz))[1][0] + aev = aev / _torch.linalg.norm(aev, ord=2, dim=1, keepdim=True) # Compute the MBIS valence shell widths. - s = self._get_s(mol_features, species_id) + s = self._gpr(aev, self._ref_mean_s, self._c_s, species_id) # Compute the electronegativities. - chi = self._get_chi(mol_features, species_id) + chi = self._gpr(aev, self._ref_mean_chi, self._c_chi, species_id) # Convert coordinates to Bohr. - xyz_qm_bohr = xyz_qm * _ANGSTROM_TO_BOHR - xyz_mm_bohr = xyz_mm * _ANGSTROM_TO_BOHR + ANGSTROM_TO_BOHR = 1.8897261258369282 + xyz_qm_bohr = xyz_qm * ANGSTROM_TO_BOHR + xyz_mm_bohr = xyz_mm * ANGSTROM_TO_BOHR # Compute the static energy. q_core = self._q_core[species_id] @@ -415,18 +226,84 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): q = self._get_q(r_data, s, chi) q_val = q - q_core mu_ind = self._get_mu_ind(r_data, mesh_data, charges_mm, s, q_val, k_Z) - vpot_q_core = self._get_vpot_q(q_core, mesh_data["T0_mesh"]) - vpot_q_val = self._get_vpot_q(q_val, mesh_data["T0_mesh_slater"]) + vpot_q_core = self._get_vpot_q(q_core, mesh_data[0]) + vpot_q_val = self._get_vpot_q(q_val, mesh_data[1]) vpot_static = vpot_q_core + vpot_q_val E_static = _torch.sum(vpot_static @ charges_mm) # Compute the induced energy. - vpot_ind = self._get_vpot_mu(mu_ind, mesh_data["T1_mesh"]) + vpot_ind = self._get_vpot_mu(mu_ind, mesh_data[2]) E_ind = _torch.sum(vpot_ind @ charges_mm) * 0.5 return _torch.stack([E_static, E_ind]) - def _get_q(self, r_data, s, chi): + @classmethod + def _get_Kinv(cls, ref_features, sigma): + """ + Internal function to compute the inverse of the K matrix for GPR. + + Parameters + ---------- + + ref_features: numpy.array (N_Z, MAX_N_REF, N_FEAT) + The basis feature vectors for each species. + + sigma: float + The uncertainty of the observations (regularizer). + + Returns + ------- + + result: numpy.array (MAX_N_REF, MAX_N_REF) + The inverse of the K matrix. + """ + n = ref_features.shape[1] + K = (ref_features @ ref_features.swapaxes(1, 2)) ** 2 + return _torch.linalg.inv( + K + sigma**2 * _torch.eye(n, dtype=_torch.float32, device=K.device) + ) + + def _gpr(self, mol_features, ref_mean, c, zid): + """ + Internal method to predict a property using Gaussian Process Regression. + + Parameters + ---------- + + mol_features: torch.Tensor (N_ATOMS, N_FEAT) + The feature vectors for each atom. + + ref_mean: torch.Tensor (N_Z,) + The mean of the reference values for each species. + + c: torch.Tensor (N_Z, MAX_N_REF) + The coefficients of the GPR model. + + zid: torch.tensor (N_ATOMS,) + The species identity value of each atom. + + Returns + ------- + + result: torch.tensor, numpy.array (N_ATOMS) + The values of the predicted property for each atom. + """ + + result = _torch.zeros( + len(zid), dtype=_torch.float32, device=mol_features.device + ) + for i in range(self._n_z): + n_ref = self._n_ref[i] + ref_features_z = self._ref_features[i, :n_ref] + mol_features_z = mol_features[zid == i, :, None] + + K_mol_ref2 = (ref_features_z @ mol_features_z) ** 2 + K_mol_ref2 = K_mol_ref2.reshape(K_mol_ref2.shape[:-1]) + result[zid == i] = K_mol_ref2 @ c[i, :n_ref] + ref_mean[i] + + return result + + def _get_q(self, r_data: Tuple[Tensor, Tensor, Tensor, Tensor], s, chi): """ Internal method that predicts MBIS charges (Eq. 16 in 10.1021/acs.jctc.2c00914) @@ -452,7 +329,7 @@ def _get_q(self, r_data, s, chi): b = _torch.hstack([-chi, self._q_total]) return _torch.linalg.solve(A, b)[:-1] - def _get_A_QEq(self, r_data, s): + def _get_A_QEq(self, r_data: Tuple[Tensor, Tensor, Tensor, Tensor], s): """ Internal method, generates A matrix for charge prediction (Eq. 16 in 10.1021/acs.jctc.2c00914) @@ -474,13 +351,21 @@ def _get_A_QEq(self, r_data, s): s2 = s_gauss**2 s_mat = _torch.sqrt(s2[:, None] + s2[None, :]) - device = r_data["r_mat"].device + device = r_data[0].device - A = self._get_T0_gaussian(r_data["T01"], r_data["r_mat"], s_mat) + A = self._get_T0_gaussian(r_data[1], r_data[0], s_mat) new_diag = _torch.ones_like( A.diagonal(), dtype=_torch.float32, device=device - ) * (1.0 / (s_gauss * _np.sqrt(_np.pi))) + ) * ( + 1.0 + / ( + s_gauss + * _torch.sqrt( + _torch.tensor([_torch.pi], dtype=_torch.float32, device=device) + ) + ) + ) mask = _torch.diag( _torch.ones_like(new_diag, dtype=_torch.float32, device=device) ) @@ -500,7 +385,15 @@ def _get_A_QEq(self, r_data, s): return B - def _get_mu_ind(self, r_data, mesh_data, q, s, q_val, k_Z): + def _get_mu_ind( + self, + r_data: Tuple[Tensor, Tensor, Tensor, Tensor], + mesh_data: Tuple[Tensor, Tensor, Tensor], + q, + s, + q_val, + k_Z, + ): """ Internal method, calculates induced atomic dipoles (Eq. 20 in 10.1021/acs.jctc.2c00914) @@ -532,17 +425,17 @@ def _get_mu_ind(self, r_data, mesh_data, q, s, q_val, k_Z): """ A = self._get_A_thole(r_data, s, q_val, k_Z) - r = 1.0 / mesh_data["T0_mesh"] + r = 1.0 / mesh_data[0] f1 = self._get_f1_slater(r, s[:, None] * 2.0) - fields = _torch.sum( - mesh_data["T1_mesh"] * f1[:, :, None] * q[:, None], axis=1 - ).flatten() + fields = _torch.sum(mesh_data[2] * f1[:, :, None] * q[:, None], dim=1).flatten() mu_ind = _torch.linalg.solve(A, fields) E_ind = mu_ind @ fields * 0.5 return mu_ind.reshape((-1, 3)) - def _get_A_thole(self, r_data, s, q_val, k_Z): + def _get_A_thole( + self, r_data: Tuple[Tensor, Tensor, Tensor, Tensor], s, q_val, k_Z + ): """ Internal method, generates A matrix for induced dipoles prediction (Eq. 20 in 10.1021/acs.jctc.2c00914) @@ -573,11 +466,11 @@ def _get_A_thole(self, r_data, s, q_val, k_Z): alphap = alpha * self._a_Thole alphap_mat = alphap[:, None] * alphap[None, :] - au3 = r_data["r_mat"] ** 3 / _torch.sqrt(alphap_mat) + au3 = r_data[0] ** 3 / _torch.sqrt(alphap_mat) au31 = au3.repeat_interleave(3, dim=1) au32 = au31.repeat_interleave(3, dim=0) - A = -self._get_T2_thole(r_data["T21"], r_data["T22"], au32) + A = -self._get_T2_thole(r_data[2], r_data[3], au32) new_diag = 1.0 / alpha.repeat_interleave(3) mask = _torch.diag( @@ -607,7 +500,7 @@ def _get_vpot_q(q, T0): result: torch.tensor (max_mm_atoms) Electrostatic potential over MM atoms. """ - return _torch.sum(T0 * q[:, None], axis=0) + return _torch.sum(T0 * q[:, None], dim=0) @staticmethod def _get_vpot_mu(mu, T1): @@ -670,11 +563,10 @@ def _get_r_data(cls, xyz): ) t01 = r_inv - t11 = -rr_mat.reshape(n_atoms, n_atoms * 3) * r_inv1**3 t21 = -id2 * r_inv2**3 t22 = 3 * outer * r_inv2**5 - return {"r_mat": r_mat, "T01": t01, "T11": t11, "T21": t21, "T22": t22} + return (r_mat, t01, t21, t22) @classmethod def _get_mesh_data(cls, xyz, xyz_mesh, s): @@ -694,13 +586,9 @@ def _get_mesh_data(cls, xyz, xyz_mesh, s): MBIS valence widths. """ rr = xyz_mesh[None, :, :] - xyz[:, None, :] - r = _torch.linalg.norm(rr, axis=2) + r = _torch.linalg.norm(rr, ord=2, dim=2) - return { - "T0_mesh": 1.0 / r, - "T0_mesh_slater": cls._get_T0_slater(r, s[:, None]), - "T1_mesh": -rr / r[:, :, None] ** 3, - } + return (1.0 / r, cls._get_T0_slater(r, s[:, None]), -rr / r[:, :, None] ** 3) @classmethod def _get_f1_slater(cls, r, s): @@ -769,7 +657,15 @@ def _get_T0_gaussian(t01, r, s_mat): results: torch.tensor (N_ATOMS, N_ATOMS) """ - return t01 * _torch.erf(r / (s_mat * _np.sqrt(2))) + return t01 * _torch.erf( + r + / ( + s_mat + * _torch.sqrt( + _torch.tensor([2.0], dtype=_torch.float32, device=r.device) + ) + ) + ) @classmethod def _get_T2_thole(cls, tr21, tr22, au3): @@ -780,10 +676,10 @@ def _get_T2_thole(cls, tr21, tr22, au3): ---------- tr21: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) - r_data["T21"] + r_data[2] tr21: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) - r_data["T22"] + r_data[3] au3: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) Scaled distance matrix (see _get_A_thole). @@ -918,8 +814,12 @@ def __init__(self, ani2x_model=None, atomic_numbers=None, device=None): # Hook the forward pass of the ANI2x model to get the AEV features. def hook_wrapper(): - def hook(module, input, output): - self._aevs = output[1][0] + def hook( + module, + input: Tuple[Tuple[Tensor, Tensor], Optional[Tensor], Optional[Tensor]], + output: _torchani.aev.SpeciesAEV, + ): + self._aev = output[1][0] return hook @@ -965,18 +865,14 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): # Convert the QM atomic numbers to elements and species IDs. species_id = [] for id in atomic_numbers: - try: - species_id.append(self._hypers["global_species"].index(id)) - except: - msg = f"Unsupported element index '{id}'." - raise ValueError(msg) + species_id.append(self._species.index(id)) species_id = _torch.tensor(_np.array(species_id), device=xyz_qm.device) # Reshape the atomic numbers. - atomic_numbers = atomic_numbers.reshape(1, *atomic_numbers.shape) + atomic_numbers = atomic_numbers.unsqueeze(0) # Reshape the coordinates, - xyz = xyz_qm.reshape(1, *xyz_qm.shape) + xyz = xyz_qm.unsqueeze(0) # Get the in vacuo energy. E_vac = self._ani2x((atomic_numbers, xyz)).energies[0] @@ -988,17 +884,20 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): return _torch.stack([E_vac, zero, zero]) # Normalise the AEVs. - self._aevs = self._aevs / _torch.linalg.norm(self._aevs, axis=1, keepdims=True) + self._aev = self._aev / _torch.linalg.norm( + self._aev, ord=2, dim=1, keepdim=True + ) # Compute the MBIS valence shell widths. - s = self._get_s(self._aevs, species_id) + s = self._gpr(self._aev, self._ref_mean_s, self._c_s, species_id) # Compute the electronegativities. - chi = self._get_chi(self._aevs, species_id) + chi = self._gpr(self._aev, self._ref_mean_chi, self._c_chi, species_id) # Convert coordinates to Bohr. - xyz_qm_bohr = xyz_qm * _ANGSTROM_TO_BOHR - xyz_mm_bohr = xyz_mm * _ANGSTROM_TO_BOHR + ANGSTROM_TO_BOHR = 1.8897261258369282 + xyz_qm_bohr = xyz_qm * ANGSTROM_TO_BOHR + xyz_mm_bohr = xyz_mm * ANGSTROM_TO_BOHR # Compute the static energy. q_core = self._q_core[species_id] @@ -1008,13 +907,13 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): q = self._get_q(r_data, s, chi) q_val = q - q_core mu_ind = self._get_mu_ind(r_data, mesh_data, charges_mm, s, q_val, k_Z) - vpot_q_core = self._get_vpot_q(q_core, mesh_data["T0_mesh"]) - vpot_q_val = self._get_vpot_q(q_val, mesh_data["T0_mesh_slater"]) + vpot_q_core = self._get_vpot_q(q_core, mesh_data[0]) + vpot_q_val = self._get_vpot_q(q_val, mesh_data[1]) vpot_static = vpot_q_core + vpot_q_val E_static = _torch.sum(vpot_static @ charges_mm) # Compute the induced energy. - vpot_ind = self._get_vpot_mu(mu_ind, mesh_data["T1_mesh"]) + vpot_ind = self._get_vpot_mu(mu_ind, mesh_data[2]) E_ind = _torch.sum(vpot_ind @ charges_mm) * 0.5 return _torch.stack([E_vac, E_static, E_ind]) From bcfb2df9bcfc45575b5e67a3837a0380326076d4 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 1 Jul 2024 19:53:54 +0100 Subject: [PATCH 037/134] Add support for masked AEV model and torch.compile. [ci skip] --- emle/calculator.py | 8 +++++++- emle/models.py | 10 +++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 20eccab..2bdc3b2 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1893,10 +1893,16 @@ def _sire_callback_optimised(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): self._nnpops_active = True # Create the model. - self._ani2x_emle = _ANI2xEMLE( + ani2x_emle = _ANI2xEMLE( ani2x_model=self._torchani_model, device=self._device ) + # Compile the model. + # This needs additional triton and cuda-nvcc dependencies. Will + # test when recreating the conda environment. + # self._ani2x_emle = _torch.compile(ani2x_emle) + self._ani2x_emle = ani2x_emle + # Compute the energy and gradients. E = self._ani2x_emle(atomic_numbers, charges_mm, xyz_qm, xyz_mm) dE_dxyz_qm, dE_dxyz_mm = _torch.autograd.grad(E.sum(), (xyz_qm, xyz_mm)) diff --git a/emle/models.py b/emle/models.py index d4c4a01..3b34dc7 100644 --- a/emle/models.py +++ b/emle/models.py @@ -61,7 +61,7 @@ def __init__(self, device=None, create_aev_calculator=True): self._module_dir = _os.path.dirname(_os.path.abspath(__file__)) # Create the name of the default model file. - self._model = _os.path.join(self._module_dir, "emle_qm7_aev.mat") + self._model = _os.path.join(self._module_dir, "emle_qm7_aev_masked.mat") # Call the base class constructor. super().__init__() @@ -95,6 +95,9 @@ def __init__(self, device=None, create_aev_calculator=True): self._q_core = _torch.tensor( self._params["q_core"], dtype=_torch.float32, device=device ) + self._aev_mask = _torch.tensor( + self._params["aev_mask"], dtype=_torch.bool, device=device + ) self._a_QEq = self._params["a_QEq"] self._a_Thole = self._params["a_Thole"] self._k_Z = _torch.tensor( @@ -148,6 +151,7 @@ def to(self, device): self._k_Z = self._k_Z.to(device) self._q_total = self._q_total.to(device) self._ref_features = self._ref_features.to(device) + self._ae_mask = self._aev_mask.to(device) self._n_ref = self._n_ref.to(device) self._ref_values_s = self._ref_values_s.to(device) self._ref_values_chi = self._ref_values_chi.to(device) @@ -204,7 +208,7 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): xyz = xyz_qm.unsqueeze(0) # Compute the AEVs. - aev = self._aev_computer((zid, xyz))[1][0] + aev = self._aev_computer((zid, xyz))[1][0][:, self._aev_mask] aev = aev / _torch.linalg.norm(aev, ord=2, dim=1, keepdim=True) # Compute the MBIS valence shell widths. @@ -819,7 +823,7 @@ def hook( input: Tuple[Tuple[Tensor, Tensor], Optional[Tensor], Optional[Tensor]], output: _torchani.aev.SpeciesAEV, ): - self._aev = output[1][0] + self._aev = output[1][0][:, self._aev_mask] return hook From eee3021a07a16521725351485f04812f27d67b91 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 1 Jul 2024 20:06:32 +0100 Subject: [PATCH 038/134] Conditionally apply NNPOps optimisations. [ci skip] --- emle/calculator.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 2bdc3b2..6679067 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1881,16 +1881,22 @@ def _sire_callback_optimised(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): # Create an internal ANI2xEMLE model if one doesn't already exist. if self._ani2x_emle is None: - from NNPOps import OptimizedTorchANI as _OptimizedTorchANI - from .models import ANI2xEMLE as _ANI2xEMLE + # Apply NNPOps optimisations if available. + try: + from NNPOps import OptimizedTorchANI as _OptimizedTorchANI - # Optimise the TorchANI model. - self._torchani_model = _OptimizedTorchANI( - self._torchani_model, atomic_numbers.reshape(-1, *atomic_numbers.shape) - ).to(self._device) + # Optimise the TorchANI model. + self._torchani_model = _OptimizedTorchANI( + self._torchani_model, + atomic_numbers.reshape(-1, *atomic_numbers.shape), + ).to(self._device) - # Flag that NNPOps is active. - self._nnpops_active = True + # Flag that NNPOps is active. + self._nnpops_active = True + except: + pass + + from .models import ANI2xEMLE as _ANI2xEMLE # Create the model. ani2x_emle = _ANI2xEMLE( From 24ecae60a6efb759b971904d2095dddaf00fbd21 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 1 Jul 2024 20:27:07 +0100 Subject: [PATCH 039/134] Use torch.empty to create empty list of tensors. [ci skip] --- emle/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emle/models.py b/emle/models.py index 3b34dc7..16928e6 100644 --- a/emle/models.py +++ b/emle/models.py @@ -192,7 +192,7 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): return _torch.zeros(2, dtype=_torch.float32, device=xyz_qm.device) # Convert the QM atomic numbers to elements and species IDs. - species_id = _torch.tensor([], dtype=_torch.int64, device=xyz_qm.device) + species_id = _torch.empty(0, dtype=_torch.int64, device=xyz_qm.device) for id in atomic_numbers: species_id = _torch.cat( ( From e4e35fc37186fedc094f1fcca4e1fe8ba7c15012 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Tue, 2 Jul 2024 09:44:21 +0100 Subject: [PATCH 040/134] Make model dtype agnostic and support all model conversion methods. [ci skip] --- emle/models.py | 226 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 169 insertions(+), 57 deletions(-) diff --git a/emle/models.py b/emle/models.py index 16928e6..72c49d5 100644 --- a/emle/models.py +++ b/emle/models.py @@ -44,13 +44,19 @@ class EMLE(_torch.nn.Module): embedding. """ - def __init__(self, device=None, create_aev_calculator=True): + def __init__(self, device=None, dtype=None, create_aev_calculator=True): """ Constructor Parameters ---------- + device: torch.device + The device on which to run the model. + + dtype: torch.dtype + The data type to use for the models floating point tensors. + create_aev_calculator: bool Whether to create an AEV calculator instance. """ @@ -72,6 +78,12 @@ def __init__(self, device=None, create_aev_calculator=True): else: device = _torch.get_default_device() + if dtype is not None: + if not isinstance(dtype, _torch.dtype): + raise TypeError("'dtype' must be of type 'torch.dtype'") + else: + dtype = _torch.get_default_dtype() + if not isinstance(create_aev_calculator, bool): raise TypeError("'create_aev_calculator' must be of type 'bool'") @@ -92,29 +104,25 @@ def __init__(self, device=None, create_aev_calculator=True): self._species = [1, 6, 7, 8, 16] # Store model parameters as tensors. - self._q_core = _torch.tensor( - self._params["q_core"], dtype=_torch.float32, device=device - ) + self._q_core = _torch.tensor(self._params["q_core"], dtype=dtype, device=device) self._aev_mask = _torch.tensor( self._params["aev_mask"], dtype=_torch.bool, device=device ) self._a_QEq = self._params["a_QEq"] self._a_Thole = self._params["a_Thole"] - self._k_Z = _torch.tensor( - self._params["k_Z"], dtype=_torch.float32, device=device - ) + self._k_Z = _torch.tensor(self._params["k_Z"], dtype=dtype, device=device) self._q_total = _torch.tensor( - self._params.get("total_charge", 0), dtype=_torch.float32, device=device + self._params.get("total_charge", 0), dtype=dtype, device=device ) # Extract the reference features. self._ref_features = _torch.tensor( - self._params["ref_soap"], dtype=_torch.float32, device=device + self._params["ref_soap"], dtype=dtype, device=device ) # Extract the reference values for the MBIS valence shell widths. self._ref_values_s = _torch.tensor( - self._params["s_ref"], dtype=_torch.float32, device=device + self._params["s_ref"], dtype=dtype, device=device ) # Compute the inverse of the K matrix. @@ -131,7 +139,7 @@ def __init__(self, device=None, create_aev_calculator=True): # Exctract the reference values for the electronegativities. self._ref_values_chi = _torch.tensor( - self._params["chi_ref"], dtype=_torch.float32, device=device + self._params["chi_ref"], dtype=dtype, device=device ) # Store additional attributes for the electronegativity GPR model. @@ -139,26 +147,100 @@ def __init__(self, device=None, create_aev_calculator=True): ref_shifted = self._ref_values_chi - self._ref_mean_chi[:, None] self._c_chi = (Kinv @ ref_shifted[:, :, None]).squeeze() - def to(self, device): + def to(self, *args, **kwargs): + """ + Performs Tensor dtype and/or device conversion on the model. + """ + if self._aev_computer is not None: + self._aev_computer = self._aev_computer.to(*args, **kwargs) + self._q_core = self._q_core.to(*args, **kwargs) + self._k_Z = self._k_Z.to(*args, **kwargs) + self._q_total = self._q_total.to(*args, **kwargs) + self._ref_features = self._ref_features.to(*args, **kwargs) + self._ae_mask = self._aev_mask.to(*args, **kwargs) + self._n_ref = self._n_ref.to(*args, **kwargs) + self._ref_values_s = self._ref_values_s.to(*args, **kwargs) + self._ref_values_chi = self._ref_values_chi.to(*args, **kwargs) + self._ref_mean_s = self._ref_mean_s.to(*args, **kwargs) + self._ref_mean_chi = self._ref_mean_chi.to(*args, **kwargs) + self._c_s = self._c_s.to(*args, **kwargs) + self._c_chi = self._c_chi.to(*args, **kwargs) + return self + + def cpu(self, **kwargs): """ - Move the model to a new device. + Returns a copy of this model in CPU memory. """ - if not isinstance(device, _torch.device): - raise TypeError("'device' must be of type 'torch.device'") if self._aev_computer is not None: - self._aev_computer = self._aev_computer.to(device) - self._q_core = self._q_core.to(device) - self._k_Z = self._k_Z.to(device) - self._q_total = self._q_total.to(device) - self._ref_features = self._ref_features.to(device) - self._ae_mask = self._aev_mask.to(device) - self._n_ref = self._n_ref.to(device) - self._ref_values_s = self._ref_values_s.to(device) - self._ref_values_chi = self._ref_values_chi.to(device) - self._ref_mean_s = self._ref_mean_s.to(device) - self._ref_mean_chi = self._ref_mean_chi.to(device) - self._c_s = self._c_s.to(device) - self._c_chi = self._c_chi.to(device) + self._aev_computer = self._aev_computer.cpu(**kwargs) + self._q_core = self._q_core.cpu(**kwargs) + self._k_Z = self._k_Z.cpu(**kwargs) + self._q_total = self._q_total.cpu(**kwargs) + self._ref_features = self._ref_features.cpu(**kwargs) + self._ae_mask = self._aev_mask.cpu(**kwargs) + self._n_ref = self._n_ref.cpu(**kwargs) + self._ref_values_s = self._ref_values_s.cpu(**kwargs) + self._ref_values_chi = self._ref_values_chi.cpu(**kwargs) + self._ref_mean_s = self._ref_mean_s.cpu(**kwargs) + self._ref_mean_chi = self._ref_mean_chi.cpu(**kwargs) + self._c_s = self._c_s.cpu(**kwargs) + self._c_chi = self._c_chi.cpu(**kwargs) + return self + + def double(self): + """ + Returns a copy of this model in float64 precision. + """ + if self._aev_computer is not None: + self._aev_computer = self._aev_computer.double() + self._q_core = self._q_core.double() + self._k_Z = self._k_Z.double() + self._q_total = self._q_total.double() + self._ref_features = self._ref_features.double() + self._ref_values_s = self._ref_values_s.double() + self._ref_values_chi = self._ref_values_chi.double() + self._ref_mean_s = self._ref_mean_s.double() + self._ref_mean_chi = self._ref_mean_chi.double() + self._c_s = self._c_s.double() + self._c_chi = self._c_chi.double() + return self + + def float(self): + """ + Returns a copy of this model in float32 precision. + """ + if self._aev_computer is not None: + self._aev_computer = self._aev_computer.float() + self._q_core = self._q_core.float() + self._k_Z = self._k_Z.float() + self._q_total = self._q_total.float() + self._ref_features = self._ref_features.float() + self._ref_values_s = self._ref_values_s.float() + self._ref_values_chi = self._ref_values_chi.float() + self._ref_mean_s = self._ref_mean_s.float() + self._ref_mean_chi = self._ref_mean_chi.float() + self._c_s = self._c_s.float() + self._c_chi = self._c_chi.float() + return self + + def cuda(self, **kwargs): + """ + Returns a copy of this model in CUDA memory. + """ + if self._aev_computer is not None: + self._aev_computer = self._aev_computer.cuda(**kwargs) + self._q_core = self._q_core.cuda(**kwargs) + self._k_Z = self._k_Z.cuda(**kwargs) + self._q_total = self._q_total.cuda(**kwargs) + self._ref_features = self._ref_features.cuda(**kwargs) + self._ae_mask = self._aev_mask.cuda(**kwargs) + self._n_ref = self._n_ref.cuda(**kwargs) + self._ref_values_s = self._ref_values_s.cuda(**kwargs) + self._ref_values_chi = self._ref_values_chi.cuda(**kwargs) + self._ref_mean_s = self._ref_mean_s.cuda(**kwargs) + self._ref_mean_chi = self._ref_mean_chi.cuda(**kwargs) + self._c_s = self._c_s.cuda(**kwargs) + self._c_chi = self._c_chi.cuda(**kwargs) return self def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): @@ -189,7 +271,7 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): # If there are no point charges, return zeros. if len(xyz_mm) == 0: - return _torch.zeros(2, dtype=_torch.float32, device=xyz_qm.device) + return _torch.zeros(2, dtype=xyz_qm.dtype, device=xyz_qm.device) # Convert the QM atomic numbers to elements and species IDs. species_id = _torch.empty(0, dtype=_torch.int64, device=xyz_qm.device) @@ -264,7 +346,7 @@ def _get_Kinv(cls, ref_features, sigma): n = ref_features.shape[1] K = (ref_features @ ref_features.swapaxes(1, 2)) ** 2 return _torch.linalg.inv( - K + sigma**2 * _torch.eye(n, dtype=_torch.float32, device=K.device) + K + sigma**2 * _torch.eye(n, dtype=ref_features.dtype, device=K.device) ) def _gpr(self, mol_features, ref_mean, c, zid): @@ -294,7 +376,7 @@ def _gpr(self, mol_features, ref_mean, c, zid): """ result = _torch.zeros( - len(zid), dtype=_torch.float32, device=mol_features.device + len(zid), dtype=mol_features.dtype, device=mol_features.device ) for i in range(self._n_z): n_ref = self._n_ref[i] @@ -356,30 +438,25 @@ def _get_A_QEq(self, r_data: Tuple[Tensor, Tensor, Tensor, Tensor], s): s_mat = _torch.sqrt(s2[:, None] + s2[None, :]) device = r_data[0].device + dtype = r_data[0].dtype A = self._get_T0_gaussian(r_data[1], r_data[0], s_mat) - new_diag = _torch.ones_like( - A.diagonal(), dtype=_torch.float32, device=device - ) * ( + new_diag = _torch.ones_like(A.diagonal(), dtype=dtype, device=device) * ( 1.0 / ( s_gauss - * _torch.sqrt( - _torch.tensor([_torch.pi], dtype=_torch.float32, device=device) - ) + * _torch.sqrt(_torch.tensor([_torch.pi], dtype=dtype, device=device)) ) ) - mask = _torch.diag( - _torch.ones_like(new_diag, dtype=_torch.float32, device=device) - ) + mask = _torch.diag(_torch.ones_like(new_diag, dtype=dtype, device=device)) A = mask * _torch.diag(new_diag) + (1.0 - mask) * A # Store the dimensions of A. x, y = A.shape # Create an tensor of ones with one more row and column than A. - B = _torch.ones(x + 1, y + 1, dtype=_torch.float32, device=device) + B = _torch.ones(x + 1, y + 1, dtype=dtype, device=device) # Copy A into B. B[:x, :y] = A @@ -477,9 +554,7 @@ def _get_A_thole( A = -self._get_T2_thole(r_data[2], r_data[3], au32) new_diag = 1.0 / alpha.repeat_interleave(3) - mask = _torch.diag( - _torch.ones_like(new_diag, dtype=_torch.float32, device=A.device) - ) + mask = _torch.diag(_torch.ones_like(new_diag, dtype=A.dtype, device=A.device)) A = mask * _torch.diag(new_diag) + (1.0 - mask) * A return A @@ -561,7 +636,7 @@ def _get_r_data(cls, xyz): id2 = _torch.tile( _torch.tile( - _torch.eye(3, dtype=_torch.float32, device=xyz.device).T, (1, n_atoms) + _torch.eye(3, dtype=xyz.dtype, device=xyz.device).T, (1, n_atoms) ).T, (1, n_atoms), ) @@ -665,9 +740,7 @@ def _get_T0_gaussian(t01, r, s_mat): r / ( s_mat - * _torch.sqrt( - _torch.tensor([2.0], dtype=_torch.float32, device=r.device) - ) + * _torch.sqrt(_torch.tensor([2.0], dtype=r.dtype, device=r.device)) ) ) @@ -735,7 +808,7 @@ def _lambda5(au3): class ANI2xEMLE(EMLE): - def __init__(self, ani2x_model=None, atomic_numbers=None, device=None): + def __init__(self, ani2x_model=None, atomic_numbers=None, device=None, dtype=None): """ Constructor @@ -755,6 +828,9 @@ def __init__(self, ani2x_model=None, atomic_numbers=None, device=None): device: torch.device The device on which to run the model. + + dtype: torch.dtype + The data type to use for the models floating point tensors. """ if device is not None: if not isinstance(device, _torch.device): @@ -762,6 +838,12 @@ def __init__(self, ani2x_model=None, atomic_numbers=None, device=None): else: device = _torch.get_default_device() + if dtype is not None: + if not isinstance(dtype, _torch.dtype): + raise TypeError("'dtype' must be of type 'torch.dtype'") + else: + dtype = _torch.get_default_dtype() + if atomic_numbers is not None: if not isinstance(atomic_numbers, _torch.Tensor): raise TypeError("'atomic_numbers' must be of type 'torch.Tensor'") @@ -770,7 +852,7 @@ def __init__(self, ani2x_model=None, atomic_numbers=None, device=None): raise ValueError("'atomic_numbers' must be of dtype 'torch.int64'") # Call the base class constructor. - super().__init__(device=device, create_aev_calculator=False) + super().__init__(device=device, dtype=dtype, create_aev_calculator=False) if ani2x_model is not None: # Add the base ANI2x model and ensemble. @@ -830,14 +912,44 @@ def hook( # Register the hook. self._aev_hook = self._ani2x.aev_computer.register_forward_hook(hook_wrapper()) - def to(self, device): + def to(self, *args, **kwargs): + """ + Performs Tensor dtype and/or device conversion on the model. + """ + module = super(ANI2xEMLE, self).to(*args, **kwargs) + module._ani2x = module._ani2x.to(*args, **kwargs) + return module + + def cpu(self, **kwargs): + """ + Returns a copy of this model in CPU memory. + """ + module = super(ANI2xEMLE, self).cpu(**kwargs) + module._ani2x = module._ani2x.cpu(**kwargs) + return module + + def cuda(self, **kwargs): + """ + Returns a copy of this model in CUDA memory. + """ + module = super(ANI2xEMLE, self).cuda(**kwargs) + module._ani2x = module._ani2x.cuda(**kwargs) + return module + + def double(self): + """ + Returns a copy of this model in float64 precision. + """ + module = super(ANI2xEMLE, self).double() + module._ani2x = module._ani2x.double() + return module + + def float(self): """ - Move the model to a new device. + Returns a copy of this model in float32 precision. """ - if not isinstance(device, _torch.device): - raise TypeError("'device' must be of type 'torch.device'") - module = super(ANI2xEMLE, self).to(device) - module._ani2x = module._ani2x.to(device) + module = super(ANI2xEMLE, self).float() + module._ani2x = module._ani2x.float() return module def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): @@ -884,7 +996,7 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): # If there are no point charges, return the in vacuo energy and zeros # for the static and induced terms. if len(xyz_mm) == 0: - zero = _torch.tensor(0.0, dtype=_torch.float32, device=xyz_qm.device) + zero = _torch.tensor(0.0, dtype=xyz_qm.dtype, device=xyz_qm.device) return _torch.stack([E_vac, zero, zero]) # Normalise the AEVs. From 208c87af2733509633240ab784f583f0560bc457 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Tue, 2 Jul 2024 13:31:56 +0100 Subject: [PATCH 041/134] Fix .to() for self._aev_mask. [ci skip] --- emle/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/emle/models.py b/emle/models.py index 72c49d5..c392a39 100644 --- a/emle/models.py +++ b/emle/models.py @@ -157,7 +157,7 @@ def to(self, *args, **kwargs): self._k_Z = self._k_Z.to(*args, **kwargs) self._q_total = self._q_total.to(*args, **kwargs) self._ref_features = self._ref_features.to(*args, **kwargs) - self._ae_mask = self._aev_mask.to(*args, **kwargs) + self._aev_mask = self._aev_mask.to(*args, **kwargs) self._n_ref = self._n_ref.to(*args, **kwargs) self._ref_values_s = self._ref_values_s.to(*args, **kwargs) self._ref_values_chi = self._ref_values_chi.to(*args, **kwargs) @@ -177,7 +177,7 @@ def cpu(self, **kwargs): self._k_Z = self._k_Z.cpu(**kwargs) self._q_total = self._q_total.cpu(**kwargs) self._ref_features = self._ref_features.cpu(**kwargs) - self._ae_mask = self._aev_mask.cpu(**kwargs) + self._aev_mask = self._aev_mask.cpu(**kwargs) self._n_ref = self._n_ref.cpu(**kwargs) self._ref_values_s = self._ref_values_s.cpu(**kwargs) self._ref_values_chi = self._ref_values_chi.cpu(**kwargs) @@ -233,7 +233,7 @@ def cuda(self, **kwargs): self._k_Z = self._k_Z.cuda(**kwargs) self._q_total = self._q_total.cuda(**kwargs) self._ref_features = self._ref_features.cuda(**kwargs) - self._ae_mask = self._aev_mask.cuda(**kwargs) + self._aev_mask = self._aev_mask.cuda(**kwargs) self._n_ref = self._n_ref.cuda(**kwargs) self._ref_values_s = self._ref_values_s.cuda(**kwargs) self._ref_values_chi = self._ref_values_chi.cuda(**kwargs) From 04475e9aa250049b16b1cab0e81fcb8db716bbc7 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Tue, 2 Jul 2024 14:35:12 +0100 Subject: [PATCH 042/134] Simplify calculation of species indices. [ci skip] --- emle/models.py | 66 +++++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/emle/models.py b/emle/models.py index c392a39..7e742b8 100644 --- a/emle/models.py +++ b/emle/models.py @@ -103,6 +103,16 @@ def __init__(self, device=None, dtype=None, create_aev_calculator=True): # Set the supported species. self._species = [1, 6, 7, 8, 16] + # Create a map between species and their indices. + self._species_map = _np.zeros(max(self._species) + 1, dtype=_np.int64) + for i, s in enumerate(self._species): + self._species_map[s] = i + + # Convert to a tensor. + self._species_map = _torch.tensor( + self._species_map, dtype=_torch.int64, device=device + ) + # Store model parameters as tensors. self._q_core = _torch.tensor(self._params["q_core"], dtype=dtype, device=device) self._aev_mask = _torch.tensor( @@ -153,6 +163,7 @@ def to(self, *args, **kwargs): """ if self._aev_computer is not None: self._aev_computer = self._aev_computer.to(*args, **kwargs) + self._species_map = self._species_map.to(*args, **kwargs) self._q_core = self._q_core.to(*args, **kwargs) self._k_Z = self._k_Z.to(*args, **kwargs) self._q_total = self._q_total.to(*args, **kwargs) @@ -167,12 +178,34 @@ def to(self, *args, **kwargs): self._c_chi = self._c_chi.to(*args, **kwargs) return self + def cuda(self, **kwargs): + """ + Returns a copy of this model in CUDA memory. + """ + if self._aev_computer is not None: + self._aev_computer = self._aev_computer.cuda(**kwargs) + self._species_map = self._species_map.cuda(**kwargs) + self._q_core = self._q_core.cuda(**kwargs) + self._k_Z = self._k_Z.cuda(**kwargs) + self._q_total = self._q_total.cuda(**kwargs) + self._ref_features = self._ref_features.cuda(**kwargs) + self._aev_mask = self._aev_mask.cuda(**kwargs) + self._n_ref = self._n_ref.cuda(**kwargs) + self._ref_values_s = self._ref_values_s.cuda(**kwargs) + self._ref_values_chi = self._ref_values_chi.cuda(**kwargs) + self._ref_mean_s = self._ref_mean_s.cuda(**kwargs) + self._ref_mean_chi = self._ref_mean_chi.cuda(**kwargs) + self._c_s = self._c_s.cuda(**kwargs) + self._c_chi = self._c_chi.cuda(**kwargs) + return self + def cpu(self, **kwargs): """ Returns a copy of this model in CPU memory. """ if self._aev_computer is not None: self._aev_computer = self._aev_computer.cpu(**kwargs) + self._species_map = self._species_map.cpu(**kwargs) self._q_core = self._q_core.cpu(**kwargs) self._k_Z = self._k_Z.cpu(**kwargs) self._q_total = self._q_total.cpu(**kwargs) @@ -223,26 +256,6 @@ def float(self): self._c_chi = self._c_chi.float() return self - def cuda(self, **kwargs): - """ - Returns a copy of this model in CUDA memory. - """ - if self._aev_computer is not None: - self._aev_computer = self._aev_computer.cuda(**kwargs) - self._q_core = self._q_core.cuda(**kwargs) - self._k_Z = self._k_Z.cuda(**kwargs) - self._q_total = self._q_total.cuda(**kwargs) - self._ref_features = self._ref_features.cuda(**kwargs) - self._aev_mask = self._aev_mask.cuda(**kwargs) - self._n_ref = self._n_ref.cuda(**kwargs) - self._ref_values_s = self._ref_values_s.cuda(**kwargs) - self._ref_values_chi = self._ref_values_chi.cuda(**kwargs) - self._ref_mean_s = self._ref_mean_s.cuda(**kwargs) - self._ref_mean_chi = self._ref_mean_chi.cuda(**kwargs) - self._c_s = self._c_s.cuda(**kwargs) - self._c_chi = self._c_chi.cuda(**kwargs) - return self - def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): """ Computes the static and induced EMLE energy components. @@ -273,17 +286,10 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): if len(xyz_mm) == 0: return _torch.zeros(2, dtype=xyz_qm.dtype, device=xyz_qm.device) - # Convert the QM atomic numbers to elements and species IDs. - species_id = _torch.empty(0, dtype=_torch.int64, device=xyz_qm.device) - for id in atomic_numbers: - species_id = _torch.cat( - ( - species_id, - _torch.tensor([self._species.index(id)], device=species_id.device), - ), - ) + # Convert the atomic numbers to species IDs. + species_id = self._species_map[atomic_numbers] - # Reshape the atomic numbers. + # Reshape the IDs. zid = species_id.unsqueeze(0) # Reshape the atomic positions. From 6b195af1ca3c2a9e0684ed015202a08165784ee7 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Tue, 2 Jul 2024 14:44:27 +0100 Subject: [PATCH 043/134] Make sure all model parameters are converted to tensors. [ci skip] --- emle/models.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/emle/models.py b/emle/models.py index 7e742b8..31989af 100644 --- a/emle/models.py +++ b/emle/models.py @@ -114,12 +114,14 @@ def __init__(self, device=None, dtype=None, create_aev_calculator=True): ) # Store model parameters as tensors. - self._q_core = _torch.tensor(self._params["q_core"], dtype=dtype, device=device) self._aev_mask = _torch.tensor( self._params["aev_mask"], dtype=_torch.bool, device=device ) - self._a_QEq = self._params["a_QEq"] - self._a_Thole = self._params["a_Thole"] + self._q_core = _torch.tensor(self._params["q_core"], dtype=dtype, device=device) + self._a_QEq = _torch.tensor(self._params["a_QEq"], dtype=dtype, device=device) + self._a_Thole = _torch.tensor( + self._params["a_Thole"], dtype=dtype, device=device + ) self._k_Z = _torch.tensor(self._params["k_Z"], dtype=dtype, device=device) self._q_total = _torch.tensor( self._params.get("total_charge", 0), dtype=dtype, device=device @@ -164,11 +166,13 @@ def to(self, *args, **kwargs): if self._aev_computer is not None: self._aev_computer = self._aev_computer.to(*args, **kwargs) self._species_map = self._species_map.to(*args, **kwargs) + self._aev_mask = self._aev_mask.to(*args, **kwargs) self._q_core = self._q_core.to(*args, **kwargs) + self._a_QEq = self._a_QEq.to(*args, **kwargs) + self._a_Thole = self._a_Thole.to(*args, **kwargs) self._k_Z = self._k_Z.to(*args, **kwargs) self._q_total = self._q_total.to(*args, **kwargs) self._ref_features = self._ref_features.to(*args, **kwargs) - self._aev_mask = self._aev_mask.to(*args, **kwargs) self._n_ref = self._n_ref.to(*args, **kwargs) self._ref_values_s = self._ref_values_s.to(*args, **kwargs) self._ref_values_chi = self._ref_values_chi.to(*args, **kwargs) @@ -185,11 +189,13 @@ def cuda(self, **kwargs): if self._aev_computer is not None: self._aev_computer = self._aev_computer.cuda(**kwargs) self._species_map = self._species_map.cuda(**kwargs) + self._aev_mask = self._aev_mask.cuda(**kwargs) self._q_core = self._q_core.cuda(**kwargs) + self._a_QEq = self._a_QEq.cuda(**kwargs) + self._a_Thole = self._a_Thole.cuda(**kwargs) self._k_Z = self._k_Z.cuda(**kwargs) self._q_total = self._q_total.cuda(**kwargs) self._ref_features = self._ref_features.cuda(**kwargs) - self._aev_mask = self._aev_mask.cuda(**kwargs) self._n_ref = self._n_ref.cuda(**kwargs) self._ref_values_s = self._ref_values_s.cuda(**kwargs) self._ref_values_chi = self._ref_values_chi.cuda(**kwargs) @@ -206,11 +212,13 @@ def cpu(self, **kwargs): if self._aev_computer is not None: self._aev_computer = self._aev_computer.cpu(**kwargs) self._species_map = self._species_map.cpu(**kwargs) + self._aev_mask = self._aev_mask.cpu(**kwargs) self._q_core = self._q_core.cpu(**kwargs) + self._a_QEq = self._a_QEq.cpu(**kwargs) + self._a_Thole = self._a_Thole.cpu(**kwargs) self._k_Z = self._k_Z.cpu(**kwargs) self._q_total = self._q_total.cpu(**kwargs) self._ref_features = self._ref_features.cpu(**kwargs) - self._aev_mask = self._aev_mask.cpu(**kwargs) self._n_ref = self._n_ref.cpu(**kwargs) self._ref_values_s = self._ref_values_s.cpu(**kwargs) self._ref_values_chi = self._ref_values_chi.cpu(**kwargs) @@ -227,6 +235,8 @@ def double(self): if self._aev_computer is not None: self._aev_computer = self._aev_computer.double() self._q_core = self._q_core.double() + self._a_QEq = self._a_QEq.double() + self._a_Thole = self._a_Thole.double() self._k_Z = self._k_Z.double() self._q_total = self._q_total.double() self._ref_features = self._ref_features.double() @@ -245,6 +255,8 @@ def float(self): if self._aev_computer is not None: self._aev_computer = self._aev_computer.float() self._q_core = self._q_core.float() + self._a_QEq = self._a_QEq.float() + self._a_Thole = self._a_Thole.float() self._k_Z = self._k_Z.float() self._q_total = self._q_total.float() self._ref_features = self._ref_features.float() From 4fcdc72b1a53b592558c6c73477b8f8f793b2b6d Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Tue, 2 Jul 2024 14:46:23 +0100 Subject: [PATCH 044/134] No need to store model params as a class attribute. [ci skip] --- emle/models.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/emle/models.py b/emle/models.py index 31989af..6ccc0e1 100644 --- a/emle/models.py +++ b/emle/models.py @@ -96,7 +96,7 @@ def __init__(self, device=None, dtype=None, create_aev_calculator=True): # Load the model parameters. try: - self._params = _scipy_io.loadmat(self._model, squeeze_me=True) + params = _scipy_io.loadmat(self._model, squeeze_me=True) except: raise IOError(f"Unable to load model parameters from: '{self._model}'") @@ -115,35 +115,29 @@ def __init__(self, device=None, dtype=None, create_aev_calculator=True): # Store model parameters as tensors. self._aev_mask = _torch.tensor( - self._params["aev_mask"], dtype=_torch.bool, device=device + params["aev_mask"], dtype=_torch.bool, device=device ) - self._q_core = _torch.tensor(self._params["q_core"], dtype=dtype, device=device) - self._a_QEq = _torch.tensor(self._params["a_QEq"], dtype=dtype, device=device) - self._a_Thole = _torch.tensor( - self._params["a_Thole"], dtype=dtype, device=device - ) - self._k_Z = _torch.tensor(self._params["k_Z"], dtype=dtype, device=device) + self._q_core = _torch.tensor(params["q_core"], dtype=dtype, device=device) + self._a_QEq = _torch.tensor(params["a_QEq"], dtype=dtype, device=device) + self._a_Thole = _torch.tensor(params["a_Thole"], dtype=dtype, device=device) + self._k_Z = _torch.tensor(params["k_Z"], dtype=dtype, device=device) self._q_total = _torch.tensor( - self._params.get("total_charge", 0), dtype=dtype, device=device + params.get("total_charge", 0), dtype=dtype, device=device ) # Extract the reference features. self._ref_features = _torch.tensor( - self._params["ref_soap"], dtype=dtype, device=device + params["ref_soap"], dtype=dtype, device=device ) # Extract the reference values for the MBIS valence shell widths. - self._ref_values_s = _torch.tensor( - self._params["s_ref"], dtype=dtype, device=device - ) + self._ref_values_s = _torch.tensor(params["s_ref"], dtype=dtype, device=device) # Compute the inverse of the K matrix. Kinv = self._get_Kinv(self._ref_features, 1e-3) # Store additional attributes for the MBIS GPR model. - self._n_ref = _torch.tensor( - self._params["n_ref"], dtype=_torch.int64, device=device - ) + self._n_ref = _torch.tensor(params["n_ref"], dtype=_torch.int64, device=device) self._n_z = len(self._n_ref) self._ref_mean_s = _torch.sum(self._ref_values_s, dim=1) / self._n_ref ref_shifted = self._ref_values_s - self._ref_mean_s[:, None] @@ -151,7 +145,7 @@ def __init__(self, device=None, dtype=None, create_aev_calculator=True): # Exctract the reference values for the electronegativities. self._ref_values_chi = _torch.tensor( - self._params["chi_ref"], dtype=dtype, device=device + params["chi_ref"], dtype=dtype, device=device ) # Store additional attributes for the electronegativity GPR model. From 962a29bc7f153837779bebdff24d2fedda5fa448 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Tue, 2 Jul 2024 15:08:54 +0100 Subject: [PATCH 045/134] Remove all redundant module attributes. [ci skip] --- emle/models.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/emle/models.py b/emle/models.py index 6ccc0e1..5752219 100644 --- a/emle/models.py +++ b/emle/models.py @@ -61,16 +61,14 @@ def __init__(self, device=None, dtype=None, create_aev_calculator=True): Whether to create an AEV calculator instance. """ - # Class attributes. + # Call the base class constructor. + super().__init__() # Get the directory of this module file. - self._module_dir = _os.path.dirname(_os.path.abspath(__file__)) + module_dir = _os.path.dirname(_os.path.abspath(__file__)) # Create the name of the default model file. - self._model = _os.path.join(self._module_dir, "emle_qm7_aev_masked.mat") - - # Call the base class constructor. - super().__init__() + model = _os.path.join(module_dir, "emle_qm7_aev_masked.mat") if device is not None: if not isinstance(device, _torch.device): @@ -96,16 +94,16 @@ def __init__(self, device=None, dtype=None, create_aev_calculator=True): # Load the model parameters. try: - params = _scipy_io.loadmat(self._model, squeeze_me=True) + params = _scipy_io.loadmat(model, squeeze_me=True) except: raise IOError(f"Unable to load model parameters from: '{self._model}'") # Set the supported species. - self._species = [1, 6, 7, 8, 16] + species = [1, 6, 7, 8, 16] # Create a map between species and their indices. - self._species_map = _np.zeros(max(self._species) + 1, dtype=_np.int64) - for i, s in enumerate(self._species): + self._species_map = _np.zeros(max(species) + 1, dtype=_np.int64) + for i, s in enumerate(species): self._species_map[s] = i # Convert to a tensor. From d6b7964b686052fc2dec2bf0230a675182be5981 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Tue, 2 Jul 2024 15:14:58 +0100 Subject: [PATCH 046/134] Remove self from model in error message. [ci skip] --- emle/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emle/models.py b/emle/models.py index 5752219..ccadd7d 100644 --- a/emle/models.py +++ b/emle/models.py @@ -96,7 +96,7 @@ def __init__(self, device=None, dtype=None, create_aev_calculator=True): try: params = _scipy_io.loadmat(model, squeeze_me=True) except: - raise IOError(f"Unable to load model parameters from: '{self._model}'") + raise IOError(f"Unable to load model parameters from: '{model}'") # Set the supported species. species = [1, 6, 7, 8, 16] From 23d8bb199916af590db1832abea14ecc788f8e3a Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Tue, 2 Jul 2024 15:15:30 +0100 Subject: [PATCH 047/134] Add missing n_z tensor attribute. [ci skip] --- emle/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/emle/models.py b/emle/models.py index ccadd7d..86f2b58 100644 --- a/emle/models.py +++ b/emle/models.py @@ -166,6 +166,7 @@ def to(self, *args, **kwargs): self._q_total = self._q_total.to(*args, **kwargs) self._ref_features = self._ref_features.to(*args, **kwargs) self._n_ref = self._n_ref.to(*args, **kwargs) + self._n_z = self._n_z.to(*args, **kwargs) self._ref_values_s = self._ref_values_s.to(*args, **kwargs) self._ref_values_chi = self._ref_values_chi.to(*args, **kwargs) self._ref_mean_s = self._ref_mean_s.to(*args, **kwargs) @@ -189,6 +190,7 @@ def cuda(self, **kwargs): self._q_total = self._q_total.cuda(**kwargs) self._ref_features = self._ref_features.cuda(**kwargs) self._n_ref = self._n_ref.cuda(**kwargs) + self._n_z = self._n_z.cuda(**kwargs) self._ref_values_s = self._ref_values_s.cuda(**kwargs) self._ref_values_chi = self._ref_values_chi.cuda(**kwargs) self._ref_mean_s = self._ref_mean_s.cuda(**kwargs) @@ -212,6 +214,7 @@ def cpu(self, **kwargs): self._q_total = self._q_total.cpu(**kwargs) self._ref_features = self._ref_features.cpu(**kwargs) self._n_ref = self._n_ref.cpu(**kwargs) + self._n_z = self._n_z.cpu(**kwargs) self._ref_values_s = self._ref_values_s.cpu(**kwargs) self._ref_values_chi = self._ref_values_chi.cpu(**kwargs) self._ref_mean_s = self._ref_mean_s.cpu(**kwargs) From ffc35c414d41980846f79d56cf5659e14e1fbb2a Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Tue, 2 Jul 2024 15:18:56 +0100 Subject: [PATCH 048/134] Remove redundant attribure. [ci skip] --- emle/models.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/emle/models.py b/emle/models.py index 86f2b58..1b4523f 100644 --- a/emle/models.py +++ b/emle/models.py @@ -136,7 +136,6 @@ def __init__(self, device=None, dtype=None, create_aev_calculator=True): # Store additional attributes for the MBIS GPR model. self._n_ref = _torch.tensor(params["n_ref"], dtype=_torch.int64, device=device) - self._n_z = len(self._n_ref) self._ref_mean_s = _torch.sum(self._ref_values_s, dim=1) / self._n_ref ref_shifted = self._ref_values_s - self._ref_mean_s[:, None] self._c_s = (Kinv @ ref_shifted[:, :, None]).squeeze() @@ -166,7 +165,6 @@ def to(self, *args, **kwargs): self._q_total = self._q_total.to(*args, **kwargs) self._ref_features = self._ref_features.to(*args, **kwargs) self._n_ref = self._n_ref.to(*args, **kwargs) - self._n_z = self._n_z.to(*args, **kwargs) self._ref_values_s = self._ref_values_s.to(*args, **kwargs) self._ref_values_chi = self._ref_values_chi.to(*args, **kwargs) self._ref_mean_s = self._ref_mean_s.to(*args, **kwargs) @@ -190,7 +188,6 @@ def cuda(self, **kwargs): self._q_total = self._q_total.cuda(**kwargs) self._ref_features = self._ref_features.cuda(**kwargs) self._n_ref = self._n_ref.cuda(**kwargs) - self._n_z = self._n_z.cuda(**kwargs) self._ref_values_s = self._ref_values_s.cuda(**kwargs) self._ref_values_chi = self._ref_values_chi.cuda(**kwargs) self._ref_mean_s = self._ref_mean_s.cuda(**kwargs) @@ -214,7 +211,6 @@ def cpu(self, **kwargs): self._q_total = self._q_total.cpu(**kwargs) self._ref_features = self._ref_features.cpu(**kwargs) self._n_ref = self._n_ref.cpu(**kwargs) - self._n_z = self._n_z.cpu(**kwargs) self._ref_values_s = self._ref_values_s.cpu(**kwargs) self._ref_values_chi = self._ref_values_chi.cpu(**kwargs) self._ref_mean_s = self._ref_mean_s.cpu(**kwargs) @@ -391,7 +387,7 @@ def _gpr(self, mol_features, ref_mean, c, zid): result = _torch.zeros( len(zid), dtype=mol_features.dtype, device=mol_features.device ) - for i in range(self._n_z): + for i in range(len(self._n_ref)): n_ref = self._n_ref[i] ref_features_z = self._ref_features[i, :n_ref] mol_features_z = mol_features[zid == i, :, None] From 2307d56a9c4b94fdd6432fcbe76a1b2572fd94b5 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Tue, 2 Jul 2024 15:24:39 +0100 Subject: [PATCH 049/134] Apply species map updates to ANI2xEMLE forward method. [ci skip] --- emle/models.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/emle/models.py b/emle/models.py index 1b4523f..a69ffec 100644 --- a/emle/models.py +++ b/emle/models.py @@ -987,11 +987,11 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): The ANI2x and static and induced EMLE energy components in Hartree. """ - # Convert the QM atomic numbers to elements and species IDs. - species_id = [] - for id in atomic_numbers: - species_id.append(self._species.index(id)) - species_id = _torch.tensor(_np.array(species_id), device=xyz_qm.device) + # Convert the atomic numbers to species IDs. + species_id = self._species_map[atomic_numbers] + + # Reshape the IDs. + zid = species_id.unsqueeze(0) # Reshape the atomic numbers. atomic_numbers = atomic_numbers.unsqueeze(0) From 9e74f359e2294ec974d4de0ad209e4ff6cc53c78 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Tue, 2 Jul 2024 15:44:19 +0100 Subject: [PATCH 050/134] Call base class methods in overloads. [ci skip] --- emle/models.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/emle/models.py b/emle/models.py index a69ffec..071221b 100644 --- a/emle/models.py +++ b/emle/models.py @@ -154,6 +154,7 @@ def to(self, *args, **kwargs): """ Performs Tensor dtype and/or device conversion on the model. """ + super().to(*args, **kwargs) if self._aev_computer is not None: self._aev_computer = self._aev_computer.to(*args, **kwargs) self._species_map = self._species_map.to(*args, **kwargs) @@ -177,6 +178,7 @@ def cuda(self, **kwargs): """ Returns a copy of this model in CUDA memory. """ + super().cuda(**kwargs) if self._aev_computer is not None: self._aev_computer = self._aev_computer.cuda(**kwargs) self._species_map = self._species_map.cuda(**kwargs) @@ -200,6 +202,7 @@ def cpu(self, **kwargs): """ Returns a copy of this model in CPU memory. """ + super().cpu(**kwargs) if self._aev_computer is not None: self._aev_computer = self._aev_computer.cpu(**kwargs) self._species_map = self._species_map.cpu(**kwargs) @@ -223,6 +226,7 @@ def double(self): """ Returns a copy of this model in float64 precision. """ + super().double() if self._aev_computer is not None: self._aev_computer = self._aev_computer.double() self._q_core = self._q_core.double() @@ -243,6 +247,7 @@ def float(self): """ Returns a copy of this model in float32 precision. """ + super().float() if self._aev_computer is not None: self._aev_computer = self._aev_computer.float() self._q_core = self._q_core.float() From 82bac3b562f79f6841aebebdca4a6d953d3a2a82 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Wed, 3 Jul 2024 09:04:37 +0100 Subject: [PATCH 051/134] Standardise type names in docstrings. [ci skip] --- emle/models.py | 114 ++++++++++++++++++++++++------------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/emle/models.py b/emle/models.py index 071221b..575afa2 100644 --- a/emle/models.py +++ b/emle/models.py @@ -271,22 +271,22 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): Parameters ---------- - atomic_numbers: torch.tensor (N_QM_ATOMS,) + atomic_numbers: torch.Tensor (N_QM_ATOMS,) Atomic numbers of QM atoms. - charges_mm: torch.tensor (max_mm_atoms,) + charges_mm: torch.Tensor (max_mm_atoms,) MM point charges in atomic units. - xyz_qm: torch.tensor (N_QM_ATOMS, 3) + xyz_qm: torch.Tensor (N_QM_ATOMS, 3) Positions of QM atoms in Angstrom. - xyz_mm: torch.tensor (N_MM_ATOMS, 3) + xyz_mm: torch.Tensor (N_MM_ATOMS, 3) Positions of MM atoms in Angstrom. Returns ------- - result: torch.tensor (2,) + result: torch.Tensor (2,) The static and induced EMLE energy components in Hartree. """ @@ -345,7 +345,7 @@ def _get_Kinv(cls, ref_features, sigma): Parameters ---------- - ref_features: numpy.array (N_Z, MAX_N_REF, N_FEAT) + ref_features: torch.Tensor (N_Z, MAX_N_REF, N_FEAT) The basis feature vectors for each species. sigma: float @@ -354,7 +354,7 @@ def _get_Kinv(cls, ref_features, sigma): Returns ------- - result: numpy.array (MAX_N_REF, MAX_N_REF) + result: torch.Tensor (MAX_N_REF, MAX_N_REF) The inverse of the K matrix. """ n = ref_features.shape[1] @@ -379,13 +379,13 @@ def _gpr(self, mol_features, ref_mean, c, zid): c: torch.Tensor (N_Z, MAX_N_REF) The coefficients of the GPR model. - zid: torch.tensor (N_ATOMS,) + zid: torch.Tensor (N_ATOMS,) The species identity value of each atom. Returns ------- - result: torch.tensor, numpy.array (N_ATOMS) + result: torch.Tensor (N_ATOMS) The values of the predicted property for each atom. """ @@ -413,16 +413,16 @@ def _get_q(self, r_data: Tuple[Tensor, Tensor, Tensor, Tensor], s, chi): r_data: r_data object (output of self._get_r_data) - s: torch.tensor (N_ATOMS,) + s: torch.Tensor (N_ATOMS,) MBIS valence shell widths. - chi: torch.tensor (N_ATOMS,) + chi: torch.Tensor (N_ATOMS,) Electronegativities. Returns ------- - result: torch.tensor (N_ATOMS,) + result: torch.Tensor (N_ATOMS,) Predicted MBIS charges. """ A = self._get_A_QEq(r_data, s) @@ -439,13 +439,13 @@ def _get_A_QEq(self, r_data: Tuple[Tensor, Tensor, Tensor, Tensor], s): r_data: r_data object (output of self._get_r_data) - s: torch.tensor (N_ATOMS,) + s: torch.Tensor (N_ATOMS,) MBIS valence shell widths. Returns ------- - result: torch.tensor (N_ATOMS + 1, N_ATOMS + 1) + result: torch.Tensor (N_ATOMS + 1, N_ATOMS + 1) """ s_gauss = s * self._a_QEq s2 = s_gauss**2 @@ -500,22 +500,22 @@ def _get_mu_ind( mesh_data: mesh_data object (output of self._get_mesh_data) - q: torch.tensor (N_MM_ATOMS,) + q: torch.Tensor (N_MM_ATOMS,) MM point charges. - s: torch.tensor (N_QM_ATOMS,) + s: torch.Tensor (N_QM_ATOMS,) MBIS valence shell widths. - q_val: torch.tensor (N_QM_ATOMS,) + q_val: torch.Tensor (N_QM_ATOMS,) MBIS valence charges. - k_Z: torch.tensor (N_Z) + k_Z: torch.Tensor (N_Z) Scaling factors for polarizabilities. Returns ------- - result: torch.tensor (N_ATOMS, 3) + result: torch.Tensor (N_ATOMS, 3) Array of induced dipoles """ A = self._get_A_thole(r_data, s, q_val, k_Z) @@ -540,19 +540,19 @@ def _get_A_thole( r_data: r_data object (output of self._get_r_data) - s: torch.tensor (N_ATOMS,) + s: torch.Tensor (N_ATOMS,) MBIS valence shell widths. - q_val: torch.tensor (N_ATOMS,) + q_val: torch.Tensor (N_ATOMS,) MBIS charges. - k_Z: torch.tensor (N_Z) + k_Z: torch.Tensor (N_Z) Scaling factors for polarizabilities. Returns ------- - result: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + result: torch.Tensor (N_ATOMS * 3, N_ATOMS * 3) The A matrix for induced dipoles prediction. """ v = -60 * q_val * s**3 @@ -581,16 +581,16 @@ def _get_vpot_q(q, T0): Parameters ---------- - q: torch.tensor (N_MM_ATOMS,) + q: torch.Tensor (N_MM_ATOMS,) MM point charges. - T0: torch.tensor (N_QM_ATOMS, max_mm_atoms) + T0: torch.Tensor (N_QM_ATOMS, max_mm_atoms) T0 tensor for QM atoms over MM atom positions. Returns ------- - result: torch.tensor (max_mm_atoms) + result: torch.Tensor (max_mm_atoms) Electrostatic potential over MM atoms. """ return _torch.sum(T0 * q[:, None], dim=0) @@ -604,16 +604,16 @@ def _get_vpot_mu(mu, T1): Parameters ---------- - mu: torch.tensor (N_ATOMS, 3) + mu: torch.Tensor (N_ATOMS, 3) Atomic dipoles. - T1: torch.tensor (N_ATOMS, max_mm_atoms, 3) + T1: torch.Tensor (N_ATOMS, max_mm_atoms, 3) T1 tensor for QM atoms over MM atom positions. Returns ------- - result: torch.tensor (max_mm_atoms) + result: torch.Tensor (max_mm_atoms) Electrostatic potential over MM atoms. """ return -_torch.tensordot(T1, mu, ((0, 2), (0, 1))) @@ -626,7 +626,7 @@ def _get_r_data(cls, xyz): Parameters ---------- - xyz: torch.tensor (N_ATOMS, 3) + xyz: torch.Tensor (N_ATOMS, 3) Atomic positions. Returns @@ -669,13 +669,13 @@ def _get_mesh_data(cls, xyz, xyz_mesh, s): Parameters ---------- - xyz: torch.tensor (N_ATOMS, 3) + xyz: torch.Tensor (N_ATOMS, 3) Atomic positions. - xyz_mesh: torch.tensor (max_mm_atoms, 3) + xyz_mesh: torch.Tensor (max_mm_atoms, 3) MM positions. - s: torch.tensor (N_ATOMS,) + s: torch.Tensor (N_ATOMS,) MBIS valence widths. """ rr = xyz_mesh[None, :, :] - xyz[:, None, :] @@ -691,16 +691,16 @@ def _get_f1_slater(cls, r, s): Parameters ---------- - r: torch.tensor (N_ATOMS, max_mm_atoms) + r: torch.Tensor (N_ATOMS, max_mm_atoms) Distances from QM to MM atoms. - s: torch.tensor (N_ATOMS,) + s: torch.Tensor (N_ATOMS,) MBIS valence widths. Returns ------- - result: torch.tensor (N_ATOMS, max_mm_atoms) + result: torch.Tensor (N_ATOMS, max_mm_atoms) """ return ( cls._get_T0_slater(r, s) * r @@ -715,16 +715,16 @@ def _get_T0_slater(r, s): Parameters ---------- - r: torch.tensor (N_ATOMS, max_mm_atoms) + r: torch.Tensor (N_ATOMS, max_mm_atoms) Distances from QM to MM atoms. - s: torch.tensor (N_ATOMS,) + s: torch.Tensor (N_ATOMS,) MBIS valence widths. Returns ------- - results: torch.tensor (N_ATOMS, max_mm_atoms) + results: torch.Tensor (N_ATOMS, max_mm_atoms) """ return (1 - (1 + r / (s * 2)) * _torch.exp(-r / s)) / r @@ -736,19 +736,19 @@ def _get_T0_gaussian(t01, r, s_mat): Parameters ---------- - t01: torch.tensor (N_ATOMS, N_ATOMS) + t01: torch.Tensor (N_ATOMS, N_ATOMS) T0 tensor for QM atoms. - r: torch.tensor (N_ATOMS, N_ATOMS) + r: torch.Tensor (N_ATOMS, N_ATOMS) Distance matrix for QM atoms. - s_mat: torch.tensor (N_ATOMS, N_ATOMS) + s_mat: torch.Tensor (N_ATOMS, N_ATOMS) Matrix of Gaussian sigmas for QM atoms. Returns ------- - results: torch.tensor (N_ATOMS, N_ATOMS) + results: torch.Tensor (N_ATOMS, N_ATOMS) """ return t01 * _torch.erf( r @@ -766,19 +766,19 @@ def _get_T2_thole(cls, tr21, tr22, au3): Parameters ---------- - tr21: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + tr21: torch.Tensor (N_ATOMS * 3, N_ATOMS * 3) r_data[2] - tr21: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + tr21: torch.Tensor (N_ATOMS * 3, N_ATOMS * 3) r_data[3] - au3: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + au3: torch.Tensor (N_ATOMS * 3, N_ATOMS * 3) Scaled distance matrix (see _get_A_thole). Returns ------- - result: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + result: torch.Tensor (N_ATOMS * 3, N_ATOMS * 3) """ return cls._lambda3(au3) * tr21 + cls._lambda5(au3) * tr22 @@ -791,13 +791,13 @@ def _lambda3(au3): Parameters ---------- - au3: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + au3: torch.Tensor (N_ATOMS * 3, N_ATOMS * 3) Scaled distance matrix (see _get_A_thole). Returns ------- - result: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + result: torch.Tensor (N_ATOMS * 3, N_ATOMS * 3) """ return 1 - _torch.exp(-au3) @@ -810,13 +810,13 @@ def _lambda5(au3): Parameters ---------- - au3: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + au3: torch.Tensor (N_ATOMS * 3, N_ATOMS * 3) Scaled distance matrix (see _get_A_thole). Returns ------- - result: torch.tensor (N_ATOMS * 3, N_ATOMS * 3) + result: torch.Tensor (N_ATOMS * 3, N_ATOMS * 3) """ return 1 - (1 + au3) * _torch.exp(-au3) @@ -835,7 +835,7 @@ def __init__(self, ani2x_model=None, atomic_numbers=None, device=None, dtype=Non the ANI2x model from which it derived was created using periodic_table_index=True. - atomic_numbers: torch.tensor (N_ATOMS,) + atomic_numbers: torch.Tensor (N_ATOMS,) List of atomic numbers to use in the ANI2x model. If specified, and NNPOps is available, then an optimised version of ANI2x will be used. @@ -973,22 +973,22 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): Parameters ---------- - atomic_numbers: torch.tensor (N_QM_ATOMS,) + atomic_numbers: torch.Tensor (N_QM_ATOMS,) Atomic numbers of QM atoms. - charges_mm: torch.tensor (max_mm_atoms,) + charges_mm: torch.Tensor (max_mm_atoms,) MM point charges in atomic units. - xyz_qm: torch.tensor (N_QM_ATOMS, 3) + xyz_qm: torch.Tensor (N_QM_ATOMS, 3) Positions of QM atoms in Angstrom. - xyz_mm: torch.tensor (N_MM_ATOMS, 3) + xyz_mm: torch.Tensor (N_MM_ATOMS, 3) Positions of MM atoms in Angstrom. Returns ------- - result: torch.tensor (3,) + result: torch.Tensor (3,) The ANI2x and static and induced EMLE energy components in Hartree. """ From c98fe5b39181807ca18804d962ecb31a0642bc41 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Wed, 3 Jul 2024 09:51:33 +0100 Subject: [PATCH 052/134] Use -1 to specify unsupported species. [ci skip] --- emle/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emle/models.py b/emle/models.py index 575afa2..d79759f 100644 --- a/emle/models.py +++ b/emle/models.py @@ -102,7 +102,7 @@ def __init__(self, device=None, dtype=None, create_aev_calculator=True): species = [1, 6, 7, 8, 16] # Create a map between species and their indices. - self._species_map = _np.zeros(max(species) + 1, dtype=_np.int64) + self._species_map = _np.full(max(species) + 1, fill_value=-1, dtype=_np.int64) for i, s in enumerate(species): self._species_map[s] = i From ad288b5e82ef60a91fb2f5acbf3729449c218473 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Wed, 3 Jul 2024 11:16:36 +0100 Subject: [PATCH 053/134] Work around TorchANI's broken .to(torch.float32) and .float() operators. [ci skip] --- emle/models.py | 51 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/emle/models.py b/emle/models.py index d79759f..db8f3e8 100644 --- a/emle/models.py +++ b/emle/models.py @@ -150,11 +150,13 @@ def __init__(self, device=None, dtype=None, create_aev_calculator=True): ref_shifted = self._ref_values_chi - self._ref_mean_chi[:, None] self._c_chi = (Kinv @ ref_shifted[:, :, None]).squeeze() + # Store the current device. + self._device = device + def to(self, *args, **kwargs): """ Performs Tensor dtype and/or device conversion on the model. """ - super().to(*args, **kwargs) if self._aev_computer is not None: self._aev_computer = self._aev_computer.to(*args, **kwargs) self._species_map = self._species_map.to(*args, **kwargs) @@ -172,13 +174,19 @@ def to(self, *args, **kwargs): self._ref_mean_chi = self._ref_mean_chi.to(*args, **kwargs) self._c_s = self._c_s.to(*args, **kwargs) self._c_chi = self._c_chi.to(*args, **kwargs) + + # Check for a device type in args and update the device attribute. + for arg in args: + if isinstance(arg, _torch.device): + self._device = arg + break + return self def cuda(self, **kwargs): """ Returns a copy of this model in CUDA memory. """ - super().cuda(**kwargs) if self._aev_computer is not None: self._aev_computer = self._aev_computer.cuda(**kwargs) self._species_map = self._species_map.cuda(**kwargs) @@ -196,13 +204,16 @@ def cuda(self, **kwargs): self._ref_mean_chi = self._ref_mean_chi.cuda(**kwargs) self._c_s = self._c_s.cuda(**kwargs) self._c_chi = self._c_chi.cuda(**kwargs) + + # Update the device attribute. + self._device = self._species_map.device + return self def cpu(self, **kwargs): """ Returns a copy of this model in CPU memory. """ - super().cpu(**kwargs) if self._aev_computer is not None: self._aev_computer = self._aev_computer.cpu(**kwargs) self._species_map = self._species_map.cpu(**kwargs) @@ -220,13 +231,16 @@ def cpu(self, **kwargs): self._ref_mean_chi = self._ref_mean_chi.cpu(**kwargs) self._c_s = self._c_s.cpu(**kwargs) self._c_chi = self._c_chi.cpu(**kwargs) + + # Update the device attribute. + self._device = self._species_map.device + return self def double(self): """ Returns a copy of this model in float64 precision. """ - super().double() if self._aev_computer is not None: self._aev_computer = self._aev_computer.double() self._q_core = self._q_core.double() @@ -247,7 +261,6 @@ def float(self): """ Returns a copy of this model in float32 precision. """ - super().float() if self._aev_computer is not None: self._aev_computer = self._aev_computer.float() self._q_core = self._q_core.float() @@ -864,6 +877,9 @@ def __init__(self, ani2x_model=None, atomic_numbers=None, device=None, dtype=Non # Check that they are integers. if atomic_numbers.dtype != _torch.int64: raise ValueError("'atomic_numbers' must be of dtype 'torch.int64'") + self._atomic_numbers = atomic_numbers.to(device) + else: + self._atomic_numbers = None # Call the base class constructor. super().__init__(device=device, dtype=dtype, create_aev_calculator=False) @@ -898,11 +914,15 @@ def __init__(self, ani2x_model=None, atomic_numbers=None, device=None, dtype=Non ) self._ani2x = ani2x_model.to(device) + if dtype == _torch.float64: + self._ani2x = self._ani2x.double() else: # Create the ANI2x model. self._ani2x = _torchani.models.ANI2x(periodic_table_index=True).to(device) + if dtype == _torch.float64: + self._ani2x = self._ani2x.double() - # Optmised the ANI2x model if atomic_numbers is specified. + # Optimise the ANI2x model if atomic_numbers are specified. if atomic_numbers is not None: try: from NNPOps import OptimizedTorchANI as _OptimizedTorchANI @@ -940,6 +960,8 @@ def cpu(self, **kwargs): """ module = super(ANI2xEMLE, self).cpu(**kwargs) module._ani2x = module._ani2x.cpu(**kwargs) + if self._atomic_numbers is not None: + module._atomic_numbers = module._atomic_numbers.cpu(**kwargs) return module def cuda(self, **kwargs): @@ -948,6 +970,8 @@ def cuda(self, **kwargs): """ module = super(ANI2xEMLE, self).cuda(**kwargs) module._ani2x = module._ani2x.cuda(**kwargs) + if self._atomic_numbers is not None: + module._atomic_numbers = module._atomic_numbers.cuda(**kwargs) return module def double(self): @@ -963,7 +987,20 @@ def float(self): Returns a copy of this model in float32 precision. """ module = super(ANI2xEMLE, self).float() - module._ani2x = module._ani2x.float() + # Using .float() or .to(torch.float32) is broken for ANI2x models. + module._ani2x = _torchani.models.ANI2x(periodic_table_index=True).to( + self._device + ) + # Optimise the ANI2x model if atomic_numbers were specified. + if self._atomic_numbers is not None: + try: + from NNPOps import OptimizedTorchANI as _OptimizedTorchANI + + species = self._atomic_numbers.reshape(1, *atomic_numbers.shape) + self._ani2x = _OptimizedTorchANI(self._ani2x, species) + except: + pass + return module def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): From 189b7a82405c10c2a3384f475e7d0c54179478f7 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Wed, 3 Jul 2024 15:34:54 +0100 Subject: [PATCH 054/134] Don't use forward_hook until it can work with TorchScript. [ci skip] --- emle/models.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/emle/models.py b/emle/models.py index db8f3e8..2a2b783 100644 --- a/emle/models.py +++ b/emle/models.py @@ -944,7 +944,11 @@ def hook( return hook # Register the hook. - self._aev_hook = self._ani2x.aev_computer.register_forward_hook(hook_wrapper()) + # TODO: This currently doesn't work with TorchSript since there's no + # way to access or assign attributes on another module from within a + # forward hook, i.e. ANI2x.aev_computer can't set attributes on this + # module. (This works in PyTorch, but not in TorchScript.) + # self._aev_hook = self._ani2x.aev_computer.register_forward_hook(hook_wrapper()) def to(self, *args, **kwargs): """ @@ -1050,16 +1054,18 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): zero = _torch.tensor(0.0, dtype=xyz_qm.dtype, device=xyz_qm.device) return _torch.stack([E_vac, zero, zero]) - # Normalise the AEVs. - self._aev = self._aev / _torch.linalg.norm( - self._aev, ord=2, dim=1, keepdim=True - ) + # TODO: This is a temporary fix to get the AEVs. The hook doesn't work + # with TorchScript, so we have to compute the AEVs again here. + + # Compute the AEVs. + aev = self._ani2x.aev_computer((zid, xyz))[1][0][:, self._aev_mask] + aev = aev / _torch.linalg.norm(aev, ord=2, dim=1, keepdim=True) # Compute the MBIS valence shell widths. - s = self._gpr(self._aev, self._ref_mean_s, self._c_s, species_id) + s = self._gpr(aev, self._ref_mean_s, self._c_s, species_id) # Compute the electronegativities. - chi = self._gpr(self._aev, self._ref_mean_chi, self._c_chi, species_id) + chi = self._gpr(aev, self._ref_mean_chi, self._c_chi, species_id) # Convert coordinates to Bohr. ANGSTROM_TO_BOHR = 1.8897261258369282 From 701acb512b76f7e892e64bd3cbff365326ebb9d2 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Wed, 3 Jul 2024 15:41:23 +0100 Subject: [PATCH 055/134] Convert model to TorchScript in optimised callback. [ci skip] --- emle/calculator.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 6679067..a4693f8 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1867,6 +1867,19 @@ def _sire_callback_optimised(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): # For performance, we assume that the input is already validated. + # Update the maximum number of MM atoms if this is the largest seen. + num_mm_atoms = len(charges_mm) + if num_mm_atoms > self._max_mm_atoms: + self._max_mm_atoms = num_mm_atoms + + # Pad the MM coordinates and charges arrays to avoid re-jitting. + if self._max_mm_atoms > num_mm_atoms: + num_pad = self._max_mm_atoms - num_mm_atoms + xyz_mm_pad = num_pad * [[0.0, 0.0, 0.0]] + charges_mm_pad = num_pad * [0.0] + xyz_mm = _np.append(xyz_mm, xyz_mm_pad, axis=0) + charges_mm = _np.append(charges_mm, charges_mm_pad) + # Convert to numpy arrays then Torch tensors. atomic_numbers = _torch.tensor(atomic_numbers, device=self._device) charges_mm = _torch.tensor( @@ -1903,15 +1916,13 @@ def _sire_callback_optimised(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): ani2x_model=self._torchani_model, device=self._device ) - # Compile the model. - # This needs additional triton and cuda-nvcc dependencies. Will - # test when recreating the conda environment. - # self._ani2x_emle = _torch.compile(ani2x_emle) - self._ani2x_emle = ani2x_emle + # Convert to TorchScript. + self._ani2x_emle = _torch.jit.script(ani2x_emle).eval() # Compute the energy and gradients. - E = self._ani2x_emle(atomic_numbers, charges_mm, xyz_qm, xyz_mm) - dE_dxyz_qm, dE_dxyz_mm = _torch.autograd.grad(E.sum(), (xyz_qm, xyz_mm)) + with _torch.jit.optimized_execution(False): + E = self._ani2x_emle(atomic_numbers, charges_mm, xyz_qm, xyz_mm) + dE_dxyz_qm, dE_dxyz_mm = _torch.autograd.grad(E.sum(), (xyz_qm, xyz_mm)) # Convert the energy and gradients to numpy arrays. E = E.sum().item() * _HARTREE_TO_KJ_MOL @@ -1920,7 +1931,7 @@ def _sire_callback_optimised(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): ).tolist() force_mm = ( -dE_dxyz_mm.cpu().numpy() * _HARTREE_TO_KJ_MOL * _NANOMETER_TO_ANGSTROM - ).tolist() + ).tolist()[:num_mm_atoms] return E, force_qm, force_mm From 2c92f29139def8d96d0574c7888624edcdd88504 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Wed, 3 Jul 2024 15:43:42 +0100 Subject: [PATCH 056/134] Add note regarding running without optimised execution. [ci skip] --- emle/calculator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/emle/calculator.py b/emle/calculator.py index a4693f8..da9a525 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1919,7 +1919,8 @@ def _sire_callback_optimised(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): # Convert to TorchScript. self._ani2x_emle = _torch.jit.script(ani2x_emle).eval() - # Compute the energy and gradients. + # Compute the energy and gradients. Don't use optimised execution to + # avoid warmup costs. with _torch.jit.optimized_execution(False): E = self._ani2x_emle(atomic_numbers, charges_mm, xyz_qm, xyz_mm) dE_dxyz_qm, dE_dxyz_mm = _torch.autograd.grad(E.sum(), (xyz_qm, xyz_mm)) From 21e576217d759daee2b9cf0a2e222fa9a6866a51 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Thu, 4 Jul 2024 12:14:13 +0100 Subject: [PATCH 057/134] Add note about forward hook and leave commented implementation. [ci skip] --- emle/models.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/emle/models.py b/emle/models.py index 2a2b783..9b25e9a 100644 --- a/emle/models.py +++ b/emle/models.py @@ -932,6 +932,10 @@ def __init__(self, ani2x_model=None, atomic_numbers=None, device=None, dtype=Non except: pass + # Assign a tensor attribute that can be used for assigning the AEVs. + # TODO: Only required when forward hook works with TorchANI. + # self._ani2x.aev_computer._aev = _torch.empty(0, device=device) + # Hook the forward pass of the ANI2x model to get the AEV features. def hook_wrapper(): def hook( @@ -939,15 +943,13 @@ def hook( input: Tuple[Tuple[Tensor, Tensor], Optional[Tensor], Optional[Tensor]], output: _torchani.aev.SpeciesAEV, ): - self._aev = output[1][0][:, self._aev_mask] + module._aev = output[1][0] return hook # Register the hook. - # TODO: This currently doesn't work with TorchSript since there's no - # way to access or assign attributes on another module from within a - # forward hook, i.e. ANI2x.aev_computer can't set attributes on this - # module. (This works in PyTorch, but not in TorchScript.) + # TODO: This currently doesn't work with TorchANI since some args are + # passed to the AEVComputer's forward method as kwargs. # self._aev_hook = self._ani2x.aev_computer.register_forward_hook(hook_wrapper()) def to(self, *args, **kwargs): @@ -1056,6 +1058,8 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): # TODO: This is a temporary fix to get the AEVs. The hook doesn't work # with TorchScript, so we have to compute the AEVs again here. + # aev = self._ani2x.aev_computer._aev[:, self._aev_mask] + # aev = aev / _torch.linalg.norm(aev, ord=2, dim=1, keepdim=True) # Compute the AEVs. aev = self._ani2x.aev_computer((zid, xyz))[1][0][:, self._aev_mask] From 8cc736a5b08c4e4d4a10de1a795be86e2bfc4449 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Thu, 4 Jul 2024 14:42:48 +0100 Subject: [PATCH 058/134] Allow user to specify the ANI2x model index. [ci skip] --- bin/emle-server | 5 +++++ emle/calculator.py | 27 ++++++++++++++++++++++++--- emle/models.py | 30 +++++++++++++++++++++++++----- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/bin/emle-server b/bin/emle-server index a459ba7..af13147 100755 --- a/bin/emle-server +++ b/bin/emle-server @@ -85,6 +85,10 @@ try: qm_xyz_frequency = int(os.getenv("EMLE_QM_XYZ_FREQUENCY")) except: qm_xyz_frequency = 0 +try: + ani2x_model_index = int(os.getenv("EMLE_ANI2X_MODEL_INDEX")) +except: + ani2x_model_index = None rascal_model = os.getenv("EMLE_RASCAL_MODEL") parm7 = os.getenv("EMLE_PARM7") try: @@ -136,6 +140,7 @@ env = { "deepmd_deviation_threshold": deepmd_deviation_threshold, "qm_xyz_file": qm_xyz_file, "qm_xyz_frequency": qm_xyz_frequency, + "ani2x_model_index": ani2x_model_index, "rascal_model": rascal_model, "lambda_interpolate": lambda_interpolate, "interpolate_steps": interpolate_steps, diff --git a/emle/calculator.py b/emle/calculator.py index da9a525..082fda1 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -266,6 +266,7 @@ def __init__( deepmd_deviation_threshold=None, qm_xyz_file="qm.xyz", qm_xyz_frequency=0, + ani2x_model_index=None, rascal_model=None, parm7=None, qm_indices=None, @@ -346,6 +347,10 @@ def __init__( How often to write the xyz trajectory of the QM region. Zero turns off writing. + ani2x_model_index: int + The index of the ANI model to use when using the TorchANI backend. + If None, then the full 8 model ensemble is used. + rascal_model: str Path to the Rascal model file used to apply delta-learning corrections to the in vacuo energies and gradients computed by the backed. @@ -1020,10 +1025,25 @@ def __init__( if self._backend == "torchani": import torchani as _torchani + if ani2x_model_index is not None: + try: + ani2x_model_index = int(ani2x_model_index) + except: + msg = "'ani2x_model_index' must be of type 'int'" + _logger.error(msg) + raise TypeError(msg) + + if ani2x_model_index < 0 or ani2x_model_index > 7: + msg = "'ani2x_model_index' must be between 0 and 7" + _logger.error(msg) + raise ValueError(msg) + + self._ani2x_model_index = ani2x_model_index + # Create the TorchANI model. - self._torchani_model = _torchani.models.ANI2x(periodic_table_index=True).to( - self._device - ) + self._torchani_model = _torchani.models.ANI2x( + periodic_table_index=True, model_index=ani2x_model_index + ).to(self._device) try: import NNPOps as _NNPOps @@ -1123,6 +1143,7 @@ def __init__( "deepmd_deviation_threshold": deepmd_deviation_threshold, "qm_xyz_file": qm_xyz_file, "qm_xyz_frequency": qm_xyz_frequency, + "ani2x_model_index": ani2x_model_index, "rascal_model": rascal_model, "parm7": parm7, "qm_indices": None if qm_indices is None else self._qm_indices, diff --git a/emle/models.py b/emle/models.py index 9b25e9a..7acc9b6 100644 --- a/emle/models.py +++ b/emle/models.py @@ -835,13 +835,24 @@ def _lambda5(au3): class ANI2xEMLE(EMLE): - def __init__(self, ani2x_model=None, atomic_numbers=None, device=None, dtype=None): + def __init__( + self, + model_index=None, + ani2x_model=None, + atomic_numbers=None, + device=None, + dtype=None, + ): """ Constructor Parameters ---------- + model_index: int + The index of the model to use. If None, then the full 8 model + ensemble will be used. + ani2x_model: torchani.models.ANI2x, NNPOPS.OptimizedTorchANI An existing ANI2x model to use. If None, a new ANI2x model will be created. If using an OptimizedTorchANI model, please ensure that @@ -859,6 +870,13 @@ def __init__(self, ani2x_model=None, atomic_numbers=None, device=None, dtype=Non dtype: torch.dtype The data type to use for the models floating point tensors. """ + if model_index is not None: + if not isinstance(model_index, int): + raise TypeError("'model_index' must be of type 'int'") + if model_index < 0 or model_index > 7: + raise ValueError("'model_index' must be in the range [0, 7]") + self._model_index = model_index + if device is not None: if not isinstance(device, _torch.device): raise TypeError("'device' must be of type 'torch.device'") @@ -918,7 +936,9 @@ def __init__(self, ani2x_model=None, atomic_numbers=None, device=None, dtype=Non self._ani2x = self._ani2x.double() else: # Create the ANI2x model. - self._ani2x = _torchani.models.ANI2x(periodic_table_index=True).to(device) + self._ani2x = _torchani.models.ANI2x( + periodic_table_index=True, model_index=model_index + ).to(device) if dtype == _torch.float64: self._ani2x = self._ani2x.double() @@ -994,9 +1014,9 @@ def float(self): """ module = super(ANI2xEMLE, self).float() # Using .float() or .to(torch.float32) is broken for ANI2x models. - module._ani2x = _torchani.models.ANI2x(periodic_table_index=True).to( - self._device - ) + module._ani2x = _torchani.models.ANI2x( + periodic_table_index=True, model_index=self._model_index + ).to(self._device) # Optimise the ANI2x model if atomic_numbers were specified. if self._atomic_numbers is not None: try: From a95005ca33f41ff7ee20d9b0982269ec2faa72f2 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 8 Jul 2024 10:05:25 +0100 Subject: [PATCH 059/134] Monkey-patch torchani so AEVComputer forward hook works with TorchScript [ci skip] --- emle/_torchani_patch.py | 563 ++++++++++++++++++++++++++++++++++++++++ emle/calculator.py | 8 + emle/models.py | 27 +- 3 files changed, 585 insertions(+), 13 deletions(-) create mode 100644 emle/_torchani_patch.py diff --git a/emle/_torchani_patch.py b/emle/_torchani_patch.py new file mode 100644 index 0000000..bd08b68 --- /dev/null +++ b/emle/_torchani_patch.py @@ -0,0 +1,563 @@ +# -*- coding: utf-8 -*- +"""The ANI model zoo that stores public ANI models. + +This is a patched version of the original torchani.models module that +calls self.aev_computer using args only to allow use of forward hooks +with TorchScript. + +Currently the model zoo has three models: ANI-1x, ANI-1ccx, and ANI-2x. +The parameters of these models are stored in `ani-model-zoo`_ repository and +will be automatically downloaded the first time any of these models are +instantiated. The classes of these models are :class:`ANI1x`, :class:`ANI1ccx`, +and :class:`ANI2x` these are subclasses of :class:`torch.nn.Module`. +To use the models just instantiate them and either +directly calculate energies or get an ASE calculator. For example: + +.. _ani-model-zoo: + https://github.com/aiqm/ani-model-zoo + +.. code-block:: python + + ani1x = torchani.models.ANI1x() + # compute energy using ANI-1x model ensemble + _, energies = ani1x((species, coordinates)) + ani1x.ase() # get ASE Calculator using this ensemble + # convert atom species from string to long tensor + ani1x.species_to_tensor(['C', 'H', 'H', 'H', 'H']) + + model0 = ani1x[0] # get the first model in the ensemble + # compute energy using the first model in the ANI-1x model ensemble + _, energies = model0((species, coordinates)) + model0.ase() # get ASE Calculator using this model + # convert atom species from string to long tensor + model0.species_to_tensor(['C', 'H', 'H', 'H', 'H']) +""" +import os +import torch +import torchani +from torch import Tensor +from typing import Tuple, Optional, NamedTuple +from torchani.nn import SpeciesConverter, SpeciesEnergies +from torchani.aev import AEVComputer + + +class SpeciesEnergiesQBC(NamedTuple): + species: Tensor + energies: Tensor + qbcs: Tensor + + +class BuiltinModel(torch.nn.Module): + r"""Private template for the builtin ANI models""" + + def __init__( + self, + species_converter, + aev_computer, + neural_networks, + energy_shifter, + species_to_tensor, + consts, + sae_dict, + periodic_table_index, + ): + super().__init__() + self.species_converter = species_converter + self.aev_computer = aev_computer + self.neural_networks = neural_networks + self.energy_shifter = energy_shifter + self._species_to_tensor = species_to_tensor + self.species = consts.species + self.periodic_table_index = periodic_table_index + + # a bit useless maybe + self.consts = consts + self.sae_dict = sae_dict + + @classmethod + def _from_neurochem_resources( + cls, info_file_path, periodic_table_index=False, model_index=0 + ): + from torchani import neurochem # noqa + + # this is used to load only 1 model (by default model 0) + ( + const_file, + sae_file, + ensemble_prefix, + ensemble_size, + ) = neurochem.parse_neurochem_resources(info_file_path) + if model_index >= ensemble_size: + raise ValueError( + "The ensemble size is only {}, model {} can't be loaded".format( + ensemble_size, model_index + ) + ) + + consts = neurochem.Constants(const_file) + species_converter = SpeciesConverter(consts.species) + aev_computer = AEVComputer(**consts) + energy_shifter, sae_dict = neurochem.load_sae(sae_file, return_dict=True) + species_to_tensor = consts.species_to_tensor + + network_dir = os.path.join( + "{}{}".format(ensemble_prefix, model_index), "networks" + ) + neural_networks = neurochem.load_model(consts.species, network_dir) + + return cls( + species_converter, + aev_computer, + neural_networks, + energy_shifter, + species_to_tensor, + consts, + sae_dict, + periodic_table_index, + ) + + def forward( + self, + species_coordinates: Tuple[Tensor, Tensor], + cell: Optional[Tensor] = None, + pbc: Optional[Tensor] = None, + ) -> SpeciesEnergies: + """Calculates predicted properties for minibatch of configurations + + Args: + species_coordinates: minibatch of configurations + cell: the cell used in PBC computation, set to None if PBC is not enabled + pbc: the bool tensor indicating which direction PBC is enabled, set to None if PBC is not enabled + + Returns: + species_energies: energies for the given configurations + + .. note:: The coordinates, and cell are in Angstrom, and the energies + will be in Hartree. + """ + if self.periodic_table_index: + species_coordinates = self.species_converter(species_coordinates) + + # check if unknown species are included + if species_coordinates[0].ge(self.aev_computer.num_species).any(): + raise ValueError(f"Unknown species found in {species_coordinates[0]}") + + species_aevs = self.aev_computer(species_coordinates, cell, pbc) + species_energies = self.neural_networks(species_aevs) + return self.energy_shifter(species_energies) + + @torch.jit.export + def atomic_energies( + self, + species_coordinates: Tuple[Tensor, Tensor], + cell: Optional[Tensor] = None, + pbc: Optional[Tensor] = None, + ) -> SpeciesEnergies: + """Calculates predicted atomic energies of all atoms in a molecule + + ..warning:: + Since this function does not call ``__call__`` directly, + hooks are not registered and profiling is not done correctly by + pytorch on it. It is meant as a convenience function for analysis + and active learning. + + .. note:: The coordinates, and cell are in Angstrom, and the energies + will be in Hartree. + + Args: + species_coordinates: minibatch of configurations + cell: the cell used in PBC computation, set to None if PBC is not enabled + pbc: the bool tensor indicating which direction PBC is enabled, set to None if PBC is not enabled + + Returns: + species_atomic_energies: species and energies for the given configurations + note that the shape of species is (C, A), where C is + the number of configurations and A the number of atoms, and + the shape of energies is (C, A) for a BuiltinModel. + """ + if self.periodic_table_index: + species_coordinates = self.species_converter(species_coordinates) + species, aevs = self.aev_computer(species_coordinates, cell, pbc) + atomic_energies = self.neural_networks._atomic_energies((species, aevs)) + self_energies = self.energy_shifter.self_energies.clone().to(species.device) + self_energies = self_energies[species] + self_energies[ + species == torch.tensor(-1, device=species.device) + ] = torch.tensor(0, device=species.device, dtype=torch.double) + # shift all atomic energies individually + assert self_energies.shape == atomic_energies.shape + atomic_energies += self_energies + return SpeciesEnergies(species, atomic_energies) + + @torch.jit.export + def _recast_long_buffers(self): + self.species_converter.conv_tensor = self.species_converter.conv_tensor.to( + dtype=torch.long + ) + self.aev_computer.triu_index = self.aev_computer.triu_index.to(dtype=torch.long) + + def species_to_tensor(self, *args, **kwargs): + """Convert species from strings to tensor. + + See also :method:`torchani.neurochem.Constant.species_to_tensor` + + Arguments: + species (:class:`str`): A string of chemical symbols + + Returns: + tensor (:class:`torch.Tensor`): A 1D tensor of integers + """ + # The only difference between this and the "raw" private version + # _species_to_tensor is that this sends the final tensor to the model + # device + return self._species_to_tensor(*args, **kwargs).to( + self.aev_computer.ShfR.device + ) + + def ase(self, **kwargs): + """Get an ASE Calculator using this ANI model + + Arguments: + kwargs: ase.Calculator kwargs + + Returns: + calculator (:class:`int`): A calculator to be used with ASE + """ + import ase + + return ase.Calculator(self.species, self, **kwargs) + + +class BuiltinEnsemble(BuiltinModel): + """Private template for the builtin ANI ensemble models. + + ANI ensemble models form the ANI models zoo are instances of this class. + This class is a torch module that sequentially calculates + AEVs, then energies from a torchani.Ensemble and then uses EnergyShifter + to shift those energies. It is essentially a sequential + + 'AEVComputer -> Ensemble -> EnergyShifter' + + (periodic_table_index=False), or a sequential + + 'SpeciesConverter -> AEVComputer -> Ensemble -> EnergyShifter' + + (periodic_table_index=True). + + .. note:: + This class is for internal use only, avoid relying on anything from it + except the public methods, always use ANI1x, ANI1ccx, etc to instance + the models. + Also, don't confuse this class with torchani.Ensemble, which is only a + container for many ANIModel instances and shouldn't be used directly + for calculations. + + Attributes: + species_converter (:class:`torchani.nn.SpeciesConverter`): Converts periodic table index to + internal indices. Only present if periodic_table_index is `True`. + aev_computer (:class:`torchani.AEVComputer`): AEV computer with + builtin constants + energy_shifter (:class:`torchani.EnergyShifter`): Energy shifter with + builtin Self Atomic Energies. + periodic_table_index (bool): Whether to use element number in periodic table + to index species. If set to `False`, then indices must be `0, 1, 2, ..., N - 1` + where `N` is the number of parametrized species. + """ + + def __init__( + self, + species_converter, + aev_computer, + neural_networks, + energy_shifter, + species_to_tensor, + consts, + sae_dict, + periodic_table_index, + ): + super().__init__( + species_converter, + aev_computer, + neural_networks, + energy_shifter, + species_to_tensor, + consts, + sae_dict, + periodic_table_index, + ) + + @torch.jit.export + def atomic_energies( + self, + species_coordinates: Tuple[Tensor, Tensor], + cell: Optional[Tensor] = None, + pbc: Optional[Tensor] = None, + average: bool = True, + ) -> SpeciesEnergies: + """Calculates predicted atomic energies of all atoms in a molecule + + see `:method:torchani.BuiltinModel.atomic_energies` + + If average is True (the default) it returns the average over all models + (shape (C, A)), otherwise it returns one atomic energy per model (shape + (M, C, A)) + """ + if self.periodic_table_index: + species_coordinates = self.species_converter(species_coordinates) + species, aevs = self.aev_computer(species_coordinates, cell, pbc) + members_list = [] + for nnp in self.neural_networks: + members_list.append(nnp._atomic_energies((species, aevs)).unsqueeze(0)) + member_atomic_energies = torch.cat(members_list, dim=0) + + self_energies = self.energy_shifter.self_energies.clone().to(species.device) + self_energies = self_energies[species] + self_energies[ + species == torch.tensor(-1, device=species.device) + ] = torch.tensor(0, device=species.device, dtype=torch.double) + # shift all atomic energies individually + assert self_energies.shape == member_atomic_energies.shape[1:] + member_atomic_energies += self_energies + if average: + return SpeciesEnergies(species, member_atomic_energies.mean(dim=0)) + return SpeciesEnergies(species, member_atomic_energies) + + @classmethod + def _from_neurochem_resources(cls, info_file_path, periodic_table_index=False): + from torchani import neurochem # noqa + + # this is used to load only 1 model (by default model 0) + ( + const_file, + sae_file, + ensemble_prefix, + ensemble_size, + ) = neurochem.parse_neurochem_resources(info_file_path) + + consts = neurochem.Constants(const_file) + species_converter = SpeciesConverter(consts.species) + aev_computer = AEVComputer(**consts) + energy_shifter, sae_dict = neurochem.load_sae(sae_file, return_dict=True) + species_to_tensor = consts.species_to_tensor + neural_networks = neurochem.load_model_ensemble( + consts.species, ensemble_prefix, ensemble_size + ) + + return cls( + species_converter, + aev_computer, + neural_networks, + energy_shifter, + species_to_tensor, + consts, + sae_dict, + periodic_table_index, + ) + + def __getitem__(self, index): + """Get a single 'AEVComputer -> ANIModel -> EnergyShifter' sequential model + + Get a single 'AEVComputer -> ANIModel -> EnergyShifter' sequential model + or + Indexing allows access to a single model inside the ensemble + that can be used directly for calculations. The model consists + of a sequence AEVComputer -> ANIModel -> EnergyShifter + and can return an ase calculator and convert species to tensor. + + Args: + index (:class:`int`): Index of the model + + Returns: + ret: (:class:`torchani.models.BuiltinModel`) Model ready for + calculations + """ + ret = BuiltinModel( + self.species_converter, + self.aev_computer, + self.neural_networks[index], + self.energy_shifter, + self._species_to_tensor, + self.consts, + self.sae_dict, + self.periodic_table_index, + ) + return ret + + @torch.jit.export + def members_energies( + self, + species_coordinates: Tuple[Tensor, Tensor], + cell: Optional[Tensor] = None, + pbc: Optional[Tensor] = None, + ) -> SpeciesEnergies: + """Calculates predicted energies of all member modules + + ..warning:: + Since this function does not call ``__call__`` directly, + hooks are not registered and profiling is not done correctly by + pytorch on it. It is meant as a convenience function for analysis + and active learning. + + .. note:: The coordinates, and cell are in Angstrom, and the energies + will be in Hartree. + + Args: + species_coordinates: minibatch of configurations + cell: the cell used in PBC computation, set to None if PBC is not enabled + pbc: the bool tensor indicating which direction PBC is enabled, set to None if PBC is not enabled + + Returns: + species_energies: species and energies for the given configurations + note that the shape of species is (C, A), where C is + the number of configurations and A the number of atoms, and + the shape of energies is (M, C), where M is the number + of modules in the ensemble + + """ + if self.periodic_table_index: + species_coordinates = self.species_converter(species_coordinates) + species, aevs = self.aev_computer(species_coordinates, cell, pbc) + member_outputs = [] + for nnp in self.neural_networks: + unshifted_energies = nnp((species, aevs)).energies + shifted_energies = self.energy_shifter( + (species, unshifted_energies) + ).energies + member_outputs.append(shifted_energies.unsqueeze(0)) + return SpeciesEnergies(species, torch.cat(member_outputs, dim=0)) + + @torch.jit.export + def energies_qbcs( + self, + species_coordinates: Tuple[Tensor, Tensor], + cell: Optional[Tensor] = None, + pbc: Optional[Tensor] = None, + unbiased: bool = True, + ) -> SpeciesEnergiesQBC: + """Calculates predicted predicted energies and qbc factors + + QBC factors are used for query-by-committee (QBC) based active learning + (as described in the ANI-1x paper `less-is-more`_ ). + + .. _less-is-more: + https://aip.scitation.org/doi/10.1063/1.5023802 + + ..warning:: + Since this function does not call ``__call__`` directly, + hooks are not registered and profiling is not done correctly by + pytorch on it. It is meant as a convenience function for analysis + and active learning. + + .. note:: The coordinates, and cell are in Angstrom, and the energies + and qbc factors will be in Hartree. + + Args: + species_coordinates: minibatch of configurations + cell: the cell used in PBC computation, set to None if PBC is not + enabled + pbc: the bool tensor indicating which direction PBC is enabled, set + to None if PBC is not enabled + unbiased: if `True` then Bessel's correction is applied to the + standard deviation over the ensemble member's. If `False` Bessel's + correction is not applied, True by default. + + Returns: + species_energies_qbcs: species, energies and qbc factors for the + given configurations note that the shape of species is (C, A), + where C is the number of configurations and A the number of + atoms, the shape of energies is (C,) and the shape of qbc + factors is also (C,). + """ + species, energies = self.members_energies(species_coordinates, cell, pbc) + + # standard deviation is taken across ensemble members + qbc_factors = energies.std(0, unbiased=unbiased) + + # rho's (qbc factors) are weighted by dividing by the square root of + # the number of atoms in each molecule + num_atoms = (species >= 0).sum(dim=1, dtype=energies.dtype) + qbc_factors = qbc_factors / num_atoms.sqrt() + energies = energies.mean(dim=0) + assert qbc_factors.shape == energies.shape + return SpeciesEnergiesQBC(species, energies, qbc_factors) + + def __len__(self): + """Get the number of networks in the ensemble + + Returns: + length (:class:`int`): Number of networks in the ensemble + """ + return len(self.neural_networks) + + +def ANI1x(periodic_table_index=False, model_index=None): + """The ANI-1x model as in `ani-1x_8x on GitHub`_ and `Active Learning Paper`_. + + The ANI-1x model is an ensemble of 8 networks that was trained using + active learning on the ANI-1x dataset, the target level of theory is + wB97X/6-31G(d). It predicts energies on HCNO elements exclusively, it + shouldn't be used with other atom types. + + .. _ani-1x_8x on GitHub: + https://github.com/isayev/ASE_ANI/tree/master/ani_models/ani-1x_8x + + .. _Active Learning Paper: + https://aip.scitation.org/doi/abs/10.1063/1.5023802 + """ + info_file = "ani-1x_8x.info" + if model_index is None: + return BuiltinEnsemble._from_neurochem_resources( + info_file, periodic_table_index + ) + return BuiltinModel._from_neurochem_resources( + info_file, periodic_table_index, model_index + ) + + +def ANI1ccx(periodic_table_index=False, model_index=None): + """The ANI-1ccx model as in `ani-1ccx_8x on GitHub`_ and `Transfer Learning Paper`_. + + The ANI-1ccx model is an ensemble of 8 networks that was trained + on the ANI-1ccx dataset, using transfer learning. The target accuracy + is CCSD(T)*/CBS (CCSD(T) using the DPLNO-CCSD(T) method). It predicts + energies on HCNO elements exclusively, it shouldn't be used with other + atom types. + + .. _ani-1ccx_8x on GitHub: + https://github.com/isayev/ASE_ANI/tree/master/ani_models/ani-1ccx_8x + + .. _Transfer Learning Paper: + https://doi.org/10.26434/chemrxiv.6744440.v1 + """ + info_file = "ani-1ccx_8x.info" + if model_index is None: + return BuiltinEnsemble._from_neurochem_resources( + info_file, periodic_table_index + ) + return BuiltinModel._from_neurochem_resources( + info_file, periodic_table_index, model_index + ) + + +def ANI2x(periodic_table_index=False, model_index=None): + """The ANI-2x model as in `ANI2x Paper`_ and `ANI2x Results on GitHub`_. + + The ANI-2x model is an ensemble of 8 networks that was trained on the + ANI-2x dataset. The target level of theory is wB97X/6-31G(d). It predicts + energies on HCNOFSCl elements exclusively it shouldn't be used with other + atom types. + + .. _ANI2x Results on GitHub: + https://github.com/cdever01/ani-2x_results + + .. _ANI2x Paper: + https://doi.org/10.26434/chemrxiv.11819268.v1 + """ + info_file = "ani-2x_8x.info" + if model_index is None: + return BuiltinEnsemble._from_neurochem_resources( + info_file, periodic_table_index + ) + return BuiltinModel._from_neurochem_resources( + info_file, periodic_table_index, model_index + ) diff --git a/emle/calculator.py b/emle/calculator.py index 082fda1..52d4d73 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1025,6 +1025,14 @@ def __init__( if self._backend == "torchani": import torchani as _torchani + from . import _torchani_patch + + # Monkey-patch the TorchANI BuiltInModel and BuiltinEnsemble classes so that + # they call self.aev_computer using args only to allow forward hooks to work + # with TorchScript. + _torchani.models.BuiltinModel = _torchani_patch.BuiltinModel + _torchani.models.BuiltinEnsemble = _torchani_patch.BuiltinEnsemble + if ani2x_model_index is not None: try: ani2x_model_index = int(ani2x_model_index) diff --git a/emle/models.py b/emle/models.py index 7acc9b6..a1b6203 100644 --- a/emle/models.py +++ b/emle/models.py @@ -37,6 +37,14 @@ from torch import Tensor from typing import Optional, Tuple +from . import _torchani_patch + +# Monkey-patch the TorchANI BuiltInModel and BuiltinEnsemble classes so that +# they call self.aev_computer using args only to allow forward hooks to work +# with TorchScript. +_torchani.models.BuiltinModel = _torchani_patch.BuiltinModel +_torchani.models.BuiltinEnsemble = _torchani_patch.BuiltinEnsemble + class EMLE(_torch.nn.Module): """ @@ -953,8 +961,7 @@ def __init__( pass # Assign a tensor attribute that can be used for assigning the AEVs. - # TODO: Only required when forward hook works with TorchANI. - # self._ani2x.aev_computer._aev = _torch.empty(0, device=device) + self._ani2x.aev_computer._aev = _torch.empty(0, device=device) # Hook the forward pass of the ANI2x model to get the AEV features. def hook_wrapper(): @@ -967,10 +974,9 @@ def hook( return hook - # Register the hook. - # TODO: This currently doesn't work with TorchANI since some args are - # passed to the AEVComputer's forward method as kwargs. - # self._aev_hook = self._ani2x.aev_computer.register_forward_hook(hook_wrapper()) + # Register the hook. Note that this currently requires a patched version of + # torchani.models.BuiltinModel and torchani.models.BuiltinEnsemble to work. + self._aev_hook = self._ani2x.aev_computer.register_forward_hook(hook_wrapper()) def to(self, *args, **kwargs): """ @@ -1076,13 +1082,8 @@ def forward(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): zero = _torch.tensor(0.0, dtype=xyz_qm.dtype, device=xyz_qm.device) return _torch.stack([E_vac, zero, zero]) - # TODO: This is a temporary fix to get the AEVs. The hook doesn't work - # with TorchScript, so we have to compute the AEVs again here. - # aev = self._ani2x.aev_computer._aev[:, self._aev_mask] - # aev = aev / _torch.linalg.norm(aev, ord=2, dim=1, keepdim=True) - - # Compute the AEVs. - aev = self._ani2x.aev_computer((zid, xyz))[1][0][:, self._aev_mask] + # Get the AEVs computer by the forward hook and normalise. + aev = self._ani2x.aev_computer._aev[:, self._aev_mask] aev = aev / _torch.linalg.norm(aev, ord=2, dim=1, keepdim=True) # Compute the MBIS valence shell widths. From aee8dd368d1d461db425418e40a4f1ba5a6a6726 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 8 Jul 2024 11:15:28 +0100 Subject: [PATCH 060/134] Monkey-patch NNPOps.OptimizedTorchANI too. [ci skip] --- emle/_torchani_patch.py | 44 +++++++++++++++++++++++++++++++++++++++++ emle/calculator.py | 8 ++++++-- emle/models.py | 33 ++++++++++++++++++++----------- 3 files changed, 72 insertions(+), 13 deletions(-) diff --git a/emle/_torchani_patch.py b/emle/_torchani_patch.py index bd08b68..be67825 100644 --- a/emle/_torchani_patch.py +++ b/emle/_torchani_patch.py @@ -561,3 +561,47 @@ def ANI2x(periodic_table_index=False, model_index=None): return BuiltinModel._from_neurochem_resources( info_file, periodic_table_index, model_index ) + + +# Patched version of NNPOps.OpimizedTorchANI + +from NNPOps.BatchedNN import TorchANIBatchedNN +from NNPOps.EnergyShifter import TorchANIEnergyShifter, SpeciesEnergies +from NNPOps.SpeciesConverter import TorchANISpeciesConverter +from NNPOps.SymmetryFunctions import TorchANISymmetryFunctions + + +class OptimizedTorchANI(torch.nn.Module): + from torchani.models import ( + BuiltinModel, + ) # https://github.com/openmm/NNPOps/issues/44 + + def __init__(self, model: BuiltinModel, atomicNumbers: Tensor) -> None: + super().__init__() + + # Optimize the components of an ANI model + self.species_converter = TorchANISpeciesConverter( + model.species_converter, atomicNumbers + ) + self.aev_computer = TorchANISymmetryFunctions( + model.species_converter, model.aev_computer, atomicNumbers + ) + self.neural_networks = TorchANIBatchedNN( + model.species_converter, model.neural_networks, atomicNumbers + ) + self.energy_shifter = TorchANIEnergyShifter( + model.species_converter, model.energy_shifter, atomicNumbers + ) + + def forward( + self, + species_coordinates: Tuple[Tensor, Tensor], + cell: Optional[Tensor] = None, + pbc: Optional[Tensor] = None, + ) -> SpeciesEnergies: + species_coordinates = self.species_converter(species_coordinates) + species_aevs = self.aev_computer(species_coordinates, cell, pbc) + species_energies = self.neural_networks(species_aevs) + species_energies = self.energy_shifter(species_energies) + + return species_energies diff --git a/emle/calculator.py b/emle/calculator.py index 52d4d73..4ba88e9 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1925,10 +1925,14 @@ def _sire_callback_optimised(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): if self._ani2x_emle is None: # Apply NNPOps optimisations if available. try: - from NNPOps import OptimizedTorchANI as _OptimizedTorchANI + import NNPOps as _NNPOps + + from ._torchani_patch import OptimizedTorchANI as _OptimizedTorchANI + + _NNPOps.OptimizedTorchANI = _OptimizedTorchANI # Optimise the TorchANI model. - self._torchani_model = _OptimizedTorchANI( + self._torchani_model = _NNPOps.OptimizedTorchANI( self._torchani_model, atomic_numbers.reshape(-1, *atomic_numbers.shape), ).to(self._device) diff --git a/emle/models.py b/emle/models.py index a1b6203..e74856b 100644 --- a/emle/models.py +++ b/emle/models.py @@ -45,6 +45,13 @@ _torchani.models.BuiltinModel = _torchani_patch.BuiltinModel _torchani.models.BuiltinEnsemble = _torchani_patch.BuiltinEnsemble +try: + import NNPOps as _NNPOps + + _NNPOps.OptimizedTorchANI = _torchani_patch.OptimizedTorchANI +except: + pass + class EMLE(_torch.nn.Module): """ @@ -919,8 +926,6 @@ def __init__( # Add the optimised model if NNPOps is available. try: - import NNPOps as _NNPOps - allowed_types.append(_NNPOps.OptimizedTorchANI) except: pass @@ -953,10 +958,8 @@ def __init__( # Optimise the ANI2x model if atomic_numbers are specified. if atomic_numbers is not None: try: - from NNPOps import OptimizedTorchANI as _OptimizedTorchANI - species = atomic_numbers.reshape(1, *atomic_numbers.shape) - self._ani2x = _OptimizedTorchANI(self._ani2x, species) + self._ani2x = _NNPOps.OptimizedTorchANI(self._ani2x, species) except: pass @@ -964,19 +967,27 @@ def __init__( self._ani2x.aev_computer._aev = _torch.empty(0, device=device) # Hook the forward pass of the ANI2x model to get the AEV features. - def hook_wrapper(): + # Note that this currently requires a patched versions of TorchANI and NNPOps. + if isinstance(self._ani2x, _NNPOps.OptimizedTorchANI): + def hook( module, input: Tuple[Tuple[Tensor, Tensor], Optional[Tensor], Optional[Tensor]], - output: _torchani.aev.SpeciesAEV, + output: Tuple[Tensor, Tensor], ): module._aev = output[1][0] - return hook + else: + + def hook( + module, + input: Tuple[Tuple[Tensor, Tensor], Optional[Tensor], Optional[Tensor]], + output: _torchani.aev.SpeciesAEV, + ): + module._aev = output[1][0] - # Register the hook. Note that this currently requires a patched version of - # torchani.models.BuiltinModel and torchani.models.BuiltinEnsemble to work. - self._aev_hook = self._ani2x.aev_computer.register_forward_hook(hook_wrapper()) + # Register the hook. + self._aev_hook = self._ani2x.aev_computer.register_forward_hook(hook) def to(self, *args, **kwargs): """ From d050116b65d1433e4476d00e6c27c0d05a558fa3 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 8 Jul 2024 12:17:22 +0100 Subject: [PATCH 061/134] Rename TorchANI monkey-patch module. [ci skip] --- emle/{_torchani_patch.py => _torchani_patches.py} | 0 emle/calculator.py | 8 ++++---- emle/models.py | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) rename emle/{_torchani_patch.py => _torchani_patches.py} (100%) diff --git a/emle/_torchani_patch.py b/emle/_torchani_patches.py similarity index 100% rename from emle/_torchani_patch.py rename to emle/_torchani_patches.py diff --git a/emle/calculator.py b/emle/calculator.py index 4ba88e9..7b6b7ba 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1025,13 +1025,13 @@ def __init__( if self._backend == "torchani": import torchani as _torchani - from . import _torchani_patch + from . import _torchani_patches # Monkey-patch the TorchANI BuiltInModel and BuiltinEnsemble classes so that # they call self.aev_computer using args only to allow forward hooks to work # with TorchScript. - _torchani.models.BuiltinModel = _torchani_patch.BuiltinModel - _torchani.models.BuiltinEnsemble = _torchani_patch.BuiltinEnsemble + _torchani.models.BuiltinModel = _torchani_patches.BuiltinModel + _torchani.models.BuiltinEnsemble = _torchani_patches.BuiltinEnsemble if ani2x_model_index is not None: try: @@ -1927,7 +1927,7 @@ def _sire_callback_optimised(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): try: import NNPOps as _NNPOps - from ._torchani_patch import OptimizedTorchANI as _OptimizedTorchANI + from ._torchani_patches import OptimizedTorchANI as _OptimizedTorchANI _NNPOps.OptimizedTorchANI = _OptimizedTorchANI diff --git a/emle/models.py b/emle/models.py index e74856b..f2c0b82 100644 --- a/emle/models.py +++ b/emle/models.py @@ -37,18 +37,18 @@ from torch import Tensor from typing import Optional, Tuple -from . import _torchani_patch +from . import _torchani_patches # Monkey-patch the TorchANI BuiltInModel and BuiltinEnsemble classes so that # they call self.aev_computer using args only to allow forward hooks to work # with TorchScript. -_torchani.models.BuiltinModel = _torchani_patch.BuiltinModel -_torchani.models.BuiltinEnsemble = _torchani_patch.BuiltinEnsemble +_torchani.models.BuiltinModel = _torchani_patches.BuiltinModel +_torchani.models.BuiltinEnsemble = _torchani_patches.BuiltinEnsemble try: import NNPOps as _NNPOps - _NNPOps.OptimizedTorchANI = _torchani_patch.OptimizedTorchANI + _NNPOps.OptimizedTorchANI = _torchani_patches.OptimizedTorchANI except: pass From d79b1f876f57897e849731a10c9b88f30cd09c4b Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 8 Jul 2024 16:27:09 +0100 Subject: [PATCH 062/134] Move OptimizedTorchANI model to device. [ci skip] --- emle/models.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/emle/models.py b/emle/models.py index f2c0b82..a770fd3 100644 --- a/emle/models.py +++ b/emle/models.py @@ -959,7 +959,9 @@ def __init__( if atomic_numbers is not None: try: species = atomic_numbers.reshape(1, *atomic_numbers.shape) - self._ani2x = _NNPOps.OptimizedTorchANI(self._ani2x, species) + self._ani2x = _NNPOps.OptimizedTorchANI(self._ani2x, species).to( + device + ) except: pass @@ -1040,7 +1042,7 @@ def float(self): from NNPOps import OptimizedTorchANI as _OptimizedTorchANI species = self._atomic_numbers.reshape(1, *atomic_numbers.shape) - self._ani2x = _OptimizedTorchANI(self._ani2x, species) + self._ani2x = _OptimizedTorchANI(self._ani2x, species).to(self._device) except: pass From 61a80409726e210f3982471b579597d3d9892a55 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 15 Jul 2024 16:31:20 +0100 Subject: [PATCH 063/134] Use register_buffer for constant model parameters. [ci skip] --- emle/models.py | 65 ++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/emle/models.py b/emle/models.py index a770fd3..ab3184b 100644 --- a/emle/models.py +++ b/emle/models.py @@ -117,57 +117,66 @@ def __init__(self, device=None, dtype=None, create_aev_calculator=True): species = [1, 6, 7, 8, 16] # Create a map between species and their indices. - self._species_map = _np.full(max(species) + 1, fill_value=-1, dtype=_np.int64) + species_map = _np.full(max(species) + 1, fill_value=-1, dtype=_np.int64) for i, s in enumerate(species): - self._species_map[s] = i + species_map[s] = i # Convert to a tensor. - self._species_map = _torch.tensor( - self._species_map, dtype=_torch.int64, device=device - ) + species_map = _torch.tensor(species_map, dtype=_torch.int64, device=device) # Store model parameters as tensors. - self._aev_mask = _torch.tensor( - params["aev_mask"], dtype=_torch.bool, device=device - ) - self._q_core = _torch.tensor(params["q_core"], dtype=dtype, device=device) - self._a_QEq = _torch.tensor(params["a_QEq"], dtype=dtype, device=device) - self._a_Thole = _torch.tensor(params["a_Thole"], dtype=dtype, device=device) - self._k_Z = _torch.tensor(params["k_Z"], dtype=dtype, device=device) - self._q_total = _torch.tensor( + aev_mask = _torch.tensor(params["aev_mask"], dtype=_torch.bool, device=device) + q_core = _torch.tensor(params["q_core"], dtype=dtype, device=device) + a_QEq = _torch.tensor(params["a_QEq"], dtype=dtype, device=device) + a_Thole = _torch.tensor(params["a_Thole"], dtype=dtype, device=device) + k_Z = _torch.tensor(params["k_Z"], dtype=dtype, device=device) + q_total = _torch.tensor( params.get("total_charge", 0), dtype=dtype, device=device ) # Extract the reference features. - self._ref_features = _torch.tensor( - params["ref_soap"], dtype=dtype, device=device - ) + ref_features = _torch.tensor(params["ref_soap"], dtype=dtype, device=device) # Extract the reference values for the MBIS valence shell widths. - self._ref_values_s = _torch.tensor(params["s_ref"], dtype=dtype, device=device) + ref_values_s = _torch.tensor(params["s_ref"], dtype=dtype, device=device) # Compute the inverse of the K matrix. - Kinv = self._get_Kinv(self._ref_features, 1e-3) + Kinv = self._get_Kinv(ref_features, 1e-3) # Store additional attributes for the MBIS GPR model. - self._n_ref = _torch.tensor(params["n_ref"], dtype=_torch.int64, device=device) - self._ref_mean_s = _torch.sum(self._ref_values_s, dim=1) / self._n_ref - ref_shifted = self._ref_values_s - self._ref_mean_s[:, None] - self._c_s = (Kinv @ ref_shifted[:, :, None]).squeeze() + n_ref = _torch.tensor(params["n_ref"], dtype=_torch.int64, device=device) + ref_mean_s = _torch.sum(ref_values_s, dim=1) / n_ref + ref_shifted = ref_values_s - ref_mean_s[:, None] + c_s = (Kinv @ ref_shifted[:, :, None]).squeeze() # Exctract the reference values for the electronegativities. - self._ref_values_chi = _torch.tensor( - params["chi_ref"], dtype=dtype, device=device - ) + ref_values_chi = _torch.tensor(params["chi_ref"], dtype=dtype, device=device) # Store additional attributes for the electronegativity GPR model. - self._ref_mean_chi = _torch.sum(self._ref_values_chi, dim=1) / self._n_ref - ref_shifted = self._ref_values_chi - self._ref_mean_chi[:, None] - self._c_chi = (Kinv @ ref_shifted[:, :, None]).squeeze() + ref_mean_chi = _torch.sum(ref_values_chi, dim=1) / n_ref + ref_shifted = ref_values_chi - ref_mean_chi[:, None] + c_chi = (Kinv @ ref_shifted[:, :, None]).squeeze() # Store the current device. self._device = device + # Register constants as buffers. + self.register_buffer("_species_map", species_map) + self.register_buffer("_aev_mask", aev_mask) + self.register_buffer("_q_core", q_core) + self.register_buffer("_a_QEq", a_QEq) + self.register_buffer("_a_Thole", a_Thole) + self.register_buffer("_k_Z", k_Z) + self.register_buffer("_q_total", q_total) + self.register_buffer("_ref_features", ref_features) + self.register_buffer("_n_ref", n_ref) + self.register_buffer("_ref_values_s", ref_values_s) + self.register_buffer("_ref_values_chi", ref_values_chi) + self.register_buffer("_ref_mean_s", ref_mean_s) + self.register_buffer("_ref_mean_chi", ref_mean_chi) + self.register_buffer("_c_s", c_s) + self.register_buffer("_c_chi", c_chi) + def to(self, *args, **kwargs): """ Performs Tensor dtype and/or device conversion on the model. From 356e306fc8aa1e21e0d1aa3c6c317a0673441f49 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Tue, 16 Jul 2024 11:09:19 +0100 Subject: [PATCH 064/134] Remove librascal and dependencies from environment file. [ci skip] --- environment_sire.yaml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/environment_sire.yaml b/environment_sire.yaml index 3592e2d..dda3324 100644 --- a/environment_sire.yaml +++ b/environment_sire.yaml @@ -7,19 +7,13 @@ channels: dependencies: - ambertools - ase - - compilers - deepmd-kit - - eigen - loguru - openmm >= 8.1 - - pip - psutil - - pybind11 - pytorch - - python < 3.11 + - python - pyyaml - sire - torchani - xtb-python - - pip: - - git+https://github.com/lab-cosmo/librascal.git From 8f8edf0bda24a06f5859ce75289ce401579d23a5 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Wed, 17 Jul 2024 09:32:55 +0100 Subject: [PATCH 065/134] Guard patched NNPOps class against import error. [ci skip] --- emle/_torchani_patches.py | 77 ++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/emle/_torchani_patches.py b/emle/_torchani_patches.py index be67825..ac1e692 100644 --- a/emle/_torchani_patches.py +++ b/emle/_torchani_patches.py @@ -565,43 +565,46 @@ def ANI2x(periodic_table_index=False, model_index=None): # Patched version of NNPOps.OpimizedTorchANI -from NNPOps.BatchedNN import TorchANIBatchedNN -from NNPOps.EnergyShifter import TorchANIEnergyShifter, SpeciesEnergies -from NNPOps.SpeciesConverter import TorchANISpeciesConverter -from NNPOps.SymmetryFunctions import TorchANISymmetryFunctions - - -class OptimizedTorchANI(torch.nn.Module): - from torchani.models import ( - BuiltinModel, - ) # https://github.com/openmm/NNPOps/issues/44 - - def __init__(self, model: BuiltinModel, atomicNumbers: Tensor) -> None: - super().__init__() +try: + from NNPOps.BatchedNN import TorchANIBatchedNN + from NNPOps.EnergyShifter import TorchANIEnergyShifter, SpeciesEnergies + from NNPOps.SpeciesConverter import TorchANISpeciesConverter + from NNPOps.SymmetryFunctions import TorchANISymmetryFunctions + + class OptimizedTorchANI(torch.nn.Module): + from torchani.models import ( + BuiltinModel, + ) # https://github.com/openmm/NNPOps/issues/44 + + def __init__(self, model: BuiltinModel, atomicNumbers: Tensor) -> None: + super().__init__() + + # Optimize the components of an ANI model + self.species_converter = TorchANISpeciesConverter( + model.species_converter, atomicNumbers + ) + self.aev_computer = TorchANISymmetryFunctions( + model.species_converter, model.aev_computer, atomicNumbers + ) + self.neural_networks = TorchANIBatchedNN( + model.species_converter, model.neural_networks, atomicNumbers + ) + self.energy_shifter = TorchANIEnergyShifter( + model.species_converter, model.energy_shifter, atomicNumbers + ) - # Optimize the components of an ANI model - self.species_converter = TorchANISpeciesConverter( - model.species_converter, atomicNumbers - ) - self.aev_computer = TorchANISymmetryFunctions( - model.species_converter, model.aev_computer, atomicNumbers - ) - self.neural_networks = TorchANIBatchedNN( - model.species_converter, model.neural_networks, atomicNumbers - ) - self.energy_shifter = TorchANIEnergyShifter( - model.species_converter, model.energy_shifter, atomicNumbers - ) + def forward( + self, + species_coordinates: Tuple[Tensor, Tensor], + cell: Optional[Tensor] = None, + pbc: Optional[Tensor] = None, + ) -> SpeciesEnergies: + species_coordinates = self.species_converter(species_coordinates) + species_aevs = self.aev_computer(species_coordinates, cell, pbc) + species_energies = self.neural_networks(species_aevs) + species_energies = self.energy_shifter(species_energies) - def forward( - self, - species_coordinates: Tuple[Tensor, Tensor], - cell: Optional[Tensor] = None, - pbc: Optional[Tensor] = None, - ) -> SpeciesEnergies: - species_coordinates = self.species_converter(species_coordinates) - species_aevs = self.aev_computer(species_coordinates, cell, pbc) - species_energies = self.neural_networks(species_aevs) - species_energies = self.energy_shifter(species_energies) + return species_energies - return species_energies +except: + pass From 20d42bec9cac32825dbcab86adfade0e64b77e7b Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Wed, 17 Jul 2024 09:42:59 +0100 Subject: [PATCH 066/134] Add flag to specify whether NNPOps is available. [ci skip] --- emle/models.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/emle/models.py b/emle/models.py index ab3184b..3b2c258 100644 --- a/emle/models.py +++ b/emle/models.py @@ -49,7 +49,10 @@ import NNPOps as _NNPOps _NNPOps.OptimizedTorchANI = _torchani_patches.OptimizedTorchANI + + _has_nnpops = True except: + _has_nnpops = False pass @@ -979,7 +982,7 @@ def __init__( # Hook the forward pass of the ANI2x model to get the AEV features. # Note that this currently requires a patched versions of TorchANI and NNPOps. - if isinstance(self._ani2x, _NNPOps.OptimizedTorchANI): + if _has_nnpops and isinstance(self._ani2x, _NNPOps.OptimizedTorchANI): def hook( module, From 6c49db227e94e82fc43e6f56baf3e844527089cc Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 29 Jul 2024 16:22:29 +0100 Subject: [PATCH 067/134] Energy needs to be an array/tensor. [ci skip] --- emle/calculator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 7b6b7ba..53432f1 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1324,7 +1324,7 @@ def run(self, path=None): # No backend. else: - E_vac, grad_vac = 0.0, _np.zeros_like(xyz_qm) + E_vac, grad_vac = _np.zeros(1), _np.zeros_like(xyz_qm) # External backend. else: @@ -1704,7 +1704,7 @@ def _sire_callback(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): # No backend. else: - E_vac, grad_vac = 0.0, _np.zeros_like(xyz_qm) + E_vac, grad_vac = _np.zeros(1), _np.zeros_like(xyz_qm) # External backend. else: From b616925fa9d4aef07d69979673b0e36f12f76d66 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Mon, 29 Jul 2024 18:08:12 +0100 Subject: [PATCH 068/134] Detach tensors and convert to NumPy arrays. [ci skip] --- emle/calculator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 53432f1..2d03e79 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1765,7 +1765,7 @@ def _sire_callback(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): dE_dxyz_mm_bohr = dE_dxyz_mm_bohr.cpu().numpy() # Compute the total energy and gradients. - E_tot = E + E_vac + E_tot = E.detach().numpy() + E_vac grad_qm = dE_dxyz_qm_bohr + grad_vac grad_mm = dE_dxyz_mm_bohr @@ -1803,7 +1803,7 @@ def _sire_callback(self, atomic_numbers, charges_mm, xyz_qm, xyz_mm): self._method = method # Store the the MM and EMLE energies. The MM energy is an approximation. - E_mm = E_mm_qm_vac + E + E_mm = E.detach().numpy() + E_mm_qm_vac E_emle = E_tot # Work out the current value of lambda. From 329ef517fabd1b896ee5ae7cf3c517b42794bc75 Mon Sep 17 00:00:00 2001 From: Kirill Zinovjev Date: Tue, 30 Jul 2024 19:55:57 +0200 Subject: [PATCH 069/134] Rename ref_soap param to ref_aev --- emle/calculator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 7b6b7ba..9c0f75e 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -1114,14 +1114,14 @@ def __init__( ) self._get_s = _GPRCalculator( self._params["s_ref"], - self._params["ref_soap"], + self._params["ref_aev"], self._params["n_ref"], 1e-3, self._device, ) self._get_chi = _GPRCalculator( self._params["chi_ref"], - self._params["ref_soap"], + self._params["ref_aev"], self._params["n_ref"], 1e-3, self._device, From b7384056365a324687bbc7980ac0343211604f2b Mon Sep 17 00:00:00 2001 From: Kirill Zinovjev Date: Tue, 30 Jul 2024 20:31:30 +0200 Subject: [PATCH 070/134] Implement 'reference' mode for polarizabilities calculation --- emle/calculator.py | 57 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/emle/calculator.py b/emle/calculator.py index 9c0f75e..0a28488 100644 --- a/emle/calculator.py +++ b/emle/calculator.py @@ -257,6 +257,7 @@ def __init__( self, model=None, method="electrostatic", + alpha_mode="species", backend="torchani", external_backend=None, plugin_path=".", @@ -310,6 +311,14 @@ def __init__( then no backend will be used, allowing you to obtain the electrostatic embedding energy and gradients only. + alpha_mode: str + How atomic polarizabilities are calculated. + "species": + one volume scaling factor is used for each species + "reference": + scaling factors are obtained with GPR using the values learned + for each reference environment + external_backend: str The name of an external backend to use to compute in vacuo energies. This should be a callback function formatted as 'module.function'. @@ -556,6 +565,12 @@ def __init__( _logger.error(msg) raise IOError(msg) + if alpha_mode not in ["species", "reference"]: + msg = "'alpha_mode' must be either 'species' or 'reference'" + _logger.error(msg) + raise ValueError(msg) + self._alpha_mode = alpha_mode + if backend is not None: if not isinstance(backend, str): msg = "'backend' must be of type 'str'" @@ -1104,9 +1119,15 @@ def __init__( ) self._a_QEq = self._params["a_QEq"] self._a_Thole = self._params["a_Thole"] - self._k_Z = _torch.tensor( - self._params["k_Z"], dtype=_torch.float32, device=self._device - ) + if self._alpha_mode == "species": + self._k_Z = _torch.tensor( + self._params["k_Z"], dtype=_torch.float32, device=self._device + ) + else: + self.sqrtk_ref = _torch.tensor( + self._params["sqrtk_ref"], dtype=_torch.float32, device=self._device + ) + self._q_total = _torch.tensor( self._params.get("total_charge", 0), dtype=_torch.float32, @@ -1126,6 +1147,14 @@ def __init__( 1e-3, self._device, ) + if self._alpha_mode == 'reference': + self._get_sqrtk = _GPRCalculator( + self._params["sqrtk_ref"], + self._params["ref_aev"], + self._params["n_ref"], + 1e-3, + self._device, + ) # Initialise the maximum number of MM atom that have been seen. self._max_mm_atoms = 0 @@ -1143,6 +1172,7 @@ def __init__( self._settings = { "model": None if model is None else self._model, "method": self._method, + "alpha_mode": self._alpha_mode, "backend": self._backend, "external_backend": None if external_backend is None else external_backend, "mm_charges": None if mm_charges is None else self._mm_charges.tolist(), @@ -2029,7 +2059,12 @@ def _get_E_components(self, charges_mm, xyz_qm_bohr, xyz_mm_bohr): q_core = self._q_core[self._species_id] else: q_core = self._q_core_mm - k_Z = self._k_Z[self._species_id] + + if self._alpha_mode == 'species': + k = self._k_Z[self._species_id] + else: + k = self._get_sqrtk(mol_features, self._species_id) ** 2 + r_data = self._get_r_data(xyz_qm_bohr, self._device) mesh_data = self._get_mesh_data(xyz_qm_bohr, xyz_mm_bohr, s) if self._method in ["electrostatic", "nonpol"]: @@ -2040,7 +2075,7 @@ def _get_E_components(self, charges_mm, xyz_qm_bohr, xyz_mm_bohr): q_val = _torch.zeros_like(q_core, dtype=_torch.float32, device=self._device) else: q_val = _torch.zeros_like(q_core, dtype=_torch.float32, device=self._device) - mu_ind = self._get_mu_ind(r_data, mesh_data, charges_mm, s, q_val, k_Z) + mu_ind = self._get_mu_ind(r_data, mesh_data, charges_mm, s, q_val, k) vpot_q_core = self._get_vpot_q(q_core, mesh_data["T0_mesh"]) vpot_q_val = self._get_vpot_q(q_val, mesh_data["T0_mesh_slater"]) vpot_static = vpot_q_core + vpot_q_val @@ -2126,7 +2161,7 @@ def _get_A_QEq(self, r_data, s): return B - def _get_mu_ind(self, r_data, mesh_data, q, s, q_val, k_Z): + def _get_mu_ind(self, r_data, mesh_data, q, s, q_val, k): """ Internal method, calculates induced atomic dipoles (Eq. 20 in 10.1021/acs.jctc.2c00914) @@ -2147,7 +2182,7 @@ def _get_mu_ind(self, r_data, mesh_data, q, s, q_val, k_Z): q_val: torch.tensor (N_ATOMS,) MBIS valence charges. - k_Z: torch.tensor (N_Z) + k: torch.tensor (N_Z) Scaling factors for polarizabilities. Returns @@ -2156,7 +2191,7 @@ def _get_mu_ind(self, r_data, mesh_data, q, s, q_val, k_Z): result: torch.tensor (N_ATOMS, 3) Array of induced dipoles """ - A = self._get_A_thole(r_data, s, q_val, k_Z) + A = self._get_A_thole(r_data, s, q_val, k) r = 1.0 / mesh_data["T0_mesh"] f1 = self._get_f1_slater(r, s[:, None] * 2.0) @@ -2168,7 +2203,7 @@ def _get_mu_ind(self, r_data, mesh_data, q, s, q_val, k_Z): E_ind = mu_ind @ fields * 0.5 return mu_ind.reshape((-1, 3)) - def _get_A_thole(self, r_data, s, q_val, k_Z): + def _get_A_thole(self, r_data, s, q_val, k): """ Internal method, generates A matrix for induced dipoles prediction (Eq. 20 in 10.1021/acs.jctc.2c00914) @@ -2184,7 +2219,7 @@ def _get_A_thole(self, r_data, s, q_val, k_Z): q_val: torch.tensor (N_ATOMS,) MBIS charges. - k_Z: torch.tensor (N_Z) + k: torch.tensor (N_Z) Scaling factors for polarizabilities. Returns @@ -2194,7 +2229,7 @@ def _get_A_thole(self, r_data, s, q_val, k_Z): The A matrix for induced dipoles prediction. """ v = -60 * q_val * s**3 - alpha = v * k_Z + alpha = v * k alphap = alpha * self._a_Thole alphap_mat = alphap[:, None] * alphap[None, :] From acb3c1a75164662f91d916f0058cb5edb7ff8be6 Mon Sep 17 00:00:00 2001 From: Kirill Zinovjev Date: Tue, 30 Jul 2024 20:32:04 +0200 Subject: [PATCH 071/134] Remove old SOAP-based model file --- emle/emle_spinv.mat | Bin 7446920 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 emle/emle_spinv.mat diff --git a/emle/emle_spinv.mat b/emle/emle_spinv.mat deleted file mode 100644 index db254201bb9806852435b31d21cc8e9f00ef381f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7446920 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Ⓢ;YQqcf$%}-=yG<^PBa?FA^Cghh@9H}83WsW^+)jccm62MX$kDB7lIQj`+82m z*%B|V@q@aESHb^`dkBL)lqw%X;9nnl$(S_$pZNau9WndJ#9b5XKHRt!DMzT&k#`Ts$Ix*U$=^$BbXf=d z!p!m7`)$4Xxsn??uh`26NwbHis#J;X-=8A5{5peb4?Z2d&rLe!QbBGN{(5TR&%WEm za1}4V_aZKhpVRgq*AV^m3KD9Q<%ID14t~Fo4EFEx_FChW+y7iQnvocPz0XOGj$>!Q9VZdJ5Sr3Z z_iB{D3$NU1%Dk<>Wi5#OZ$EmXMA4n72c4Jfhcnb0G+Qtd!^^L#{^+5df4KA6y?mSM z5=0hO)bR4#qR^{JQ=`FaZ``i!W6myg9bm6KB>m$x9resHN4#=VPriHp=j;CCIKr

vNSP5*8eW_qtl?N^avc#DSe((ZgNJ`0NA0T<| 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

yu*g74|cYmN3XdEWXC36@fRB*zchS30%GTo79+m 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