Skip to content
12 changes: 12 additions & 0 deletions deepmd/dpmodel/descriptor/dpa3.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ class RepFlowArgs:
smooth_edge_update : bool, optional
Whether to make edge update smooth.
If True, the edge update from angle message will not use self as padding.
use_exp_switch : bool, optional
Whether to use an exponential switch function instead of a polynomial one in the neighbor update.
The exponential switch function ensures neighbor contributions smoothly diminish as the interatomic distance
`r` approaches the cutoff radius `rcut`. Specifically, the function is defined as:
s(r) = \\exp(-\\exp(20 * (r - rcut_smth) / rcut_smth)) for 0 < r \\leq rcut, and s(r) = 0 for r > rcut.
Here, `rcut_smth` is an adjustable smoothing factor and `rcut_smth` should be chosen carefully
according to `rcut`, ensuring s(r) approaches zero smoothly at the cutoff.
Typical recommended values are `rcut_smth` = 5.3 for `rcut` = 6.0, and 3.5 for `rcut` = 4.0.
use_dynamic_sel : bool, optional
Whether to dynamically select neighbors within the cutoff radius.
If True, the exact number of neighbors within the cutoff radius is used
Expand Down Expand Up @@ -162,6 +170,7 @@ def __init__(
skip_stat: bool = False,
optim_update: bool = True,
smooth_edge_update: bool = False,
use_exp_switch: bool = False,
use_dynamic_sel: bool = False,
sel_reduce_factor: float = 10.0,
) -> None:
Expand Down Expand Up @@ -190,6 +199,7 @@ def __init__(
self.a_compress_use_split = a_compress_use_split
self.optim_update = optim_update
self.smooth_edge_update = smooth_edge_update
self.use_exp_switch = use_exp_switch
self.use_dynamic_sel = use_dynamic_sel
self.sel_reduce_factor = sel_reduce_factor

Expand Down Expand Up @@ -223,6 +233,7 @@ def serialize(self) -> dict:
"fix_stat_std": self.fix_stat_std,
"optim_update": self.optim_update,
"smooth_edge_update": self.smooth_edge_update,
"use_exp_switch": self.use_exp_switch,
"use_dynamic_sel": self.use_dynamic_sel,
"sel_reduce_factor": self.sel_reduce_factor,
}
Expand Down Expand Up @@ -321,6 +332,7 @@ def init_subclass_params(sub_data, sub_class):
fix_stat_std=self.repflow_args.fix_stat_std,
optim_update=self.repflow_args.optim_update,
smooth_edge_update=self.repflow_args.smooth_edge_update,
use_exp_switch=self.repflow_args.use_exp_switch,
use_dynamic_sel=self.repflow_args.use_dynamic_sel,
sel_reduce_factor=self.repflow_args.sel_reduce_factor,
exclude_types=exclude_types,
Expand Down
21 changes: 19 additions & 2 deletions deepmd/dpmodel/descriptor/repflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ class DescrptBlockRepflows(NativeOP, DescriptorBlock):
smooth_edge_update : bool, optional
Whether to make edge update smooth.
If True, the edge update from angle message will not use self as padding.
use_exp_switch : bool, optional
Whether to use an exponential switch function instead of a polynomial one in the neighbor update.
The exponential switch function ensures neighbor contributions smoothly diminish as the interatomic distance
`r` approaches the cutoff radius `rcut`. Specifically, the function is defined as:
s(r) = \\exp(-\\exp(20 * (r - rcut_smth) / rcut_smth)) for 0 < r \\leq rcut, and s(r) = 0 for r > rcut.
Here, `rcut_smth` is an adjustable smoothing factor and `rcut_smth` should be chosen carefully
according to `rcut`, ensuring s(r) approaches zero smoothly at the cutoff.
Typical recommended values are `rcut_smth` = 5.3 for `rcut` = 6.0, and 3.5 for `rcut` = 4.0.
use_dynamic_sel : bool, optional
Whether to dynamically select neighbors within the cutoff radius.
If True, the exact number of neighbors within the cutoff radius is used
Expand Down Expand Up @@ -185,6 +193,7 @@ def __init__(
fix_stat_std: float = 0.3,
optim_update: bool = True,
smooth_edge_update: bool = False,
use_exp_switch: bool = False,
use_dynamic_sel: bool = False,
sel_reduce_factor: float = 10.0,
seed: Optional[Union[int, list[int]]] = None,
Expand Down Expand Up @@ -218,6 +227,7 @@ def __init__(
self.a_compress_use_split = a_compress_use_split
self.optim_update = optim_update
self.smooth_edge_update = smooth_edge_update
self.use_exp_switch = use_exp_switch
self.use_dynamic_sel = use_dynamic_sel
self.sel_reduce_factor = sel_reduce_factor
if self.use_dynamic_sel and not self.smooth_edge_update:
Expand Down Expand Up @@ -290,10 +300,16 @@ def __init__(

wanted_shape = (self.ntypes, self.nnei, 4)
self.env_mat_edge = EnvMat(
self.e_rcut, self.e_rcut_smth, protection=self.env_protection
self.e_rcut,
self.e_rcut_smth,
protection=self.env_protection,
use_exp_switch=self.use_exp_switch,
)
self.env_mat_angle = EnvMat(
self.a_rcut, self.a_rcut_smth, protection=self.env_protection
self.a_rcut,
self.a_rcut_smth,
protection=self.env_protection,
use_exp_switch=self.use_exp_switch,
)
self.mean = np.zeros(wanted_shape, dtype=PRECISION_DICT[self.precision])
self.stddev = np.ones(wanted_shape, dtype=PRECISION_DICT[self.precision])
Expand Down Expand Up @@ -647,6 +663,7 @@ def serialize(self):
"precision": self.precision,
"fix_stat_std": self.fix_stat_std,
"optim_update": self.optim_update,
"use_exp_switch": self.use_exp_switch,
"smooth_edge_update": self.smooth_edge_update,
"use_dynamic_sel": self.use_dynamic_sel,
"sel_reduce_factor": self.sel_reduce_factor,
Expand Down
30 changes: 29 additions & 1 deletion deepmd/dpmodel/utils/env_mat.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,32 @@
return vv


@support_array_api(version="2023.12")
def compute_exp_sw(
distance: np.ndarray,
rmin: float,
rmax: float,
):
"""Compute the exponential switch function for neighbor update."""
if rmin >= rmax:
raise ValueError("rmin should be less than rmax.")

Check warning on line 46 in deepmd/dpmodel/utils/env_mat.py

View check run for this annotation

Codecov / codecov/patch

deepmd/dpmodel/utils/env_mat.py#L46

Added line #L46 was not covered by tests
xp = array_api_compat.array_namespace(distance)
distance = xp.clip(distance, min=0.0, max=rmax)
C = 20
a = C / rmin
b = rmin
exp_sw = xp.exp(-xp.exp(a * (distance - b)))
return exp_sw


def _make_env_mat(
nlist,
coord,
rcut: float,
ruct_smth: float,
radial_only: bool = False,
protection: float = 0.0,
use_exp_switch: bool = False,
):
"""Make smooth environment matrix."""
xp = array_api_compat.array_namespace(nlist)
Expand All @@ -66,7 +85,11 @@
length = length + xp.astype(~xp.expand_dims(mask, axis=-1), length.dtype)
t0 = 1 / (length + protection)
t1 = diff / (length + protection) ** 2
weight = compute_smooth_weight(length, ruct_smth, rcut)
weight = (
compute_smooth_weight(length, ruct_smth, rcut)
if not use_exp_switch
else compute_exp_sw(length, ruct_smth, rcut)
)
weight = weight * xp.astype(xp.expand_dims(mask, axis=-1), weight.dtype)
if radial_only:
env_mat = t0 * weight
Expand All @@ -81,10 +104,12 @@
rcut,
rcut_smth,
protection: float = 0.0,
use_exp_switch: bool = False,
) -> None:
self.rcut = rcut
self.rcut_smth = rcut_smth
self.protection = protection
self.use_exp_switch = use_exp_switch

def call(
self,
Expand Down Expand Up @@ -142,6 +167,7 @@
self.rcut_smth,
radial_only=radial_only,
protection=self.protection,
use_exp_switch=self.use_exp_switch,
)
return em, diff, ww

Expand All @@ -151,6 +177,8 @@
return {
"rcut": self.rcut,
"rcut_smth": self.rcut_smth,
"protection": self.protection,
"use_exp_switch": self.use_exp_switch,
}

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion deepmd/pd/model/descriptor/dpa1.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,9 @@ def serialize(self) -> dict:
"precision": RESERVED_PRECISION_DICT[obj.prec],
"embeddings": obj.filter_layers.serialize(),
"attention_layers": obj.dpa1_attention.serialize(),
"env_mat": DPEnvMat(obj.rcut, obj.rcut_smth).serialize(),
"env_mat": DPEnvMat(
obj.rcut, obj.rcut_smth, obj.env_protection
).serialize(),
"type_embedding": self.type_embedding.embedding.serialize(),
"exclude_types": obj.exclude_types,
"env_protection": obj.env_protection,
Expand Down
4 changes: 3 additions & 1 deletion deepmd/pd/model/descriptor/se_a.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,9 @@ def serialize(self) -> dict:
# make deterministic
"precision": RESERVED_PRECISION_DICT[obj.prec],
"embeddings": obj.filter_layers.serialize(),
"env_mat": DPEnvMat(obj.rcut, obj.rcut_smth).serialize(),
"env_mat": DPEnvMat(
obj.rcut, obj.rcut_smth, obj.env_protection
).serialize(),
"exclude_types": obj.exclude_types,
"env_protection": obj.env_protection,
"@variables": {
Expand Down
4 changes: 3 additions & 1 deletion deepmd/pt/model/descriptor/dpa1.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,9 @@ def serialize(self) -> dict:
"precision": RESERVED_PRECISION_DICT[obj.prec],
"embeddings": obj.filter_layers.serialize(),
"attention_layers": obj.dpa1_attention.serialize(),
"env_mat": DPEnvMat(obj.rcut, obj.rcut_smth).serialize(),
"env_mat": DPEnvMat(
obj.rcut, obj.rcut_smth, obj.env_protection
).serialize(),
"type_embedding": self.type_embedding.embedding.serialize(),
"exclude_types": obj.exclude_types,
"env_protection": obj.env_protection,
Expand Down
1 change: 1 addition & 0 deletions deepmd/pt/model/descriptor/dpa3.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ def init_subclass_params(sub_data, sub_class):
fix_stat_std=self.repflow_args.fix_stat_std,
optim_update=self.repflow_args.optim_update,
smooth_edge_update=self.repflow_args.smooth_edge_update,
use_exp_switch=self.repflow_args.use_exp_switch,
use_dynamic_sel=self.repflow_args.use_dynamic_sel,
sel_reduce_factor=self.repflow_args.sel_reduce_factor,
exclude_types=exclude_types,
Expand Down
11 changes: 10 additions & 1 deletion deepmd/pt/model/descriptor/env_mat.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import torch

from deepmd.pt.utils.preprocess import (
compute_exp_sw,
compute_smooth_weight,
)

Expand All @@ -14,6 +15,7 @@ def _make_env_mat(
ruct_smth: float,
radial_only: bool = False,
protection: float = 0.0,
use_exp_switch: bool = False,
):
"""Make smooth environment matrix."""
bsz, natoms, nnei = nlist.shape
Expand All @@ -33,7 +35,11 @@ def _make_env_mat(
length = length + ~mask.unsqueeze(-1)
t0 = 1 / (length + protection)
t1 = diff / (length + protection) ** 2
weight = compute_smooth_weight(length, ruct_smth, rcut)
weight = (
compute_smooth_weight(length, ruct_smth, rcut)
if not use_exp_switch
else compute_exp_sw(length, ruct_smth, rcut)
)
weight = weight * mask.unsqueeze(-1)
if radial_only:
env_mat = t0 * weight
Expand All @@ -52,6 +58,7 @@ def prod_env_mat(
rcut_smth: float,
radial_only: bool = False,
protection: float = 0.0,
use_exp_switch: bool = False,
):
"""Generate smooth environment matrix from atom coordinates and other context.

Expand All @@ -64,6 +71,7 @@ def prod_env_mat(
- rcut_smth: Smooth hyper-parameter for pair force & energy.
- radial_only: Whether to return a full description or a radial-only descriptor.
- protection: Protection parameter to prevent division by zero errors during calculations.
- use_exp_switch: Whether to use the exponential switch function.

Returns
-------
Expand All @@ -76,6 +84,7 @@ def prod_env_mat(
rcut_smth,
radial_only,
protection=protection,
use_exp_switch=use_exp_switch,
) # shape [n_atom, dim, 4 or 1]
t_avg = mean[atype] # [n_atom, dim, 4 or 1]
t_std = stddev[atype] # [n_atom, dim, 4 or 1]
Expand Down
12 changes: 12 additions & 0 deletions deepmd/pt/model/descriptor/repflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ class DescrptBlockRepflows(DescriptorBlock):
smooth_edge_update : bool, optional
Whether to make edge update smooth.
If True, the edge update from angle message will not use self as padding.
use_exp_switch : bool, optional
Whether to use an exponential switch function instead of a polynomial one in the neighbor update.
The exponential switch function ensures neighbor contributions smoothly diminish as the interatomic distance
`r` approaches the cutoff radius `rcut`. Specifically, the function is defined as:
s(r) = \\exp(-\\exp(20 * (r - rcut_smth) / rcut_smth)) for 0 < r \\leq rcut, and s(r) = 0 for r > rcut.
Here, `rcut_smth` is an adjustable smoothing factor and `rcut_smth` should be chosen carefully
according to `rcut`, ensuring s(r) approaches zero smoothly at the cutoff.
Typical recommended values are `rcut_smth` = 5.3 for `rcut` = 6.0, and 3.5 for `rcut` = 4.0.
use_dynamic_sel : bool, optional
Whether to dynamically select neighbors within the cutoff radius.
If True, the exact number of neighbors within the cutoff radius is used
Expand Down Expand Up @@ -198,6 +206,7 @@ def __init__(
precision: str = "float64",
fix_stat_std: float = 0.3,
smooth_edge_update: bool = False,
use_exp_switch: bool = False,
use_dynamic_sel: bool = False,
sel_reduce_factor: float = 10.0,
optim_update: bool = True,
Expand Down Expand Up @@ -232,6 +241,7 @@ def __init__(
self.a_compress_use_split = a_compress_use_split
self.optim_update = optim_update
self.smooth_edge_update = smooth_edge_update
self.use_exp_switch = use_exp_switch
self.use_dynamic_sel = use_dynamic_sel
self.sel_reduce_factor = sel_reduce_factor
if self.use_dynamic_sel and not self.smooth_edge_update:
Expand Down Expand Up @@ -425,6 +435,7 @@ def forward(
self.e_rcut,
self.e_rcut_smth,
protection=self.env_protection,
use_exp_switch=self.use_exp_switch,
)
nlist_mask = nlist != -1
sw = torch.squeeze(sw, -1)
Expand All @@ -446,6 +457,7 @@ def forward(
self.a_rcut,
self.a_rcut_smth,
protection=self.env_protection,
use_exp_switch=self.use_exp_switch,
)
a_nlist_mask = a_nlist != -1
a_sw = torch.squeeze(a_sw, -1)
Expand Down
4 changes: 3 additions & 1 deletion deepmd/pt/model/descriptor/se_a.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,9 @@ def serialize(self) -> dict:
# make deterministic
"precision": RESERVED_PRECISION_DICT[obj.prec],
"embeddings": obj.filter_layers.serialize(),
"env_mat": DPEnvMat(obj.rcut, obj.rcut_smth).serialize(),
"env_mat": DPEnvMat(
obj.rcut, obj.rcut_smth, obj.env_protection
).serialize(),
"exclude_types": obj.exclude_types,
"env_protection": obj.env_protection,
"@variables": {
Expand Down
4 changes: 3 additions & 1 deletion deepmd/pt/model/descriptor/se_t.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,9 @@ def serialize(self) -> dict:
"activation_function": obj.activation_function,
"precision": RESERVED_PRECISION_DICT[obj.prec],
"embeddings": obj.filter_layers.serialize(),
"env_mat": DPEnvMat(obj.rcut, obj.rcut_smth).serialize(),
"env_mat": DPEnvMat(
obj.rcut, obj.rcut_smth, obj.env_protection
).serialize(),
"exclude_types": obj.exclude_types,
"env_protection": obj.env_protection,
"type_map": self.type_map,
Expand Down
12 changes: 12 additions & 0 deletions deepmd/pt/utils/preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,15 @@ def compute_smooth_weight(distance, rmin: float, rmax: float):
uu2 = uu * uu
vv = uu2 * uu * (-6 * uu2 + 15 * uu - 10) + 1
return vv


def compute_exp_sw(distance, rmin: float, rmax: float):
"""Compute the exponential switch function for neighbor update."""
if rmin >= rmax:
raise ValueError("rmin should be less than rmax.")
distance = torch.clamp(distance, min=0.0, max=rmax)
C = 20
a = C / rmin
b = rmin
exp_sw = torch.exp(-torch.exp(a * (distance - b)))
return exp_sw
Comment thread
iProzd marked this conversation as resolved.
17 changes: 17 additions & 0 deletions deepmd/utils/argcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -1497,6 +1497,15 @@ def dpa3_repflow_args():
"Whether to make edge update smooth. "
"If True, the edge update from angle message will not use self as padding."
)
doc_use_exp_switch = (
"Whether to use an exponential switch function instead of a polynomial one in the neighbor update. "
"The exponential switch function ensures neighbor contributions smoothly diminish as the interatomic distance "
"`r` approaches the cutoff radius `rcut`. Specifically, the function is defined as: "
"s(r) = \\exp(-\\exp(20 * (r - rcut_smth) / rcut_smth)) for 0 < r \\leq rcut, and s(r) = 0 for r > rcut. "
"Here, `rcut_smth` is an adjustable smoothing factor and should be chosen carefully according to `rcut`, "
"ensuring s(r) approaches zero smoothly at the cutoff. "
"Typical recommended values are `rcut_smth` = 5.3 for `rcut` = 6.0, and 3.5 for `rcut` = 4.0."
)
doc_use_dynamic_sel = (
"Whether to dynamically select neighbors within the cutoff radius. "
"If True, the exact number of neighbors within the cutoff radius is used "
Expand Down Expand Up @@ -1611,6 +1620,14 @@ def dpa3_repflow_args():
default=False, # For compatability. This will be True in the future
doc=doc_smooth_edge_update,
),
Argument(
"use_exp_switch",
bool,
optional=True,
default=False,
alias=["use_env_envelope"],
doc=doc_use_exp_switch,
),
Argument(
"use_dynamic_sel",
bool,
Expand Down
Loading