已合并
add op revreseSequence #3544
dongjiangtao创建于 4月6日
add op revreseSequence #3544
已合并
dongjiangtao创建于 4月6日
6 个文件变更+832-0
Aindex/reverse_sequence/examples/test_geir_reverse_sequence.cpp+320-0
@@ -0,0 +1,320 @@
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 "ge_ir_build.h"
27+ 
28+#include "experiment_ops.h"
29+#include "nn_other.h"
30+#include "../op_graph/reverse_sequence_proto.h"
31+ 
32+#define FAILED -1
33+#define SUCCESS 0
34+ 
35+#include "graph/operator.h"
36+#include "graph/operator_reg.h"
37+namespace ge {
38+ 
39+REG_OP(Data).INPUT(x, TensorType::ALL()).OUTPUT(y, TensorType::ALL()).ATTR(index, Int, 0).OP_END_FACTORY_REG(Data)
40+}
41+ 
42+using namespace ge;
43+using std::map;
44+using std::string;
45+using std::vector;
46+ 
47+#define ADD_INPUT_INT64(inputIndex, inputName, inputDtype, inputShape, val) \
48+ vector<int64_t> placeholder##inputIndex##_shape = inputShape; \
49+ auto placeholder##inputIndex = op::Data("placeholder" + inputIndex).set_attr_index(0); \
50+ TensorDesc placeholder##inputIndex##_desc = \
51+ TensorDesc(ge::Shape(placeholder##inputIndex##_shape), FORMAT_ND, inputDtype); \
52+ placeholder##inputIndex##_desc.SetPlacement(ge::kPlacementHost); \
53+ placeholder##inputIndex##_desc.SetFormat(FORMAT_ND); \
54+ Tensor tensor_placeholder##inputIndex; \
55+ ret = GenOnesDataInt64(placeholder##inputIndex##_shape, \
56+ tensor_placeholder##inputIndex, \
57+ placeholder##inputIndex##_desc, \
58+ val); \
59+ if (ret != SUCCESS) { \
60+ printf("%s - ERROR - [XIR]: Generate input data failed\n", GetTime().c_str()); \
61+ return FAILED; \
62+ } \
63+ placeholder##inputIndex.update_input_desc_x(placeholder##inputIndex##_desc); \
64+ input.push_back(tensor_placeholder##inputIndex); \
65+ graph.AddOp(placeholder##inputIndex); \
66+ add1.set_input_##inputName(placeholder##inputIndex); \
67+ inputs.push_back(placeholder##inputIndex)
68+ 
69+#define ADD_INPUT_DOUBLE(inputIndex, inputName, inputDtype, inputShape, val) \
70+ vector<int64_t> placeholder##inputIndex##_shape = inputShape; \
71+ auto placeholder##inputIndex = op::Data("placeholder" + inputIndex).set_attr_index(0); \
72+ TensorDesc placeholder##inputIndex##_desc = \
73+ TensorDesc(ge::Shape(placeholder##inputIndex##_shape), FORMAT_ND, inputDtype); \
74+ placeholder##inputIndex##_desc.SetPlacement(ge::kPlacementHost); \
75+ placeholder##inputIndex##_desc.SetFormat(FORMAT_ND); \
76+ Tensor tensor_placeholder##inputIndex; \
77+ ret = GenOnesDataDouble(placeholder##inputIndex##_shape, \
78+ tensor_placeholder##inputIndex, \
79+ placeholder##inputIndex##_desc, \
80+ val); \
81+ if (ret != SUCCESS) { \
82+ printf("%s - ERROR - [XIR]: Generate input data failed\n", GetTime().c_str()); \
83+ return FAILED; \
84+ } \
85+ placeholder##inputIndex.update_input_desc_x(placeholder##inputIndex##_desc); \
86+ input.push_back(tensor_placeholder##inputIndex); \
87+ graph.AddOp(placeholder##inputIndex); \
88+ add1.set_input_##inputName(placeholder##inputIndex); \
89+ inputs.push_back(placeholder##inputIndex)
90+ 
91+#define ADD_OUTPUT(outputIndex, outputName, outputDtype, outputShape) \
92+ TensorDesc outputName##outputIndex##_desc = \
93+ TensorDesc(ge::Shape(outputShape), FORMAT_ND, outputDtype); \
94+ add1.update_output_desc_##outputName(outputName##outputIndex##_desc)
95+ 
96+#define LOG_PRINT(message, ...) \
97+ do { \
98+ printf(message, ##__VA_ARGS__); \
99+ } while (0)
100+ 
101+#define ADD_INPUT_ATTR(attrName, attrValue) \
102+ add1.set_attr_##attrName(attrValue)
103+ 
104+string GetTime()
105+{
106+ time_t timep;
107+ time(&timep);
108+ char tmp[64];
109+ strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S,000", localtime(&timep));
110+ return tmp;
111+}
112+ 
113+int32_t GenOnesDataDouble(vector<int64_t> shapes, Tensor& input_tensor, TensorDesc& input_tensor_desc, double *value)
114+{
115+ input_tensor_desc.SetRealDimCnt(shapes.size());
116+ size_t size = 1;
117+ for (uint32_t i = 0; i < shapes.size(); i++) {
118+ size *= shapes[i];
119+ }
120+ 
121+ double* pData = new (std::nothrow) double[size];
122+ if (pData == nullptr) {
123+ LOG_PRINT("ERROR: Failed to allocate memory.\n");
124+ return FAILED;
125+ }
126+ for (size_t i = 0; i < size; ++i) {
127+ *(pData + i) = value[i];
128+ }
129+ 
130+ uint32_t data_len = size * sizeof(double);
131+ input_tensor = Tensor(input_tensor_desc, reinterpret_cast<uint8_t*>(pData), data_len);
132+ return SUCCESS;
133+}
134+ 
135+int32_t GenOnesDataInt64(vector<int64_t> shapes, Tensor& input_tensor, TensorDesc& input_tensor_desc, int64_t *value)
136+{
137+ input_tensor_desc.SetRealDimCnt(shapes.size());
138+ size_t size = 1;
139+ for (uint32_t i = 0; i < shapes.size(); i++) {
140+ size *= shapes[i];
141+ }
142+ 
143+ int64_t* pData = new (std::nothrow) int64_t[size];
144+ if (pData == nullptr) {
145+ LOG_PRINT("ERROR: Failed to allocate memory.\n");
146+ return FAILED;
147+ }
148+
149+ for (size_t i = 0; i < size; ++i) {
150+ *(pData + i) = value[i];
151+ }
152+ 
153+ uint32_t data_len = size * sizeof(int64_t);
154+ input_tensor = Tensor(input_tensor_desc, reinterpret_cast<uint8_t*>(pData), data_len);
155+ return SUCCESS;
156+}
157+ 
158+int CreateOppInGraph(DataType inDtype1, DataType inDtype2, std::vector<ge::Tensor> &input, std::vector<Operator> &inputs,
159+ std::vector<Operator> &outputs, Graph &graph)
160+{
161+ Status ret = SUCCESS;
162+ // 自定义代码:添加单算子定义到图中
163+ auto add1 = op::ReverseSequence("ReverseSequence");
164+ std::vector<std::vector<int64_t>> shapes = {{3, 3}, {3}};
165+ double x_data[9] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0};
166+ int64_t sqe_len_data[3] = {1, 2, 3};
167+
168+ ADD_INPUT_DOUBLE(1, x, inDtype1, shapes[0], x_data);
169+ ADD_INPUT_INT64(2, seq_lengths, inDtype2, shapes[1], sqe_len_data);
170+ ADD_OUTPUT(3, y, inDtype1, shapes[0]);
171+ 
172+ // 添加属性
173+ int32_t seq_dim = 0;
174+ int32_t batch_dim = 1;
175+ 
176+ ADD_INPUT_ATTR(seq_dim, seq_dim);
177+ ADD_INPUT_ATTR(batch_dim, batch_dim);
178+ 
179+ outputs.push_back(add1);
180+ // 添加完毕
181+ return SUCCESS;
182+}
183+ 
184+bool InitEnv() {
185+ std::map<AscendString, AscendString> global_options = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}};
186+ Status ret = ge::GEInitialize(global_options);
187+ if (ret != SUCCESS) {
188+ LOG_PRINT("%s - INFO - [XIR]: Initialize ge using ge global options failed\n", GetTime().c_str());
189+ return false;
190+ }
191+ return true;
192+}
193+ 
194+bool CreateAndConfigGraph(Graph& graph, std::vector<ge::Tensor>& input) {
195+ std::vector<Operator> inputs{};
196+ std::vector<Operator> outputs{};
197+ 
198+ Status ret = CreateOppInGraph(DT_DOUBLE, DT_INT64, input, inputs, outputs, graph);
199+ if (ret != SUCCESS) {
200+ LOG_PRINT("%s - ERROR - [XIR]: Create ir session using build options failed\n", GetTime().c_str());
201+ return false;
202+ }
203+ 
204+ if (!inputs.empty() && !outputs.empty()) {
205+ graph.SetInputs(inputs).SetOutputs(outputs);
206+ }
207+ return true;
208+}
209+ 
210+bool AddGraphToSession(ge::Session* session, Graph& graph, uint32_t graph_id) {
211+ std::map<AscendString, AscendString> graph_options = {};
212+ 
213+ Status ret = session->AddGraph(graph_id, graph, graph_options);
214+ if (ret != SUCCESS) {
215+ LOG_PRINT("%s - INFO - [XIR]: Add graph failed\n", GetTime().c_str());
216+ delete session;
217+ ge::GEFinalize();
218+ return false;
219+ }
220+ return true;
221+}
222+ 
223+bool DumpAndRunGraph(
224+ ge::Session* session, Graph& graph, std::vector<ge::Tensor>& input, std::vector<ge::Tensor>& output,
225+ uint32_t graph_id)
226+{
227+ std::string file_path = "./dump";
228+ aclgrphDumpGraph(graph, file_path.c_str(), file_path.length());
229+ 
230+ Status ret = session->RunGraph(graph_id, input, output);
231+ if (ret != SUCCESS) {
232+ LOG_PRINT("%s - INFO - [XIR]: Run graph failed\n", GetTime().c_str());
233+ delete session;
234+ ge::GEFinalize();
235+ return false;
236+ }
237+ return true;
238+}
239+ 
240+void ProcessOutputData(std::vector<ge::Tensor>& output) {
241+ int output_num = output.size();
242+ double epsilon = 1e-9;
243+ for (int i = 0; i < output_num; i++) {
244+ std::cout << "output " << i << " dtype : " << output[i].GetTensorDesc().GetDataType() << std::endl;
245+ double* output_data_i = (double*)output[i].GetData();
246+ int64_t output_size = output[i].GetTensorDesc().GetShape().GetShapeSize();
247+ double expect_out[9] = {1.0, 5.0, 9.0, 4.0, 2.0, 6.0, 7.0, 8.0, 3.0};
248+ for (int64_t j = 0; j < output_size; j++) {
249+ if (std::abs(expect_out[j] - output_data_i[j]) > epsilon) {
250+ LOG_PRINT("ERROR - [XIR]: Precision is fail, please check. \n");
251+ return;
252+ }
253+ }
254+ LOG_PRINT("INFO - [XIR]: Precison is ok. \n");
255+ }
256+}
257+ 
258+int FinalizeRes() {
259+ ge::AscendString error_msg = ge::GEGetErrorMsgV2();
260+ std::string error_str(error_msg.GetString());
261+ std::cout << "Error message: " << error_str << std::endl;
262+ ge::AscendString warning_msg = ge::GEGetWarningMsgV2();
263+ std::string warning_str(warning_msg.GetString());
264+ std::cout << "Warning message: " << warning_str << std::endl;
265+ Status ret = ge::GEFinalize();
266+ if (ret != SUCCESS) {
267+ LOG_PRINT("%s - INFO - [XIR]: Finalize ir graph session failed\n", GetTime().c_str());
268+ return FAILED;
269+ }
270+ 
271+ LOG_PRINT("=== Test completed successfully ===\n");
272+ return SUCCESS;
273+}
274+ 
275+int main(int argc, char* argv[])
276+{
277+ LOG_PRINT("=== ReverseSequence GEIR Test Start ===\n");
278+ // 初始化环境
279+ if (!InitEnv()) {
280+ return FAILED;
281+ }
282+ 
283+ // 创建计算图
284+ const char* graph_name = "tc_ge_irrun_test";
285+ Graph graph(graph_name);
286+ std::vector<ge::Tensor> input;
287+ 
288+ if (!CreateAndConfigGraph(graph, input)) {
289+ LOG_PRINT("ERROR: CreateAndConfigGraph failed\n");
290+ return FAILED;
291+ }
292+ 
293+ // 创建会话并添加图
294+ std::map<AscendString, AscendString> build_options = {};
295+ ge::Session* session = new Session(build_options);
296+ if (session == nullptr) {
297+ LOG_PRINT("ERROR: Failed to create session\n");
298+ ge::GEFinalize();
299+ return FAILED;
300+ }
301+ 
302+ uint32_t graph_id = 0;
303+ if (!AddGraphToSession(session, graph, graph_id)) {
304+ LOG_PRINT("ERROR: AddGraphToSession failed\n");
305+ return FAILED;
306+ }
307+ 
308+ // 执行图
309+ std::vector<ge::Tensor> output;
310+ if (!DumpAndRunGraph(session, graph, input, output, graph_id)) {
311+ LOG_PRINT("ERROR: DumpAndRunGraph failed\n");
312+ return FAILED;
313+ }
314+
315+ // 处理输入输出数据
316+ ProcessOutputData(output);
317+ 
318+ // 清理资源
319+ return FinalizeRes();
320+}
Aindex/reverse_sequence/op_kernel_aicpu/CMakeLists.txt+41-0
@@ -0,0 +1,41 @@
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+set(ASCEND_OP_NAME "" CACHE STRING "Ascend op names to compile")
12+set(OP_TYPE "reverse_sequence")
13+ 
14+skip_aicpu_kernel("${OP_TYPE}" "${ASCEND_OP_NAME}")
15+if(SKIP_AICPU_FLAG)
16+ return()
17+endif()
18+ 
19+ 
20+if (BUILD_WITH_INSTALLED_DEPENDENCY_CANN_PKG)
21+ if (NOT (UT_TEST_ALL OR OP_KERNEL_AICPU_UT))
22+ add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1)
23+ set(CMAKE_CXX_COMPILER ${ASCEND_DIR}/toolkit/toolchain/hcc/bin/aarch64-target-linux-gnu-g++)
24+ endif()
25+ # aicpu json
26+ file(GLOB_RECURSE JSON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/*.json)
27+ set_property(GLOBAL APPEND PROPERTY AICPU_JSON_FILES ${JSON_FILE})
28+ 
29+ # aicpu cust kernel
30+ file(GLOB AICPU_SRC ${CMAKE_CURRENT_SOURCE_DIR}/*_aicpu*.cpp)
31+ 
32+ set(OBJ_NAME reverse_sequence_cust_obj)
33+ add_aicpu_cust_kernel_modules(${OBJ_NAME})
34+ target_sources(${OBJ_NAME} PRIVATE ${AICPU_SRC})
35+else()
36+ add_modules_sources(HOSTNAME ${OPHOST_NAME} MODE PRIVATE DIR ${CMAKE_CURRENT_SOURCE_DIR} OPTYPE reverse_sequence ACLNNTYPE aclnn_exclude)
37+endif()
38+ 
39+ if(UT_TEST_ALL OR OP_KERNEL_AICPU_UT)
40+ AddAicpuOpTestCase(reverse_sequence)
41+ endif()
Aindex/reverse_sequence/op_kernel_aicpu/reverse_sequence.json+27-0
@@ -0,0 +1,27 @@
1+{
2+ "ReverseSequence": {
3+ "opInfo": {
4+ "engine": "DNN_VM_AICPU",
5+ "flagPartial": "False",
6+ "computeCost": "100",
7+ "flagAsync": "False",
8+ "opKernelLib": "CUSTAICPUKernel",
9+ "kernelSo": "libnn_aicpu_kernels.so",
10+ "functionName": "RunCpuKernel",
11+ "userDefined": "True",
12+ "formatAgnostic": "False"
13+ },
14+ "input0": {
15+ "type": "DT_BOOL,DT_FLOAT,DT_FLOAT16,DT_DOUBLE,DT_UINT8,DT_INT8,DT_UINT16,DT_INT16,DT_INT32,DT_UINT32,DT_UINT64,DT_INT64",
16+ "name": "x"
17+ },
18+ "input1": {
19+ "type": "DT_INT32,DT_INT64",
20+ "name": "seq_lengths"
21+ },
22+ "output0": {
23+ "type": "DT_BOOL,DT_FLOAT,DT_FLOAT16,DT_DOUBLE,DT_UINT8,DT_INT8,DT_UINT16,DT_INT16,DT_INT32,DT_UINT32,DT_UINT64,DT_INT64",
24+ "name": "y"
25+ }
26+ }
27+}
Aindex/reverse_sequence/op_kernel_aicpu/reverse_sequence_aicpu.cpp+217-0
@@ -0,0 +1,217 @@
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 "reverse_sequence_aicpu.h"
12+#include "Eigen/Core"
13+#include "cpu_kernel_utils.h"
14+#include "log.h"
15+#include "utils/kernel_util.h"
16+ 
17+namespace {
18+const char *const kReverseSequence = "ReverseSequence";
19+const int kOutputIndex = 2;
20+const int64_t kEven = 2;
21+}
22+ 
23+namespace aicpu {
24+template <typename Tlen>
25+static KernelStatus CalcSeqParam(int64_t &seqStep, int64_t &batchSize, int64_t &totalSize, const Tlen *seq, CpuKernelContext &ctx)
26+{
27+ size_t seqDim = static_cast<size_t>(ctx.GetAttr("seq_dim")->GetInt());
28+ size_t batchDim = static_cast<size_t>(ctx.GetAttr("batch_dim")->GetInt());
29+ std::vector<int64_t> shape = ctx.Input(0)->GetTensorShape()->GetDimSizes();
30+ std::vector<int64_t> seqLengthsShape = ctx.Input(1)->GetTensorShape()->GetDimSizes();
31+ 
32+ KERNEL_CHECK_FALSE((shape[seqDim] != 0), static_cast<uint32_t>(KERNEL_STATUS_PARAM_INVALID),
33+ "The shape[%zu] of input[0] cannot be 0.", seqDim);
34+ KERNEL_CHECK_FALSE((shape[batchDim] != 0), static_cast<uint32_t>(KERNEL_STATUS_PARAM_INVALID),
35+ "The shape[%zu] of input[0] cannot be 0.", batchDim);
36+ 
37+ for (int64_t d = 0; d < static_cast<int64_t>(seqLengthsShape[0]); d++) {
38+ if (seq[d] < 0) {
39+ KERNEL_LOG_ERROR("Invalid seq_lengths value[%ld]: [%ld]", d, static_cast<int64_t>(seq[d]));
40+ return KERNEL_STATUS_PARAM_INVALID;
41+ }
42+ if (seq[d] > shape[seqDim]) {
43+ KERNEL_LOG_ERROR("CheckSequence, seq[%ld]: [%ld], shape[%zu]: [%ld]",
44+ d, static_cast<int64_t>(seq[d]), seqDim, shape[seqDim]);
45+ return KERNEL_STATUS_PARAM_INVALID;
46+ }
47+ }
48+ 
49+ size_t shapeSize = shape.size();
50+ for (size_t i = seqDim + 1U; i < shapeSize; i++) {
51+ seqStep *= shape[i];
52+ }
53+ 
54+ for (size_t i = batchDim + 1U; i < shapeSize; ++i) {
55+ batchSize *= shape[i];
56+ }
57+ 
58+ for (size_t i = 0; i < shapeSize; ++i) {
59+ totalSize *= shape[i];
60+ }
61+ 
62+ KERNEL_CHECK_FALSE((batchSize != 0), KERNEL_STATUS_PARAM_INVALID, "The value of batchSize cannot be 0.");
63+ return KERNEL_STATUS_OK;
64+}
65+ 
66+template <typename T, typename Tlen>
67+KernelStatus CalReverseSequence(const std::vector<void *> &ioAddrs, std::vector<int64_t> &shape, CpuKernelContext &ctx)
68+{
69+ int64_t seqStep = 1;
70+ int64_t batchSize = 1;
71+ int64_t totalSize = 1;
72+ Tlen *seq = reinterpret_cast<Tlen *>(ioAddrs[1]);
73+ KERNEL_CHECK_ERROR(CalcSeqParam(seqStep, batchSize, totalSize, seq, ctx));
74+ int64_t runLen = seqStep;
75+ 
76+ T *input = reinterpret_cast<T *>(ioAddrs[0]);
77+ T *output = reinterpret_cast<T *>(ioAddrs[kOutputIndex]);
78+ size_t seqDim = static_cast<size_t>(ctx.GetAttr("seq_dim")->GetInt());
79+ size_t batchDim = static_cast<size_t>(ctx.GetAttr("batch_dim")->GetInt());
80+ int64_t n = totalSize / (runLen * shape[seqDim]);
81+ bool parallelIn = runLen > n;
82+ const int64_t kMaxCoreNum = std::max(static_cast<uint32_t>(1), aicpu::CpuKernelUtils::GetCPUNum(ctx) - kResvCpuNum);
83+ 
84+ auto reverseSequenceFunc = [&](int64_t offset, int64_t reverseNum) {
85+ for (int64_t i = 0; i < shape[seqDim]; ++i) {
86+ if (i < reverseNum / kEven) {
87+ output[i * seqStep + offset] = input[((reverseNum - i) - 1) * seqStep + offset];
88+ output[((reverseNum - i) - 1) * seqStep + offset] = input[i * seqStep + offset];
89+ }
90+ if ((i >= reverseNum) || (i == reverseNum / kEven && reverseNum % kEven)) {
91+ output[i * seqStep + offset] = input[i * seqStep + offset];
92+ }
93+ }
94+ };
95+ 
96+ auto shard = [&](const int64_t start, const int64_t end) {
97+ for (int64_t j = start; j < end; ++j) {
98+ int64_t begin = runLen * shape[seqDim] * j;
99+ auto shardIn = [&](int64_t startIn, int64_t endIn) {
100+ for (int64_t r = startIn; r < endIn; ++r) {
101+ int64_t offset = r + begin;
102+ int64_t reverseNum = static_cast<int64_t>(seq[offset / batchSize % shape[batchDim]]);
103+ reverseSequenceFunc(offset, reverseNum);
104+ }
105+ };
106+ if (parallelIn) {
107+ (void)CpuKernelUtils::ParallelFor(ctx, runLen, runLen / kMaxCoreNum, shardIn);
108+ } else {
109+ shardIn(0, runLen);
110+ }
111+ }
112+ };
113+ 
114+ if (parallelIn) {
115+ shard(0, n);
116+ return KERNEL_STATUS_OK;
117+ }
118+ 
119+ auto ret = CpuKernelUtils::ParallelFor(ctx, n, n / kMaxCoreNum, shard);
120+ KERNEL_CHECK_FALSE(ret == KERNEL_STATUS_OK, ret, "CpuKernelUtils::ParallelFor failed");
121+ 
122+ return KERNEL_STATUS_OK;
123+}
124+ 
125+KernelStatus ReverseSequenceMsCpuKernel::GetInputAndCheck(CpuKernelContext &ctx)
126+{
127+ KERNEL_CHECK_NULLPTR(ctx.GetAttr("seq_dim"), KERNEL_STATUS_PARAM_INVALID, "Get attr:[seq_dim] failed.");
128+ size_t seqDim = static_cast<size_t>(ctx.GetAttr("seq_dim")->GetInt());
129+ 
130+ KERNEL_CHECK_NULLPTR(ctx.GetAttr("batch_dim"), KERNEL_STATUS_PARAM_INVALID, "Get attr:[batch_dim] failed.");
131+ size_t batchDim = static_cast<size_t>(ctx.GetAttr("batch_dim")->GetInt());
132+ 
133+ // input_0: x
134+ Tensor *xTensor = ctx.Input(0);
135+ KERNEL_CHECK_NULLPTR(xTensor, KERNEL_STATUS_PARAM_INVALID, "Get input:[0] failed")
136+ xDtype_ = static_cast<DataType>(xTensor->GetDataType());
137+ std::shared_ptr<TensorShape> x_shape = xTensor->GetTensorShape();
138+ xShape_ = xTensor->GetTensorShape()->GetDimSizes();
139+ 
140+ // input_1: seq_lengths
141+ Tensor *seqLengthsTensor = ctx.Input(1);
142+ KERNEL_CHECK_NULLPTR(seqLengthsTensor, KERNEL_STATUS_PARAM_INVALID, "Get input:[1] failed")
143+ seqLengthsDtype_ = static_cast<DataType>(seqLengthsTensor->GetDataType());
144+ std::vector<int64_t> seqLengthsShape = ctx.Input(1)->GetTensorShape()->GetDimSizes();
145+ if (seqLengthsDtype_ != DT_INT32 && seqLengthsDtype_ != DT_INT64) {
146+ KERNEL_LOG_ERROR("Invalid type of seq_lengths: [%s]", DTypeStr(seqLengthsDtype_).c_str());
147+ return KERNEL_STATUS_PARAM_INVALID;
148+ }
149+ if (seqLengthsShape.size() != 1) {
150+ KERNEL_LOG_ERROR("Invalid seq_lengths shape size: [%ld]", seqLengthsShape.size());
151+ return KERNEL_STATUS_PARAM_INVALID;
152+ }
153+ 
154+ if ((batchDim == seqDim) || (seqDim >= xShape_.size()) || (batchDim >= xShape_.size())) {
155+ KERNEL_LOG_ERROR("Invalid batchDim: [%zu], seqDim: [%zu], x dims:[ %zu]", batchDim, seqDim, xShape_.size());
156+ return KERNEL_STATUS_PARAM_INVALID;
157+ }
158+ 
159+ if (seqLengthsShape[0] != x_shape->GetDimSize(static_cast<int32_t>(batchDim))) {
160+ KERNEL_LOG_ERROR("seqLengthsShape[0] != x_shape.dim(%zu) size: [%ld]",
161+ batchDim, x_shape->GetDimSize(static_cast<int32_t>(batchDim)));
162+ return KERNEL_STATUS_PARAM_INVALID;
163+ }
164+ 
165+ Tensor *outputTensor = ctx.Output(0);
166+ KERNEL_CHECK_NULLPTR(outputTensor, KERNEL_STATUS_PARAM_INVALID, "Get output:[0] failed")
167+ ioAddrs_.push_back(reinterpret_cast<void *>(xTensor->GetData()));
168+ ioAddrs_.push_back(reinterpret_cast<void *>(seqLengthsTensor->GetData()));
169+ ioAddrs_.push_back(reinterpret_cast<void *>(outputTensor->GetData()));
170+ 
171+ KERNEL_LOG_INFO("Parse done, seqDim: [%zu], batchDim: %zu, x_dtype: [%d]",
172+ seqDim, batchDim, static_cast<int32_t>(xDtype_));
173+ 
174+ return KERNEL_STATUS_OK;
175+}
176+ 
177+uint32_t ReverseSequenceMsCpuKernel::Compute(CpuKernelContext &ctx) {
178+ KernelStatus res = GetInputAndCheck(ctx);
179+ if (res != KERNEL_STATUS_OK) {
180+ return static_cast<uint32_t>(res);
181+ }
182+ 
183+ std::map<DataType,
184+ std::map<DataType,
185+ std::function<uint32_t(std::vector<void *> &, std::vector<int64_t> &, CpuKernelContext &)>>> calls;
186+ 
187+ calls[DT_FLOAT16][DT_INT32] = CalReverseSequence<Eigen::half, int32_t>;
188+ calls[DT_FLOAT][DT_INT32] = CalReverseSequence<float, int32_t>;
189+ calls[DT_DOUBLE][DT_INT32] = CalReverseSequence<double, int32_t>;
190+ calls[DT_INT8][DT_INT32] = CalReverseSequence<int8_t, int32_t>;
191+ calls[DT_INT16][DT_INT32] = CalReverseSequence<int16_t, int32_t>;
192+ calls[DT_INT32][DT_INT32] = CalReverseSequence<int32_t, int32_t>;
193+ calls[DT_INT64][DT_INT32] = CalReverseSequence<int64_t, int32_t>;
194+ calls[DT_UINT8][DT_INT32] = CalReverseSequence<uint8_t, int32_t>;
195+ calls[DT_UINT16][DT_INT32] = CalReverseSequence<uint16_t, int32_t>;
196+ calls[DT_UINT32][DT_INT32] = CalReverseSequence<uint32_t, int32_t>;
197+ calls[DT_UINT64][DT_INT32] = CalReverseSequence<uint64_t, int32_t>;
198+ calls[DT_BOOL][DT_INT32] = CalReverseSequence<bool, int32_t>;
199+ 
200+ calls[DT_FLOAT16][DT_INT64] = CalReverseSequence<Eigen::half, int64_t>;
201+ calls[DT_FLOAT][DT_INT64] = CalReverseSequence<float, int64_t>;
202+ calls[DT_DOUBLE][DT_INT64] = CalReverseSequence<double, int64_t>;
203+ calls[DT_INT8][DT_INT64] = CalReverseSequence<int8_t, int64_t>;
204+ calls[DT_INT16][DT_INT64] = CalReverseSequence<int16_t, int64_t>;
205+ calls[DT_INT32][DT_INT64] = CalReverseSequence<int32_t, int64_t>;
206+ calls[DT_INT64][DT_INT64] = CalReverseSequence<int64_t, int64_t>;
207+ calls[DT_UINT8][DT_INT64] = CalReverseSequence<uint8_t, int64_t>;
208+ calls[DT_UINT16][DT_INT64] = CalReverseSequence<uint16_t, int64_t>;
209+ calls[DT_UINT32][DT_INT64] = CalReverseSequence<uint32_t, int64_t>;
210+ calls[DT_UINT64][DT_INT64] = CalReverseSequence<uint64_t, int64_t>;
211+ calls[DT_BOOL][DT_INT64] = CalReverseSequence<bool, int64_t>;
212+ 
213+ return calls[xDtype_][seqLengthsDtype_](ioAddrs_, xShape_, ctx);
214+}
215+ 
216+REGISTER_CPU_KERNEL(kReverseSequence, ReverseSequenceMsCpuKernel);
217+} // namespace aicpu
Aindex/reverse_sequence/op_kernel_aicpu/reverse_sequence_aicpu.h+34-0
@@ -0,0 +1,34 @@
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 OPS_NN_INDEX_REVERSE_SEQUENCE_AICPU_H
12+#define OPS_NN_INDEX_REVERSE_SEQUENCE_AICPU_H
13+ 
14+#include "cpu_kernel.h"
15+#include "utils/status.h"
16+ 
17+namespace aicpu {
18+class ReverseSequenceMsCpuKernel : public CpuKernel {
19+public:
20+ ~ReverseSequenceMsCpuKernel() = default;
21+ uint32_t Compute(CpuKernelContext &ctx) override;
22+ 
23+private:
24+ KernelStatus GetInputAndCheck(CpuKernelContext &ctx);
25+ 
26+ std::vector<void *> ioAddrs_;
27+ std::vector<int64_t> xShape_;
28+ 
29+ DataType xDtype_ = DT_INT32;
30+ DataType seqLengthsDtype_ = DT_INT32;
31+};
32+} // namespace aicpu
33+ 
34+#endif // OPS_NN_INDEX_REVERSE_SEQUENCE_AICPU_H
Aindex/reverse_sequence/tests/ut/op_kernel_aicpu/test_reverse_sequence.cpp+193-0
@@ -0,0 +1,193 @@
1+#include "gtest/gtest.h"
2+#ifndef private
3+#define private public
4+#define protected public
5+#endif
6+#include "utils/aicpu_test_utils.h"
7+#include "cpu_kernel_utils.h"
8+#include "node_def_builder.h"
9+#undef private
10+#undef protected
11+#include "Eigen/Core"
12+ 
13+using namespace std;
14+using namespace aicpu;
15+ 
16+class TEST_ReverseSequence_UTest : public testing::Test {};
17+ 
18+TEST_F(TEST_ReverseSequence_UTest, ReverseSequence_Success) {
19+ // raw data
20+ float x[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
21+ uint64_t seq_lengths[3] = {1, 2, 3};
22+ float y[3][3] = {0};
23+ float y_expect[3][3] = {{1, 5, 9}, {4, 2, 6}, {7, 8, 3}};
24+ 
25+ auto nodeDef = CpuKernelUtils::CreateNodeDef();
26+ nodeDef->SetOpType("ReverseSequence");
27+ 
28+ // set attr
29+ auto seq_dim = CpuKernelUtils::CreateAttrValue();
30+ seq_dim->SetInt(0);
31+ nodeDef->AddAttrs("seq_dim", seq_dim.get());
32+ 
33+ auto batch_dim = CpuKernelUtils::CreateAttrValue();
34+ batch_dim->SetInt(1);
35+ nodeDef->AddAttrs("batch_dim", batch_dim.get());
36+ 
37+ // set input
38+ auto inputTensor0 = nodeDef->AddInputs();
39+ EXPECT_NE(inputTensor0, nullptr);
40+ auto aicpuShape0 = inputTensor0->GetTensorShape();
41+ std::vector<int64_t> shapes0 = {3, 3};
42+ aicpuShape0->SetDimSizes(shapes0);
43+ inputTensor0->SetDataType(DT_FLOAT);
44+ inputTensor0->SetData(x);
45+ inputTensor0->SetDataSize(3 * 3 * sizeof(float));
46+ 
47+ auto inputTensor1 = nodeDef->AddInputs();
48+ EXPECT_NE(inputTensor1, nullptr);
49+ auto aicpuShape1 = inputTensor1->GetTensorShape();
50+ std::vector<int64_t> shapes1 = {3};
51+ aicpuShape1->SetDimSizes(shapes1);
52+ inputTensor1->SetDataType(DT_INT64);
53+ inputTensor1->SetData(seq_lengths);
54+ inputTensor1->SetDataSize(3 * sizeof(uint64_t));
55+ 
56+ // set output
57+ auto outputTensor1 = nodeDef->AddOutputs();
58+ EXPECT_NE(outputTensor1, nullptr);
59+ outputTensor1->SetDataType(DT_FLOAT);
60+ outputTensor1->SetData(y);
61+ outputTensor1->SetDataSize(3 * 3 * sizeof(float));
62+ 
63+ CpuKernelContext ctx(DEVICE);
64+ EXPECT_EQ(ctx.Init(nodeDef.get()), KERNEL_STATUS_OK);
65+ uint32_t ret = CpuKernelRegister::Instance().RunCpuKernel(ctx);
66+ EXPECT_EQ(ret, KERNEL_STATUS_OK);
67+ 
68+ float eps = 0.0001;
69+ for (int i = 0; i < 3; i++) {
70+ for (int j = 0; j < 3; j++) {
71+ EXPECT_LT(std::abs(y[i][j] - y_expect[i][j]), eps);
72+ }
73+ }
74+}
75+ 
76+#define CREATE_NODEDEF(shapes, data_types, datas, seq_dim, batch_dim) \
77+ auto node_def = CpuKernelUtils::CpuKernelUtils::CreateNodeDef(); \
78+ NodeDefBuilder(node_def.get(), "ReverseSequence", "ReverseSequence") \
79+ .Input({"x", data_types[0], shapes[0], datas[0]}) \
80+ .Input({"seq_lengths", data_types[1], shapes[1], datas[1]}) \
81+ .Attr("seq_dim", seq_dim) \
82+ .Attr("batch_dim", batch_dim) \
83+ .Output({"y", data_types[2], shapes[2], datas[2]}) \
84+ 
85+TEST_F(TEST_ReverseSequence_UTest, ReverseSequence_Input_Error) {
86+ vector<DataType> data_types = {DT_FLOAT, DT_UINT64, DT_FLOAT};
87+ vector<vector<int64_t>> shapes = {{3, 3}, {3}, {3, 3}};
88+ float x[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
89+ uint64_t seq_lengths[3] = {1, 2, 3};
90+ float output_y[3][3] = {0};
91+ vector<void *> datas = {(void *)x,
92+ (void *)seq_lengths,
93+ (void *)output_y};
94+ CREATE_NODEDEF(shapes, data_types, datas, 0, 1);
95+ RUN_KERNEL(node_def, HOST, KERNEL_STATUS_PARAM_INVALID);
96+}
97+ 
98+TEST_F(TEST_ReverseSequence_UTest, ReverseSequence_ZeroLengthSequence) {
99+ // raw data
100+ float x[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
101+ uint64_t seq_lengths[4] = {0, 2, 1, 0};
102+ float y[3][4] = {0};
103+ float y_expect[3][4] = {{1, 6, 3, 4}, {5, 2, 7, 8}, {9, 10, 11, 12}};
104+ 
105+ auto nodeDef = CpuKernelUtils::CreateNodeDef();
106+ nodeDef->SetOpType("ReverseSequence");
107+ 
108+ // set attr
109+ auto seq_dim = CpuKernelUtils::CreateAttrValue();
110+ seq_dim->SetInt(0);
111+ nodeDef->AddAttrs("seq_dim", seq_dim.get());
112+ 
113+ auto batch_dim = CpuKernelUtils::CreateAttrValue();
114+ batch_dim->SetInt(1);
115+ nodeDef->AddAttrs("batch_dim", batch_dim.get());
116+ 
117+ // set input
118+ auto inputTensor0 = nodeDef->AddInputs();
119+ EXPECT_NE(inputTensor0, nullptr);
120+ auto aicpuShape0 = inputTensor0->GetTensorShape();
121+ std::vector<int64_t> shapes0 = {3, 4};
122+ aicpuShape0->SetDimSizes(shapes0);
123+ inputTensor0->SetDataType(DT_FLOAT);
124+ inputTensor0->SetData(x);
125+ inputTensor0->SetDataSize(3 * 4 * sizeof(float));
126+ 
127+ auto inputTensor1 = nodeDef->AddInputs();
128+ EXPECT_NE(inputTensor1, nullptr);
129+ auto aicpuShape1 = inputTensor1->GetTensorShape();
130+ std::vector<int64_t> shapes1 = {4};
131+ aicpuShape1->SetDimSizes(shapes1);
132+ inputTensor1->SetDataType(DT_INT64);
133+ inputTensor1->SetData(seq_lengths);
134+ inputTensor1->SetDataSize(4 * sizeof(uint64_t));
135+ 
136+ // set output
137+ auto outputTensor1 = nodeDef->AddOutputs();
138+ EXPECT_NE(outputTensor1, nullptr);
139+ outputTensor1->SetDataType(DT_FLOAT);
140+ outputTensor1->SetData(y);
141+ outputTensor1->SetDataSize(3 * 4 * sizeof(float));
142+ 
143+ CpuKernelContext ctx(DEVICE);
144+ EXPECT_EQ(ctx.Init(nodeDef.get()), KERNEL_STATUS_OK);
145+ uint32_t ret = CpuKernelRegister::Instance().RunCpuKernel(ctx);
146+ EXPECT_EQ(ret, KERNEL_STATUS_OK);
147+ 
148+ float eps = 0.0001;
149+ for (int i = 0; i < 3; i++) {
150+ for (int j = 0; j < 4; j++) {
151+ EXPECT_LT(std::abs(y[i][j] - y_expect[i][j]), eps);
152+ }
153+ }
154+}
155+ 
156+TEST_F(TEST_ReverseSequence_UTest, ReverseSequence_SeqDimEqualsBatchDim) {
157+ vector<DataType> data_types = {DT_FLOAT, DT_INT64, DT_FLOAT};
158+ vector<vector<int64_t>> shapes = {{3, 3}, {3}, {3, 3}};
159+ float x[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
160+ uint64_t seq_lengths[3] = {1, 2, 3};
161+ float output_y[3][3] = {0};
162+ vector<void *> datas = {(void *)x,
163+ (void *)seq_lengths,
164+ (void *)output_y};
165+ CREATE_NODEDEF(shapes, data_types, datas, 0, 0);
166+ RUN_KERNEL(node_def, HOST, KERNEL_STATUS_PARAM_INVALID);
167+}
168+ 
169+TEST_F(TEST_ReverseSequence_UTest, ReverseSequence_SeqDimOutOfRange) {
170+ vector<DataType> data_types = {DT_FLOAT, DT_INT64, DT_FLOAT};
171+ vector<vector<int64_t>> shapes = {{3, 3}, {3}, {3, 3}};
172+ float x[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
173+ uint64_t seq_lengths[3] = {1, 2, 3};
174+ float output_y[3][3] = {0};
175+ vector<void *> datas = {(void *)x,
176+ (void *)seq_lengths,
177+ (void *)output_y};
178+ CREATE_NODEDEF(shapes, data_types, datas, 2, 1);
179+ RUN_KERNEL(node_def, HOST, KERNEL_STATUS_PARAM_INVALID);
180+}
181+ 
182+TEST_F(TEST_ReverseSequence_UTest, ReverseSequence_BatchDimOutOfRange) {
183+ vector<DataType> data_types = {DT_FLOAT, DT_INT64, DT_FLOAT};
184+ vector<vector<int64_t>> shapes = {{3, 3}, {3}, {3, 3}};
185+ float x[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
186+ uint64_t seq_lengths[3] = {1, 2, 3};
187+ float output_y[3][3] = {0};
188+ vector<void *> datas = {(void *)x,
189+ (void *)seq_lengths,
190+ (void *)output_y};
191+ CREATE_NODEDEF(shapes, data_types, datas, 0, 2);
192+ RUN_KERNEL(node_def, HOST, KERNEL_STATUS_PARAM_INVALID);
193+}