This issue comes from a Codex global scan of deepmodeling/deepmd-kit at commit 73de44b1f94471b2e3bdb6b11f57b34d7bc791bb.
Problem
dprc_pairwise_map_cpu() reports every local atom as a local QM atom when the pairwise DPRc index map contains only one non-placeholder fragment.
The mapper groups atoms by fragment index, ignoring -1 placeholders:
|
void deepmd::group_atoms_cpu(std::vector<std::vector<int>>& fragments, |
|
const std::vector<int>& idxs) { |
|
int natoms = idxs.size(); |
|
// sort idxs |
|
std::vector<size_t> idxs_idx = sort_indexes(idxs); |
|
// now idxs_idx is sorted index, so we can easily group atoms in only one loop |
|
int last_frag = -1; |
|
for (size_t ii = 0; ii < idxs.size(); ii++) { |
|
int frag = idxs[idxs_idx[ii]]; |
|
if (frag == -1) { |
|
// -1 is the place holder |
|
continue; |
|
} |
|
if (frag != last_frag) { |
|
last_frag = frag; |
|
fragments.emplace_back(); |
|
} |
|
// push to the last fragment |
|
fragments.back().push_back(idxs_idx[ii]); |
|
} |
The pairwise map then initializes nqm_real to nloc for the nfragments == 1 case:
|
int max_fragment_size = max_fragment_real_size + max_fragment_ghost_size; |
|
int map_size = nqm + max_fragment_real_size + max_fragment_ghost_size; |
|
// (3, 4, 0, 1, 2, 10, 11), |
|
// (3, 4, 5, 6, 7, 10, -1), |
|
// (3, 4, 8, 9, -1, 10, -1) |
|
forward_qmmm_map.resize(static_cast<size_t>(nfragments - 1) * map_size); |
|
std::fill(forward_qmmm_map.begin(), forward_qmmm_map.end(), -1); |
|
int nqm_real = nloc; // init for nfragments = 1 |
|
for (int ii = 0; ii < nfragments - 1; ++ii) { |
However, the only code path that counts how many atoms in fragments[0] are local runs inside the for (ii < nfragments - 1) loop:
|
for (int ii = 0; ii < nfragments - 1; ++ii) { |
|
// real |
|
for (int jj = 0, kk = 0; jj < nqm; ++jj) { |
|
if (fragments[0][jj] < nloc) { |
|
forward_qmmm_map[ii * map_size + kk] = fragments[0][jj]; |
|
kk++; |
|
} |
|
if (jj == nqm - 1) { |
|
nqm_real = kk; |
|
} |
|
} |
With a single QM fragment that covers only a subset of local atoms, nloc_qm is returned as nloc instead of the local count in fragments[0]:
|
nloc_qm = nqm_real; |
|
nloc_qmmm = nqm_real + max_fragment_real_size; |
|
nall_qm = nqm; |
|
nall_qmmm = nqm + max_fragment_size; |
The TensorFlow op trusts that count when copying forward_qm_map, so it can read past the vector that actually contains only the QM fragment atoms:
|
for (int jj = 0; jj < max_nloc_qm + max_nghost_qm; ++jj) { |
|
if (jj < nloc_qm[ii]) { |
|
m_forward_qm_map(ii, jj) = forward_qm_maps[ii][jj]; |
|
} else if (jj < max_nloc_qm) { |
|
m_forward_qm_map(ii, jj) = -1; |
|
} else if (jj < max_nloc_qm + nghost_qm[ii]) { |
|
m_forward_qm_map(ii, jj) = |
|
forward_qm_maps[ii][jj - (max_nloc_qm - nloc_qm[ii])]; |
Impact
For frames where all non-QM entries are placeholders or where no MM fragment is present, the op can emit incorrect natoms_qm metadata and may read out of bounds while filling forward_qm_map. If single-fragment pairwise DPRc frames are not supported, the current code should reject them explicitly instead of producing inconsistent maps.
Suggested fix
Count local atoms in fragments[0] before the QMMM-fragment loop and use that value for nloc_qm. Separately, if pairwise DPRc requires at least one MM fragment, validate nfragments >= 2 and return a clear TensorFlow InvalidArgument error.
Add coverage for an idxs row with one QM fragment plus -1 placeholders, and verify nloc_qm, forward_qm_map, and padding behavior.
This issue comes from a Codex global scan of
deepmodeling/deepmd-kitat commit73de44b1f94471b2e3bdb6b11f57b34d7bc791bb.Problem
dprc_pairwise_map_cpu()reports every local atom as a local QM atom when the pairwise DPRc index map contains only one non-placeholder fragment.The mapper groups atoms by fragment index, ignoring
-1placeholders:deepmd-kit/source/lib/src/pairwise.cc
Lines 21 to 40 in 73de44b
The pairwise map then initializes
nqm_realtonlocfor thenfragments == 1case:deepmd-kit/source/lib/src/pairwise.cc
Lines 91 to 99 in 73de44b
However, the only code path that counts how many atoms in
fragments[0]are local runs inside thefor (ii < nfragments - 1)loop:deepmd-kit/source/lib/src/pairwise.cc
Lines 99 to 109 in 73de44b
With a single QM fragment that covers only a subset of local atoms,
nloc_qmis returned asnlocinstead of the local count infragments[0]:deepmd-kit/source/lib/src/pairwise.cc
Lines 147 to 150 in 73de44b
The TensorFlow op trusts that count when copying
forward_qm_map, so it can read past the vector that actually contains only the QM fragment atoms:deepmd-kit/source/op/tf/pairwise.cc
Lines 159 to 166 in 73de44b
Impact
For frames where all non-QM entries are placeholders or where no MM fragment is present, the op can emit incorrect
natoms_qmmetadata and may read out of bounds while fillingforward_qm_map. If single-fragment pairwise DPRc frames are not supported, the current code should reject them explicitly instead of producing inconsistent maps.Suggested fix
Count local atoms in
fragments[0]before the QMMM-fragment loop and use that value fornloc_qm. Separately, if pairwise DPRc requires at least one MM fragment, validatenfragments >= 2and return a clear TensorFlowInvalidArgumenterror.Add coverage for an
idxsrow with one QM fragment plus-1placeholders, and verifynloc_qm,forward_qm_map, and padding behavior.