Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: add se_r descriptor #3338

Merged
merged 23 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
af51721
feat: add se_r descriptor
anyangml Feb 26, 2024
c0af6fa
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 26, 2024
8a8107c
fix: UTs, removed old impl
anyangml Feb 26, 2024
fb6340b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 26, 2024
2eb4041
fix: pre-commit
anyangml Feb 26, 2024
6c5224f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 26, 2024
b771db4
fix: update se_r output
anyangml Feb 26, 2024
8a1a86c
chore: refactor
anyangml Feb 26, 2024
50cdfe0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 26, 2024
84d61da
feat: add numpy impl
anyangml Feb 26, 2024
08a6988
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 26, 2024
1f0fd99
fix: UTs
anyangml Feb 27, 2024
c07e02c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 27, 2024
3485608
Merge branch 'devel' into devel
anyangml Feb 27, 2024
e5b074e
gix: match serialization
anyangml Feb 27, 2024
8265242
Merge branch 'devel' into devel
anyangml Feb 27, 2024
3fe3ed4
chore: refactor device
anyangml Feb 27, 2024
9d23e96
chore: refactor device
anyangml Feb 27, 2024
c073241
fix: UTs
anyangml Feb 27, 2024
ce85c0f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 27, 2024
72d7a37
fix: dtype
anyangml Feb 27, 2024
54375bb
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 27, 2024
184e6a0
Merge branch 'devel' into devel
anyangml Feb 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions deepmd/dpmodel/descriptor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
from .se_e2_a import (
DescrptSeA,
)
from .se_r import (
DescrptSeR,
)

__all__ = [
"DescrptSeA",
"DescrptSeR",
"make_base_descriptor",
]
353 changes: 353 additions & 0 deletions deepmd/dpmodel/descriptor/se_r.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,353 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
import numpy as np

from deepmd.utils.path import (
DPPath,
)

try:
from deepmd._version import version as __version__
except ImportError:
__version__ = "unknown"

Check warning on line 11 in deepmd/dpmodel/descriptor/se_r.py

View check run for this annotation

Codecov / codecov/patch

deepmd/dpmodel/descriptor/se_r.py#L10-L11

Added lines #L10 - L11 were not covered by tests

import copy
from typing import (
Any,
List,
Optional,
)

from deepmd.dpmodel import (
DEFAULT_PRECISION,
PRECISION_DICT,
NativeOP,
)
from deepmd.dpmodel.utils import (
EmbeddingNet,
EnvMat,
NetworkCollection,
PairExcludeMask,
)

from .base_descriptor import (
BaseDescriptor,
)


@BaseDescriptor.register("se_e2_r")
@BaseDescriptor.register("se_r")
class DescrptSeR(NativeOP, BaseDescriptor):
r"""DeepPot-SE constructed from all information (both angular and radial) of
anyangml marked this conversation as resolved.
Show resolved Hide resolved
atomic configurations. The embedding takes the distance between atoms as input.

The descriptor :math:`\mathcal{D}^i \in \mathcal{R}^{M_1 \times M_2}` is given by [1]_

.. math::
\mathcal{D}^i = (\mathcal{G}^i)^T \mathcal{R}^i (\mathcal{R}^i)^T \mathcal{G}^i_<

where :math:`\mathcal{R}^i \in \mathbb{R}^{N \times 4}` is the coordinate
matrix, and each row of :math:`\mathcal{R}^i` can be constructed as follows

.. math::
(\mathcal{R}^i)_j = [
\begin{array}{c}
s(r_{ji}) & \frac{s(r_{ji})x_{ji}}{r_{ji}} & \frac{s(r_{ji})y_{ji}}{r_{ji}} & \frac{s(r_{ji})z_{ji}}{r_{ji}}
\end{array}
]

where :math:`\mathbf{R}_{ji}=\mathbf{R}_j-\mathbf{R}_i = (x_{ji}, y_{ji}, z_{ji})` is
the relative coordinate and :math:`r_{ji}=\lVert \mathbf{R}_{ji} \lVert` is its norm.
The switching function :math:`s(r)` is defined as:

.. math::
s(r)=
\begin{cases}
\frac{1}{r}, & r<r_s \\
\frac{1}{r} \{ {(\frac{r - r_s}{ r_c - r_s})}^3 (-6 {(\frac{r - r_s}{ r_c - r_s})}^2 +15 \frac{r - r_s}{ r_c - r_s} -10) +1 \}, & r_s \leq r<r_c \\
0, & r \geq r_c
\end{cases}

Each row of the embedding matrix :math:`\mathcal{G}^i \in \mathbb{R}^{N \times M_1}` consists of outputs
of a embedding network :math:`\mathcal{N}` of :math:`s(r_{ji})`:

.. math::
(\mathcal{G}^i)_j = \mathcal{N}(s(r_{ji}))

:math:`\mathcal{G}^i_< \in \mathbb{R}^{N \times M_2}` takes first :math:`M_2` columns of
:math:`\mathcal{G}^i`. The equation of embedding network :math:`\mathcal{N}` can be found at
:meth:`deepmd.tf.utils.network.embedding_net`.

Parameters
----------
rcut
The cut-off radius :math:`r_c`
rcut_smth
From where the environment matrix should be smoothed :math:`r_s`
sel : list[str]
sel[i] specifies the maxmum number of type i atoms in the cut-off radius
neuron : list[int]
Number of neurons in each hidden layers of the embedding net :math:`\mathcal{N}`
axis_neuron
Number of the axis neuron :math:`M_2` (number of columns of the sub-matrix of the embedding matrix)
resnet_dt
Time-step `dt` in the resnet construction:
y = x + dt * \phi (Wx + b)
trainable
If the weights of embedding net are trainable.
type_one_side
Try to build N_types embedding nets. Otherwise, building N_types^2 embedding nets
exclude_types : List[List[int]]
The excluded pairs of types which have no interaction with each other.
For example, `[[0, 1]]` means no interaction between type 0 and type 1.
set_davg_zero
Set the shift of embedding net input to zero.
activation_function
The activation function in the embedding net. Supported options are |ACTIVATION_FN|
precision
The precision of the embedding net parameters. Supported options are |PRECISION|
multi_task
If the model has multi fitting nets to train.
spin
The deepspin object.

Limitations
-----------
The currently implementation does not support the following features

1. type_one_side == False
2. exclude_types != []
3. spin is not None

References
----------
.. [1] Linfeng Zhang, Jiequn Han, Han Wang, Wissam A. Saidi, Roberto Car, and E. Weinan. 2018.
End-to-end symmetry preserving inter-atomic potential energy model for finite and extended
systems. In Proceedings of the 32nd International Conference on Neural Information Processing
Systems (NIPS'18). Curran Associates Inc., Red Hook, NY, USA, 4441-4451.
"""

def __init__(
self,
rcut: float,
rcut_smth: float,
sel: List[str],
neuron: List[int] = [24, 48, 96],
resnet_dt: bool = False,
trainable: bool = True,
type_one_side: bool = True,
exclude_types: List[List[int]] = [],
set_davg_zero: bool = False,
activation_function: str = "tanh",
precision: str = DEFAULT_PRECISION,
spin: Optional[Any] = None,
# consistent with argcheck, not used though
seed: Optional[int] = None,
) -> None:
## seed, uniform_seed, multi_task, not included.
if not type_one_side:
raise NotImplementedError("type_one_side == False not implemented")

Check warning on line 148 in deepmd/dpmodel/descriptor/se_r.py

View check run for this annotation

Codecov / codecov/patch

deepmd/dpmodel/descriptor/se_r.py#L148

Added line #L148 was not covered by tests
if spin is not None:
raise NotImplementedError("spin is not implemented")

Check warning on line 150 in deepmd/dpmodel/descriptor/se_r.py

View check run for this annotation

Codecov / codecov/patch

deepmd/dpmodel/descriptor/se_r.py#L150

Added line #L150 was not covered by tests

self.rcut = rcut
self.rcut_smth = rcut_smth
self.sel = sel
self.ntypes = len(self.sel)
self.neuron = neuron
self.resnet_dt = resnet_dt
self.trainable = trainable
self.type_one_side = type_one_side
self.exclude_types = exclude_types
self.set_davg_zero = set_davg_zero
self.activation_function = activation_function
self.precision = precision
self.spin = spin
self.emask = PairExcludeMask(self.ntypes, self.exclude_types)

in_dim = 1 # not considiering type embedding
self.embeddings = NetworkCollection(
ntypes=self.ntypes,
ndim=(1 if self.type_one_side else 2),
network_type="embedding_network",
)
if not self.type_one_side:
raise NotImplementedError("type_one_side == False not implemented")

Check warning on line 174 in deepmd/dpmodel/descriptor/se_r.py

View check run for this annotation

Codecov / codecov/patch

deepmd/dpmodel/descriptor/se_r.py#L174

Added line #L174 was not covered by tests
for ii in range(self.ntypes):
self.embeddings[(ii,)] = EmbeddingNet(
in_dim,
self.neuron,
self.activation_function,
self.resnet_dt,
self.precision,
)
self.env_mat = EnvMat(self.rcut, self.rcut_smth)
self.nnei = np.sum(self.sel)
self.davg = np.zeros([self.ntypes, self.nnei, 1])
self.dstd = np.ones([self.ntypes, self.nnei, 1])
anyangml marked this conversation as resolved.
Show resolved Hide resolved
self.orig_sel = self.sel

def __setitem__(self, key, value):
if key in ("avg", "data_avg", "davg"):
self.davg = value
elif key in ("std", "data_std", "dstd"):
self.dstd = value
else:
raise KeyError(key)

Check warning on line 195 in deepmd/dpmodel/descriptor/se_r.py

View check run for this annotation

Codecov / codecov/patch

deepmd/dpmodel/descriptor/se_r.py#L195

Added line #L195 was not covered by tests

def __getitem__(self, key):
if key in ("avg", "data_avg", "davg"):
return self.davg
elif key in ("std", "data_std", "dstd"):
return self.dstd

Check warning on line 201 in deepmd/dpmodel/descriptor/se_r.py

View check run for this annotation

Codecov / codecov/patch

deepmd/dpmodel/descriptor/se_r.py#L198-L201

Added lines #L198 - L201 were not covered by tests
else:
raise KeyError(key)

Check warning on line 203 in deepmd/dpmodel/descriptor/se_r.py

View check run for this annotation

Codecov / codecov/patch

deepmd/dpmodel/descriptor/se_r.py#L203

Added line #L203 was not covered by tests

@property
def dim_out(self):
"""Returns the output dimension of this descriptor."""
return self.get_dim_out()

Check warning on line 208 in deepmd/dpmodel/descriptor/se_r.py

View check run for this annotation

Codecov / codecov/patch

deepmd/dpmodel/descriptor/se_r.py#L208

Added line #L208 was not covered by tests

def get_dim_out(self):
"""Returns the output dimension of this descriptor."""
return self.neuron[-1]

Check warning on line 212 in deepmd/dpmodel/descriptor/se_r.py

View check run for this annotation

Codecov / codecov/patch

deepmd/dpmodel/descriptor/se_r.py#L212

Added line #L212 was not covered by tests

def get_dim_emb(self):
"""Returns the embedding (g2) dimension of this descriptor."""
raise NotImplementedError

Check warning on line 216 in deepmd/dpmodel/descriptor/se_r.py

View check run for this annotation

Codecov / codecov/patch

deepmd/dpmodel/descriptor/se_r.py#L216

Added line #L216 was not covered by tests

def get_rcut(self):
"""Returns cutoff radius."""
return self.rcut

def get_sel(self):
"""Returns cutoff radius."""
return self.sel

def mixed_types(self):
"""Returns if the descriptor requires a neighbor list that distinguish different
atomic types or not.
"""
return False

Check warning on line 230 in deepmd/dpmodel/descriptor/se_r.py

View check run for this annotation

Codecov / codecov/patch

deepmd/dpmodel/descriptor/se_r.py#L230

Added line #L230 was not covered by tests

def get_ntypes(self) -> int:
"""Returns the number of element types."""
return self.ntypes

Check warning on line 234 in deepmd/dpmodel/descriptor/se_r.py

View check run for this annotation

Codecov / codecov/patch

deepmd/dpmodel/descriptor/se_r.py#L234

Added line #L234 was not covered by tests

def compute_input_stats(self, merged: List[dict], path: Optional[DPPath] = None):
"""Update mean and stddev for descriptor elements."""
raise NotImplementedError

Check warning on line 238 in deepmd/dpmodel/descriptor/se_r.py

View check run for this annotation

Codecov / codecov/patch

deepmd/dpmodel/descriptor/se_r.py#L238

Added line #L238 was not covered by tests

def cal_g(
self,
ss,
ll,
):
nf, nloc, nnei = ss.shape[0:3]
ss = ss.reshape(nf, nloc, nnei, 1)
# nf x nloc x nnei x ng
gg = self.embeddings[(ll,)].call(ss)
return gg

def call(
self,
coord_ext,
atype_ext,
nlist,
mapping: Optional[np.ndarray] = None,
):
"""Compute the descriptor.

Parameters
----------
coord_ext
The extended coordinates of atoms. shape: nf x (nallx3)
atype_ext
The extended aotm types. shape: nf x nall
nlist
The neighbor list. shape: nf x nloc x nnei
mapping
The index mapping from extended to lcoal region. not used by this descriptor.

Returns
-------
descriptor
The descriptor. shape: nf x nloc x (ng x axis_neuron)
gr
The rotationally equivariant and permutationally invariant single particle
representation. shape: nf x nloc x ng x 3
g2
The rotationally invariant pair-partical representation.
this descriptor returns None
h2
The rotationally equivariant pair-partical representation.
this descriptor returns None
sw
The smooth switch function.
"""
del mapping
# nf x nloc x nnei x 1
rr, ww = self.env_mat.call(
coord_ext, atype_ext, nlist, self.davg, self.dstd, True
)
nf, nloc, nnei, _ = rr.shape
sec = np.append([0], np.cumsum(self.sel))

ng = self.neuron[-1]
xyz_scatter = np.zeros([nf, nloc, ng])
anyangml marked this conversation as resolved.
Show resolved Hide resolved
exclude_mask = self.emask.build_type_exclude_mask(nlist, atype_ext)
for tt in range(self.ntypes):
mm = exclude_mask[:, :, sec[tt] : sec[tt + 1]]
tr = rr[:, :, sec[tt] : sec[tt + 1], :]
tr = tr * mm[:, :, :, None]
gg = self.cal_g(tr, tt)
gg = np.mean(gg, axis=2)
# nf x nloc x ng x 1
xyz_scatter += gg

res_rescale = 1.0 / 10.0
res = xyz_scatter * res_rescale
res = res.reshape(nf, nloc, -1)
return res, None, None, None, ww

def serialize(self) -> dict:
"""Serialize the descriptor to dict."""
return {
"@class": "Descriptor",
"type": "se_r",
"rcut": self.rcut,
"rcut_smth": self.rcut_smth,
"sel": self.sel,
"neuron": self.neuron,
"resnet_dt": self.resnet_dt,
"trainable": self.trainable,
"type_one_side": self.type_one_side,
"exclude_types": self.exclude_types,
"set_davg_zero": self.set_davg_zero,
"activation_function": self.activation_function,
# make deterministic
"precision": np.dtype(PRECISION_DICT[self.precision]).name,
"spin": self.spin,
"env_mat": self.env_mat.serialize(),
"embeddings": self.embeddings.serialize(),
"@variables": {
"davg": self.davg,
"dstd": self.dstd,
},
}

@classmethod
def deserialize(cls, data: dict) -> "DescrptSeR":
"""Deserialize from dict."""
data = copy.deepcopy(data)
data.pop("@class", None)
data.pop("type", None)
variables = data.pop("@variables")
embeddings = data.pop("embeddings")
env_mat = data.pop("env_mat")
obj = cls(**data)

obj["davg"] = variables["davg"]
obj["dstd"] = variables["dstd"]
obj.embeddings = NetworkCollection.deserialize(embeddings)
obj.env_mat = EnvMat.deserialize(env_mat)
return obj
Loading