@@ -453,30 +453,47 @@ def call(
453453 rr = xp .reshape (rr , (nf * nloc , nnei , 4 ))
454454 rr = xp .astype (rr , self .dstd .dtype )
455455
456- if not self .type_one_side :
457- # nf x nloc -> (nf * nloc)
456+ if self .type_one_side :
457+ for tt in range (self .ntypes ):
458+ mm = exclude_mask [:, sec [tt ] : sec [tt + 1 ]]
459+ tr = rr [:, sec [tt ] : sec [tt + 1 ], :]
460+ tr = tr * xp .astype (mm [:, :, None ], tr .dtype )
461+ ss = tr [..., 0 :1 ]
462+ gg = self .cal_g (ss , (tt ,))
463+ gr += xp .sum (gg [:, :, :, None ] * tr [:, :, None , :], axis = 1 )
464+ else :
465+ # Sort atoms by center type so each type forms a contiguous block.
466+ # Slice indexing (arr[s:e]) is array-api compatible and lets us
467+ # run cal_g only on atoms of the matching center type, keeping the
468+ # same O(nf*nloc) total embedding cost as the original numpy code.
458469 atype_loc = xp .reshape (atype_ext [:, :nloc ], (nf * nloc ,))
459-
460- for embedding_idx in itertools .product (
461- range (self .ntypes ), repeat = self .embeddings .ndim
462- ):
463- if self .type_one_side :
464- (tt ,) = embedding_idx
465- else :
466- ti , tt = embedding_idx
467- mm = exclude_mask [:, sec [tt ] : sec [tt + 1 ]]
468- tr = rr [:, sec [tt ] : sec [tt + 1 ], :]
469- tr = tr * xp .astype (mm [:, :, None ], tr .dtype )
470- ss = tr [..., 0 :1 ]
471- gg = self .cal_g (ss , embedding_idx )
472- gr_tmp = xp .sum (gg [:, :, :, None ] * tr [:, :, None , :], axis = 1 )
473- if not self .type_one_side :
474- # (nf * nloc) x 1 x 1
475- ti_mask = xp .astype (
476- xp .reshape (atype_loc == ti , (nf * nloc , 1 , 1 )), gr_tmp .dtype
477- )
478- gr_tmp = gr_tmp * ti_mask
479- gr += gr_tmp
470+ sort_idx = xp .argsort (atype_loc )
471+ unsort_idx = xp .argsort (sort_idx )
472+ rr_s = xp .take (rr , sort_idx , axis = 0 )
473+ mask_s = xp .take (exclude_mask , sort_idx , axis = 0 )
474+ dev = array_api_compat .device (coord_ext )
475+ gr_s = xp .zeros ([nf * nloc , ng , 4 ], dtype = input_dtype , device = dev )
476+ # Per-type boundaries in sorted order
477+ type_ends = []
478+ offset = 0
479+ for ti in range (self .ntypes ):
480+ offset += int (xp .sum (xp .astype (atype_loc == ti , xp .int32 )))
481+ type_ends .append (offset )
482+ type_starts = [0 , * type_ends [:- 1 ]]
483+ for ti in range (self .ntypes ):
484+ s , e = type_starts [ti ], type_ends [ti ]
485+ if s == e :
486+ continue
487+ for tt in range (self .ntypes ):
488+ mm = mask_s [s :e , sec [tt ] : sec [tt + 1 ]]
489+ tr = rr_s [s :e , sec [tt ] : sec [tt + 1 ], :]
490+ tr = tr * xp .astype (mm [:, :, None ], tr .dtype )
491+ ss = tr [..., 0 :1 ]
492+ gg = self .cal_g (ss , (ti , tt ))
493+ gr_s [s :e ] = gr_s [s :e ] + xp .sum (
494+ gg [:, :, :, None ] * tr [:, :, None , :], axis = 1
495+ )
496+ gr = xp .take (gr_s , unsort_idx , axis = 0 )
480497 gr = xp .reshape (gr , (nf , nloc , ng , 4 ))
481498 # nf x nloc x ng x 4
482499 gr /= self .nnei
0 commit comments