Files
paddlepaddle--paddle/python/paddle/incubate/nn/functional/int_bincount.py
T
2026-07-13 12:40:42 +08:00

108 lines
3.5 KiB
Python

# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import paddle
from paddle import _C_ops
from paddle.base.data_feeder import convert_dtype
from paddle.base.framework import (
convert_nptype_to_datatype_or_vartype,
core,
in_dynamic_or_pir_mode,
)
from paddle.base.layer_helper import LayerHelper
def math_int_bincount(x, low, high, dtype):
"""
A mathematically equivalent implementation of int_bincount using scatter and sum
Args:
x (Tensor): A 1D or 2D int64 tensor containing category indices.
low (int): The minimum possible category index (usually 0).
high (int): One past the maximum category index (i.e., number of categories).
dtype (paddle.dtype): Data type of the output tensor (e.g., paddle.int64).
Returns:
Tensor: A 1D tensor of shape [high - low], where each element is
the count of occurrences of that category in `x`.
"""
if x.ndim not in [0, 1, 2]:
raise ValueError(
f"x must be a 0D, 1D or 2D tensor, but got ndim={x.ndim}"
)
if x.dtype not in [paddle.int32, paddle.int64]:
raise ValueError(f"x.dtype must be int32 or int64, but got {x.dtype}")
if dtype not in ['int32', 'int64', paddle.int32, paddle.int64]:
raise ValueError(f"dtype must be 'int32' or 'int64', but got '{dtype}'")
if high < low:
raise ValueError(
f"'high' ({high}) must be greater than or equal to 'low' ({low})"
)
if x.numel().item() == 0:
return paddle.zeros([high - low], dtype=dtype)
if x.ndim == 0:
x = x.reshape([-1]).unsqueeze(0) # Shape: [1, N]
elif x.ndim == 1:
x = x.unsqueeze(0) # Shape: [1, N]
x_min = x.min().item()
x_max = x.max().item()
if x_min < 0:
raise ValueError(
f"Elements of x must be non-negative, but got min={x_min}"
)
max_val = max(x_max + 1, high)
mask = paddle.zeros([x.shape[0], max_val], dtype=x.dtype)
mask = mask.put_along_axis(
x, paddle.to_tensor(1.0, dtype=x.dtype), axis=1, reduce='add'
)
count = paddle.sum(mask, axis=0).cast(dtype)
return count[low:high]
def int_bincount(x, low, high, dtype=None, name=None):
if in_dynamic_or_pir_mode():
if not isinstance(dtype, (core.VarDesc.VarType, core.DataType)):
dtype = convert_nptype_to_datatype_or_vartype(dtype)
if paddle.is_compiled_with_xpu():
return math_int_bincount(x, low, high, dtype)
else:
return _C_ops.int_bincount(x, low, high, dtype)
helper = LayerHelper("int_bincount", **locals())
out_dtype = dtype if dtype is not None else x.dtype
y = helper.create_variable_for_type_inference(dtype=out_dtype)
dtype_attr = convert_dtype(out_dtype)
helper.append_op(
type="int_bincount",
inputs={"x": x},
outputs={"y": y},
attrs={
"low": low,
"high": high,
"dtype": dtype_attr,
},
)
return y