Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 25 additions & 15 deletions deepmd/dpmodel/array_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
Version,
)

from deepmd.dpmodel.common import (
to_numpy_array,
Comment thread
OutisLi marked this conversation as resolved.
Dismissed
)

# Type alias for array_api compatible arrays
Array = np.ndarray | Any # Any to support JAX, PyTorch, etc. arrays

Expand All @@ -27,22 +31,28 @@
``torch.asarray`` detaches its input from the autograd graph, so calling
``xp.asarray`` on a weight attribute that is already a backend tensor
(e.g. a ``torch.nn.Parameter`` registered by the pt_expt backend)
silently breaks gradient flow to that weight. This helper converts
genuine non-backend data (numpy arrays, python scalars/lists) via
``xp.asarray``; backend tensors are returned as-is, with an optional
differentiable dtype cast via ``xp.astype``.

The ``device`` argument only applies to the conversion path: backend
tensors are assumed to already live on the working device (they are
created together with the inputs).
silently breaks gradient flow to that weight. Backend tensors already in
``xp`` are therefore returned as-is, with an optional differentiable dtype
cast via ``xp.astype``.

An array from another namespace cannot retain its autograd graph. It is
converted through NumPy before entering ``xp``; this also performs the
required device-to-host copy when a CUDA-backed model constant is consumed
by a NumPy statistics path.

The ``device`` argument only applies to the conversion path. Arrays already
in ``xp`` are assumed to live on the working device because model buffers
and inputs are moved together.
"""
if isinstance(obj, np.ndarray) or not array_api_compat.is_array_api_obj(obj):
if dtype is None:
return xp.asarray(obj, device=device)
return xp.asarray(obj, dtype=dtype, device=device)
if dtype is not None and obj.dtype != dtype:
obj = xp.astype(obj, dtype)
return obj
if array_api_compat.is_array_api_obj(obj):
if array_api_compat.array_namespace(obj) is xp:
if dtype is not None and obj.dtype != dtype:
obj = xp.astype(obj, dtype)
return obj
obj = to_numpy_array(obj)
if dtype is None:
return xp.asarray(obj, device=device)
return xp.asarray(obj, dtype=dtype, device=device)


# array api adds take_along_axis in https://github.com/data-apis/array-api/pull/816
Expand Down
2 changes: 1 addition & 1 deletion deepmd/dpmodel/descriptor/dpa4.py
Original file line number Diff line number Diff line change
Expand Up @@ -2186,7 +2186,7 @@ def _canonicalize_charge_spin(
raise ValueError("`charge_spin` is required for this SeZM descriptor.")
charge_spin = xp.reshape(
xp_asarray_nodetach(
xp, np.asarray(self.default_chg_spin), dtype=dtype, device=device
xp, self.default_chg_spin, dtype=dtype, device=device
),
(1, 2),
)
Expand Down
4 changes: 2 additions & 2 deletions deepmd/dpmodel/loss/dos.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def call(
)
diff3d = local_pred - local_label # [nf, natoms, numb_dos]
if "mask" in model_dict:
# idiom 1: per-frame masked mean, then average over frames
# Idiom 1 (per-atom masked mean, ncomp=numb_dos).
maskf = xp.astype(model_dict["mask"], diff3d.dtype) # [nf, natoms]
l2_local_loss_dos = masked_atom_mean(
xp.square(diff3d), maskf, self.numb_dos
Expand All @@ -184,7 +184,7 @@ def call(
)
diff3d = local_pred_cdf - local_label_cdf # [nf, natoms, numb_dos]
if "mask" in model_dict:
# idiom 1: per-frame masked mean, then average over frames
# Idiom 1 (per-atom masked mean, ncomp=numb_dos).
maskf = xp.astype(model_dict["mask"], diff3d.dtype) # [nf, natoms]
l2_local_loss_cdf = masked_atom_mean(
xp.square(diff3d), maskf, self.numb_dos
Expand Down
Loading
Loading