#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (c) 2026 Huawei Technologies Co., Ltd.
# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
# CANN Open Software License Agreement Version 2.0 (the "License").
# Please refer to the License for details. You may not use this file except in compliance with the License.
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
# See LICENSE in the root of the software repository for the full text of the License.
# ----------------------------------------------------------------------------
# Generated By CANNBot
import numpy as np
__golden__ = {
"kernel": {"ball_query": "ball_query_golden"},
}
def ball_query_golden(xyz, center_xyz, *, min_radius, max_radius, sample_num, **kwargs):
"""
Golden function for ball_query.
All the parameters (names and order) follow SE doc prototype definition without outputs.
All the input Tensors are numpy.ndarray.
布局与距离判定:
- xyz 布局 (B, 3, N):坐标在中间维
- center_xyz 布局 (M, B, 3)
- idx 输出 (M, B, sample_num)
- 距离判定:d2 == 0 || (min_radius^2 <= d2 < max_radius^2) 环形区间+重合点
- 首点填充:命中不足时用 first_num 填充剩余位置
Args:
xyz (np.ndarray): (B, 3, N) all point coordinates, float16 or float32.
坐标在中间维。
center_xyz (np.ndarray): (M, B, 3) query center coordinates, same dtype as xyz.
布局 (M, B, 3)。
min_radius (float): 最小查询半径(环形内边界),>= 0。
max_radius (float): 最大查询半径(球外边界),> min_radius。
sample_num (int): 每个查询球最大采样点数,>= 1。
**kwargs: {input,output}_{dtypes,ori_shapes,formats,ori_formats},
full_soc_version, short_soc_version, testcase_name
Returns:
np.ndarray: (M, B, sample_num) int32, sampled point indices。
"""
# 统一用 float32 计算距离,避免 float16 溢出
xyz_f = xyz.astype(np.float32)
center_xyz_f = center_xyz.astype(np.float32)
M, B, _ = center_xyz_f.shape
_, _, N = xyz_f.shape
# N=0 边界处理:空点集时无候选点,first_num 初始值 0,未命中位置填充 0。
# 短路返回可避免下方 cumsum[:, -1] 对空数组索引越界
# (index -1 is out of bounds for axis 1 with size 0)。
if N == 0:
return np.zeros((M, B, sample_num), dtype=np.int32)
# 输出初始化为 0:未命中时 first_num=0,填充 0
idx = np.zeros((M, B, sample_num), dtype=np.int32)
# 半径平方化
min_radius2 = float(min_radius) * float(min_radius)
max_radius2 = float(max_radius) * float(max_radius)
# 输出位置网格(用于向量化首点填充掩码)
pos_grid = np.arange(sample_num, dtype=np.int32) # (sample_num,)
# 向量化优化:按 m 维度外层循环,B×N 维度用 numpy 向量化计算
# (替代原 M×B×N 三重纯 Python 标量循环,大 shape 性能提升数千倍)
# 计算逻辑与原实现完全一致:距离判定、前 sample_num 个命中点收集、首点填充
for m in range(M):
# 距离计算(向量化):d2[b, k] = ||center_xyz[m,b,:] - xyz[b,:,k]||^2
# center_xyz_f[m]: (B, 3) → 广播为 (B, 3, 1)
# xyz_f: (B, 3, N)
diff = center_xyz_f[m, :, :, None] - xyz_f # (B, 3, N)
d2 = np.sum(diff * diff, axis=1) # (B, N)
# 距离判定:d2 == 0 || (min_radius2 <= d2 < max_radius2)
hit = (d2 == 0.0) | ((d2 >= min_radius2) & (d2 < max_radius2)) # (B, N) bool
# 命中点累积计数(等价于原循环提前终止:取前 sample_num 个命中点)
cumsum = np.cumsum(hit, axis=1) # (B, N) 命中点从1开始编号
cnt = np.minimum(
cumsum[:, -1], sample_num
) # (B,) 每批次命中数(截断到 sample_num)
# first_num:每个批次首个命中点索引
# 无命中时 first_num=0
has_hit = cnt > 0
first_idx = np.argmax(
hit, axis=1
) # (B,) 每行首个 True 索引(全 False 行返回0)
first_num = np.where(has_hit, first_idx, 0).astype(np.int32) # (B,)
# 收集前 sample_num 个命中点的索引(等价于原循环 cnt < sample_num 时收集)
idx_m = np.zeros((B, sample_num), dtype=np.int32)
selected = hit & (cumsum <= sample_num) # (B, N) 前 sample_num 个命中点
b_sel, k_sel = np.nonzero(selected) # 选中的 (b, k) 坐标
pos_sel = cumsum[b_sel, k_sel] - 1 # 对应输出位置(0-based)
idx_m[b_sel, pos_sel] = k_sel.astype(np.int32)
# 首点填充:命中不足时用 first_num 填充剩余位置(pos >= cnt 的位置)
fill_mask = pos_grid[None, :] >= cnt[:, None] # (B, sample_num) 需填充位置
idx_m = np.where(fill_mask, first_num[:, None], idx_m)
idx[m] = idx_m
return idx