Skip to content

Commit 1cd6556

Browse files
wanghan-iapcmHan Wang
andauthored
fix(pd): preserve dtype in Paddle RepFlow dynamic aggregation (#5712)
## Problem The Paddle `aggregate()` helper in `deepmd/pd/model/network/utils.py` allocated its output with `paddle.zeros([num_owner, data.shape[1]])` without specifying a dtype, so it fell back to Paddle's default floating dtype (float32). It then cast the input `data` to `output.dtype` before `index_add_`. For float64 Paddle RepFlow/DPA3 models, the dynamic-selection aggregation therefore accumulated descriptor updates in float32, silently downcasting intermediate values before returning them to the descriptor path. ## Fix Allocate the output with `dtype=data.dtype` so the input precision is preserved (and the subsequent `data.astype(output.dtype)` becomes a no-op for matching dtypes). ## Test A new test aggregates float64 input and asserts the result stays float64 (and has the expected values). On the current code the output is float32; after the fix it is float64. This exercises the shared `output = paddle.zeros(..., dtype=data.dtype)` allocation via the summation path. ## Note on verification Verified locally with `paddlepaddle==3.3.1`. The CI target is a newer nightly (`paddlepaddle==3.4.0.dev20260310`), but the behavior fixed here is version-agnostic: `paddle.zeros` without `dtype` defaults to float32 in all versions, and `dtype=data.dtype` corrects it regardless. The test is scoped to the summation path to keep it independent of an unrelated `Tensor.where` API difference in the older local Paddle. Fix #5688 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Preserved the input tensor’s numeric dtype when aggregating values, preventing unintended dtype fallback during summation. * **Tests** * Added unit coverage to confirm aggregation keeps `float64` output dtype and returns the expected summed values. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Han Wang <wang_han@iapcm.ac.cn>
1 parent e975e13 commit 1cd6556

2 files changed

Lines changed: 31 additions & 2 deletions

File tree

deepmd/pd/model/network/utils.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ def aggregate(
3939
else:
4040
bin_count = None
4141

42-
# make sure this operation is done on the same device of data and owners
43-
output = paddle.zeros([num_owner, data.shape[1]])
42+
# preserve the input dtype instead of falling back to the default float;
43+
# paddle.zeros creates the tensor on the current global device (it has no
44+
# place argument), which matches data/owners under deepmd's global device.
45+
output = paddle.zeros([num_owner, data.shape[1]], dtype=data.dtype)
4446
output = output.index_add_(owners, 0, data.astype(output.dtype))
4547
if average:
4648
assert bin_count is not None
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# SPDX-License-Identifier: LGPL-3.0-or-later
2+
"""Test that ``aggregate`` preserves the input dtype (float64 must not downcast)."""
3+
4+
import unittest
5+
6+
import numpy as np
7+
import paddle
8+
9+
from deepmd.pd.model.network.utils import (
10+
aggregate,
11+
)
12+
13+
14+
class TestAggregateDtype(unittest.TestCase):
15+
def _data(self) -> tuple[paddle.Tensor, paddle.Tensor]:
16+
# rows 0,1 -> owner 0 ; row 2 -> owner 1
17+
data = paddle.to_tensor([[1.0], [2.0], [3.0]], dtype="float64")
18+
owners = paddle.to_tensor([0, 0, 1], dtype="int64")
19+
return data, owners
20+
21+
def test_sum_preserves_float64(self) -> None:
22+
# exercises the shared ``output = paddle.zeros(..., dtype=data.dtype)``
23+
# allocation via the summation path
24+
data, owners = self._data()
25+
out = aggregate(data, owners, average=False, num_owner=2)
26+
self.assertEqual(out.dtype, paddle.float64)
27+
np.testing.assert_allclose(out.numpy().ravel(), [3.0, 3.0])

0 commit comments

Comments
 (0)