Skip to content

Commit 919537d

Browse files
authored
Add index_fill / index_fill_ operator with CPU/GPU kernels (PaddlePaddle#78041)
* index_fill writing * solve static gra * delete annotations * testfile change
1 parent 242e802 commit 919537d

16 files changed

Lines changed: 1470 additions & 7 deletions

File tree

paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/binary_infer_sym.cc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2683,6 +2683,31 @@ bool IndexAdd_OpInferSymbolicShape(
26832683
return IndexAddOpInferSymbolicShape(op, infer_context);
26842684
}
26852685

2686+
bool IndexFillOpInferSymbolicShape(
2687+
pir::Operation *op, pir::InferSymbolicShapeContext *infer_context) {
2688+
const auto &x_shape_or_data =
2689+
infer_context->GetShapeOrDataForValue(op->operand_source(0));
2690+
std::vector<symbol::DimExpr> x_shape = x_shape_or_data.shape();
2691+
2692+
PADDLE_ENFORCE_LT(
2693+
x_shape.size(),
2694+
7,
2695+
common::errors::InvalidArgument(
2696+
"The rank of input should be less than 7, but received %d.",
2697+
x_shape.size()));
2698+
2699+
infer_context->SetShapeOrDataForValue(
2700+
op->result(0),
2701+
symbol::ShapeOrDataDimExprs{symbol::TensorShapeOrDataDimExprs(x_shape)});
2702+
2703+
return true;
2704+
}
2705+
2706+
bool IndexFill_OpInferSymbolicShape(
2707+
pir::Operation *op, pir::InferSymbolicShapeContext *infer_context) {
2708+
return IndexFillOpInferSymbolicShape(op, infer_context);
2709+
}
2710+
26862711
bool IndexPutOpInferSymbolicShape(
26872712
pir::Operation *op, pir::InferSymbolicShapeContext *infer_context) {
26882713
const auto &x_shape_or_data =

paddle/fluid/pir/dialect/operator/interface/infer_symbolic_shape/binary_infer_sym.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ OP_DECLARE_INFER_SYMBOLIC_SHAPE(Histogram)
6363
OP_DECLARE_INFER_SYMBOLIC_SHAPE(Isclose)
6464
OP_DECLARE_INFER_SYMBOLIC_SHAPE(IndexAdd)
6565
OP_DECLARE_INFER_SYMBOLIC_SHAPE(IndexAdd_)
66+
OP_DECLARE_INFER_SYMBOLIC_SHAPE(IndexFill)
67+
OP_DECLARE_INFER_SYMBOLIC_SHAPE(IndexFill_)
6668
OP_DECLARE_INFER_SYMBOLIC_SHAPE(IndexPut)
6769
OP_DECLARE_INFER_SYMBOLIC_SHAPE(IndexPut_)
6870
OP_DECLARE_INFER_SYMBOLIC_SHAPE(IndexSelect)

paddle/phi/infermeta/multiary.cc

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3813,6 +3813,50 @@ void LegacyInterpolateInferMeta(
38133813
config);
38143814
}
38153815

3816+
void IndexFillInferMeta(const MetaTensor& x,
3817+
const MetaTensor& index,
3818+
int dim,
3819+
const Scalar& value,
3820+
MetaTensor* out) {
3821+
auto in_dims = x.dims();
3822+
auto index_dims = index.dims();
3823+
int rank = in_dims.size();
3824+
3825+
PADDLE_ENFORCE_LT(
3826+
rank,
3827+
7,
3828+
common::errors::InvalidArgument(
3829+
"The rank of Input(X) should be less than 7, but received %d.",
3830+
rank));
3831+
3832+
if (dim < 0) {
3833+
dim += rank;
3834+
}
3835+
3836+
PADDLE_ENFORCE_GE(dim,
3837+
0,
3838+
common::errors::InvalidArgument(
3839+
"The dim must be >= -%d and < %d, but received %d.",
3840+
rank,
3841+
rank,
3842+
dim));
3843+
PADDLE_ENFORCE_LT(dim,
3844+
rank,
3845+
common::errors::InvalidArgument(
3846+
"The dim must be >= -%d and < %d, but received %d.",
3847+
rank,
3848+
rank,
3849+
dim));
3850+
3851+
PADDLE_ENFORCE_EQ(index_dims.size(),
3852+
1,
3853+
common::errors::InvalidArgument(
3854+
"The index tensor must be 1-D, but received %d-D.",
3855+
index_dims.size()));
3856+
3857+
out->share_meta(x);
3858+
}
3859+
38163860
void IndexPutInferMeta(const MetaTensor& x,
38173861
const std::vector<const MetaTensor*>& indices,
38183862
const MetaTensor& value,

paddle/phi/infermeta/multiary.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,12 @@ PADDLE_API void LegacyInterpolateInferMeta(
739739
MetaTensor* output,
740740
MetaConfig config = MetaConfig());
741741

742+
PADDLE_API void IndexFillInferMeta(const MetaTensor& x,
743+
const MetaTensor& index,
744+
int dim,
745+
const Scalar& value,
746+
MetaTensor* out);
747+
742748
PADDLE_API void IndexPutInferMeta(const MetaTensor& x,
743749
const std::vector<const MetaTensor*>& indices,
744750
const MetaTensor& value,
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "paddle/phi/kernels/index_fill_grad_kernel.h"
16+
#include "paddle/phi/backends/cpu/cpu_context.h"
17+
#include "paddle/phi/core/kernel_registry.h"
18+
#include "paddle/phi/core/tensor_utils.h"
19+
20+
namespace phi {
21+
22+
// CPU implementation of the index_fill backward kernel.
23+
// Same logic as the GPU version:
24+
// For each position selected by the index, set x_grad to 0.
25+
//
26+
// The flat iteration index is decomposed into (outer, index, inner) using
27+
// the same three-segment scheme, with OMP parallelization on the outermost
28+
// loop.
29+
template <typename T>
30+
void index_fill_grad_kernel(const int64_t N,
31+
const int64_t* index_data,
32+
const int64_t index_size,
33+
const int64_t dim_size,
34+
const int64_t outer_size,
35+
const int64_t inner_size,
36+
T* x_grad) {
37+
#ifdef PADDLE_WITH_MKLML
38+
#pragma omp parallel for
39+
#endif
40+
for (int64_t idx = 0; idx < N; ++idx) {
41+
// Decompose flat index → (outer_idx, index_idx, inner_idx)
42+
int64_t inner_idx = idx % inner_size;
43+
int64_t temp = idx / inner_size;
44+
int64_t index_idx = temp % index_size;
45+
int64_t outer_idx = temp / index_size;
46+
47+
int64_t dim_idx = index_data[index_idx];
48+
if (dim_idx < 0) {
49+
dim_idx += dim_size;
50+
}
51+
52+
int64_t offset =
53+
outer_idx * dim_size * inner_size + dim_idx * inner_size + inner_idx;
54+
55+
// Zero out gradient at the filled position.
56+
*(x_grad + offset) = static_cast<T>(0);
57+
}
58+
}
59+
60+
// CPU host-side launch function for the backward kernel.
61+
template <typename T, typename Context>
62+
void LaunchIndexFillGradKernel(const Context& dev_ctx,
63+
const DenseTensor& index,
64+
const DenseTensor& out_grad,
65+
const int dim,
66+
DenseTensor* x_grad) {
67+
// Step 1: x_grad = out_grad (full copy).
68+
T* x_grad_data = dev_ctx.template Alloc<T>(x_grad);
69+
Copy(dev_ctx, out_grad, dev_ctx.GetPlace(), false, x_grad);
70+
71+
auto out_grad_dims = out_grad.dims();
72+
const int rank = out_grad_dims.size();
73+
74+
// Cast index to int64 if needed.
75+
DenseTensor index_int64;
76+
const DenseTensor* ptr_index = nullptr;
77+
78+
if (index.dtype() == phi::DataType::INT32) {
79+
index_int64.Resize(index.dims());
80+
int64_t* index_int64_data = dev_ctx.template Alloc<int64_t>(&index_int64);
81+
const int32_t* index_int32_data = index.data<int32_t>();
82+
83+
int64_t index_numel = index.numel();
84+
for (int64_t i = 0; i < index_numel; ++i) {
85+
index_int64_data[i] = static_cast<int64_t>(index_int32_data[i]);
86+
}
87+
88+
ptr_index = &index_int64;
89+
} else {
90+
ptr_index = &index;
91+
}
92+
93+
const int64_t* index_data = ptr_index->data<int64_t>();
94+
int64_t index_size = ptr_index->numel();
95+
96+
if (index_size == 0) {
97+
return;
98+
}
99+
100+
// Three-segment decomposition (same as forward).
101+
int64_t outer_size = 1;
102+
int64_t inner_size = 1;
103+
int64_t dim_size = out_grad_dims[dim];
104+
105+
for (int i = 0; i < dim; ++i) {
106+
outer_size *= out_grad_dims[i];
107+
}
108+
for (int i = dim + 1; i < rank; ++i) {
109+
inner_size *= out_grad_dims[i];
110+
}
111+
112+
// Step 2: zero out the positions that were filled in forward pass.
113+
int64_t numel = outer_size * index_size * inner_size;
114+
115+
index_fill_grad_kernel<T>(numel,
116+
index_data,
117+
index_size,
118+
dim_size,
119+
outer_size,
120+
inner_size,
121+
x_grad_data);
122+
}
123+
124+
// Top-level CPU backward kernel entry.
125+
template <typename T, typename Context>
126+
void IndexFillGradKernel(const Context& dev_ctx,
127+
const DenseTensor& index,
128+
const DenseTensor& out_grad,
129+
int dim,
130+
DenseTensor* x_grad) {
131+
if (out_grad.numel() == 0) {
132+
dev_ctx.template Alloc<T>(x_grad);
133+
return;
134+
}
135+
136+
dev_ctx.template Alloc<T>(x_grad);
137+
138+
auto out_grad_dims = out_grad.dims();
139+
const int rank = out_grad_dims.size();
140+
141+
if (dim < 0) {
142+
dim += rank;
143+
}
144+
145+
if (index.numel() == 0) {
146+
Copy(dev_ctx, out_grad, dev_ctx.GetPlace(), false, x_grad);
147+
return;
148+
}
149+
150+
LaunchIndexFillGradKernel<T, Context>(dev_ctx, index, out_grad, dim, x_grad);
151+
}
152+
153+
} // namespace phi
154+
155+
PD_REGISTER_KERNEL(index_fill_grad,
156+
CPU,
157+
ALL_LAYOUT,
158+
phi::IndexFillGradKernel,
159+
float,
160+
double,
161+
int,
162+
int64_t,
163+
bool,
164+
int16_t,
165+
uint8_t,
166+
int8_t,
167+
phi::float16,
168+
phi::bfloat16,
169+
phi::complex64,
170+
phi::complex128) {}

0 commit comments

Comments
 (0)