已合并
add op revreseSequence #3544
dongjiangtao创建于 4月6日
add op revreseSequence #3544
已合并
共 6 个文件变更+832-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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 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 | + | ||
| 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 | + | ||
| 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 | + | ||
| 92 | + TensorDesc outputName##outputIndex##_desc = \ | ||
| 93 | + TensorDesc(ge::Shape(outputShape), FORMAT_ND, outputDtype); \ | ||
| 94 | + add1.update_output_desc_##outputName(outputName##outputIndex##_desc) | ||
| 95 | + | ||
| 96 | + | ||
| 97 | + do { \ | ||
| 98 | + printf(message, ##__VA_ARGS__); \ | ||
| 99 | + } while (0) | ||
| 100 | + | ||
| 101 | + | ||
| 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 | +} | ||
| @@ -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() | ||
| @@ -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 | +} | ||
| @@ -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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 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 | ||
| @@ -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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 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 | + | ||
| @@ -0,0 +1,193 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | + | ||
| 10 | + | ||
| 11 | + | ||
| 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 | + | ||
| 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 | +} | ||