已合并
feat: 新增BatchToSpaceND A5实现 #1943
zhanw_coding创建于 3月27日
feat: 新增BatchToSpaceND A5实现 #1943
已合并
zhanw_coding创建于 3月27日
23 个文件变更+7277-0
@@ -0,0 +1,17 @@
1+# ----------------------------------------------------------------------------
2+# This program is free software, you can redistribute it and/or modify it.
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This file is a part of the CANN Open Software.
5+# Licensed under CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
8+# BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# ----------------------------------------------------------------------------
11+ 
12+add_all_modules_sources(
13+ OPTYPE batch_to_space_nd
14+ ACLNNTYPE aclnn_exclude
15+ COMPUTE_UNIT ascend950
16+ TILING_DIR arch35
17+)
@@ -0,0 +1,104 @@
1+# BatchToSpaceND
2+ 
3+## 产品支持情况
4+ 
5+| 产品 | 是否支持 |
6+| :----------------------------------------------------------- | :------: |
7+| <term>Ascend 950PR/Ascend 950DT</term> | √ |
8+| <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term> | √ |
9+| <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term> | √ |
10+| <term>Atlas 200I/500 A2 推理产品</term> | √ |
11+| <term>Atlas 推理系列产品</term> | √ |
12+| <term>Atlas 训练系列产品</term> | √ |
C
Cchenjiao3月30日

确认所有产品都支持?

likedislike
zhanw_coding
zhanw_coding
3月30日 评论:
13+ 
14+## 功能说明
15+ 
16+- 算子功能:将批次维度的数据重新排列到空间维度,并裁剪空间维度。
17+ 
18+- 功能描述:
19+ 该算子是 SpaceToBatchND 的逆操作。它首先将输入张量的批次维度按照指定的块形状(block_shape)重新排列到空间维度中,然后根据裁剪参数(crops)裁剪空间维度。具体来说,它将批次维度中的数据分散到空间维度中,从而增加空间维度的大小,同时减少批次维度的大小。
20+ 
21+- 计算公式:
22+ 设输入张量 x 为 N 维,形状为 $[x_0, x_1, \ldots, x_{N-1}]$,block_shape 为 M 维 1D 张量 $[b_0, b_1, \ldots, b_{M-1}]$,crops 为 $M \times 2$ 的 2D 张量 $[[c_{00}, c_{01}], [c_{10}, c_{11}], \ldots, [c_{M-1,0}, c_{M-1,1}]]$,满足 $1 \leq M < N \leq 8$。
23+
24+ 输出张量 y 形状为 $[y_0, y_1, \ldots, y_{N-1}]$,计算方式如下:
25+
26+ $$
27+ y_i = \begin{cases}
28+ \frac{x_0}{\prod_{j=0}^{M-1} b_j}, & i = 0 \\
29+ x_i \times b_{i-1} - c_{i-1,0} - c_{i-1,1}, & 1 \leq i \leq M \\
30+ x_i, & M+1 \leq i \leq N-1
31+ \end{cases}
32+ $$
33+
34+ 其中,$x_0$ 必须能够被 $\prod_{j=0}^{M-1} b_j$ 整除。
35+ 
36+## 参数说明
37+ 
38+<table style="undefined;table-layout: fixed; width: 1480px">
39+ <colgroup>
40+ <col style="width: 177px">
41+ <col style="width: 120px">
42+ <col style="width: 273px">
43+ <col style="width: 292px">
44+ <col style="width: 152px">
45+ </colgroup>
46+ <thead>
47+ <tr>
48+ <th>参数名</th>
49+ <th>输入/输出/属性</th>
50+ <th>描述</th>
51+ <th>数据类型</th>
52+ <th>数据格式</th>
53+ </tr>
54+ </thead>
55+ <tbody>
56+ <tr>
57+ <td>x</td>
58+ <td>输入</td>
59+ <td>表示输入张量,支持多种数据类型</td>
60+ <td>INT8、UINT8、INT16、UINT16、INT32、UINT32、INT64、UINT64、BF16、FLOAT16、FLOAT、DOUBLE、BOOL、COMPLEX32、COMPLEX64</td>
61+ <td>ND</td>
62+ </tr>
63+ <tr>
64+ <td>block_shape</td>
65+ <td>输入</td>
66+ <td>表示空间块的形状,1D张量,形状为[M],指定每个空间维度的块大小</td>
67+ <td>INT32、INT64</td>
68+ <td>-</td>
69+ </tr>
70+ <tr>
71+ <td>crops</td>
72+ <td>输入</td>
73+ <td>表示裁剪量,2D张量,形状为[M, 2],指定每个空间维度从顶部和底部(或左侧和右侧)裁剪的元素数量</td>
74+ <td>INT32、INT64</td>
75+ <td>-</td>
76+ </tr>
77+ <tr>
78+ <td>y</td>
79+ <td>输出</td>
80+ <td>表示输出张量,与输入x具有相同的数据类型。输出形状根据block_shape和crops进行计算</td>
81+ <td>与x一致</td>
82+ <td>ND</td>
83+ </tr>
84+ </tbody>
85+</table>
86+ 
87+- <term>Atlas 训练系列产品</term><term>Atlas 推理系列产品</term><term>Atlas 200I/500 A2 推理产品</term><term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term><term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term>:不支持BOOL。
88+ 
89+## 约束说明
90+ 
91+1. 输入张量 x 的维度 N 必须满足 2 ≤ N ≤ 8。
92+2. block_shape 的维度 M 必须满足 1 ≤ M < N。
93+3. block_shape 的长度必须等于 crops 的第一维度长度。
94+4. crops 的形状必须为 [M, 2]。
95+5. 输入张量的第 0 维(batch 维度)必须能够被 block_shape 中所有元素的乘积整除。
96+6. block_shape 中的每个元素必须大于 0。
97+7. crops 中的每个元素必须是非负整数。
98+8. 对于每个空间维度 i(i = 1, 2, ..., M),裁剪后的维度大小必须大于等于 0,即:x.shape[i] × block_shape[i-1] - crops[i-1][0] - crops[i-1][1] ≥ 0。
99+ 
100+## 调用说明
101+ 
102+| 调用方式 | 样例代码 | 说明 |
103+| :-------- | :----------------------------------------------------------- | :----------------------------------------------------------- |
104+| 图模式调用 | [test_geir_batch_to_space_nd](./examples/test_geir_batch_to_space_nd.cpp) | 通过[算子IR](./op_graph/batch_to_space_nd_proto.h)构图方式调用BatchToSpaceND算子。 |
@@ -0,0 +1,309 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include <iostream>
12+#include <fstream>
13+#include <string.h>
14+#include <stdint.h>
15+#include <vector>
16+#include <string>
17+#include <map>
18+#include "assert.h"
19+ 
20+#include "graph.h"
21+#include "types.h"
22+#include "tensor.h"
23+#include "ge_error_codes.h"
24+#include "ge_api_types.h"
25+#include "ge_api.h"
26+#include "array_ops.h"
27+#include "ge_ir_build.h"
28+ 
29+#include "experiment_ops.h"
30+#include "nn_other.h"
31+#include "../op_graph/batch_to_space_nd_proto.h"
32+ 
33+#define FAILED -1
34+#define SUCCESS 0
35+ 
36+using namespace ge;
37+using std::map;
38+using std::string;
39+using std::vector;
40+ 
41+#define ADD_SEQUENCE_INPUT(intputIndex, intputName, intputDtype, inputShape) \
42+ vector<int64_t> placeholder##intputIndex##_shape = inputShape; \
43+ auto placeholder##intputIndex = op::Data("placeholder" + intputIndex).set_attr_index(0); \
44+ TensorDesc placeholder##intputIndex##_desc = \
45+ TensorDesc(ge::Shape(placeholder##intputIndex##_shape), FORMAT_ND, intputDtype); \
46+ placeholder##intputIndex##_desc.SetPlacement(ge::kPlacementHost); \
47+ placeholder##intputIndex##_desc.SetFormat(FORMAT_ND); \
48+ Tensor tensor_placeholder##intputIndex; \
49+ ret = GenSequenceData( \
50+ placeholder##intputIndex##_shape, tensor_placeholder##intputIndex, placeholder##intputIndex##_desc, \
51+ intputDtype); \
52+ if (ret != SUCCESS) { \
53+ printf("%s - ERROR - [XIR]: Generate input data failed\n", GetTime().c_str()); \
54+ return FAILED; \
55+ } \
56+ placeholder##intputIndex.update_input_desc_x(placeholder##intputIndex##_desc); \
57+ input.push_back(tensor_placeholder##intputIndex); \
58+ graph.AddOp(placeholder##intputIndex); \
59+ node.set_input_##intputName(placeholder##intputIndex); \
60+ inputs.push_back(placeholder##intputIndex);
61+ 
62+#define ADD_INT_INPUT(intputIndex, intputName, intputDtype, inputShape, value) \
63+ vector<int64_t> placeholder##intputIndex##_shape = inputShape; \
64+ auto placeholder##intputIndex = op::Data("placeholder" + intputIndex).set_attr_index(0); \
65+ TensorDesc placeholder##intputIndex##_desc = \
66+ TensorDesc(ge::Shape(placeholder##intputIndex##_shape), FORMAT_ND, intputDtype); \
67+ placeholder##intputIndex##_desc.SetPlacement(ge::kPlacementHost); \
68+ placeholder##intputIndex##_desc.SetFormat(FORMAT_ND); \
69+ Tensor tensor_placeholder##intputIndex; \
70+ ret = GenOnesData( \
71+ placeholder##intputIndex##_shape, tensor_placeholder##intputIndex, placeholder##intputIndex##_desc, \
72+ intputDtype, value); \
73+ if (ret != SUCCESS) { \
74+ printf("%s - ERROR - [XIR]: Generate input data failed\n", GetTime().c_str()); \
75+ return FAILED; \
76+ } \
77+ placeholder##intputIndex.update_input_desc_x(placeholder##intputIndex##_desc); \
78+ input.push_back(tensor_placeholder##intputIndex); \
79+ graph.AddOp(placeholder##intputIndex); \
80+ node.set_input_##intputName(placeholder##intputIndex); \
81+ inputs.push_back(placeholder##intputIndex);
82+ 
83+#define ADD_OUTPUT(outputIndex, outputName, outputDtype, outputShape) \
84+ TensorDesc outputName##outputIndex##_desc = TensorDesc(ge::Shape(outputShape), FORMAT_ND, outputDtype); \
85+ node.update_output_desc_##outputName(outputName##outputIndex##_desc);
86+ 
87+#define LOG_PRINT(message, ...) \
88+ do { \
89+ printf(message, ##__VA_ARGS__); \
90+ } while (0)
91+ 
92+string GetTime()
93+{
94+ time_t timep;
95+ time(&timep);
96+ char tmp[64];
97+ strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S,000", localtime(&timep));
98+ return tmp;
99+}
100+ 
101+uint32_t GetDataTypeSize(DataType dt)
102+{
103+ uint32_t dilation = 1;
104+ uint32_t oneByte = 1;
105+ uint32_t twoByte = 2;
106+ uint32_t fourByte = 4;
107+ uint32_t eightByte = 8;
108+ 
109+ if (dt == ge::DT_FLOAT) {
110+ dilation = fourByte;
111+ } else if (dt == ge::DT_FLOAT16) {
112+ dilation = twoByte;
113+ } else if (dt == ge::DT_BF16) {
114+ dilation = twoByte;
115+ } else if (dt == ge::DT_INT16) {
116+ dilation = twoByte;
117+ } else if (dt == ge::DT_UINT16) {
118+ dilation = twoByte;
119+ } else if (dt == ge::DT_INT32) {
120+ dilation = fourByte;
121+ } else if (dt == ge::DT_UINT32) {
122+ dilation = fourByte;
123+ } else if (dt == ge::DT_INT64) {
124+ dilation = eightByte;
125+ } else if (dt == ge::DT_UINT64) {
126+ dilation = eightByte;
127+ } else if (dt == ge::DT_INT8) {
128+ dilation = oneByte;
129+ }
130+ return dilation;
131+}
132+ 
133+int32_t GenSequenceData(vector<int64_t> shapes, Tensor& input_tensor, TensorDesc& input_tensor_desc, DataType data_type)
134+{
135+ input_tensor_desc.SetRealDimCnt(shapes.size());
136+ size_t size = 1;
137+ for (uint32_t i = 0; i < shapes.size(); i++) {
138+ size *= shapes[i];
139+ }
140+ uint32_t data_len = size * GetDataTypeSize(data_type);
141+ int32_t* pData = new (std::nothrow) int32_t[data_len];
142+ int value = 0;
143+ for (uint32_t i = 0; i < size; ++i) {
144+ *(pData + i) = value++;
145+ }
146+ input_tensor = Tensor(input_tensor_desc, reinterpret_cast<uint8_t*>(pData), data_len);
147+ return SUCCESS;
148+}
149+ 
150+int32_t GenOnesData(
151+ vector<int64_t> shapes, Tensor& input_tensor, TensorDesc& input_tensor_desc, DataType data_type,
152+ const vector<int32_t>& value)
153+{
154+ input_tensor_desc.SetRealDimCnt(shapes.size());
155+ size_t size = 1;
156+ for (uint32_t i = 0; i < shapes.size(); i++) {
157+ size *= shapes[i];
158+ }
159+ uint32_t data_len = size * GetDataTypeSize(data_type);
160+ int32_t* pData = new (std::nothrow) int32_t[data_len];
161+ for (uint32_t i = 0; i < size; ++i) {
162+ *(pData + i) = value[i];
163+ }
164+ input_tensor = Tensor(input_tensor_desc, reinterpret_cast<uint8_t*>(pData), data_len);
165+ return SUCCESS;
166+}
167+ 
168+int32_t WriteDataToFile(string bin_file, uint64_t data_size, uint8_t* inputData)
169+{
170+ FILE* fp = fopen(bin_file.c_str(), "w");
171+ fwrite(inputData, sizeof(uint8_t), data_size, fp);
172+ fclose(fp);
173+ return SUCCESS;
174+}
175+ 
176+int CreateOppInGraph(
177+ DataType inDtype, std::vector<ge::Tensor>& input, std::vector<Operator>& inputs, std::vector<Operator>& outputs,
178+ Graph& graph)
179+{
180+ Status ret = SUCCESS;
181+ // 自定义代码:添加单算子定义到图中
182+ auto node = op::BatchToSpaceND("node");
183+ std::vector<int64_t> xShape = {4, 2, 3};
184+ std::vector<int64_t> yShape = {2, 3, 3};
185+ std::vector<int64_t> blockShapeShape = {1};
186+ std::vector<int32_t> blockShapeValue = {2};
187+ std::vector<int64_t> cropsShape = {1, 2};
188+ std::vector<int32_t> cropsValue = {1, 0};
189+ 
190+ ADD_SEQUENCE_INPUT(1, x, inDtype, xShape);
191+ 
192+ ADD_INT_INPUT(2, block_shape, DT_INT32, blockShapeShape, blockShapeValue);
193+ ADD_INT_INPUT(3, crops, DT_INT32, cropsShape, cropsValue);
194+ 
195+ ADD_OUTPUT(1, y, inDtype, yShape);
196+ outputs.push_back(node);
197+ // 添加完毕
198+ return SUCCESS;
199+}
200+ 
201+int main(int argc, char* argv[])
202+{
203+ const char* graph_name = "tc_ge_irrun_test";
204+ Graph graph(graph_name);
205+ std::vector<ge::Tensor> input;
206+ 
207+ printf("%s - INFO - [XIR]: Start to initialize ge using ge global options\n", GetTime().c_str());
208+ std::map<AscendString, AscendString> global_options = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}};
209+ Status ret = ge::GEInitialize(global_options);
210+ if (ret != SUCCESS) {
211+ printf("%s - INFO - [XIR]: Initialize ge using ge global options failed\n", GetTime().c_str());
212+ return FAILED;
213+ }
214+ printf("%s - INFO - [XIR]: Initialize ge using ge global options success\n", GetTime().c_str());
215+ 
216+ std::vector<Operator> inputs{};
217+ std::vector<Operator> outputs{};
218+ 
219+ std::cout << argv[1] << std::endl;
220+ char* endptr;
221+ 
222+ DataType inDtype = DT_INT32;
223+ std::cout << inDtype << std::endl;
224+ 
225+ ret = CreateOppInGraph(inDtype, input, inputs, outputs, graph);
226+ if (ret != SUCCESS) {
227+ printf("%s - ERROR - [XIR]: Create ir session using build options failed\n", GetTime().c_str());
228+ return FAILED;
229+ }
230+ 
231+ if (!inputs.empty() && !outputs.empty()) {
232+ graph.SetInputs(inputs).SetOutputs(outputs);
233+ }
234+ 
235+ std::map<AscendString, AscendString> build_options = {
236+ 
237+ };
238+ printf("%s - INFO - [XIR]: Start to create ir session using build options\n", GetTime().c_str());
239+ ge::Session* session = new Session(build_options);
240+ 
241+ if (session == nullptr) {
242+ printf("%s - ERROR - [XIR]: Create ir session using build options failed\n", GetTime().c_str());
243+ return FAILED;
244+ }
245+ printf("%s - INFO - [XIR]: Create ir session using build options success\n", GetTime().c_str());
246+ printf("%s - INFO - [XIR]: Start to add compute graph to ir session\n", GetTime().c_str());
247+ 
248+ std::map<AscendString, AscendString> graph_options = {
249+ 
250+ };
251+ uint32_t graph_id = 0;
252+ ret = session->AddGraph(graph_id, graph, graph_options);
253+ 
254+ printf("%s - INFO - [XIR]: Session add ir compute graph to ir session success\n", GetTime().c_str());
255+ printf("%s - INFO - [XIR]: dump graph to txt\n", GetTime().c_str());
256+ std::string file_path = "./dump";
257+ aclgrphDumpGraph(graph, file_path.c_str(), file_path.length());
258+ printf("%s - INFO - [XIR]: Start to run ir compute graph\n", GetTime().c_str());
259+ std::vector<ge::Tensor> output;
260+ ret = session->RunGraph(graph_id, input, output);
261+ if (ret != SUCCESS) {
262+ printf("%s - INFO - [XIR]: Run graph failed\n", GetTime().c_str());
263+ delete session;
264+ GEFinalize();
265+ return FAILED;
266+ }
267+ printf("%s - INFO - [XIR]: Session run ir compute graph success\n", GetTime().c_str());
268+ 
269+ int input_num = input.size();
270+ for (int i = 0; i < input_num; i++) {
271+ std::cout << "input " << i << " dtype : " << input[i].GetTensorDesc().GetDataType() << std::endl;
272+ string input_file = "./tc_ge_irrun_test_0008_npu_input_" + std::to_string(i) + ".bin";
273+ uint8_t* input_data_i = input[i].GetData();
274+ int64_t input_shape = input[i].GetTensorDesc().GetShape().GetShapeSize();
275+ std::cout << "this is " << i << "th input, input shape size =" << input_shape << std::endl;
276+ uint32_t data_size = input_shape * GetDataTypeSize(input[i].GetTensorDesc().GetDataType());
277+ WriteDataToFile((const char*)input_file.c_str(), data_size, input_data_i);
278+ }
279+ 
280+ int output_num = output.size();
281+ for (int i = 0; i < output_num; i++) {
282+ std::cout << "output " << i << " dtype : " << output[i].GetTensorDesc().GetDataType() << std::endl;
283+ string output_file = "./tc_ge_irrun_test_0008_npu_output_" + std::to_string(i) + ".bin";
284+ uint8_t* output_data_i = output[i].GetData();
285+ int64_t output_shape = output[i].GetTensorDesc().GetShape().GetShapeSize();
286+ std::cout << "this is " << i << "th output, output shape size =" << output_shape << std::endl;
287+ uint32_t data_size = output_shape * GetDataTypeSize(output[i].GetTensorDesc().GetDataType());
288+ WriteDataToFile((const char*)output_file.c_str(), data_size, output_data_i);
289+ int32_t* resultData = (int32_t*)output_data_i;
290+ for (int64_t j = 0; j < output_shape; j++) {
291+ LOG_PRINT("result[%ld] is: %u\n", j, resultData[j]);
292+ }
293+ }
294+ 
295+ ge::AscendString error_msg = ge::GEGetErrorMsgV2();
296+ std::string error_str(error_msg.GetString());
297+ std::cout << "Error message: " << error_str << std::endl;
298+ ge::AscendString warning_msg = ge::GEGetWarningMsgV2();
299+ std::string warning_str(warning_msg.GetString());
300+ std::cout << "Warning message: " << warning_str << std::endl;
301+ printf("%s - INFO - [XIR]: Start to finalize ir graph session\n", GetTime().c_str());
302+ ret = ge::GEFinalize();
303+ if (ret != SUCCESS) {
304+ printf("%s - INFO - [XIR]: Finalize ir graph session failed\n", GetTime().c_str());
305+ return FAILED;
306+ }
307+ printf("%s - INFO - [XIR]: Finalize ir graph session success\n", GetTime().c_str());
308+ return SUCCESS;
309+}
@@ -0,0 +1,50 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#ifndef OP_PROTO_BATCH_TO_SPACE_N_D_PROTO_H_
12+#define OP_PROTO_BATCH_TO_SPACE_N_D_PROTO_H_
13+ 
14+#include "graph/operator_reg.h"
15+ 
16+namespace ge {
17+/**
18+* @brief Permutes data from batch into blocks of spatial data and then prunes them.
19+* The values from the batch dimension are moved in spatial blocks to the height and width dimensions.
20+* And then prunes the height and width dimensions.
21+ 
22+* @par Inputs:
23+* @li x: A ND tensor, must be one of the following types:
24+* float16, float32, double, int64, int32, uint8, uint16, uint32, uint64, int8,
25+* int16, complex64, complex128, qint8, quint8, qint16, quint16, qint32, bfloat16.
26+* @li block_shape: A 1D tensor with shape [M], support int32 or int64.
27+* @li crops: A 2D tensor with shape [M, 2], support int32 or int64. \n
28+ 
29+* @par Outputs:
30+* y: A ND tensor, the same type as "x". \n
31+ 
32+* @attention Constraints:
33+* If N is 4 and M is 2: \n
34+* The size of the first dimension of input "x" must be divisible by the product of all elements in block_shape. \n
35+* "y" is a 4D shape [batch, height, width, depth], batch = x.shape[0] / (block_shape[0] * block_shape[1]),
36+* depth = x.shape[3], height = height_pad - crop_top - crop_bottom, width = width_pad - crop_left - crop_right
37+* where height_pad = x.shape[1] * block_shape[0], width_pad = x.shape[2] * block_shape[1],
38+* crop_top = crops[0][0], crop_bottom = crops[0][1], crop_left = crops[1][0], crop_right = crops[1][1]
39+*@par Third-party framework compatibility
40+* Compatible with the TensorFlow operator BatchToSpaceND.
41+*/
42+REG_OP(BatchToSpaceND)
43+ .INPUT(x, TensorType({BasicType(), DT_BOOL}))
44+ .INPUT(block_shape, TensorType::IndexNumberType())
45+ .INPUT(crops, TensorType::IndexNumberType())
46+ .OUTPUT(y, TensorType::BasicType())
47+ .OP_END_FACTORY_REG(BatchToSpaceND)
48+} // namespace ge
49+ 
50+#endif
@@ -0,0 +1,784 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include <vector>
12+#include <sstream>
13+#include "register/op_impl_registry.h"
14+#include "conversion/batch_to_space_nd/op_kernel/arch35/batch_to_space_nd_tiling_data.h"
15+#include "conversion/batch_to_space_nd/op_kernel/arch35/batch_to_space_nd_tiling_key.h"
16+#include "batch_to_space_nd_tiling_dual_side_tiling.h"
17+#include "platform/platform_ascendc.h"
18+#include "op_host/util/platform_util.h"
19+#include "op_host/util/math_util.h"
20+#include "op_host/util/const_util.h"
21+ 
22+namespace optiling {
23+// 属性、索引
24+static constexpr size_t INPUT_IDX_X = 0;
25+static constexpr size_t INPUT_IDX_BLOCK_SHAPE = 1;
26+static constexpr size_t INPUT_IDX_CROPS = 2;
27+static constexpr size_t MIN_BLOCK_SHAPE_DIM = 1;
28+static constexpr size_t MIN_X_RANK = MIN_BLOCK_SHAPE_DIM + 1;
29+static constexpr size_t BLOCK_SHAPE_RANK = 1;
30+static constexpr size_t CROPS_RANK = 2;
31+static constexpr size_t CROPS_DIM_NUM_1 = 2;
32+ 
33+// 公共常量
34+ 
35+// 大尾轴模板 常量
36+// BUFFER分割数量
37+static constexpr uint32_t LARGE_C_BUFFER_NUM = 2;
38+static constexpr uint32_t LARGE_C_MAX_BUFFER_SIZE = 64 * 1024U;
39+// 能切分的最外层的轴,正负数均可
40+static constexpr int32_t LARGE_C_OUTMOST_CUT_AXIS = -3;
41+ 
42+// 小尾轴模板 常量
43+// BUFFER分割数量
44+static constexpr uint32_t SMALL_C_BUFFER_NUM = 2;
45+// 输入输出各分一半
46+static constexpr uint32_t SMALL_C_BUFFER_FACTOR = 2;
47+// 最大UB大小
48+static constexpr uint32_t SMALL_C_MAX_BUFFER_SIZE = 64 * 1024U;
49+ 
50+// SIMT 常量
51+static constexpr size_t MIN_RANK_FOR_SIMT = 6;
52+static constexpr int64_t MAX_UINT32_NUM = std::numeric_limits<uint32_t>::max();
53+static constexpr uint32_t SIMT_DCACHE_SIZE = 32 * 1024U;
54+static constexpr uint32_t SIMT_MAX_UB_SIZE = 64 * 1024U;
55+static constexpr uint32_t SIMT_BUFFER_NUM = 2;
56+static constexpr uint32_t SIMT_THREAD_FACTOR = 1;
57+ 
58+class BatchToSpaceNDTiling {
59+private:
60+ /* data */
61+ // soc info
62+ uint32_t ubSize_{0};
63+ uint32_t ubBlockSize_{0};
64+ uint32_t coreNum_{0};
65+ uint32_t cacheLineSize_{0};
66+ int32_t ubBlockElements_{0};
67+ int32_t cacheLineElements_{0};
68+ uint32_t vRegSize_{0};
69+ uint32_t simtMaxThreads_{0};
70+ 
71+ // tiling key param
72+ uint8_t mode_;
73+ uint8_t blockShapeDimNum_{0};
74+ bool isBigShape_{false};
75+ 
76+ // 输入参数
77+ int32_t dSize_{0};
78+ int64_t xShapeSize_{0};
79+ int64_t yShapeSize_{0};
80+ size_t originBlockShapeDim_{0};
81+ B2SNDInput originInput_;
82+ B2SNDInput mergedInput_;
83+ 
84+ // 中间计算结果
85+ // 实际核数
86+ uint32_t realCoreNum_{0};
87+ 
88+ // tiling context
89+ gert::TilingContext* context_;
90+ 
91+public:
92+ explicit BatchToSpaceNDTiling(gert::TilingContext* context) : context_(context) {};
93+ ~BatchToSpaceNDTiling() {};
94+ 
95+ ge::graphStatus DoTiling();
96+ 
97+private:
98+ // 参数检查,数据获取
99+ ge::graphStatus ParamCheck();
100+ ge::graphStatus GetSocInfo();
101+ ge::graphStatus CheckX();
102+ ge::graphStatus CheckBlockShape();
103+ ge::graphStatus CheckCrops();
104+ ge::graphStatus CheckY();
105+ ge::graphStatus MergeInput();
106+ 
107+ // tiling 计算
108+ ge::graphStatus DoOpTiling();
109+ ge::graphStatus Tiling4LargeC();
110+ [[maybe_unused]] ge::graphStatus Tiling4SmallC();
111+ ge::graphStatus Tiling4SIMT();
112+ 
113+ // 辅助函数
114+ // LargeC
115+ ge::graphStatus moveAlignTilingBlock(
116+ uint32_t maxUBElements, const std::vector<uint64_t>& ubFactorAlign, const std::vector<uint64_t>& leftAlign,
117+ const std::vector<uint64_t>& dimValue, int32_t minCutAxis, B2SNDLargeCTilingData* tilingData);
118+ // SmallC
119+ void SmallCSetInput(B2SNDSmallCTilingData* tilingData, size_t yAxisPerm[]);
120+ void SmallCDoubleTiling(uint32_t inputElements, size_t yAxisPerm[], B2SNDSmallCTilingData* tilingData);
121+ // SIMT
122+ 
123+ // 公共方法
124+ template <typename T>
125+ inline T AlignBlockElement(T elementCount);
126+ 
127+ // 打印
128+ void ShowBaseTilingData();
129+ void ShowLargeCTilingData();
130+ void ShowSmallCTilingData();
131+ void ShowSIMTTilingData();
132+};
133+ 
134+ge::graphStatus BatchToSpaceNDTiling::DoTiling()
135+{
136+ // 校验属性
137+ auto ret = ParamCheck();
138+ OP_CHECK_IF(ret == ge::GRAPH_FAILED, OP_LOGE(context_, "DoTiling failed"), return ge::GRAPH_FAILED);
139+ 
140+ // soc信息获取
141+ ret = GetSocInfo();
142+ OP_CHECK_IF(ret == ge::GRAPH_FAILED, OP_LOGE(context_, "DoTiling failed"), return ge::GRAPH_FAILED);
143+ 
144+ ret = DoOpTiling();
145+ OP_CHECK_IF(ret == ge::GRAPH_FAILED, OP_LOGE(context_, "DoTiling failed"), return ge::GRAPH_FAILED);
146+ 
147+ const uint64_t tilingKey = GET_TPL_TILING_KEY(mode_, blockShapeDimNum_, isBigShape_);
148+ OP_LOGI(
149+ context_, "tilingKey is %lu, mode %u, blockShapeDimNum %u, isBigShape %d", tilingKey, mode_, blockShapeDimNum_,
150+ isBigShape_);
151+ context_->SetTilingKey(tilingKey);
152+ context_->SetBlockDim(realCoreNum_);
153+ size_t* workSpaceSize = context_->GetWorkspaceSizes(1);
154+ OP_CHECK_NULL_WITH_CONTEXT(context_, workSpaceSize);
155+ workSpaceSize[0] = 0;
156+ return ge::GRAPH_SUCCESS;
157+}
158+ 
159+template <typename T>
160+inline T BatchToSpaceNDTiling::AlignBlockElement(T elementCount)
161+{
162+ return Ops::Base::CeilAlign(elementCount, static_cast<T>(ubBlockElements_));
163+}
164+ 
165+template <typename T>
166+static std::string ArrayToString(const T* v, size_t size)
167+{
168+ std::ostringstream oss;
169+ oss << "[";
170+ if (size > 0) {
171+ for (size_t i = 0; i < size - 1; ++i) {
172+ oss << v[i] << ", ";
173+ }
174+ oss << v[size - 1];
175+ }
176+ oss << "]";
177+ return oss.str();
178+}
179+ 
180+void BatchToSpaceNDTiling::ShowBaseTilingData()
181+{
182+ // 输入信息
183+ OP_LOGI(
184+ context_, "input: x_shape %s, block_shape %s, crops %s, y_shape %s, data type size %d",
185+ ArrayToString(mergedInput_.inShape, mergedInput_.rank).c_str(),
186+ ArrayToString(mergedInput_.blockShape, mergedInput_.rank - 2).c_str(),
187+ ArrayToString(*mergedInput_.crops, (mergedInput_.rank - 2) * 2).c_str(),
188+ ArrayToString(mergedInput_.outShape, mergedInput_.rank).c_str(), dSize_);
189+ // soc 信息
190+ OP_LOGI(
191+ context_, "soc info: ubSize %lu, coreNum %u, cacheLineSize %lu, ubBlockSize %lu, simtMaxThreads %lu", ubSize_,
192+ coreNum_, cacheLineSize_, ubBlockSize_, simtMaxThreads_);
193+ // 中间计算结果
194+ OP_LOGI(context_, "middle data: realCoreNum %lu", realCoreNum_);
195+}
196+ 
197+void BatchToSpaceNDTiling::ShowLargeCTilingData()
198+{
199+ ShowBaseTilingData();
200+ auto tilingData = context_->GetTilingData<B2SNDLargeCTilingData>();
201+ OP_LOGI(
202+ context_, "tiling data: ubAxis %lu, ubFactor %lu, outputBufferSize %lu", tilingData->ubAxis,
203+ tilingData->ubFactor, tilingData->outputBufferSize);
204+ OP_LOGI(context_, "\t: totalCount %u, perCoreCount %u", tilingData->totalCount, tilingData->perCoreCount);
205+}
206+ 
207+void BatchToSpaceNDTiling::ShowSmallCTilingData()
208+{
209+ ShowBaseTilingData();
210+ auto tilingData = context_->GetTilingData<B2SNDSmallCTilingData>();
211+ OP_LOGI(
212+ context_, "tiling data: oriInShape %s, croppedInShape %s, crops %s",
213+ ArrayToString(tilingData->oriInShape, mergedInput_.rank + blockShapeDimNum_).c_str(),
214+ ArrayToString(tilingData->croppedInShape, mergedInput_.rank + blockShapeDimNum_).c_str(),
215+ ArrayToString(*tilingData->crops, blockShapeDimNum_ * 2).c_str());
216+ OP_LOGI(
217+ context_, "\t: coreNum %u, inUbAxis %u, outUbAxis %u, inUbFactor %u, outUbFactor %u", tilingData->coreNum,
218+ tilingData->inUbAxis, tilingData->outUbAxis, tilingData->inUbFactor, tilingData->outUbFactor);
219+ OP_LOGI(
220+ context_, "\t: ubTotalCount %u, ubPerCount %u, ubTileSize %u", tilingData->ubTotalCount, tilingData->ubPerCount,
221+ tilingData->ubTileSize);
222+}
223+ 
224+void BatchToSpaceNDTiling::ShowSIMTTilingData()
225+{
226+ ShowBaseTilingData();
227+ auto tilingData = context_->GetTilingData<B2SNDSimtTilingData>();
228+ OP_LOGI(
229+ context_, "tiling data: totalBlock %lu, mainCoreBlock %lu", tilingData->totalBlock, tilingData->mainCoreBlock);
230+ OP_LOGI(
231+ context_, "\t: needCoreNum %u, mainCoreNum %u, blockSize %u, tailBlockSize %u", tilingData->needCoreNum,
232+ tilingData->mainCoreNum, tilingData->blockSize, tilingData->tailBlockSize);
233+}
234+ 
235+ge::graphStatus BatchToSpaceNDTiling::CheckX()
236+{
237+ // 获取x
238+ auto inputValueDesc = context_->GetInputDesc(INPUT_IDX_X);
239+ OP_CHECK_NULL_WITH_CONTEXT(context_, inputValueDesc);
240+ auto inputDataType = inputValueDesc->GetDataType();
241+ dSize_ = ge::GetSizeByDataType(inputDataType);
242+ OP_CHECK_IF(dSize_ <= 0, OP_LOGE(context_, "data size should be positive"), return ge::GRAPH_FAILED);
243+ 
244+ // 校验输入shape
245+ auto xInputShape = context_->GetInputShape(INPUT_IDX_X);
246+ OP_CHECK_NULL_WITH_CONTEXT(context_, xInputShape);
247+ auto xShape = xInputShape->GetStorageShape();
248+ originInput_.rank = xShape.GetDimNum();
249+ OP_CHECK_IF(
250+ originInput_.rank < MIN_X_RANK, OP_LOGE(context_, "the rank of x should be more than %lu", MIN_X_RANK),
251+ return ge::GRAPH_FAILED);
252+ 
253+ // 校验溢出
254+ xShapeSize_ = xShape.GetShapeSize();
255+ OP_CHECK_IF(xShapeSize_ <= 0, OP_LOGE(context_, "the shape size of x overflows"), return ge::GRAPH_FAILED);
256+ 
257+ // 获取 shape
258+ for (size_t i = 0; i < originInput_.rank; ++i) {
259+ originInput_.inShape[i] = xShape.GetDim(i);
260+ }
261+ return ge::GRAPH_SUCCESS;
262+}
263+ 
264+ge::graphStatus BatchToSpaceNDTiling::CheckBlockShape()
265+{
266+ // 获取block shape
267+ auto bsInputShape = context_->GetInputShape(INPUT_IDX_BLOCK_SHAPE);
268+ OP_CHECK_NULL_WITH_CONTEXT(context_, bsInputShape);
269+ 
270+ // 校验shape
271+ auto bsShape = bsInputShape->GetStorageShape();
272+ size_t bsRank = bsShape.GetDimNum();
273+ OP_CHECK_IF(
274+ bsRank != BLOCK_SHAPE_RANK,
275+ OP_LOGE(context_, "the rank of block_shape should be %lu, but got %lu", BLOCK_SHAPE_RANK, bsRank),
276+ return ge::GRAPH_FAILED);
277+ 
278+ // 获取 block_shape 值
279+ gert::Shape blockShape;
280+ OP_CHECK_IF(
281+ !Ops::Base::GetConstIntToShape(context_, INPUT_IDX_BLOCK_SHAPE, blockShape),
282+ OP_LOGE(context_, "get block_shape tensor failed"), return ge::GRAPH_FAILED);
283+ 
284+ // 校验维度
285+ originBlockShapeDim_ = blockShape.GetDimNum();
286+ OP_CHECK_IF(
287+ originBlockShapeDim_ < MIN_BLOCK_SHAPE_DIM,
288+ OP_LOGE(context_, "the dimension of block_shape should be greater than %lu", MIN_BLOCK_SHAPE_DIM),
289+ return ge::GRAPH_FAILED);
290+ OP_CHECK_IF(
291+ originBlockShapeDim_ >= originInput_.rank,
292+ OP_LOGE(
293+ context_, "input rank (%u) should be greater than the dimension of block_shape (%lu)", originInput_.rank,
294+ originBlockShapeDim_),
295+ return ge::GRAPH_FAILED);
296+ 
297+ // block_shape 为正数
298+ for (size_t i = 0; i < originBlockShapeDim_; ++i) {
299+ OP_CHECK_IF(
300+ blockShape[i] <= 0, OP_LOGE(context_, "the value of block_shape must be positive"),
301+ return ge::GRAPH_FAILED);
302+ originInput_.blockShape[i] = static_cast<uint64_t>(blockShape[i]);
303+ }
304+ int64_t block_size = blockShape.GetShapeSize();
305+ OP_CHECK_IF(block_size <= 0, OP_LOGE(context_, "the product of block_shape overflows"), return ge::GRAPH_FAILED);
306+ 
307+ // block_shape 能被batch整除
308+ int64_t batch = originInput_.inShape[0];
309+ OP_CHECK_IF(
310+ ((batch % block_size) != 0),
311+ OP_LOGE(
312+ context_, "input batch dimension (%ld) not divisible by product of block size (%ld)", batch, block_size),
313+ return ge::GRAPH_FAILED);
314+ originInput_.outShape[0] = Ops::Base::FloorDiv(batch, block_size);
315+ yShapeSize_ = originInput_.outShape[0];
316+ return ge::GRAPH_SUCCESS;
317+}
318+ 
319+ge::graphStatus BatchToSpaceNDTiling::CheckCrops()
320+{
321+ // 获取 crops
322+ auto cropsInputShape = context_->GetInputShape(INPUT_IDX_CROPS);
323+ OP_CHECK_NULL_WITH_CONTEXT(context_, cropsInputShape);
324+ auto cropsShape = cropsInputShape->GetStorageShape();
325+ size_t cropsRank = cropsShape.GetDimNum();
326+ OP_CHECK_IF(
327+ cropsRank != CROPS_RANK,
328+ OP_LOGE(
329+ context_, "the shape of crops should be (%lu, %lu), but got %s", originBlockShapeDim_, CROPS_DIM_NUM_1,
330+ Ops::Base::ToString(cropsShape).c_str()),
331+ return ge::GRAPH_FAILED);
332+ 
333+ // crops 和 block_shape shape 相等
334+ OP_CHECK_IF(
335+ (cropsShape.GetDim(0) != static_cast<int64_t>(originBlockShapeDim_) ||
336+ cropsShape.GetDim(1) != static_cast<int64_t>(CROPS_DIM_NUM_1)),
337+ OP_LOGE(
338+ context_, "the shape of crops should be (%lu, %lu), but got %s", originBlockShapeDim_, CROPS_DIM_NUM_1,
339+ Ops::Base::ToString(cropsShape).c_str()),
340+ return ge::GRAPH_FAILED);
341+ 
342+ // 获取 crops 值
343+ gert::Shape crops;
344+ OP_CHECK_IF(
345+ !Ops::Base::GetConstIntToShape(context_, INPUT_IDX_CROPS, crops), OP_LOGE(context_, "get crops tensor failed"),
346+ return ge::GRAPH_FAILED);
347+ 
348+ // crops >= 0
349+ auto dims = crops.GetDimNum();
350+ for (size_t i = 0; i < dims; ++i) {
351+ OP_CHECK_IF(
352+ crops[i] < 0, OP_LOGE(context_, "the value of crops must be non-negative"), return ge::GRAPH_FAILED);
353+ originInput_.crops[i / CROPS_DIM_NUM_1][i % CROPS_DIM_NUM_1] = static_cast<uint64_t>(crops[i]);
354+ }
355+ 
356+ return ge::GRAPH_SUCCESS;
357+}
358+ 
359+ge::graphStatus BatchToSpaceNDTiling::CheckY()
360+{
361+ // spatial shape
362+ size_t i = 1;
363+ for (size_t j = 0; j < originBlockShapeDim_; ++i, ++j) {
364+ // x shape 已判断不会溢出,block_shape已判断整除batch,这里不会翻转
365+ uint64_t cropedShape = originInput_.inShape[i] * originInput_.blockShape[j];
366+ // crops 是否溢出
367+ OP_CHECK_IF(
368+ originInput_.crops[j][0] > std::numeric_limits<uint64_t>::max() - originInput_.crops[j][1],
369+ OP_LOGE(context_, "crops overflows"), return ge::GRAPH_FAILED);
370+ // y shape 不能为负
371+ uint64_t crops = originInput_.crops[j][0] + originInput_.crops[j][1];
372+ OP_CHECK_IF(
373+ cropedShape < crops, OP_LOGE(context_, "the croped shape must be non-negative"), return ge::GRAPH_FAILED);
374+ originInput_.outShape[i] = cropedShape - crops;
375+ // 比x shape size 小,不会溢出
376+ yShapeSize_ *= originInput_.outShape[i];
377+ }
378+ 
379+ // remain shape
380+ for (; i < originInput_.rank; ++i) {
381+ originInput_.outShape[i] = originInput_.inShape[i];
382+ yShapeSize_ *= originInput_.outShape[i];
383+ }
384+ return ge::GRAPH_SUCCESS;
385+}
386+ 
387+ge::graphStatus BatchToSpaceNDTiling::MergeInput()
388+{
389+ size_t oldIdx = 0;
390+ size_t newIdx = 0;
391+ // batch不变
392+ mergedInput_.inShape[newIdx] = originInput_.inShape[oldIdx];
393+ mergedInput_.outShape[newIdx++] = originInput_.outShape[oldIdx++];
394+ 
395+ // 合 block_shape/crops
396+ uint64_t x = 1, y = 1, crops0 = 0, crops1 = 0;
397+ uint64_t remainShape = 1;
398+ for (; oldIdx <= originBlockShapeDim_; ++oldIdx) {
399+ size_t j = oldIdx - 1;
400+ // 合并
401+ x *= originInput_.inShape[oldIdx];
402+ y *= originInput_.outShape[oldIdx];
403+ // crops如果上一维非0,则本维必为0,扩大 y[i] 倍
404+ // 如果上一维为0,则赋值为本维
405+ crops0 = crops0 * originInput_.outShape[oldIdx] + originInput_.crops[j][0];
406+ crops1 = crops1 * originInput_.outShape[oldIdx] + originInput_.crops[j][1];
407+ 
408+ // 当前 block_shape 为 1
409+ if (originInput_.blockShape[j] == 1) {
410+ // block_shape 非最后一维,且下一个 crops 为 0,合并到下一维
411+ if (oldIdx < originBlockShapeDim_ && originInput_.crops[oldIdx][0] == 0 &&
412+ originInput_.crops[oldIdx][1] == 0) {
413+ continue;
414+ }
415+ // block_shape 最后一维,且当前 crops 为 0,合并到 remain_shape
416+ if (oldIdx == originBlockShapeDim_ && crops0 == 0 && crops1 == 0) {
417+ remainShape = x;
418+ continue;
419+ }
420+ }
421+ // 写入
422+ mergedInput_.inShape[newIdx] = x;
423+ mergedInput_.blockShape[newIdx - 1] = originInput_.blockShape[j];
424+ mergedInput_.crops[newIdx - 1][0] = crops0;
425+ mergedInput_.crops[newIdx - 1][1] = crops1;
426+ mergedInput_.outShape[newIdx++] = y;
427+ // 初始化
428+ x = 1;
429+ crops0 = 0;
430+ crops1 = 0;
431+ y = 1;
432+ }
433+ 
434+ // space维度全被合并,保留1维
435+ if (newIdx == 1) {
436+ mergedInput_.inShape[newIdx] = 1;
437+ mergedInput_.outShape[newIdx++] = 1;
438+ mergedInput_.blockShape[0] = 1;
439+ mergedInput_.crops[0][0] = 0;
440+ mergedInput_.crops[0][1] = 0;
441+ }
442+ 
443+ // 合 remain_shape
444+ for (; oldIdx < originInput_.rank; ++oldIdx) {
445+ remainShape *= originInput_.inShape[oldIdx];
446+ }
447+ mergedInput_.inShape[newIdx] = remainShape;
448+ mergedInput_.outShape[newIdx] = remainShape;
449+ mergedInput_.rank = newIdx + 1;
450+ 
451+ return ge::GRAPH_SUCCESS;
452+}
453+ 
454+ge::graphStatus BatchToSpaceNDTiling::ParamCheck()
455+{
456+ // 获取并校验参数
457+ auto ret = CheckX();
458+ OP_CHECK_IF(ret != ge::GRAPH_SUCCESS, OP_LOGE(context_, "Check x failed"), return ret);
459+ ret = CheckBlockShape();
460+ OP_CHECK_IF(ret != ge::GRAPH_SUCCESS, OP_LOGE(context_, "Check block_shape failed"), return ret);
461+ ret = CheckCrops();
462+ OP_CHECK_IF(ret != ge::GRAPH_SUCCESS, OP_LOGE(context_, "Check crops failed"), return ret);
463+ ret = CheckY();
464+ OP_CHECK_IF(ret != ge::GRAPH_SUCCESS, OP_LOGE(context_, "Check y failed"), return ret);
465+ // 合轴
466+ return MergeInput();
467+}
468+ 
469+ge::graphStatus BatchToSpaceNDTiling::moveAlignTilingBlock(
470+ uint32_t maxUBElements, const std::vector<uint64_t>& ubFactorAlign, const std::vector<uint64_t>& leftAlign,
471+ const std::vector<uint64_t>& dimValue, int32_t minCutAxis, B2SNDLargeCTilingData* tilingData)
472+{
473+ uint64_t totalCount = 1;
474+ uint32_t restFactor = maxUBElements;
475+ uint32_t ubAxis = 0, ubFactor = 0;
476+ 
477+ int32_t rank = static_cast<int32_t>(dimValue.size());
478+ int32_t tailIdx = rank - 1;
479+ int32_t minIdx = std::max(0, (rank + minCutAxis) % rank);
480+ for (int32_t i = tailIdx; i >= minIdx; --i) {
481+ ubAxis = i;
482+ // 尾轴要ub block对齐
483+ uint64_t dimVal = (i == tailIdx) ? AlignBlockElement(dimValue[i]) : dimValue[i];
484+ // 塞得下就直接算,但最小维要做对齐处理
485+ if (restFactor >= dimVal && i != minIdx) {
486+ // 剩余空间充足,按维度值对齐后赋值
487+ ubFactor = static_cast<uint32_t>(dimVal);
488+ restFactor /= ubFactor;
489+ continue;
490+ }
491+ // 塞不下
492+ // 头尾要单独处理
493+ OP_CHECK_IF(
494+ ubFactorAlign[i] == 0, OP_LOGE(context_, "the ub factor align must be non-zero"), return ge::GRAPH_FAILED);
495+ uint64_t lastLeftAlign = leftAlign[i] % ubFactorAlign[i];
496+ uint64_t head = 0, tail = 0;
497+ if (lastLeftAlign + dimVal > ubFactorAlign[i]) {
498+ head = lastLeftAlign == 0 ? 0 : ubFactorAlign[i] - lastLeftAlign;
499+ tail = (lastLeftAlign + dimVal) % ubFactorAlign[i];
500+ } else {
501+ head = dimVal;
502+ }
503+ uint64_t middle = dimVal > (head + tail) ? dimVal - (head + tail) : 0;
504+ OP_LOGI(context_, "split axis %u into head %lu, middle %lu, tail %lu", ubAxis, head, middle, tail);
505+ restFactor = static_cast<uint32_t>(std::min(static_cast<uint64_t>(restFactor), dimVal));
506+ if (restFactor >= ubFactorAlign[i]) {
507+ // 超过对齐值,按对齐值倍数分组
508+ ubFactor = Ops::Base::FloorAlign(restFactor, static_cast<uint32_t>(ubFactorAlign[i]));
509+ // 中间部分对齐分组长度
510+ totalCount = Ops::Base::CeilDiv(middle, static_cast<uint64_t>(ubFactor));
511+ // 头尾单独分组
512+ totalCount += (head > 0) + (tail > 0);
513+ } else {
514+ // 不足对齐值,每个分组内再按 ub factor 分组
515+ ubFactor = restFactor;
516+ totalCount = Ops::Base::CeilDiv(ubFactorAlign[i], static_cast<uint64_t>(ubFactor)) *
517+ Ops::Base::CeilDiv(middle, ubFactorAlign[i]);
518+ totalCount += Ops::Base::CeilDiv(head, static_cast<uint64_t>(ubFactor));
519+ totalCount += Ops::Base::CeilDiv(tail, static_cast<uint64_t>(ubFactor));
520+ }
521+ break;
522+ }
523+ // 非切分轴不需要对齐
524+ for (int32_t i = ubAxis - 1; i >= 0; --i) {
525+ totalCount *= dimValue[i];
526+ }
527+ 
528+ tilingData->ubAxis = ubAxis;
529+ tilingData->totalCount = totalCount;
530+ tilingData->ubFactor = ubFactor;
531+ return ge::GRAPH_SUCCESS;
532+}
533+ 
534+ge::graphStatus BatchToSpaceNDTiling::Tiling4LargeC()
535+{
536+ // tiling key
537+ mode_ = TPL_MODE_LARGE_C;
538+ 
539+ // tiling data
540+ auto tilingData = context_->GetTilingData<B2SNDLargeCTilingData>();
541+ OP_CHECK_NULL_WITH_CONTEXT(context_, tilingData);
542+ tilingData->input = mergedInput_;
543+ 
544+ // ub 大小
545+ tilingData->outputBufferSize = ubSize_ / LARGE_C_BUFFER_NUM;
546+ tilingData->outputBufferSize = std::min(tilingData->outputBufferSize, LARGE_C_MAX_BUFFER_SIZE);
547+ uint32_t maxUBElements = tilingData->outputBufferSize / static_cast<uint32_t>(dSize_);
548+ 
549+ // 各轴 ub factor 对齐值
550+ std::vector<uint64_t> ubFactorAlign;
551+ ubFactorAlign.resize(mergedInput_.rank);
552+ // batch 不对齐
553+ ubFactorAlign[0] = 1;
554+ // space 对齐 block shape
555+ std::copy(mergedInput_.blockShape, mergedInput_.blockShape + mergedInput_.rank - 2, ubFactorAlign.begin() + 1);
556+ // remain shape 对齐 ub block
557+ ubFactorAlign[mergedInput_.rank - 1] = ubBlockElements_;
558+ 
559+ std::vector<uint64_t> dimValue =
560+ std::vector<uint64_t>(mergedInput_.outShape, mergedInput_.outShape + mergedInput_.rank);
561+ std::vector<uint64_t> leftAlign{};
562+ leftAlign.resize(mergedInput_.rank);
563+ // batch 不对齐
564+ leftAlign[0] = 0;
565+ // space 要按crop前的大小对齐 block shape
566+ for (size_t i = 1; i < mergedInput_.rank - 1; ++i) {
567+ // 前面补齐crop前的部分
568+ leftAlign[i] = mergedInput_.crops[i - 1][0];
569+ }
570+ // remain shape 对齐 ub block
571+ leftAlign[mergedInput_.rank - 1] = 0;
572+ 
573+ // 分块
574+ auto ret =
575+ moveAlignTilingBlock(maxUBElements, ubFactorAlign, leftAlign, dimValue, LARGE_C_OUTMOST_CUT_AXIS, tilingData);
576+ OP_CHECK_IF(ret != ge::GRAPH_SUCCESS, OP_LOGE(context_, "large C tiling failed"), return ret);
577+ 
578+ // 分核
579+ tilingData->perCoreCount = Ops::Base::CeilDiv(tilingData->totalCount, static_cast<uint64_t>(coreNum_));
580+ realCoreNum_ = Ops::Base::CeilDiv(tilingData->totalCount, tilingData->perCoreCount);
581+ 
582+ // 打印 tiling data
583+ ShowLargeCTilingData();
584+ return ge::GRAPH_SUCCESS;
585+}
586+ 
587+void BatchToSpaceNDTiling::SmallCSetInput(B2SNDSmallCTilingData* tilingData, size_t yAxisPerm[])
588+{
589+ // 展开 x shape
590+ size_t rank = blockShapeDimNum_ + mergedInput_.rank;
591+ // batch
592+ std::copy(mergedInput_.blockShape, mergedInput_.blockShape + blockShapeDimNum_, tilingData->oriInShape);
593+ tilingData->oriInShape[blockShapeDimNum_] = mergedInput_.outShape[0];
594+ // space + remain
595+ std::copy(
596+ mergedInput_.inShape + 1, mergedInput_.inShape + mergedInput_.rank,
597+ tilingData->oriInShape + blockShapeDimNum_ + 1);
598+ 
599+ // 预crop
600+ for (size_t i = 0; i < blockShapeDimNum_; ++i) {
601+ uint64_t& bs = mergedInput_.blockShape[i];
602+ uint64_t& x = mergedInput_.inShape[i + 1];
603+ uint64_t yStart = mergedInput_.crops[i][0];
604+ uint64_t yEnd = x * bs - mergedInput_.crops[i][1] - 1;
605+ uint64_t xStart = yStart / bs; // 截取前的输出对应 x 的起始坐标
606+ uint64_t xEnd = yEnd / bs; // 截取前的输出对应 x 的结束坐标
607+ // 截取后的 space
608+ tilingData->croppedInShape[blockShapeDimNum_ + 1 + i] = xEnd + 1 - xStart;
609+ if (xStart == xEnd) {
610+ // 截取后的 block shape
611+ tilingData->croppedInShape[i] = yEnd + 1 - yStart;
612+ } else {
613+ // 不截取 block shape
614+ tilingData->croppedInShape[i] = bs;
615+ }
616+ }
617+ // y batch
618+ tilingData->croppedInShape[blockShapeDimNum_] = mergedInput_.outShape[0];
619+ // remain
620+ tilingData->croppedInShape[rank - 1] = mergedInput_.inShape[mergedInput_.rank - 1];
621+ 
622+ // 输出轴映射输入轴
623+ // batch
624+ yAxisPerm[0] = blockShapeDimNum_;
625+ // remain
626+ yAxisPerm[rank - 1] = rank - 1;
627+ for (size_t i = 0; i < blockShapeDimNum_; ++i) {
628+ // block shape
629+ yAxisPerm[2 + i * 2] = i;
630+ // space
631+ yAxisPerm[1 + i * 2] = blockShapeDimNum_ + 1 + i;
632+ }
633+ 
634+ // crops
635+ std::copy(*mergedInput_.crops, (*mergedInput_.crops) + blockShapeDimNum_ * 2, *(tilingData->crops));
636+}
637+ 
638+ge::graphStatus BatchToSpaceNDTiling::Tiling4SmallC()
639+{
640+ // tiling key
641+ mode_ = TPL_MODE_SMALL_C;
642+ blockShapeDimNum_ = mergedInput_.rank - 2;
643+ 
644+ // tiling data
645+ auto tilingData = context_->GetTilingData<B2SNDSmallCTilingData>();
R
RRuiWang_3月28日

tilingdata指针判空

likedislike
646+ OP_CHECK_NULL_WITH_CONTEXT(context_, tilingData);
647+ // 处理输入数据
648+ size_t yAxisPerm[MAX_EXPAND_RANK]{};
649+ SmallCSetInput(tilingData, yAxisPerm);
650+ 
651+ // 可用UB大小
652+ uint32_t validBufSize = ubSize_ / SMALL_C_BUFFER_NUM;
653+ tilingData->ubTileSize = std::min(validBufSize / SMALL_C_BUFFER_FACTOR, SMALL_C_MAX_BUFFER_SIZE);
654+ uint32_t inputElements = tilingData->ubTileSize / dSize_;
655+ 
656+ // 输入输出双切分
657+ auto tiling = DualSideTiling(
658+ context_, ubBlockElements_, tilingData->croppedInShape, yAxisPerm, blockShapeDimNum_ + mergedInput_.rank);
659+ tiling.DoTiling(inputElements);
660+ tilingData->inUbAxis = tiling.inAxis;
661+ tilingData->inUbFactor = tiling.inFactor;
662+ tilingData->outUbAxis = tiling.outAxis;
663+ tilingData->outUbFactor = tiling.outFactor;
664+ tilingData->ubTotalCount = tiling.totalCount;
665+ 
666+ // 分核
667+ tilingData->ubPerCount = Ops::Base::CeilDiv(tilingData->ubTotalCount, static_cast<uint64_t>(coreNum_));
668+ realCoreNum_ = Ops::Base::CeilDiv(tilingData->ubTotalCount, tilingData->ubPerCount);
669+ tilingData->coreNum = realCoreNum_;
670+ 
671+ // 打印 tiling data
672+ ShowSmallCTilingData();
673+ return ge::GRAPH_SUCCESS;
674+}
675+ 
676+ge::graphStatus BatchToSpaceNDTiling::Tiling4SIMT()
677+{
678+ // tiling key
679+ mode_ = TPL_MODE_SIMT;
680+ isBigShape_ = xShapeSize_ > MAX_UINT32_NUM;
681+ 
682+ // 可用UB大小
683+ OP_CHECK_IF((ubSize_ < SIMT_DCACHE_SIZE), OP_LOGE(context_, "ub size invalid"), return ge::GRAPH_FAILED);
684+ uint32_t validBufSize = (ubSize_ - SIMT_DCACHE_SIZE) / SIMT_BUFFER_NUM;
685+ uint32_t usedBufSize = std::min(validBufSize, SIMT_MAX_UB_SIZE);
686+ auto ret = context_->SetLocalMemorySize(usedBufSize * SIMT_BUFFER_NUM);
687+ OP_CHECK_IF(ret != ge::GRAPH_SUCCESS, OP_LOGE(context_, "set local memory size failed."), return ret);
688+ 
689+ // tiling data
690+ auto tilingData = context_->GetTilingData<B2SNDSimtTilingData>();
R
RRuiWang_3月28日

tilingdata指针判空

likedislike
691+ OP_CHECK_NULL_WITH_CONTEXT(context_, tilingData);
692+ tilingData->input = mergedInput_;
693+ 
694+ // 按1维分块
695+ tilingData->blockSize = usedBufSize / dSize_;
696+ // 向下对齐线程数
697+ uint32_t threadNum = simtMaxThreads_ / SIMT_THREAD_FACTOR;
698+ tilingData->blockSize = Ops::Base::FloorAlign(tilingData->blockSize, threadNum);
699+ // 防止输出为空,至少要1
700+ tilingData->totalBlock = std::max(
701+ 1UL, Ops::Base::CeilDiv(static_cast<uint64_t>(yShapeSize_), static_cast<uint64_t>(tilingData->blockSize)));
702+ tilingData->tailBlockSize = static_cast<uint32_t>(static_cast<uint64_t>(yShapeSize_) % tilingData->blockSize);
703+ if (tilingData->tailBlockSize == 0) {
704+ tilingData->tailBlockSize = yShapeSize_ == 0 ? 0 : tilingData->blockSize;
705+ }
706+ 
707+ // 均分分核
708+ realCoreNum_ = tilingData->totalBlock > coreNum_ ? coreNum_ : static_cast<uint32_t>(tilingData->totalBlock);
709+ tilingData->needCoreNum = realCoreNum_;
710+ tilingData->mainCoreBlock = Ops::Base::CeilDiv(tilingData->totalBlock, static_cast<uint64_t>(realCoreNum_));
711+ tilingData->mainCoreNum = static_cast<uint32_t>(tilingData->totalBlock % realCoreNum_);
712+ if (tilingData->mainCoreNum == 0) {
713+ tilingData->mainCoreNum = realCoreNum_;
714+ }
715+ 
716+ // 打印 tiling data
717+ ShowSIMTTilingData();
718+ return ge::GRAPH_SUCCESS;
719+}
720+ 
721+ge::graphStatus BatchToSpaceNDTiling::DoOpTiling()
722+{
723+ cacheLineElements_ = cacheLineSize_ / dSize_;
724+ ubBlockElements_ = ubBlockSize_ / dSize_;
725+ 
726+ // 输出空tensor
727+ if (yShapeSize_ == 0) {
728+ return Tiling4SIMT();
729+ }
730+ 
731+ // 维度较多
732+ if (mergedInput_.rank >= MIN_RANK_FOR_SIMT) {
733+ return Tiling4SIMT();
734+ }
735+ 
736+ // 尾轴 >= cacheline
737+ if (mergedInput_.inShape[mergedInput_.rank - 1] >= static_cast<uint64_t>(cacheLineElements_)) {
738+ return Tiling4LargeC();
739+ }
740+ return Tiling4SIMT();
741+}
742+ 
743+ge::graphStatus BatchToSpaceNDTiling::GetSocInfo()
744+{
745+ // 获取soc信息, 如ub大小, core数等
746+ auto platformInfoPtr = context_->GetPlatformInfo();
747+ OP_CHECK_NULL_WITH_CONTEXT(context_, platformInfoPtr);
748+ auto ascendcPlatform = platform_ascendc::PlatformAscendC(platformInfoPtr);
749+ coreNum_ = ascendcPlatform.GetCoreNumAiv();
750+ realCoreNum_ = coreNum_;
751+ OP_CHECK_IF((coreNum_ == 0U), OP_LOGE(context_, "coreNum is 0"), return ge::GRAPH_FAILED);
752+ uint64_t ubSize = 0;
753+ ascendcPlatform.GetCoreMemSize(platform_ascendc::CoreMemType::UB, ubSize);
754+ OP_CHECK_IF((ubSize == 0U), OP_LOGE(context_, "ubSize is 0"), return ge::GRAPH_FAILED);
755+ OP_CHECK_IF((ubSize > MAX_UINT32_NUM), OP_LOGE(context_, "ub size not support"), return ge::GRAPH_FAILED);
756+ ubSize_ = ubSize;
757+ cacheLineSize_ = Ops::Base::GetCacheLineSize(context_);
758+ OP_CHECK_IF((cacheLineSize_ == 0U), OP_LOGE(context_, "Failed to get cache line size."), return ge::GRAPH_FAILED);
759+ ubBlockSize_ = Ops::Base::GetUbBlockSize(context_);
760+ OP_CHECK_IF((ubBlockSize_ == 0U), OP_LOGE(context_, "Failed to get ub block size."), return ge::GRAPH_FAILED);
761+ vRegSize_ = Ops::Base::GetVRegSize(context_);
762+ OP_CHECK_IF((vRegSize_ == 0U), OP_LOGE(context_, "Failed to get vector register size."), return ge::GRAPH_FAILED);
763+ simtMaxThreads_ = Ops::Base::GetSimtMaxThreadNum(context_);
764+ OP_CHECK_IF((simtMaxThreads_ == 0U), OP_LOGE(context_, "Failed to get simt thread num."), return ge::GRAPH_FAILED);
765+ return ge::GRAPH_SUCCESS;
766+}
767+ 
768+static ge::graphStatus Tiling4BatchToSpaceND(gert::TilingContext* context)
769+{
770+ // DoTiling
771+ BatchToSpaceNDTiling tiling{context};
772+ return tiling.DoTiling();
773+}
774+ 
775+static ge::graphStatus TilingPrepareForBatchToSpaceND([[maybe_unused]] gert::TilingParseContext* context)
776+{
777+ return ge::GRAPH_SUCCESS;
778+}
779+ 
780+IMPL_OP_OPTILING(BatchToSpaceND)
781+ .Tiling(Tiling4BatchToSpaceND)
782+ .TilingInputsDataDependency({INPUT_IDX_BLOCK_SHAPE, INPUT_IDX_CROPS})
783+ .TilingParse<B2SNDCompileInfo>(TilingPrepareForBatchToSpaceND);
784+} // namespace optiling
@@ -0,0 +1,337 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include <cstdint>
12+#include <cstddef>
13+#include <algorithm>
14+#include <set>
15+#include <utility>
16+#include "op_host/util/math_util.h"
17+ 
18+namespace optiling {
19+ 
20+namespace {
21+struct CutInfo { // 切分信息
22+ std::set<size_t> axisSet{}; // 轴集合
23+ size_t cutAxis{0}; // 切分轴
24+ uint32_t cutFactor{1}; // 切分轴维度数量
E
Eesok113月28日

size_t cutAxis{0}; 格式化模板就是这样的吗?是否需要执行格式化?

likedislike
zhanw_coding
zhanw_coding
3月28日 评论:
25+ uint32_t innerProd{1}; // 内轴积
26+ uint32_t outterProd{1}; // 外轴积
27+ const size_t* axisPerm; // 轴的排列
28+ 
29+ CutInfo() : axisPerm(nullptr) {};
30+ CutInfo(const size_t* perm) : axisPerm(perm) {};
31+ ~CutInfo() {};
32+ 
33+ size_t Idx2Axis(int16_t idx) const
34+ {
35+ return axisPerm == nullptr ? idx : axisPerm[idx];
36+ }
37+};
38+} // namespace
39+ 
40+class DualSideTiling {
41+private:
42+ // tiling context
43+ gert::TilingContext* context_;
44+ 
45+ // 输入
46+ const uint32_t ubBlockElements_; // UB block 对应元素个数
47+ const uint64_t* axisSizeList_; // 每根轴大小
48+ const size_t* outAxisPerm_; // 输出轴的排列
49+ const size_t rank_; // 轴的数量
50+ uint32_t maxBufElements_; // 单侧最大缓存元素个数
51+ 
52+ // 中间变量
53+ int startIdx_{0}; // 起始轴
54+ uint32_t commAxisProd_{1}; // 公共轴的积
55+ uint64_t expectMaxInnerProd_{0}; // 每一侧内轴积期望最大值
56+ std::set<size_t> commAxisSet_{}; // 公共轴集合
57+ std::set<size_t> kernelAxisSet_{}; // 所有核间轴集合
58+ bool isCommAxisUpdated_{false}; // 公共轴是否有更新
59+public:
60+ // 输出
61+ size_t inAxis{0}; // 输入切分轴
E
Eesok113月28日
  1. public变量是必须的吗?
public:
 	     // 输出
 	     size_t inAxis{0};    
  1. 命名规则和private不一样的原因是?
private:
     int startIdx_{0};                  // 起始轴
public:     
      size_t inAxis{0};       // 输入切分轴
likedislike
zhanw_coding
zhanw_coding
3月28日 评论:
62+ size_t outAxis{0}; // 输出切分轴(按输入索引)
63+ uint32_t inFactor{1}; // 输入切分轴维度数量
64+ uint32_t outFactor{1}; // 输出切分轴维度数量
65+ uint64_t totalCount{1}; // 块数
66+public:
67+ DualSideTiling(
68+ gert::TilingContext* context, const uint32_t ubBlockElements, const uint64_t* axisSizeList,
69+ const size_t* outAxisPerm, const size_t rank)
70+ : context_(context),
71+ ubBlockElements_(ubBlockElements),
72+ axisSizeList_(axisSizeList),
73+ outAxisPerm_(outAxisPerm),
74+ rank_(rank) {};
75+ ~DualSideTiling() {};
76+ 
77+ void DoTiling(uint32_t maxBufElements);
78+ 
79+private:
80+ // block 对齐
81+ template <typename T>
82+ inline T CeilAlignBlockElement(T elementCount) const;
83+ template <typename T>
84+ inline T FloorAlignBlockElement(T elementCount) const;
85+ 
86+ // 初始化
87+ void Init();
88+ // 计算最大内轴积
89+ inline uint32_t ComputeActualMaxInnerProd(const CutInfo& currInfo) const;
90+ // 新增公共轴
91+ inline void AddCommonAxis(size_t axis);
92+ // 计算应该切哪根轴
93+ inline size_t ComputeCutAxis(int16_t& currIdx, CutInfo& currInfo, CutInfo& otherInfo);
94+ // 计算切分轴上的最大维度数量
95+ inline uint64_t ComputeMaxFactor(const CutInfo& currInfo, uint64_t maxInnerProd) const;
96+ // 计算切分轴上的维度数量
97+ inline uint32_t ComputeAxisFactor(const CutInfo& currInfo) const;
98+ // 填满切分轴维度数量
99+ inline void FillAxisFactor(CutInfo& currInfo, const CutInfo& otherInfo);
E
Eesok113月28日

函数签名是否可以优化?

     inline void FillAxisFactor(CutInfo& currInfo, const CutInfo& otherInfo);

==>

     inline void FillAxisFactor(const CutInfo& currInfo, const CutInfo& otherInfo);
likedislike
zhanw_coding
zhanw_coding
3月28日 评论:
100+ // 调整每侧的切分轴维度数量
101+ inline void AdjustAxisFactor(CutInfo& inputInfo, CutInfo& outputInfo);
102+ // 切轴
103+ void CutAxis();
104+ // 计算总块数
105+ void ComputeTotalCount();
106+ 
107+ // 全载
108+ bool TryFullLoad();
109+ // 非全载
110+ void DoNonFullLoad();
111+};
112+ 
113+template <typename T>
114+inline T DualSideTiling::CeilAlignBlockElement(T elementCount) const
115+{
116+ return Ops::Base::CeilAlign(elementCount, static_cast<T>(ubBlockElements_));
117+}
118+ 
119+template <typename T>
120+inline T DualSideTiling::FloorAlignBlockElement(T elementCount) const
121+{
122+ return Ops::Base::FloorAlign(elementCount, static_cast<T>(ubBlockElements_));
E
Eesok113月28日

是否会导致除0错误?

likedislike
zhanw_coding
zhanw_coding
3月28日 评论:
123+}
124+ 
125+void DualSideTiling::Init()
126+{
127+ startIdx_ = rank_ - 1;
128+ // 核间轴初始为 0 ~ -2 轴,必不包含 -1 轴
129+ for (size_t i = 0; i < rank_ - 1; ++i) {
130+ kernelAxisSet_.emplace_hint(kernelAxisSet_.end(), i);
131+ }
132+ // 本模板尾轴小,如果存在公共尾轴则从-2轴开始切
133+ if (static_cast<size_t>(startIdx_) == outAxisPerm_[startIdx_]) {
134+ AddCommonAxis(startIdx_--);
135+ } else {
136+ expectMaxInnerProd_ = FloorAlignBlockElement(static_cast<uint64_t>(std::sqrt(maxBufElements_)));
137+ }
138+}
139+ 
140+bool DualSideTiling::TryFullLoad()
141+{
142+ uint64_t allElements = 1;
143+ for (size_t i = 0; i < rank_; ++i) {
144+ allElements *= axisSizeList_[i];
145+ }
O
Oonanfield3月28日

预裁剪前后大小不同的维度,搬入时需要32B对齐,此处计算时需要考虑

likedislike
146+ // 能全载
147+ if (allElements > maxBufElements_) {
148+ return false;
149+ }
150+ inAxis = 0;
151+ inFactor = axisSizeList_[0];
152+ outAxis = outAxisPerm_[0];
153+ outFactor = axisSizeList_[outAxis];
154+ totalCount = 1;
155+ return true;
156+}
157+ 
158+inline uint32_t DualSideTiling::ComputeActualMaxInnerProd(const CutInfo& currInfo) const
159+{
160+ // 每侧元素数 = 内轴向上block对齐 * 外轴
161+ // 则每侧的最大值为:最大元素数/当前侧的UB外轴,然后向下block对齐
162+ return FloorAlignBlockElement(Ops::Base::FloorDiv(maxBufElements_, currInfo.outterProd));
163+}
164+ 
165+inline void DualSideTiling::AddCommonAxis(size_t axis)
166+{
167+ commAxisProd_ *= axisSizeList_[axis];
168+ commAxisSet_.insert(axis);
169+ expectMaxInnerProd_ = FloorAlignBlockElement(static_cast<uint64_t>(std::sqrt(maxBufElements_ * commAxisProd_)));
170+ isCommAxisUpdated_ = true;
171+}
172+ 
173+inline size_t DualSideTiling::ComputeCutAxis(int16_t& currIdx, CutInfo& currInfo, CutInfo& otherInfo)
174+{
175+ uint32_t maxInnerProd = ComputeActualMaxInnerProd(currInfo);
176+ size_t currAxis;
177+ for (; currIdx >= 0; --currIdx) {
178+ currAxis = currInfo.Idx2Axis(currIdx);
179+ uint64_t axisSize = axisSizeList_[currAxis];
180+ // 如果另一边已经放下了这跟轴,则表示为公共轴,必然能放下,跳过
181+ if (otherInfo.axisSet.find(currAxis) != otherInfo.axisSet.end()) {
182+ // 加入公共轴
183+ AddCommonAxis(currAxis);
184+ otherInfo.axisSet.erase(currAxis);
185+ // 加入内轴
186+ currInfo.innerProd *= axisSize;
187+ // 从外轴中去除
188+ currInfo.outterProd /= std::max(axisSize, 1UL);
189+ maxInnerProd = ComputeActualMaxInnerProd(currInfo);
190+ continue;
191+ }
192+ uint64_t tmpInner = currInfo.innerProd * axisSize;
193+ // 放不下,当前轴即为切分轴
194+ if (tmpInner > expectMaxInnerProd_ || tmpInner > maxInnerProd) {
195+ return currAxis;
196+ }
197+ currInfo.innerProd = tmpInner;
198+ currInfo.axisSet.emplace(currAxis);
199+ otherInfo.outterProd *= axisSize; // 加入另一侧外轴
200+ kernelAxisSet_.erase(currAxis);
201+ }
202+ return currInfo.Idx2Axis(0);
203+}
204+ 
205+inline uint64_t DualSideTiling::ComputeMaxFactor(const CutInfo& currInfo, uint64_t maxInnerProd) const
206+{
207+ return Ops::Base::FloorDiv(maxInnerProd, static_cast<uint64_t>(currInfo.innerProd));
208+}
209+ 
210+inline uint32_t DualSideTiling::ComputeAxisFactor(const CutInfo& currInfo) const
211+{
212+ // 实际最大内轴积
213+ uint32_t actualMaxInnerProd = ComputeActualMaxInnerProd(currInfo);
214+ uint64_t maxInnerProd = std::min(expectMaxInnerProd_, static_cast<uint64_t>(actualMaxInnerProd));
215+ uint64_t axisFactor = ComputeMaxFactor(currInfo, maxInnerProd);
216+ return static_cast<uint32_t>(std::clamp(axisFactor, 1UL, axisSizeList_[currInfo.cutAxis]));
217+}
218+ 
219+inline void DualSideTiling::FillAxisFactor(CutInfo& currInfo, const CutInfo& otherInfo)
220+{
221+ uint32_t maxInnerProd = ComputeActualMaxInnerProd(currInfo);
222+ maxInnerProd = Ops::Base::FloorDiv(maxInnerProd, otherInfo.cutFactor);
223+ uint64_t axisFactor = ComputeMaxFactor(currInfo, maxInnerProd);
224+ currInfo.cutFactor = static_cast<uint32_t>(std::clamp(axisFactor, 1UL, axisSizeList_[currInfo.cutAxis]));
225+}
226+ 
227+inline void DualSideTiling::AdjustAxisFactor(CutInfo& inputInfo, CutInfo& outputInfo)
228+{
229+ // 把剩余部分调给小轴
230+ uint32_t inputInner = inputInfo.cutFactor * inputInfo.innerProd;
231+ uint32_t outputInner = outputInfo.cutFactor * outputInfo.innerProd;
232+ if (inputInner <= outputInner) {
233+ FillAxisFactor(inputInfo, outputInfo);
234+ } else {
235+ FillAxisFactor(outputInfo, inputInfo);
236+ }
237+}
238+ 
239+void DualSideTiling::CutAxis()
240+{
241+ // 起始索引
242+ int16_t xIdx = startIdx_;
243+ int16_t yIdx = startIdx_;
244+ CutInfo inputInfo{}; // 输入切分信息
245+ CutInfo outputInfo{outAxisPerm_}; // 输出切分信息
246+ // 初始内积为公共内积
247+ inputInfo.innerProd = commAxisProd_;
248+ outputInfo.innerProd = commAxisProd_;
249+ 
250+ while (true) {
251+ isCommAxisUpdated_ = false;
252+ // 遍历输入切分轴
253+ inputInfo.cutAxis = ComputeCutAxis(xIdx, inputInfo, outputInfo);
254+ // 遍历输出切分轴
255+ outputInfo.cutAxis = ComputeCutAxis(yIdx, outputInfo, inputInfo);
256+ if (isCommAxisUpdated_) {
257+ continue;
258+ }
259+ // 切同一根轴,即当前切分轴为公共轴
260+ if (inputInfo.cutAxis == outputInfo.cutAxis) {
261+ uint64_t axisElements = axisSizeList_[inputInfo.cutAxis];
262+ // 输入侧当前轴最大切分值
263+ uint64_t maxInFactor = ComputeMaxFactor(inputInfo, ComputeActualMaxInnerProd(inputInfo));
264+ // 输出侧当前轴最大切分值
265+ uint64_t maxOutFactor = ComputeMaxFactor(outputInfo, ComputeActualMaxInnerProd(outputInfo));
266+ // 能放下
267+ if (axisElements <= maxInFactor && axisElements <= maxOutFactor) {
268+ AddCommonAxis(inputInfo.cutAxis);
269+ kernelAxisSet_.erase(inputInfo.cutAxis);
270+ // 加入内轴,不需要更新外轴
271+ inputInfo.innerProd *= axisElements;
272+ outputInfo.innerProd *= axisElements;
273+ xIdx--;
274+ yIdx--;
275+ continue;
276+ }
277+ // 放不下,就切这根轴
278+ uint64_t factor = std::min(maxInFactor, maxOutFactor);
279+ factor = std::clamp(factor, 1UL, axisElements);
280+ inputInfo.cutFactor = factor;
281+ outputInfo.cutFactor = factor;
282+ break;
283+ }
284+ // 计算切分大小
285+ inputInfo.cutFactor = ComputeAxisFactor(inputInfo);
286+ outputInfo.cutFactor = ComputeAxisFactor(outputInfo);
287+ AdjustAxisFactor(inputInfo, outputInfo);
288+ break;
289+ }
290+ // 剔除当前切分轴
291+ kernelAxisSet_.erase(inputInfo.cutAxis);
292+ kernelAxisSet_.erase(outputInfo.cutAxis);
293+ // 赋值
294+ inAxis = inputInfo.cutAxis;
295+ inFactor = inputInfo.cutFactor;
296+ outAxis = outputInfo.cutAxis;
297+ outFactor = outputInfo.cutFactor;
298+}
299+ 
300+void DualSideTiling::ComputeTotalCount()
301+{
302+ // 核间轴乘积即为总块数
303+ totalCount = 1;
304+ // 被切的轴的外轴
305+ if (inAxis == outAxis) {
306+ totalCount *= Ops::Base::CeilDiv(axisSizeList_[inAxis], static_cast<uint64_t>(inFactor));
307+ } else {
308+ totalCount *= Ops::Base::CeilDiv(axisSizeList_[inAxis], static_cast<uint64_t>(inFactor));
309+ totalCount *= Ops::Base::CeilDiv(axisSizeList_[outAxis], static_cast<uint64_t>(outFactor));
310+ }
311+ // 剩余核间轴
312+ for (auto i : kernelAxisSet_) {
313+ totalCount *= axisSizeList_[i];
314+ }
315+}
316+ 
317+void DualSideTiling::DoNonFullLoad()
318+{
319+ Init();
320+ 
321+ CutAxis();
322+ 
323+ // 计算核间轴
324+ ComputeTotalCount();
325+}
326+ 
327+void DualSideTiling::DoTiling(uint32_t maxBufElements)
328+{
329+ maxBufElements_ = maxBufElements;
330+ // 全载
331+ if (TryFullLoad()) {
332+ return;
333+ }
334+ 
335+ DoNonFullLoad();
336+}
337+} // namespace optiling
@@ -0,0 +1,85 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include "register/op_def_registry.h"
12+#include <array>
13+ 
14+namespace ops {
15+// x data type 所有取值
16+static constexpr std::array VALUE_DATA_TYPE_ALL{
17+ ge::DT_INT8, ge::DT_UINT8, ge::DT_INT16, ge::DT_UINT16, ge::DT_INT32,
18+ ge::DT_UINT32, ge::DT_INT64, ge::DT_UINT64, ge::DT_BF16, ge::DT_FLOAT16,
19+ ge::DT_FLOAT, ge::DT_DOUBLE, ge::DT_BOOL, ge::DT_COMPLEX32, ge::DT_COMPLEX64,
F
Ffocusforce3月28日

最后这个逗号看着没有意义

likedislike
zhanw_coding
zhanw_coding
3月28日 评论:
20+};
21+// 索引类型 data type 所有取值
22+static constexpr std::array INDEX_DATA_TYPE_ALL{ge::DT_INT32, ge::DT_INT64};
23+// 所有组合数量
24+static constexpr size_t DATA_TYPE_COMBINE_COUNT =
25+ VALUE_DATA_TYPE_ALL.size() * INDEX_DATA_TYPE_ALL.size() * INDEX_DATA_TYPE_ALL.size();
26+// 计算各输入的 data type 组合
27+static constexpr std::array<std::array<ge::DataType, DATA_TYPE_COMBINE_COUNT>, 3> CombineDataTypes()
28+{
29+ std::array<ge::DataType, DATA_TYPE_COMBINE_COUNT> xDTs{}, blockShapeDTs{}, cropsDTs{};
30+ 
31+ std::size_t idx = 0;
32+ for (std::size_t i3 = 0; i3 < INDEX_DATA_TYPE_ALL.size(); ++i3) {
33+ for (std::size_t i2 = 0; i2 < INDEX_DATA_TYPE_ALL.size(); ++i2) {
34+ for (std::size_t i1 = 0; i1 < VALUE_DATA_TYPE_ALL.size(); ++i1) {
35+ xDTs[idx] = VALUE_DATA_TYPE_ALL[i1];
36+ blockShapeDTs[idx] = INDEX_DATA_TYPE_ALL[i2];
37+ cropsDTs[idx] = INDEX_DATA_TYPE_ALL[i3];
38+ ++idx;
39+ }
40+ }
41+ }
42+ std::array res{xDTs, blockShapeDTs, cropsDTs};
43+ return res;
44+}
45+static constexpr auto DATA_TYPE_LIST = CombineDataTypes();
46+static constexpr auto& X_DATA_TYPE_LIST = std::get<0>(DATA_TYPE_LIST);
47+static constexpr auto& BS_DATA_TYPE_LIST = std::get<1>(DATA_TYPE_LIST);
48+static constexpr auto& CROPS_DATA_TYPE_LIST = std::get<2>(DATA_TYPE_LIST);
49+static const auto DATA_FORMAT_LIST = std::vector<ge::Format>(DATA_TYPE_COMBINE_COUNT, ge::FORMAT_ND);
50+ 
51+class BatchToSpaceND : public OpDef {
52+public:
53+ explicit BatchToSpaceND(const char* name) : OpDef(name)
54+ {
55+ this->Input("x")
56+ .ParamType(REQUIRED)
57+ .DataType(std::vector<ge::DataType>(X_DATA_TYPE_LIST.begin(), X_DATA_TYPE_LIST.end()))
58+ .Format(DATA_FORMAT_LIST);
59+ // block_shape 参数(1D 张量)
60+ this->Input("block_shape")
61+ .ParamType(REQUIRED)
62+ .DataType(std::vector<ge::DataType>(BS_DATA_TYPE_LIST.begin(), BS_DATA_TYPE_LIST.end()))
63+ .Format(DATA_FORMAT_LIST);
64+ // crops 参数(2D 张量)
65+ this->Input("crops")
66+ .ParamType(REQUIRED)
67+ .DataType(std::vector<ge::DataType>(CROPS_DATA_TYPE_LIST.begin(), CROPS_DATA_TYPE_LIST.end()))
68+ .Format(DATA_FORMAT_LIST);
69+ // 输出张量 y
70+ this->Output("y").Follow("x");
71+ 
72+ OpAICoreConfig aicore_config;
73+ aicore_config.DynamicCompileStaticFlag(true)
74+ .DynamicFormatFlag(false)
75+ .DynamicRankSupportFlag(true)
76+ .DynamicShapeSupportFlag(true)
77+ .NeedCheckSupportFlag(false)
78+ .ExtendCfgInfo("opFile.value", "batch_to_space_nd_apt");
79+ 
80+ this->AICore().AddConfig("ascend950", aicore_config);
81+ }
82+};
83+ 
84+OP_ADD(BatchToSpaceND);
85+} // namespace ops
@@ -0,0 +1,142 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include "register/op_impl_registry.h"
12+#include "log/log.h"
13+#include "op_api/op_util.h"
14+#include "op_host/util/const_util.h"
15+#include "op_host/util/shape_util.h"
16+ 
17+using namespace ge;
18+namespace ops {
19+// 输入索引
20+static constexpr size_t INPUT_IDX_X = 0;
21+static constexpr size_t INPUT_IDX_BLOCK_SHAPE = 1;
22+static constexpr size_t INPUT_IDX_CROPS = 2;
23+// 输出索引
24+static constexpr size_t OUTPUT_IDX_Y = 0;
25+// 未知维度值
26+static constexpr int64_t UNKNOWN_DIM = -1;
27+// crops 第二维的长度
28+static constexpr size_t CROPS_LENGTH = 2;
29+ 
30+class BatchToSpaceNDInferShapeHelper {
31+public:
32+ explicit BatchToSpaceNDInferShapeHelper(gert::InferShapeContext* context) : context_(context)
33+ {}
34+ 
35+ ge::graphStatus Inference();
36+ 
37+private:
38+ ge::graphStatus Init();
39+ 
40+private:
41+ gert::InferShapeContext* context_;
42+ const gert::Shape* xShape_{nullptr};
43+ gert::Shape* yShape_{nullptr};
44+ size_t blockNum_;
45+ gert::Shape blockVec_;
46+ gert::Shape cropsVec_;
47+ bool isConstBlock_;
48+ bool isConstCrops_;
49+};
50+ 
51+ge::graphStatus BatchToSpaceNDInferShapeHelper::Init()
52+{
53+ xShape_ = context_->GetInputShape(INPUT_IDX_X);
54+ OP_CHECK_NULL_WITH_CONTEXT(context_, xShape_);
55+ 
56+ const gert::Tensor* blockTensor = context_->GetInputTensor(INPUT_IDX_BLOCK_SHAPE);
57+ OP_CHECK_NULL_WITH_CONTEXT(context_, blockTensor);
58+ blockNum_ = blockTensor->GetShapeSize();
59+ 
60+ const gert::Tensor* cropsTensor = context_->GetInputTensor(INPUT_IDX_CROPS);
61+ OP_CHECK_NULL_WITH_CONTEXT(context_, cropsTensor);
62+ 
63+ yShape_ = context_->GetOutputShape(OUTPUT_IDX_Y);
64+ OP_CHECK_NULL_WITH_CONTEXT(context_, yShape_);
65+ 
66+ isConstBlock_ = false;
67+ isConstCrops_ = false;
68+ if (IsConstTensor(blockTensor)) {
69+ isConstBlock_ = true;
70+ OP_CHECK_IF(
71+ !Ops::Base::GetConstIntToShape<gert::InferShapeContext>(context_, INPUT_IDX_BLOCK_SHAPE, blockVec_),
72+ OP_LOGE(context_, "get const block_shape data failed!"), return ge::GRAPH_FAILED);
73+ }
74+ 
75+ if (IsConstTensor(cropsTensor)) {
76+ isConstCrops_ = true;
77+ OP_CHECK_IF(
78+ !Ops::Base::GetConstIntToShape<gert::InferShapeContext>(context_, INPUT_IDX_CROPS, cropsVec_),
79+ OP_LOGE(context_, "get const crops data failed!"), return ge::GRAPH_FAILED);
80+ }
81+ 
82+ return ge::GRAPH_SUCCESS;
83+}
84+ 
85+ge::graphStatus BatchToSpaceNDInferShapeHelper::Inference()
86+{
87+ auto ret = Init();
88+ if (ret != ge::GRAPH_SUCCESS) {
89+ return ret;
90+ }
91+ 
92+ if (Ops::Base::IsUnknownRank(*xShape_) || !isConstBlock_) {
93+ Ops::Base::SetUnknownRank(*yShape_);
94+ return ge::GRAPH_SUCCESS;
95+ }
96+ 
97+ yShape_->SetDimNum(0);
98+ 
99+ // batch
100+ int64_t batch = xShape_->GetDim(0);
101+ if (batch != UNKNOWN_DIM) {
102+ for (size_t i = 0; i < blockNum_; ++i) {
103+ OP_CHECK_IF(
104+ blockVec_.GetDim(i) == 0,
105+ OP_LOGE(
106+ context_, "block_value has 0 data which is not supported, but get %s",
107+ Ops::Base::ToString(blockVec_).c_str()),
108+ return ge::GRAPH_FAILED);
109+ batch = batch / blockVec_.GetDim(i);
110+ }
111+ }
112+ yShape_->AppendDim(batch);
113+ 
114+ // spatial shape
115+ for (size_t i = 1; i <= blockNum_; ++i) {
116+ size_t j = i - 1;
117+ if (xShape_->GetDim(i) != UNKNOWN_DIM && isConstCrops_) {
118+ int64_t totalCrop = cropsVec_.GetDim(CROPS_LENGTH * j) + cropsVec_.GetDim(CROPS_LENGTH * j + 1);
119+ yShape_->AppendDim(xShape_->GetDim(i) * blockVec_.GetDim(j) - totalCrop);
120+ } else {
121+ yShape_->AppendDim(UNKNOWN_DIM);
122+ }
123+ }
124+ 
125+ // remain shape
126+ for (size_t i = blockNum_ + 1; i < xShape_->GetDimNum(); ++i) {
127+ yShape_->AppendDim(xShape_->GetDim(i));
128+ }
129+ 
130+ return ge::GRAPH_SUCCESS;
131+}
132+ 
133+static ge::graphStatus Infershape4BatchToSpaceND(gert::InferShapeContext* context)
134+{
135+ BatchToSpaceNDInferShapeHelper helper(context);
136+ return helper.Inference();
137+}
138+ 
139+IMPL_OP_INFERSHAPE(BatchToSpaceND)
140+ .InferShape(Infershape4BatchToSpaceND)
141+ .InputsDataDependency({INPUT_IDX_BLOCK_SHAPE, INPUT_IDX_CROPS});
142+} // namespace ops
@@ -0,0 +1,853 @@
1+{
2+ "op_type": "BatchToSpaceND",
3+ "op_list": [
4+ {
5+ "bin_filename": "BatchToSpaceND_1B_int32_int32",
6+ "inputs": [
7+ {
8+ "name": "x",
9+ "index": 0,
10+ "dtype": "int8",
11+ "format": "ND",
12+ "paramType": "required",
13+ "shape": [
14+ -2
15+ ],
16+ "format_match_mode": "FormatAgnostic",
17+ "dtype_match_mode": "DtypeByte"
18+ },
19+ {
20+ "name": "block_shape",
21+ "index": 1,
22+ "dtype": "int32",
23+ "format": "ND",
24+ "paramType": "required",
25+ "shape": [
26+ -2
27+ ],
28+ "format_match_mode": "FormatAgnostic"
29+ },
30+ {
31+ "name": "crops",
32+ "index": 2,
33+ "dtype": "int32",
34+ "format": "ND",
35+ "paramType": "required",
36+ "shape": [
37+ -2
38+ ],
39+ "format_match_mode": "FormatAgnostic"
40+ }
41+ ],
42+ "outputs": [
43+ {
44+ "name": "y",
45+ "index": 0,
46+ "dtype": "int8",
47+ "format": "ND",
48+ "paramType": "required",
49+ "shape": [
50+ -2
51+ ],
52+ "format_match_mode": "FormatAgnostic",
53+ "dtype_match_mode": "DtypeByte"
54+ }
55+ ]
56+ },
57+ {
58+ "bin_filename": "BatchToSpaceND_1B_int32_int64",
59+ "inputs": [
60+ {
61+ "name": "x",
62+ "index": 0,
63+ "dtype": "int8",
64+ "format": "ND",
65+ "paramType": "required",
66+ "shape": [
67+ -2
68+ ],
69+ "format_match_mode": "FormatAgnostic",
70+ "dtype_match_mode": "DtypeByte"
71+ },
72+ {
73+ "name": "block_shape",
74+ "index": 1,
75+ "dtype": "int32",
76+ "format": "ND",
77+ "paramType": "required",
78+ "shape": [
79+ -2
80+ ],
81+ "format_match_mode": "FormatAgnostic"
82+ },
83+ {
84+ "name": "crops",
85+ "index": 2,
86+ "dtype": "int64",
87+ "format": "ND",
88+ "paramType": "required",
89+ "shape": [
90+ -2
91+ ],
92+ "format_match_mode": "FormatAgnostic"
93+ }
94+ ],
95+ "outputs": [
96+ {
97+ "name": "y",
98+ "index": 0,
99+ "dtype": "int8",
100+ "format": "ND",
101+ "paramType": "required",
102+ "shape": [
103+ -2
104+ ],
105+ "format_match_mode": "FormatAgnostic",
106+ "dtype_match_mode": "DtypeByte"
107+ }
108+ ]
109+ },
110+ {
111+ "bin_filename": "BatchToSpaceND_1B_int64_int32",
112+ "inputs": [
113+ {
114+ "name": "x",
115+ "index": 0,
116+ "dtype": "int8",
117+ "format": "ND",
118+ "paramType": "required",
119+ "shape": [
120+ -2
121+ ],
122+ "format_match_mode": "FormatAgnostic",
123+ "dtype_match_mode": "DtypeByte"
124+ },
125+ {
126+ "name": "block_shape",
127+ "index": 1,
128+ "dtype": "int64",
129+ "format": "ND",
130+ "paramType": "required",
131+ "shape": [
132+ -2
133+ ],
134+ "format_match_mode": "FormatAgnostic"
135+ },
136+ {
137+ "name": "crops",
138+ "index": 2,
139+ "dtype": "int32",
140+ "format": "ND",
141+ "paramType": "required",
142+ "shape": [
143+ -2
144+ ],
145+ "format_match_mode": "FormatAgnostic"
146+ }
147+ ],
148+ "outputs": [
149+ {
150+ "name": "y",
151+ "index": 0,
152+ "dtype": "int8",
153+ "format": "ND",
154+ "paramType": "required",
155+ "shape": [
156+ -2
157+ ],
158+ "format_match_mode": "FormatAgnostic",
159+ "dtype_match_mode": "DtypeByte"
160+ }
161+ ]
162+ },
163+ {
164+ "bin_filename": "BatchToSpaceND_1B_int64_int64",
165+ "inputs": [
166+ {
167+ "name": "x",
168+ "index": 0,
169+ "dtype": "int8",
170+ "format": "ND",
171+ "paramType": "required",
172+ "shape": [
173+ -2
174+ ],
175+ "format_match_mode": "FormatAgnostic",
176+ "dtype_match_mode": "DtypeByte"
177+ },
178+ {
179+ "name": "block_shape",
180+ "index": 1,
181+ "dtype": "int64",
182+ "format": "ND",
183+ "paramType": "required",
184+ "shape": [
185+ -2
186+ ],
187+ "format_match_mode": "FormatAgnostic"
188+ },
189+ {
190+ "name": "crops",
191+ "index": 2,
192+ "dtype": "int64",
193+ "format": "ND",
194+ "paramType": "required",
195+ "shape": [
196+ -2
197+ ],
198+ "format_match_mode": "FormatAgnostic"
199+ }
200+ ],
201+ "outputs": [
202+ {
203+ "name": "y",
204+ "index": 0,
205+ "dtype": "int8",
206+ "format": "ND",
207+ "paramType": "required",
208+ "shape": [
209+ -2
210+ ],
211+ "format_match_mode": "FormatAgnostic",
212+ "dtype_match_mode": "DtypeByte"
213+ }
214+ ]
215+ },
216+ {
217+ "bin_filename": "BatchToSpaceND_2B_int32_int32",
218+ "inputs": [
219+ {
220+ "name": "x",
221+ "index": 0,
222+ "dtype": "float16",
223+ "format": "ND",
224+ "paramType": "required",
225+ "shape": [
226+ -2
227+ ],
228+ "format_match_mode": "FormatAgnostic",
229+ "dtype_match_mode": "DtypeByte"
230+ },
231+ {
232+ "name": "block_shape",
233+ "index": 1,
234+ "dtype": "int32",
235+ "format": "ND",
236+ "paramType": "required",
237+ "shape": [
238+ -2
239+ ],
240+ "format_match_mode": "FormatAgnostic"
241+ },
242+ {
243+ "name": "crops",
244+ "index": 2,
245+ "dtype": "int32",
246+ "format": "ND",
247+ "paramType": "required",
248+ "shape": [
249+ -2
250+ ],
251+ "format_match_mode": "FormatAgnostic"
252+ }
253+ ],
254+ "outputs": [
255+ {
256+ "name": "y",
257+ "index": 0,
258+ "dtype": "float16",
259+ "format": "ND",
260+ "paramType": "required",
261+ "shape": [
262+ -2
263+ ],
264+ "format_match_mode": "FormatAgnostic",
265+ "dtype_match_mode": "DtypeByte"
266+ }
267+ ]
268+ },
269+ {
270+ "bin_filename": "BatchToSpaceND_2B_int32_int64",
271+ "inputs": [
272+ {
273+ "name": "x",
274+ "index": 0,
275+ "dtype": "float16",
276+ "format": "ND",
277+ "paramType": "required",
278+ "shape": [
279+ -2
280+ ],
281+ "format_match_mode": "FormatAgnostic",
282+ "dtype_match_mode": "DtypeByte"
283+ },
284+ {
285+ "name": "block_shape",
286+ "index": 1,
287+ "dtype": "int32",
288+ "format": "ND",
289+ "paramType": "required",
290+ "shape": [
291+ -2
292+ ],
293+ "format_match_mode": "FormatAgnostic"
294+ },
295+ {
296+ "name": "crops",
297+ "index": 2,
298+ "dtype": "int64",
299+ "format": "ND",
300+ "paramType": "required",
301+ "shape": [
302+ -2
303+ ],
304+ "format_match_mode": "FormatAgnostic"
305+ }
306+ ],
307+ "outputs": [
308+ {
309+ "name": "y",
310+ "index": 0,
311+ "dtype": "float16",
312+ "format": "ND",
313+ "paramType": "required",
314+ "shape": [
315+ -2
316+ ],
317+ "format_match_mode": "FormatAgnostic",
318+ "dtype_match_mode": "DtypeByte"
319+ }
320+ ]
321+ },
322+ {
323+ "bin_filename": "BatchToSpaceND_2B_int64_int32",
324+ "inputs": [
325+ {
326+ "name": "x",
327+ "index": 0,
328+ "dtype": "float16",
329+ "format": "ND",
330+ "paramType": "required",
331+ "shape": [
332+ -2
333+ ],
334+ "format_match_mode": "FormatAgnostic",
335+ "dtype_match_mode": "DtypeByte"
336+ },
337+ {
338+ "name": "block_shape",
339+ "index": 1,
340+ "dtype": "int64",
341+ "format": "ND",
342+ "paramType": "required",
343+ "shape": [
344+ -2
345+ ],
346+ "format_match_mode": "FormatAgnostic"
347+ },
348+ {
349+ "name": "crops",
350+ "index": 2,
351+ "dtype": "int32",
352+ "format": "ND",
353+ "paramType": "required",
354+ "shape": [
355+ -2
356+ ],
357+ "format_match_mode": "FormatAgnostic"
358+ }
359+ ],
360+ "outputs": [
361+ {
362+ "name": "y",
363+ "index": 0,
364+ "dtype": "float16",
365+ "format": "ND",
366+ "paramType": "required",
367+ "shape": [
368+ -2
369+ ],
370+ "format_match_mode": "FormatAgnostic",
371+ "dtype_match_mode": "DtypeByte"
372+ }
373+ ]
374+ },
375+ {
376+ "bin_filename": "BatchToSpaceND_2B_int64_int64",
377+ "inputs": [
378+ {
379+ "name": "x",
380+ "index": 0,
381+ "dtype": "float16",
382+ "format": "ND",
383+ "paramType": "required",
384+ "shape": [
385+ -2
386+ ],
387+ "format_match_mode": "FormatAgnostic",
388+ "dtype_match_mode": "DtypeByte"
389+ },
390+ {
391+ "name": "block_shape",
392+ "index": 1,
393+ "dtype": "int64",
394+ "format": "ND",
395+ "paramType": "required",
396+ "shape": [
397+ -2
398+ ],
399+ "format_match_mode": "FormatAgnostic"
400+ },
401+ {
402+ "name": "crops",
403+ "index": 2,
404+ "dtype": "int64",
405+ "format": "ND",
406+ "paramType": "required",
407+ "shape": [
408+ -2
409+ ],
410+ "format_match_mode": "FormatAgnostic"
411+ }
412+ ],
413+ "outputs": [
414+ {
415+ "name": "y",
416+ "index": 0,
417+ "dtype": "float16",
418+ "format": "ND",
419+ "paramType": "required",
420+ "shape": [
421+ -2
422+ ],
423+ "format_match_mode": "FormatAgnostic",
424+ "dtype_match_mode": "DtypeByte"
425+ }
426+ ]
427+ },
428+ {
429+ "bin_filename": "BatchToSpaceND_4B_int32_int32",
430+ "inputs": [
431+ {
432+ "name": "x",
433+ "index": 0,
434+ "dtype": "float32",
435+ "format": "ND",
436+ "paramType": "required",
437+ "shape": [
438+ -2
439+ ],
440+ "format_match_mode": "FormatAgnostic",
441+ "dtype_match_mode": "DtypeByte"
442+ },
443+ {
444+ "name": "block_shape",
445+ "index": 1,
446+ "dtype": "int32",
447+ "format": "ND",
448+ "paramType": "required",
449+ "shape": [
450+ -2
451+ ],
452+ "format_match_mode": "FormatAgnostic"
453+ },
454+ {
455+ "name": "crops",
456+ "index": 2,
457+ "dtype": "int32",
458+ "format": "ND",
459+ "paramType": "required",
460+ "shape": [
461+ -2
462+ ],
463+ "format_match_mode": "FormatAgnostic"
464+ }
465+ ],
466+ "outputs": [
467+ {
468+ "name": "y",
469+ "index": 0,
470+ "dtype": "float32",
471+ "format": "ND",
472+ "paramType": "required",
473+ "shape": [
474+ -2
475+ ],
476+ "format_match_mode": "FormatAgnostic",
477+ "dtype_match_mode": "DtypeByte"
478+ }
479+ ]
480+ },
481+ {
482+ "bin_filename": "BatchToSpaceND_4B_int32_int64",
483+ "inputs": [
484+ {
485+ "name": "x",
486+ "index": 0,
487+ "dtype": "float32",
488+ "format": "ND",
489+ "paramType": "required",
490+ "shape": [
491+ -2
492+ ],
493+ "format_match_mode": "FormatAgnostic",
494+ "dtype_match_mode": "DtypeByte"
495+ },
496+ {
497+ "name": "block_shape",
498+ "index": 1,
499+ "dtype": "int32",
500+ "format": "ND",
501+ "paramType": "required",
502+ "shape": [
503+ -2
504+ ],
505+ "format_match_mode": "FormatAgnostic"
506+ },
507+ {
508+ "name": "crops",
509+ "index": 2,
510+ "dtype": "int64",
511+ "format": "ND",
512+ "paramType": "required",
513+ "shape": [
514+ -2
515+ ],
516+ "format_match_mode": "FormatAgnostic"
517+ }
518+ ],
519+ "outputs": [
520+ {
521+ "name": "y",
522+ "index": 0,
523+ "dtype": "float32",
524+ "format": "ND",
525+ "paramType": "required",
526+ "shape": [
527+ -2
528+ ],
529+ "format_match_mode": "FormatAgnostic",
530+ "dtype_match_mode": "DtypeByte"
531+ }
532+ ]
533+ },
534+ {
535+ "bin_filename": "BatchToSpaceND_4B_int64_int32",
536+ "inputs": [
537+ {
538+ "name": "x",
539+ "index": 0,
540+ "dtype": "float32",
541+ "format": "ND",
542+ "paramType": "required",
543+ "shape": [
544+ -2
545+ ],
546+ "format_match_mode": "FormatAgnostic",
547+ "dtype_match_mode": "DtypeByte"
548+ },
549+ {
550+ "name": "block_shape",
551+ "index": 1,
552+ "dtype": "int64",
553+ "format": "ND",
554+ "paramType": "required",
555+ "shape": [
556+ -2
557+ ],
558+ "format_match_mode": "FormatAgnostic"
559+ },
560+ {
561+ "name": "crops",
562+ "index": 2,
563+ "dtype": "int32",
564+ "format": "ND",
565+ "paramType": "required",
566+ "shape": [
567+ -2
568+ ],
569+ "format_match_mode": "FormatAgnostic"
570+ }
571+ ],
572+ "outputs": [
573+ {
574+ "name": "y",
575+ "index": 0,
576+ "dtype": "float32",
577+ "format": "ND",
578+ "paramType": "required",
579+ "shape": [
580+ -2
581+ ],
582+ "format_match_mode": "FormatAgnostic",
583+ "dtype_match_mode": "DtypeByte"
584+ }
585+ ]
586+ },
587+ {
588+ "bin_filename": "BatchToSpaceND_4B_int64_int64",
589+ "inputs": [
590+ {
591+ "name": "x",
592+ "index": 0,
593+ "dtype": "float32",
594+ "format": "ND",
595+ "paramType": "required",
596+ "shape": [
597+ -2
598+ ],
599+ "format_match_mode": "FormatAgnostic",
600+ "dtype_match_mode": "DtypeByte"
601+ },
602+ {
603+ "name": "block_shape",
604+ "index": 1,
605+ "dtype": "int64",
606+ "format": "ND",
607+ "paramType": "required",
608+ "shape": [
609+ -2
610+ ],
611+ "format_match_mode": "FormatAgnostic"
612+ },
613+ {
614+ "name": "crops",
615+ "index": 2,
616+ "dtype": "int64",
617+ "format": "ND",
618+ "paramType": "required",
619+ "shape": [
620+ -2
621+ ],
622+ "format_match_mode": "FormatAgnostic"
623+ }
624+ ],
625+ "outputs": [
626+ {
627+ "name": "y",
628+ "index": 0,
629+ "dtype": "float32",
630+ "format": "ND",
631+ "paramType": "required",
632+ "shape": [
633+ -2
634+ ],
635+ "format_match_mode": "FormatAgnostic",
636+ "dtype_match_mode": "DtypeByte"
637+ }
638+ ]
639+ },
640+ {
641+ "bin_filename": "BatchToSpaceND_8B_int32_int32",
642+ "inputs": [
643+ {
644+ "name": "x",
645+ "index": 0,
646+ "dtype": "int64",
647+ "format": "ND",
648+ "paramType": "required",
649+ "shape": [
650+ -2
651+ ],
652+ "format_match_mode": "FormatAgnostic",
653+ "dtype_match_mode": "DtypeByte"
654+ },
655+ {
656+ "name": "block_shape",
657+ "index": 1,
658+ "dtype": "int32",
659+ "format": "ND",
660+ "paramType": "required",
661+ "shape": [
662+ -2
663+ ],
664+ "format_match_mode": "FormatAgnostic"
665+ },
666+ {
667+ "name": "crops",
668+ "index": 2,
669+ "dtype": "int32",
670+ "format": "ND",
671+ "paramType": "required",
672+ "shape": [
673+ -2
674+ ],
675+ "format_match_mode": "FormatAgnostic"
676+ }
677+ ],
678+ "outputs": [
679+ {
680+ "name": "y",
681+ "index": 0,
682+ "dtype": "int64",
683+ "format": "ND",
684+ "paramType": "required",
685+ "shape": [
686+ -2
687+ ],
688+ "format_match_mode": "FormatAgnostic",
689+ "dtype_match_mode": "DtypeByte"
690+ }
691+ ]
692+ },
693+ {
694+ "bin_filename": "BatchToSpaceND_8B_int32_int64",
695+ "inputs": [
696+ {
697+ "name": "x",
698+ "index": 0,
699+ "dtype": "int64",
700+ "format": "ND",
701+ "paramType": "required",
702+ "shape": [
703+ -2
704+ ],
705+ "format_match_mode": "FormatAgnostic",
706+ "dtype_match_mode": "DtypeByte"
707+ },
708+ {
709+ "name": "block_shape",
710+ "index": 1,
711+ "dtype": "int32",
712+ "format": "ND",
713+ "paramType": "required",
714+ "shape": [
715+ -2
716+ ],
717+ "format_match_mode": "FormatAgnostic"
718+ },
719+ {
720+ "name": "crops",
721+ "index": 2,
722+ "dtype": "int64",
723+ "format": "ND",
724+ "paramType": "required",
725+ "shape": [
726+ -2
727+ ],
728+ "format_match_mode": "FormatAgnostic"
729+ }
730+ ],
731+ "outputs": [
732+ {
733+ "name": "y",
734+ "index": 0,
735+ "dtype": "int64",
736+ "format": "ND",
737+ "paramType": "required",
738+ "shape": [
739+ -2
740+ ],
741+ "format_match_mode": "FormatAgnostic",
742+ "dtype_match_mode": "DtypeByte"
743+ }
744+ ]
745+ },
746+ {
747+ "bin_filename": "BatchToSpaceND_8B_int64_int32",
748+ "inputs": [
749+ {
750+ "name": "x",
751+ "index": 0,
752+ "dtype": "int64",
753+ "format": "ND",
754+ "paramType": "required",
755+ "shape": [
756+ -2
757+ ],
758+ "format_match_mode": "FormatAgnostic",
759+ "dtype_match_mode": "DtypeByte"
760+ },
761+ {
762+ "name": "block_shape",
763+ "index": 1,
764+ "dtype": "int64",
765+ "format": "ND",
766+ "paramType": "required",
767+ "shape": [
768+ -2
769+ ],
770+ "format_match_mode": "FormatAgnostic"
771+ },
772+ {
773+ "name": "crops",
774+ "index": 2,
775+ "dtype": "int32",
776+ "format": "ND",
777+ "paramType": "required",
778+ "shape": [
779+ -2
780+ ],
781+ "format_match_mode": "FormatAgnostic"
782+ }
783+ ],
784+ "outputs": [
785+ {
786+ "name": "y",
787+ "index": 0,
788+ "dtype": "int64",
789+ "format": "ND",
790+ "paramType": "required",
791+ "shape": [
792+ -2
793+ ],
794+ "format_match_mode": "FormatAgnostic",
795+ "dtype_match_mode": "DtypeByte"
796+ }
797+ ]
798+ },
799+ {
800+ "bin_filename": "BatchToSpaceND_8B_int64_int64",
801+ "inputs": [
802+ {
803+ "name": "x",
804+ "index": 0,
805+ "dtype": "int64",
806+ "format": "ND",
807+ "paramType": "required",
808+ "shape": [
809+ -2
810+ ],
811+ "format_match_mode": "FormatAgnostic",
812+ "dtype_match_mode": "DtypeByte"
813+ },
814+ {
815+ "name": "block_shape",
816+ "index": 1,
817+ "dtype": "int64",
818+ "format": "ND",
819+ "paramType": "required",
820+ "shape": [
821+ -2
822+ ],
823+ "format_match_mode": "FormatAgnostic"
824+ },
825+ {
826+ "name": "crops",
827+ "index": 2,
828+ "dtype": "int64",
829+ "format": "ND",
830+ "paramType": "required",
831+ "shape": [
832+ -2
833+ ],
834+ "format_match_mode": "FormatAgnostic"
835+ }
836+ ],
837+ "outputs": [
838+ {
839+ "name": "y",
840+ "index": 0,
841+ "dtype": "int64",
842+ "format": "ND",
843+ "paramType": "required",
844+ "shape": [
845+ -2
846+ ],
847+ "format_match_mode": "FormatAgnostic",
848+ "dtype_match_mode": "DtypeByte"
849+ }
850+ ]
851+ }
852+ ]
853+}
@@ -0,0 +1,2 @@
1+[BatchToSpaceND]
2+default=0