Skip to content

Commit 7b0050f

Browse files
authored
fix(jax): avoid device lookup during type embedding tracing (#5736)
## Summary Fix a JAX/Flax NNX tracing failure in `TypeEmbedNet.call()` by avoiding a hard requirement that traced values expose a readable `.device` attribute. ## Background JAX training can call `TypeEmbedNet.call()` from inside `nnx.jit` / `nnx.grad`. In that context the type-embedding weights are represented by NNX/JAX traced values such as `DynamicJaxprTracer`. The previous code passed ```python device=array_api_compat.device(sample_array) ``` to `xp.eye`, and did the same for the padding `xp.zeros`. For an NNX traced parameter, `array_api_compat.device(...)` eventually tries to read `.device`; the tracer does not provide that attribute during tracing, so the training step fails with an error like: ```text AttributeError: DynamicJaxprTracer has no attribute device ``` or, through JAX core wrapping: ```text AttributeError: 'ShapedArray' object has no attribute 'device' ``` ## Change Add a small local helper in `deepmd/dpmodel/utils/type_embed.py` that returns `array_api_compat.device(array)` when available, and falls back to `None` when the backend value has no readable device during tracing. Passing `device=None` lets JAX create the constants on its default traced device, while preserving the existing explicit-device behavior for backends that expose it normally. This keeps the change limited to the type-embedding constants that triggered the crash, rather than changing global array API device handling. ## Tests Added `source/tests/jax/test_type_embed.py` to cover both: - `TypeEmbedNet.call()` under `nnx.jit` - `TypeEmbedNet.call()` under `nnx.jit` + `nnx.grad` The old implementation fails this test during tracing before producing an output. Validation run locally: ```bash pytest source/tests/jax/test_type_embed.py -v ruff check . ruff format . ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved compatibility for model execution on array backends that do not support device detection in all cases. * Reduced failures when padding or initializing arrays during type embedding operations. * **Tests** * Added JAX coverage to verify the type embedding model runs under JIT, supports gradient tracing, returns the expected output shape, and produces valid numeric results. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 26ad0fb commit 7b0050f

2 files changed

Lines changed: 53 additions & 2 deletions

File tree

deepmd/dpmodel/utils/type_embed.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@
2424
)
2525

2626

27+
def _array_device_or_none(array: Array) -> Any:
28+
try:
29+
return array_api_compat.device(array)
30+
except AttributeError:
31+
return None
32+
33+
2734
class TypeEmbedNet(NativeOP):
2835
r"""Type embedding network.
2936
@@ -104,7 +111,7 @@ def call(self) -> Array:
104111
xp.eye(
105112
self.ntypes,
106113
dtype=sample_array.dtype,
107-
device=array_api_compat.device(sample_array),
114+
device=_array_device_or_none(sample_array),
108115
)
109116
)
110117
else:
@@ -113,7 +120,7 @@ def call(self) -> Array:
113120
embed_pad = xp.zeros(
114121
(1, embed.shape[-1]),
115122
dtype=embed.dtype,
116-
device=array_api_compat.device(embed),
123+
device=_array_device_or_none(embed),
117124
)
118125
embed = xp.concat([embed, embed_pad], axis=0)
119126
return embed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# SPDX-License-Identifier: LGPL-3.0-or-later
2+
import unittest
3+
4+
import numpy as np
5+
6+
from deepmd.jax.env import (
7+
jnp,
8+
nnx,
9+
)
10+
from deepmd.jax.utils.type_embed import (
11+
TypeEmbedNet,
12+
)
13+
14+
15+
class TestJAXTypeEmbedNet(unittest.TestCase):
16+
def test_call_supports_nnx_jit_and_grad_tracing(self) -> None:
17+
type_embedding = TypeEmbedNet(
18+
ntypes=2,
19+
neuron=[4],
20+
padding=True,
21+
activation_function="Linear",
22+
precision="float32",
23+
seed=1,
24+
)
25+
26+
@nnx.jit
27+
def forward(model):
28+
return model.call()
29+
30+
@nnx.jit
31+
def grad(model):
32+
def loss(model):
33+
return jnp.sum(model.call())
34+
35+
return nnx.grad(loss)(model)
36+
37+
out = forward(type_embedding)
38+
self.assertEqual(tuple(out.shape), (3, 4))
39+
self.assertIsNotNone(grad(type_embedding))
40+
self.assertFalse(np.any(np.isnan(np.asarray(out))))
41+
42+
43+
if __name__ == "__main__":
44+
unittest.main()

0 commit comments

Comments
 (0)