已合并
[feat]Add two aicpu op ReduceAll and Div #2750
FengHaozhan创建于 5月14日
[feat]Add two aicpu op ReduceAll and Div #2750
已合并
共 13 个文件变更+1682-3
| @@ -592,7 +592,7 @@ | |||
| 592 | <td>√</td> | 592 | <td>√</td> |
| 593 | <td>√</td> | 593 | <td>√</td> |
| 594 | <td>√</td> | 594 | <td>√</td> |
| 595 | - <td>AI Core</td> | 595 | + <td>AI Core/AI CPU</td> |
| 596 | <td>张量运算函数,用于执行乘除加组合操作,将张量除法(带缩放)+ 张量加法合并为单个操作。</td> | 596 | <td>张量运算函数,用于执行乘除加组合操作,将张量除法(带缩放)+ 张量加法合并为单个操作。</td> |
| 597 | </tr> | 597 | </tr> |
| 598 | <tr> | 598 | <tr> |
| @@ -1392,7 +1392,7 @@ | |||
| 1392 | <td>×</td> | 1392 | <td>×</td> |
| 1393 | <td>×</td> | 1393 | <td>×</td> |
| 1394 | <td>×</td> | 1394 | <td>×</td> |
| 1395 | - <td>AI Core</td> | 1395 | + <td>AI Core/AI CPU</td> |
| 1396 | <td>该算子暂无Ascend C代码实现,欢迎开发者补充贡献,贡献方式参考<a href="../../CONTRIBUTING.md">贡献指南</a>。</td> | 1396 | <td>该算子暂无Ascend C代码实现,欢迎开发者补充贡献,贡献方式参考<a href="../../CONTRIBUTING.md">贡献指南</a>。</td> |
| 1397 | </tr> | 1397 | </tr> |
| 1398 | <tr> | 1398 | <tr> |
| @@ -73,3 +73,4 @@ | |||
| 73 | | 调用方式 | 样例代码 | 说明 | | 73 | | 调用方式 | 样例代码 | 说明 | |
| 74 | | ---------------- | --------------------------- | --------------------------------------------------- | | 74 | | ---------------- | --------------------------- | --------------------------------------------------- | |
| 75 | | aclnn接口 | [test_aclnn_div](examples/test_aclnn_div.cpp) | 通过[AclnnDiv](docs/aclnnDiv&aclnnInplaceDiv.md)接口方式调用Div算子。 | | 75 | | aclnn接口 | [test_aclnn_div](examples/test_aclnn_div.cpp) | 通过[AclnnDiv](docs/aclnnDiv&aclnnInplaceDiv.md)接口方式调用Div算子。 | |
| 76 | +| 图模式调用 | [test_geir_div](examples/test_geir_div.cpp) | 通过[算子IR](op_graph/div_proto.h)构图方式调用Div算子。 | | ||
| @@ -0,0 +1,216 @@ | |||
| 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 | + * \file test_geir_div.cpp | ||
| 13 | + * \brief Test Div via GE IR graph mode | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + | ||
| 39 | +using namespace ge; | ||
| 40 | +using std::map; | ||
| 41 | +using std::string; | ||
| 42 | +using std::vector; | ||
| 43 | + | ||
| 44 | +string GetTime() | ||
| 45 | +{ | ||
| 46 | + time_t timep; | ||
| 47 | + time(&timep); | ||
| 48 | + char tmp[64]; | ||
| 49 | + struct tm tm_info; | ||
| 50 | + strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S,000", localtime_r(&timep, &tm_info)); | ||
| 51 | + return tmp; | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +template <typename T> | ||
| 55 | +int32_t GenTensorData(const vector<int64_t>& shapes, const vector<T>& values, TensorDesc& tensor_desc, Tensor& tensor) | ||
| 56 | +{ | ||
| 57 | + tensor_desc.SetRealDimCnt(shapes.size()); | ||
| 58 | + size_t element_count = 1; | ||
| 59 | + for (size_t i = 0; i < shapes.size(); ++i) { | ||
| 60 | + element_count *= static_cast<size_t>(shapes[i]); | ||
| 61 | + } | ||
| 62 | + if (values.size() != element_count) { | ||
| 63 | + return FAILED; | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + tensor = Tensor( | ||
| 67 | + tensor_desc, reinterpret_cast<const uint8_t*>(values.data()), | ||
| 68 | + values.size() * sizeof(typename vector<T>::value_type)); | ||
| 69 | + return SUCCESS; | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +int CreateOpInGraph(vector<Tensor>& input_tensors, vector<Operator>& inputs, vector<Operator>& outputs, Graph& graph) | ||
| 73 | +{ | ||
| 74 | + Status ret = SUCCESS; | ||
| 75 | + auto div_op = op::Div("div_graph"); | ||
| 76 | + vector<int64_t> input_shape = {4, 2}; | ||
| 77 | + | ||
| 78 | + auto x1 = op::Data("x1").set_attr_index(0); | ||
| 79 | + TensorDesc x1_desc(Shape(input_shape), FORMAT_ND, DT_DOUBLE); | ||
| 80 | + x1_desc.SetPlacement(ge::kPlacementHost); | ||
| 81 | + x1_desc.SetFormat(FORMAT_ND); | ||
| 82 | + Tensor x1_tensor; | ||
| 83 | + ret = GenTensorData<double>(input_shape, {8.0, 9.0, 10.0, 12.0, 15.0, 21.0, 28.0, 32.0}, x1_desc, x1_tensor); | ||
| 84 | + if (ret != SUCCESS) { | ||
| 85 | + printf("%s - ERROR - [XIR]: Generate x1 data failed\n", GetTime().c_str()); | ||
| 86 | + return FAILED; | ||
| 87 | + } | ||
| 88 | + x1.update_input_desc_x(x1_desc); | ||
| 89 | + x1.update_output_desc_y(x1_desc); | ||
| 90 | + input_tensors.push_back(x1_tensor); | ||
| 91 | + graph.AddOp(x1); | ||
| 92 | + div_op.set_input_x1(x1); | ||
| 93 | + div_op.update_input_desc_x1(x1_desc); | ||
| 94 | + inputs.push_back(x1); | ||
| 95 | + | ||
| 96 | + auto x2 = op::Data("x2").set_attr_index(1); | ||
| 97 | + TensorDesc x2_desc(Shape(input_shape), FORMAT_ND, DT_DOUBLE); | ||
| 98 | + x2_desc.SetPlacement(ge::kPlacementHost); | ||
| 99 | + x2_desc.SetFormat(FORMAT_ND); | ||
| 100 | + Tensor x2_tensor; | ||
| 101 | + ret = GenTensorData<double>(input_shape, {2.0, 3.0, 5.0, 3.0, 5.0, 7.0, 4.0, 8.0}, x2_desc, x2_tensor); | ||
| 102 | + if (ret != SUCCESS) { | ||
| 103 | + printf("%s - ERROR - [XIR]: Generate x2 data failed\n", GetTime().c_str()); | ||
| 104 | + return FAILED; | ||
| 105 | + } | ||
| 106 | + x2.update_input_desc_x(x2_desc); | ||
| 107 | + x2.update_output_desc_y(x2_desc); | ||
| 108 | + input_tensors.push_back(x2_tensor); | ||
| 109 | + graph.AddOp(x2); | ||
| 110 | + div_op.set_input_x2(x2); | ||
| 111 | + div_op.update_input_desc_x2(x2_desc); | ||
| 112 | + inputs.push_back(x2); | ||
| 113 | + | ||
| 114 | + TensorDesc y_desc(Shape(input_shape), FORMAT_ND, DT_DOUBLE); | ||
| 115 | + div_op.update_output_desc_y(y_desc); | ||
| 116 | + outputs.push_back(div_op); | ||
| 117 | + return SUCCESS; | ||
| 118 | +} | ||
| 119 | + | ||
| 120 | +int VerifyOutput(const vector<Tensor>& output_tensors) | ||
| 121 | +{ | ||
| 122 | + static const double expected[] = {4.0, 3.0, 2.0, 4.0, 3.0, 3.0, 7.0, 4.0}; | ||
| 123 | + if (output_tensors.size() != 1) { | ||
| 124 | + printf("Unexpected output tensor count: %zu\n", output_tensors.size()); | ||
| 125 | + return FAILED; | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + const Tensor& output_tensor = output_tensors[0]; | ||
| 129 | + int64_t element_count = output_tensor.GetTensorDesc().GetShape().GetShapeSize(); | ||
| 130 | + if (element_count != static_cast<int64_t>(sizeof(expected) / sizeof(expected[0]))) { | ||
| 131 | + printf("Unexpected output element count: %ld\n", element_count); | ||
| 132 | + return FAILED; | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + const auto* result_data = reinterpret_cast<const double*>(output_tensor.GetData()); | ||
| 136 | + for (int64_t i = 0; i < element_count; ++i) { | ||
| 137 | + printf("result[%ld] is: %.6f\n", i, result_data[i]); | ||
| 138 | + if (std::fabs(result_data[i] - expected[i]) > 1e-9) { | ||
| 139 | + printf("Div output mismatch at index %ld, expect %.6f but got %.6f\n", i, expected[i], result_data[i]); | ||
| 140 | + return FAILED; | ||
| 141 | + } | ||
| 142 | + } | ||
| 143 | + return SUCCESS; | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +int main(int argc, char* argv[]) | ||
| 147 | +{ | ||
| 148 | + (void)argc; | ||
| 149 | + (void)argv; | ||
| 150 | + | ||
| 151 | + const char* graph_name = "tc_ge_irrun_div"; | ||
| 152 | + Graph graph(graph_name); | ||
| 153 | + vector<Tensor> input_tensors; | ||
| 154 | + | ||
| 155 | + printf("%s - INFO - [XIR]: Start to initialize ge using ge global options\n", GetTime().c_str()); | ||
| 156 | + map<AscendString, AscendString> global_options = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}}; | ||
| 157 | + Status ret = ge::GEInitialize(global_options); | ||
| 158 | + if (ret != SUCCESS) { | ||
| 159 | + printf("%s - ERROR - [XIR]: Initialize ge using ge global options failed\n", GetTime().c_str()); | ||
| 160 | + return FAILED; | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + vector<Operator> inputs; | ||
| 164 | + vector<Operator> outputs; | ||
| 165 | + ret = CreateOpInGraph(input_tensors, inputs, outputs, graph); | ||
| 166 | + if (ret != SUCCESS) { | ||
| 167 | + ge::GEFinalize(); | ||
| 168 | + return FAILED; | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + if (!inputs.empty() && !outputs.empty()) { | ||
| 172 | + graph.SetInputs(inputs).SetOutputs(outputs); | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + map<AscendString, AscendString> build_options; | ||
| 176 | + ge::Session* session = new (std::nothrow) Session(build_options); | ||
| 177 | + if (session == nullptr) { | ||
| 178 | + printf("%s - ERROR - [XIR]: Create ir session failed\n", GetTime().c_str()); | ||
| 179 | + ge::GEFinalize(); | ||
| 180 | + return FAILED; | ||
| 181 | + } | ||
| 182 | + | ||
| 183 | + map<AscendString, AscendString> graph_options = {{"ge.exec.precision_mode", "allow_mix_precision"}}; | ||
| 184 | + uint32_t graph_id = 0; | ||
| 185 | + ret = session->AddGraph(graph_id, graph, graph_options); | ||
| 186 | + if (ret != SUCCESS) { | ||
| 187 | + printf("%s - ERROR - [XIR]: Add graph failed\n", GetTime().c_str()); | ||
| 188 | + delete session; | ||
| 189 | + ge::GEFinalize(); | ||
| 190 | + return FAILED; | ||
| 191 | + } | ||
| 192 | + | ||
| 193 | + vector<Tensor> output_tensors; | ||
| 194 | + ret = session->RunGraph(graph_id, input_tensors, output_tensors); | ||
| 195 | + if (ret != SUCCESS) { | ||
| 196 | + printf("%s - ERROR - [XIR]: Run graph failed\n", GetTime().c_str()); | ||
| 197 | + delete session; | ||
| 198 | + ge::GEFinalize(); | ||
| 199 | + return FAILED; | ||
| 200 | + } | ||
| 201 | + | ||
| 202 | + ret = VerifyOutput(output_tensors); | ||
| 203 | + delete session; | ||
| 204 | + if (ret != SUCCESS) { | ||
| 205 | + ge::GEFinalize(); | ||
| 206 | + return FAILED; | ||
| 207 | + } | ||
| 208 | + | ||
| 209 | + printf("%s - INFO - [XIR]: Div double graph example verified successfully\n", GetTime().c_str()); | ||
| 210 | + ret = ge::GEFinalize(); | ||
| 211 | + if (ret != SUCCESS) { | ||
| 212 | + printf("%s - ERROR - [XIR]: GE Finalize failed\n", GetTime().c_str()); | ||
| 213 | + return FAILED; | ||
| 214 | + } | ||
| 215 | + return SUCCESS; | ||
| 216 | +} | ||
| @@ -0,0 +1,458 @@ | |||
| 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 | +namespace { | ||
| 23 | +const uint32_t kOutputNum = 1; | ||
| 24 | +const uint32_t kInputNum = 2; | ||
| 25 | +const char* kDiv = "Div"; | ||
| 26 | +constexpr int64_t kParallelDataNum = 2 * 1024; | ||
| 27 | +constexpr int64_t kParallelDataNumMid = 16 * 1024; | ||
| 28 | +constexpr int64_t kParallelDataNumSameShape = 7 * 1024; | ||
| 29 | +constexpr int64_t kParallelDataNumSameShapeMid = 35 * 1024; | ||
| 30 | + | ||
| 31 | +template <typename T> | ||
| 32 | +typename std::enable_if<std::is_signed<T>::value, bool>::type IsDivOverflow(T lhs, T rhs) | ||
| 33 | +{ | ||
| 34 | + return lhs == std::numeric_limits<T>::min() && rhs == static_cast<T>(-1); | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +template <typename T> | ||
| 38 | +typename std::enable_if<!std::is_signed<T>::value, bool>::type IsDivOverflow(T, T) | ||
| 39 | +{ | ||
| 40 | + return false; | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +template <typename T> | ||
| 44 | +typename std::enable_if<std::is_signed<T>::value, bool>::type NeedFloorAdjust(T lhs, T rhs, T mod) | ||
| 45 | +{ | ||
| 46 | + return mod != static_cast<T>(0) && ((lhs < static_cast<T>(0)) != (rhs < static_cast<T>(0))); | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +template <typename T> | ||
| 50 | +typename std::enable_if<!std::is_signed<T>::value, bool>::type NeedFloorAdjust(T, T, T) | ||
| 51 | +{ | ||
| 52 | + return false; | ||
| 53 | +} | ||
| 54 | + | ||
| 55 | +template <typename T> | ||
| 56 | +uint32_t CheckDivOverflow(T lhs, T rhs) | ||
| 57 | +{ | ||
| 58 | + if (IsDivOverflow(lhs, rhs)) { | ||
| 59 | + KERNEL_LOG_ERROR("Invalid argument: Integer division overflow."); | ||
| 60 | + return aicpu::KERNEL_STATUS_INNER_ERROR; | ||
| 61 | + } | ||
| 62 | + return aicpu::KERNEL_STATUS_OK; | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | +template <typename T> | ||
| 66 | +uint32_t CheckNoBcastDivOverflow(aicpu::CpuKernelContext& ctx) | ||
| 67 | +{ | ||
| 68 | + if (!std::is_signed<T>::value) { | ||
| 69 | + return aicpu::KERNEL_STATUS_OK; | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + auto input0 = reinterpret_cast<const T*>(ctx.Input(0)->GetData()); | ||
| 73 | + auto input1 = reinterpret_cast<const T*>(ctx.Input(1)->GetData()); | ||
| 74 | + int64_t input0_elements_nums = ctx.Input(0)->NumElements(); | ||
| 75 | + int64_t input1_elements_nums = ctx.Input(1)->NumElements(); | ||
| 76 | + int64_t data_num = ctx.Output(0)->NumElements(); | ||
| 77 | + aicpu::BcastShapeType type = input0_elements_nums == input1_elements_nums ? | ||
| 78 | + aicpu::BcastShapeType::SAME_SHAPE : | ||
| 79 | + (input0_elements_nums == 1 ? aicpu::BcastShapeType::X_ONE_ELEMENT : | ||
| 80 | + aicpu::BcastShapeType::Y_ONE_ELEMENT); | ||
| 81 | + | ||
| 82 | + switch (type) { | ||
| 83 | + case aicpu::BcastShapeType::SAME_SHAPE: | ||
| 84 | + for (int64_t i = 0; i < data_num; ++i) { | ||
| 85 | + uint32_t result = CheckDivOverflow(*(input0 + i), *(input1 + i)); | ||
| 86 | + if (result != aicpu::KERNEL_STATUS_OK) { | ||
| 87 | + return result; | ||
| 88 | + } | ||
| 89 | + } | ||
| 90 | + break; | ||
| 91 | + case aicpu::BcastShapeType::X_ONE_ELEMENT: | ||
| 92 | + for (int64_t i = 0; i < data_num; ++i) { | ||
| 93 | + uint32_t result = CheckDivOverflow(*input0, *(input1 + i)); | ||
| 94 | + if (result != aicpu::KERNEL_STATUS_OK) { | ||
| 95 | + return result; | ||
| 96 | + } | ||
| 97 | + } | ||
| 98 | + break; | ||
| 99 | + case aicpu::BcastShapeType::Y_ONE_ELEMENT: | ||
| 100 | + for (int64_t i = 0; i < data_num; ++i) { | ||
| 101 | + uint32_t result = CheckDivOverflow(*(input0 + i), *input1); | ||
| 102 | + if (result != aicpu::KERNEL_STATUS_OK) { | ||
| 103 | + return result; | ||
| 104 | + } | ||
| 105 | + } | ||
| 106 | + break; | ||
| 107 | + default: | ||
| 108 | + KERNEL_LOG_WARN("Invalid type [%d]", static_cast<int32_t>(type)); | ||
| 109 | + break; | ||
| 110 | + } | ||
| 111 | + return aicpu::KERNEL_STATUS_OK; | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | +template <typename T> | ||
| 115 | +uint32_t CheckBcastDivOverflow(aicpu::CpuKernelContext& ctx, aicpu::Bcast& bcast) | ||
| 116 | +{ | ||
| 117 | + if (!std::is_signed<T>::value) { | ||
| 118 | + return aicpu::KERNEL_STATUS_OK; | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + auto input0 = reinterpret_cast<const T*>(ctx.Input(0)->GetData()); | ||
| 122 | + auto input1 = reinterpret_cast<const T*>(ctx.Input(1)->GetData()); | ||
| 123 | + int64_t data_num = ctx.Output(0)->NumElements(); | ||
| 124 | + for (int64_t i = 0; i < data_num; ++i) { | ||
| 125 | + T lhs = *(input0 + bcast.GetBroadcastXIndex(i)); | ||
| 126 | + T rhs = *(input1 + bcast.GetBroadcastYIndex(i)); | ||
| 127 | + uint32_t result = CheckDivOverflow(lhs, rhs); | ||
| 128 | + if (result != aicpu::KERNEL_STATUS_OK) { | ||
| 129 | + return result; | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + return aicpu::KERNEL_STATUS_OK; | ||
| 133 | +} | ||
| 134 | + | ||
| 135 | +inline aicpu::BcastShapeType GetNoBcastShapeType(int64_t input0_elements_nums, int64_t input1_elements_nums) | ||
| 136 | +{ | ||
| 137 | + return input0_elements_nums == input1_elements_nums ? | ||
| 138 | + aicpu::BcastShapeType::SAME_SHAPE : | ||
| 139 | + (input0_elements_nums == 1 ? aicpu::BcastShapeType::X_ONE_ELEMENT : | ||
| 140 | + aicpu::BcastShapeType::Y_ONE_ELEMENT); | ||
| 141 | +} | ||
| 142 | + | ||
| 143 | +inline uint32_t GetDivParallelCoreNum( | ||
| 144 | + const aicpu::CpuKernelContext& ctx, int64_t data_num, int64_t parallel_data_num_mid) | ||
| 145 | +{ | ||
| 146 | + uint32_t min_core_num = 1; | ||
| 147 | + uint32_t max_core_num = std::max(min_core_num, aicpu::CpuKernelUtils::GetCPUNum(ctx) - 2); | ||
| 148 | + if (data_num <= parallel_data_num_mid) { | ||
| 149 | + max_core_num = std::min(max_core_num, 4U); | ||
| 150 | + } | ||
| 151 | + if (max_core_num > data_num) { | ||
| 152 | + max_core_num = data_num; | ||
| 153 | + } | ||
| 154 | + return max_core_num; | ||
| 155 | +} | ||
| 156 | + | ||
| 157 | +template <typename ComputeFn> | ||
| 158 | +uint32_t RunDivRangeCompute(const aicpu::CpuKernelContext& ctx, int64_t data_num, int64_t parallel_data_num, | ||
| 159 | + int64_t parallel_data_num_mid, const ComputeFn& compute) | ||
| 160 | +{ | ||
| 161 | + if (data_num < parallel_data_num) { | ||
| 162 | + return compute(0, data_num); | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + uint32_t max_core_num = GetDivParallelCoreNum(ctx, data_num, parallel_data_num_mid); | ||
| 166 | + auto sharder_div = [&](int64_t start, int64_t end) { (void)compute(start, end); }; | ||
| 167 | + return aicpu::CpuKernelUtils::ParallelFor(ctx, data_num, data_num / max_core_num, sharder_div); | ||
| 168 | +} | ||
| 169 | + | ||
| 170 | +template <typename T> | ||
| 171 | +uint32_t ComputeIntDivValue(T lhs, T rhs, T* output) | ||
| 172 | +{ | ||
| 173 | + if (rhs == static_cast<T>(0)) { | ||
| 174 | + KERNEL_LOG_ERROR("Invalid argument: Division by zero."); | ||
| 175 | + return aicpu::KERNEL_STATUS_INNER_ERROR; | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | + T mod = lhs % rhs; | ||
| 179 | + if (NeedFloorAdjust(lhs, rhs, mod)) { | ||
| 180 | + *output = lhs / rhs - static_cast<T>(1); | ||
| 181 | + } else { | ||
| 182 | + *output = lhs / rhs; | ||
| 183 | + } | ||
| 184 | + return aicpu::KERNEL_STATUS_OK; | ||
| 185 | +} | ||
| 186 | + | ||
| 187 | +template <typename T, typename LhsGetter, typename RhsGetter> | ||
| 188 | +uint32_t ComputeIntDivRange( | ||
| 189 | + int64_t start, int64_t end, T* output, const LhsGetter& lhs_getter, const RhsGetter& rhs_getter) | ||
| 190 | +{ | ||
| 191 | + for (int64_t i = start; i < end; ++i) { | ||
| 192 | + uint32_t result = ComputeIntDivValue(lhs_getter(i), rhs_getter(i), output + i); | ||
| 193 | + if (result != aicpu::KERNEL_STATUS_OK) { | ||
| 194 | + return result; | ||
| 195 | + } | ||
| 196 | + } | ||
| 197 | + return aicpu::KERNEL_STATUS_OK; | ||
| 198 | +} | ||
| 199 | + | ||
| 200 | +template <typename T, typename LhsGetter, typename RhsGetter> | ||
| 201 | +uint32_t ComputeDivRange( | ||
| 202 | + int64_t start, int64_t end, T* output, const LhsGetter& lhs_getter, const RhsGetter& rhs_getter) | ||
| 203 | +{ | ||
| 204 | + for (int64_t i = start; i < end; ++i) { | ||
| 205 | + *(output + i) = lhs_getter(i) / rhs_getter(i); | ||
| 206 | + } | ||
| 207 | + return aicpu::KERNEL_STATUS_OK; | ||
| 208 | +} | ||
| 209 | + | ||
| 210 | + | ||
| 211 | + case (DTYPE): { \ | ||
| 212 | + uint32_t result = DivComputeInt<TYPE>(CTX); \ | ||
| 213 | + if (result != KERNEL_STATUS_OK) { \ | ||
| 214 | + KERNEL_LOG_ERROR("Div kernel compute failed."); \ | ||
| 215 | + return result; \ | ||
| 216 | + } \ | ||
| 217 | + break; \ | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | + | ||
| 221 | + case (DTYPE): { \ | ||
| 222 | + uint32_t result = DivCompute<TYPE>(CTX); \ | ||
| 223 | + if (result != KERNEL_STATUS_OK) { \ | ||
| 224 | + KERNEL_LOG_ERROR("Div kernel compute failed."); \ | ||
| 225 | + return result; \ | ||
| 226 | + } \ | ||
| 227 | + break; \ | ||
| 228 | + } | ||
| 229 | +} // namespace | ||
| 230 | + | ||
| 231 | +namespace aicpu { | ||
| 232 | +uint32_t DivCpuKernel::Compute(CpuKernelContext& ctx) | ||
| 233 | +{ | ||
| 234 | + KERNEL_HANDLE_ERROR(NormalCheck(ctx, kInputNum, kOutputNum), "[%s] check input and output failed.", kDiv); | ||
| 235 | + KERNEL_HANDLE_ERROR(DivParamCheck(ctx), "Div check params failed."); | ||
| 236 | + | ||
| 237 | + auto data_type = ctx.Input(0)->GetDataType(); | ||
| 238 | + switch (data_type) { | ||
| 239 | + DIV_COMPUTE_CASE_INT(DT_INT8, int8_t, ctx) | ||
| 240 | + DIV_COMPUTE_CASE_INT(DT_INT16, int16_t, ctx) | ||
| 241 | + DIV_COMPUTE_CASE_INT(DT_INT32, int32_t, ctx) | ||
| 242 | + DIV_COMPUTE_CASE_INT(DT_INT64, int64_t, ctx) | ||
| 243 | + DIV_COMPUTE_CASE_INT(DT_UINT8, uint8_t, ctx) | ||
| 244 | + DIV_COMPUTE_CASE_INT(DT_UINT16, uint16_t, ctx) | ||
| 245 | + DIV_COMPUTE_CASE(DT_FLOAT16, Eigen::half, ctx) | ||
| 246 | + DIV_COMPUTE_CASE(DT_FLOAT, float, ctx) | ||
| 247 | + DIV_COMPUTE_CASE(DT_DOUBLE, double, ctx) | ||
| 248 | + DIV_COMPUTE_CASE(DT_COMPLEX64, std::complex<float>, ctx) | ||
| 249 | + DIV_COMPUTE_CASE(DT_COMPLEX128, std::complex<double>, ctx) | ||
| 250 | + default: | ||
| 251 | + KERNEL_LOG_ERROR("Div kernel data type [%s] not support.", DTypeStr(data_type).c_str()); | ||
| 252 | + return KERNEL_STATUS_PARAM_INVALID; | ||
| 253 | + } | ||
| 254 | + return KERNEL_STATUS_OK; | ||
| 255 | +} | ||
| 256 | + | ||
| 257 | +uint32_t DivCpuKernel::DivParamCheck(CpuKernelContext& ctx) | ||
| 258 | +{ | ||
| 259 | + Tensor* input_0 = ctx.Input(0); | ||
| 260 | + Tensor* input_1 = ctx.Input(1); | ||
| 261 | + Tensor* output = ctx.Output(0); | ||
| 262 | + KERNEL_CHECK_NULLPTR(input_0->GetData(), KERNEL_STATUS_PARAM_INVALID, "Get input 0 data failed.") | ||
| 263 | + KERNEL_CHECK_NULLPTR(input_1->GetData(), KERNEL_STATUS_PARAM_INVALID, "Get input 1 data failed.") | ||
| 264 | + KERNEL_CHECK_NULLPTR(output->GetData(), KERNEL_STATUS_PARAM_INVALID, "Get output data failed") | ||
| 265 | + DataType input0_type = input_0->GetDataType(); | ||
| 266 | + DataType input1_type = input_1->GetDataType(); | ||
| 267 | + KERNEL_CHECK_FALSE( | ||
| 268 | + (input0_type == input1_type), KERNEL_STATUS_PARAM_INVALID, | ||
| 269 | + "The data type of input0 [%s] need be same with " | ||
| 270 | + "input1 [%s].", | ||
| 271 | + DTypeStr(input0_type).c_str(), DTypeStr(input1_type).c_str()) | ||
| 272 | + KERNEL_LOG_DEBUG( | ||
| 273 | + "DivCpuKernel[%s], input0: size[%llu];" | ||
| 274 | + "input1: size[%llu], output: size[%llu].", | ||
| 275 | + ctx.GetOpType().c_str(), input_0->GetDataSize(), input_1->GetDataSize(), output->GetDataSize()); | ||
| 276 | + | ||
| 277 | + return KERNEL_STATUS_OK; | ||
| 278 | +} | ||
| 279 | + | ||
| 280 | +template <typename T> | ||
| 281 | +uint32_t DivCpuKernel::DivParamCheckZero(CpuKernelContext& ctx) | ||
| 282 | +{ | ||
| 283 | + auto input1 = reinterpret_cast<T*>(ctx.Input(1)->GetData()); | ||
| 284 | + int64_t input1_elements_nums = ctx.Input(1)->NumElements(); | ||
| 285 | + for (int64_t i = 0; i < input1_elements_nums; i++) { | ||
| 286 | + if (static_cast<double>(*(input1 + i)) == 0) { | ||
| 287 | + KERNEL_LOG_ERROR("Invalid argument: Division by zero."); | ||
| 288 | + return KERNEL_STATUS_INNER_ERROR; | ||
| 289 | + } | ||
| 290 | + } | ||
| 291 | + return KERNEL_STATUS_OK; | ||
| 292 | +} | ||
| 293 | + | ||
| 294 | +/** | ||
| 295 | +special compute is used in the following situations. | ||
| 296 | +1. the shapes of input1 and input2 are the same | ||
| 297 | +2. input1 is a 1D tensor with only one element or input1 is scalar | ||
| 298 | +3. input2 is a 1D tensor with only one element or input2 is scalar | ||
| 299 | +4. the shapes of input1 and input2 are different | ||
| 300 | +*/ | ||
| 301 | +template <typename T> | ||
| 302 | +uint32_t DivCpuKernel::SpecialComputeInt( | ||
| 303 | + BcastShapeType type, int64_t start, int64_t end, const T* input1, const T* input2, T* output) | ||
| 304 | +{ | ||
| 305 | + switch (type) { | ||
| 306 | + case BcastShapeType::SAME_SHAPE: | ||
| 307 | + return ComputeIntDivRange<T>( | ||
| 308 | + start, end, output, [&](int64_t i) { return *(input1 + i); }, [&](int64_t i) { return *(input2 + i); }); | ||
| 309 | + case BcastShapeType::X_ONE_ELEMENT: | ||
| 310 | + return ComputeIntDivRange<T>( | ||
| 311 | + start, end, output, [&](int64_t) { return *input1; }, [&](int64_t i) { return *(input2 + i); }); | ||
| 312 | + case BcastShapeType::Y_ONE_ELEMENT: | ||
| 313 | + return ComputeIntDivRange<T>( | ||
| 314 | + start, end, output, [&](int64_t i) { return *(input1 + i); }, [&](int64_t) { return *input2; }); | ||
| 315 | + default: | ||
| 316 | + KERNEL_LOG_WARN("Invalid type [%d]", static_cast<int32_t>(type)); | ||
| 317 | + break; | ||
| 318 | + } | ||
| 319 | + return KERNEL_STATUS_OK; | ||
| 320 | +} | ||
| 321 | + | ||
| 322 | +template <typename T> | ||
| 323 | +uint32_t DivCpuKernel::SpecialCompute( | ||
| 324 | + BcastShapeType type, int64_t start, int64_t end, const T* input1, const T* input2, T* output) | ||
| 325 | +{ | ||
| 326 | + switch (type) { | ||
| 327 | + case BcastShapeType::SAME_SHAPE: | ||
| 328 | + return ComputeDivRange<T>( | ||
| 329 | + start, end, output, [&](int64_t i) { return *(input1 + i); }, [&](int64_t i) { return *(input2 + i); }); | ||
| 330 | + case BcastShapeType::X_ONE_ELEMENT: | ||
| 331 | + return ComputeDivRange<T>( | ||
| 332 | + start, end, output, [&](int64_t) { return *input1; }, [&](int64_t i) { return *(input2 + i); }); | ||
| 333 | + case BcastShapeType::Y_ONE_ELEMENT: | ||
| 334 | + return ComputeDivRange<T>( | ||
| 335 | + start, end, output, [&](int64_t i) { return *(input1 + i); }, [&](int64_t) { return *input2; }); | ||
| 336 | + default: | ||
| 337 | + KERNEL_LOG_WARN("Invalid type [%d]", static_cast<int32_t>(type)); | ||
| 338 | + break; | ||
| 339 | + } | ||
| 340 | + return KERNEL_STATUS_OK; | ||
| 341 | +} | ||
| 342 | + | ||
| 343 | +template <typename T> | ||
| 344 | +uint32_t DivCpuKernel::NoBcastComputeInt(CpuKernelContext& ctx) | ||
| 345 | +{ | ||
| 346 | + auto in0 = reinterpret_cast<T*>(ctx.Input(0)->GetData()); | ||
| 347 | + auto in1 = reinterpret_cast<T*>(ctx.Input(1)->GetData()); | ||
| 348 | + auto out = reinterpret_cast<T*>(ctx.Output(0)->GetData()); | ||
| 349 | + int64_t in0_elements_nums = ctx.Input(0)->NumElements(); | ||
| 350 | + int64_t in1_elements_nums = ctx.Input(1)->NumElements(); | ||
| 351 | + int64_t data_num = ctx.Output(0)->NumElements(); | ||
| 352 | + BcastShapeType type = GetNoBcastShapeType(in0_elements_nums, in1_elements_nums); | ||
| 353 | + return RunDivRangeCompute(ctx, data_num, kParallelDataNumSameShape, kParallelDataNumSameShapeMid, | ||
| 354 | + [&](int64_t start, int64_t end) { return SpecialComputeInt<T>(type, start, end, in0, in1, out); }); | ||
| 355 | +} | ||
| 356 | + | ||
| 357 | +template <typename T> | ||
| 358 | +uint32_t DivCpuKernel::NoBcastCompute(CpuKernelContext& ctx) | ||
| 359 | +{ | ||
| 360 | + auto in0 = reinterpret_cast<T*>(ctx.Input(0)->GetData()); | ||
| 361 | + auto in1 = reinterpret_cast<T*>(ctx.Input(1)->GetData()); | ||
| 362 | + auto out = reinterpret_cast<T*>(ctx.Output(0)->GetData()); | ||
| 363 | + int64_t in0_elements_nums = ctx.Input(0)->NumElements(); | ||
| 364 | + int64_t in1_elements_nums = ctx.Input(1)->NumElements(); | ||
| 365 | + int64_t data_num = ctx.Output(0)->NumElements(); | ||
| 366 | + BcastShapeType type = GetNoBcastShapeType(in0_elements_nums, in1_elements_nums); | ||
| 367 | + return RunDivRangeCompute(ctx, data_num, kParallelDataNumSameShape, kParallelDataNumSameShapeMid, | ||
| 368 | + [&](int64_t start, int64_t end) { return SpecialCompute<T>(type, start, end, in0, in1, out); }); | ||
| 369 | +} | ||
| 370 | + | ||
| 371 | +template <typename T> | ||
| 372 | +uint32_t DivCpuKernel::BcastComputeInt(CpuKernelContext& ctx, Bcast& bcast) | ||
| 373 | +{ | ||
| 374 | + auto in0 = reinterpret_cast<T*>(ctx.Input(0)->GetData()); | ||
| 375 | + auto in1 = reinterpret_cast<T*>(ctx.Input(1)->GetData()); | ||
| 376 | + auto out = reinterpret_cast<T*>(ctx.Output(0)->GetData()); | ||
| 377 | + int64_t data_num = ctx.Output(0)->NumElements(); | ||
| 378 | + return RunDivRangeCompute(ctx, data_num, kParallelDataNum, kParallelDataNumMid, | ||
| 379 | + [&](int64_t start, int64_t end) { | ||
| 380 | + return ComputeIntDivRange<T>(start, end, out, [&](int64_t i) { return *(in0 + bcast.GetBroadcastXIndex(i)); }, | ||
| 381 | + [&](int64_t i) { return *(in1 + bcast.GetBroadcastYIndex(i)); }); | ||
| 382 | + }); | ||
| 383 | +} | ||
| 384 | + | ||
| 385 | +template <typename T> | ||
| 386 | +uint32_t DivCpuKernel::BcastCompute(CpuKernelContext& ctx, Bcast& bcast) | ||
| 387 | +{ | ||
| 388 | + auto in0 = reinterpret_cast<T*>(ctx.Input(0)->GetData()); | ||
| 389 | + auto in1 = reinterpret_cast<T*>(ctx.Input(1)->GetData()); | ||
| 390 | + auto out = reinterpret_cast<T*>(ctx.Output(0)->GetData()); | ||
| 391 | + int64_t data_num = ctx.Output(0)->NumElements(); | ||
| 392 | + return RunDivRangeCompute(ctx, data_num, kParallelDataNum, kParallelDataNumMid, | ||
| 393 | + [&](int64_t start, int64_t end) { | ||
| 394 | + return ComputeDivRange<T>(start, end, out, [&](int64_t i) { return *(in0 + bcast.GetBroadcastXIndex(i)); }, | ||
| 395 | + [&](int64_t i) { return *(in1 + bcast.GetBroadcastYIndex(i)); }); | ||
| 396 | + }); | ||
| 397 | +} | ||
| 398 | + | ||
| 399 | +template <typename T> | ||
| 400 | +uint32_t DivCpuKernel::DivComputeInt(CpuKernelContext& ctx) | ||
| 401 | +{ | ||
| 402 | + Tensor* input0_tensor = ctx.Input(0); | ||
| 403 | + auto input0_shape = input0_tensor->GetTensorShape()->GetDimSizes(); | ||
| 404 | + int64_t input0_elements_nums = input0_tensor->NumElements(); | ||
| 405 | + Tensor* input1_tensor = ctx.Input(1); | ||
| 406 | + auto input1_shape = input1_tensor->GetTensorShape()->GetDimSizes(); | ||
| 407 | + int64_t input1_elements_nums = input1_tensor->NumElements(); | ||
| 408 | + bool is_need_bcast = (input0_shape == input1_shape) || (input0_elements_nums == 1) || (input1_elements_nums == 1); | ||
| 409 | + uint32_t result = DivParamCheckZero<T>(ctx); | ||
| 410 | + if (result != KERNEL_STATUS_OK) { | ||
| 411 | + KERNEL_LOG_ERROR("Invalid argument: Division by zero."); | ||
| 412 | + return result; | ||
| 413 | + } | ||
| 414 | + | ||
| 415 | + if (is_need_bcast) { | ||
| 416 | + result = CheckNoBcastDivOverflow<T>(ctx); | ||
| 417 | + if (result != KERNEL_STATUS_OK) { | ||
| 418 | + return result; | ||
| 419 | + } | ||
| 420 | + return NoBcastComputeInt<T>(ctx); | ||
| 421 | + } | ||
| 422 | + | ||
| 423 | + Bcast bcast(input0_shape, input1_shape); | ||
| 424 | + if (!bcast.IsValid()) { | ||
| 425 | + KERNEL_LOG_ERROR("[%s] broadcast failed.", ctx.GetOpType().c_str()); | ||
| 426 | + return KERNEL_STATUS_PARAM_INVALID; | ||
| 427 | + } | ||
| 428 | + result = CheckBcastDivOverflow<T>(ctx, bcast); | ||
| 429 | + if (result != KERNEL_STATUS_OK) { | ||
| 430 | + return result; | ||
| 431 | + } | ||
| 432 | + return BcastComputeInt<T>(ctx, bcast); | ||
| 433 | +} | ||
| 434 | + | ||
| 435 | +template <typename T> | ||
| 436 | +uint32_t DivCpuKernel::DivCompute(CpuKernelContext& ctx) | ||
| 437 | +{ | ||
| 438 | + Tensor* input0_tensor = ctx.Input(0); | ||
| 439 | + auto input0_shape = input0_tensor->GetTensorShape()->GetDimSizes(); | ||
| 440 | + int64_t input0_elements_nums = input0_tensor->NumElements(); | ||
| 441 | + Tensor* input1_tensor = ctx.Input(1); | ||
| 442 | + auto input1_shape = input1_tensor->GetTensorShape()->GetDimSizes(); | ||
| 443 | + int64_t input1_elements_nums = input1_tensor->NumElements(); | ||
| 444 | + bool is_need_bcast = (input0_shape == input1_shape) || (input0_elements_nums == 1) || (input1_elements_nums == 1); | ||
| 445 | + if (is_need_bcast) { | ||
| 446 | + return NoBcastCompute<T>(ctx); | ||
| 447 | + } | ||
| 448 | + | ||
| 449 | + Bcast bcast(input0_shape, input1_shape); | ||
| 450 | + if (!bcast.IsValid()) { | ||
| 451 | + KERNEL_LOG_ERROR("[%s] broadcast failed.", ctx.GetOpType().c_str()); | ||
| 452 | + return KERNEL_STATUS_PARAM_INVALID; | ||
| 453 | + } | ||
| 454 | + return BcastCompute<T>(ctx, bcast); | ||
| 455 | +} | ||
| 456 | + | ||
| 457 | +REGISTER_CPU_KERNEL(kDiv, DivCpuKernel); | ||
| 458 | +} // namespace aicpu | ||
| @@ -0,0 +1,64 @@ | |||
| 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 | +namespace aicpu { | ||
| 22 | +class DivCpuKernel : public CpuKernel { | ||
| 23 | +public: | ||
| 24 | + DivCpuKernel() = default; | ||
| 25 | + ~DivCpuKernel() override = default; | ||
| 26 | + | ||
| 27 | +protected: | ||
| 28 | + uint32_t Compute(CpuKernelContext& ctx) override; | ||
| 29 | + | ||
| 30 | +private: | ||
| 31 | + uint32_t DivParamCheck(CpuKernelContext& ctx); | ||
| 32 | + | ||
| 33 | + template <typename T> | ||
| 34 | + uint32_t DivParamCheckZero(CpuKernelContext& ctx); | ||
| 35 | + | ||
| 36 | + template <typename T> | ||
| 37 | + uint32_t SpecialComputeInt( | ||
| 38 | + BcastShapeType type, int64_t start, int64_t end, const T* input1, const T* input2, T* output); | ||
| 39 | + | ||
| 40 | + template <typename T> | ||
| 41 | + uint32_t SpecialCompute( | ||
| 42 | + BcastShapeType type, int64_t start, int64_t end, const T* input1, const T* input2, T* output); | ||
| 43 | + | ||
| 44 | + template <typename T> | ||
| 45 | + uint32_t NoBcastComputeInt(CpuKernelContext& ctx); | ||
| 46 | + | ||
| 47 | + template <typename T> | ||
| 48 | + uint32_t NoBcastCompute(CpuKernelContext& ctx); | ||
| 49 | + | ||
| 50 | + template <typename T> | ||
| 51 | + uint32_t BcastComputeInt(CpuKernelContext& ctx, Bcast& bcast); | ||
| 52 | + | ||
| 53 | + template <typename T> | ||
| 54 | + uint32_t BcastCompute(CpuKernelContext& ctx, Bcast& bcast); | ||
| 55 | + | ||
| 56 | + template <typename T> | ||
| 57 | + uint32_t DivComputeInt(CpuKernelContext& ctx); | ||
| 58 | + | ||
| 59 | + template <typename T> | ||
| 60 | + uint32_t DivCompute(CpuKernelContext& ctx); | ||
| 61 | +}; | ||
| 62 | +} // namespace aicpu | ||
| 63 | + | ||
| 64 | + | ||
| @@ -0,0 +1,36 @@ | |||
| 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 | +namespace ops { | ||
| 15 | +class Div : public OpDef { | ||
| 16 | +public: | ||
| 17 | + explicit Div(const char* name) : OpDef(name) | ||
| 18 | + { | ||
| 19 | + this->Input("x1").DataType( | ||
| 20 | + {ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_DOUBLE, ge::DT_UINT8, ge::DT_INT8, ge::DT_UINT16, ge::DT_INT16, | ||
| 21 | + ge::DT_INT32, ge::DT_INT64, ge::DT_COMPLEX64, ge::DT_COMPLEX128}); | ||
| 22 | + this->Input("x2").DataType( | ||
| 23 | + {ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_DOUBLE, ge::DT_UINT8, ge::DT_INT8, ge::DT_UINT16, ge::DT_INT16, | ||
| 24 | + ge::DT_INT32, ge::DT_INT64, ge::DT_COMPLEX64, ge::DT_COMPLEX128}); | ||
| 25 | + this->Output("y").DataType( | ||
| 26 | + {ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_DOUBLE, ge::DT_UINT8, ge::DT_INT8, ge::DT_UINT16, ge::DT_INT16, | ||
| 27 | + ge::DT_INT32, ge::DT_INT64, ge::DT_COMPLEX64, ge::DT_COMPLEX128}); | ||
| 28 | + | ||
| 29 | + ApplyMathAicpuDefaultCfg(*this); | ||
| 30 | + this->AICPU().ExtendCfgInfo(OP_INFO_FORMAT_AGNOSTIC.c_str(), TRUE_FORMAT_AGNOSTIC.c_str()); | ||
| 31 | + this->AICPU().ExtendCfgInfo(OP_INFO_OPS_FLAG.c_str(), OPEN_OPS_FLAG.c_str()); | ||
| 32 | + } | ||
| 33 | +}; | ||
| 34 | + | ||
| 35 | +OP_ADD(Div); | ||
| 36 | +} // namespace ops | ||
| @@ -0,0 +1,173 @@ | |||
| 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 | +using namespace std; | ||
| 30 | +using namespace aicpu; | ||
| 31 | + | ||
| 32 | +class TEST_DIV_UT : public testing::Test {}; | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + auto node_def = CpuKernelUtils::CreateNodeDef(); \ | ||
| 36 | + NodeDefBuilder(node_def.get(), "Div", "Div") \ | ||
| 37 | + .Input({"x1", data_types[0], shapes[0], datas[0]}) \ | ||
| 38 | + .Input({"x2", data_types[1], shapes[1], datas[1]}) \ | ||
| 39 | + .Output({"y", data_types[2], shapes[2], datas[2]}) | ||
| 40 | + | ||
| 41 | +TEST_F(TEST_DIV_UT, IntFloorDivisionSameShape) | ||
| 42 | +{ | ||
| 43 | + vector<DataType> data_types = {DT_INT32, DT_INT32, DT_INT32}; | ||
| 44 | + vector<vector<int64_t>> shapes = {{4}, {4}, {4}}; | ||
| 45 | + int32_t input1[4] = {-7, -5, 7, 5}; | ||
| 46 | + int32_t input2[4] = {2, -2, -2, 2}; | ||
| 47 | + int32_t output[4] = {0}; | ||
| 48 | + vector<void*> datas = {input1, input2, output}; | ||
| 49 | + | ||
| 50 | + CREATE_DIV_NODEDEF(shapes, data_types, datas); | ||
| 51 | + RUN_KERNEL(node_def, HOST, KERNEL_STATUS_OK); | ||
| 52 | + | ||
| 53 | + int32_t expected[4] = {-4, 2, -4, 2}; | ||
| 54 | + EXPECT_EQ(CompareResult(output, expected, 4), true); | ||
| 55 | +} | ||
| 56 | + | ||
| 57 | +TEST_F(TEST_DIV_UT, IntBroadcastDivScalar) | ||
| 58 | +{ | ||
| 59 | + vector<DataType> data_types = {DT_INT16, DT_INT16, DT_INT16}; | ||
| 60 | + vector<vector<int64_t>> shapes = {{4}, {1}, {4}}; | ||
| 61 | + int16_t input1[4] = {12, 5, -12, -5}; | ||
| 62 | + int16_t input2[1] = {3}; | ||
| 63 | + int16_t output[4] = {0}; | ||
| 64 | + vector<void*> datas = {input1, input2, output}; | ||
| 65 | + | ||
| 66 | + CREATE_DIV_NODEDEF(shapes, data_types, datas); | ||
| 67 | + RUN_KERNEL(node_def, HOST, KERNEL_STATUS_OK); | ||
| 68 | + | ||
| 69 | + int16_t expected[4] = {4, 1, -4, -2}; | ||
| 70 | + EXPECT_EQ(CompareResult(output, expected, 4), true); | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +TEST_F(TEST_DIV_UT, FloatBroadcastScalarDivVector) | ||
| 74 | +{ | ||
| 75 | + vector<DataType> data_types = {DT_FLOAT, DT_FLOAT, DT_FLOAT}; | ||
| 76 | + vector<vector<int64_t>> shapes = {{1}, {4}, {4}}; | ||
| 77 | + float input1[1] = {8.0f}; | ||
| 78 | + float input2[4] = {16.0f, 8.0f, 4.0f, 2.0f}; | ||
| 79 | + float output[4] = {0.0f}; | ||
| 80 | + vector<void*> datas = {input1, input2, output}; | ||
| 81 | + | ||
| 82 | + CREATE_DIV_NODEDEF(shapes, data_types, datas); | ||
| 83 | + RUN_KERNEL(node_def, HOST, KERNEL_STATUS_OK); | ||
| 84 | + | ||
| 85 | + float expected[4] = {0.5f, 1.0f, 2.0f, 4.0f}; | ||
| 86 | + EXPECT_EQ(CompareResult(output, expected, 4), true); | ||
| 87 | +} | ||
| 88 | + | ||
| 89 | +TEST_F(TEST_DIV_UT, ComplexSameShape) | ||
| 90 | +{ | ||
| 91 | + vector<DataType> data_types = {DT_COMPLEX64, DT_COMPLEX64, DT_COMPLEX64}; | ||
| 92 | + vector<vector<int64_t>> shapes = {{2}, {2}, {2}}; | ||
| 93 | + std::complex<float> input1[2] = {{4.0f, 2.0f}, {3.0f, -3.0f}}; | ||
| 94 | + std::complex<float> input2[2] = {{2.0f, 0.0f}, {1.0f, -1.0f}}; | ||
| 95 | + std::complex<float> output[2] = {{0.0f, 0.0f}, {0.0f, 0.0f}}; | ||
| 96 | + vector<void*> datas = {input1, input2, output}; | ||
| 97 | + | ||
| 98 | + CREATE_DIV_NODEDEF(shapes, data_types, datas); | ||
| 99 | + RUN_KERNEL(node_def, HOST, KERNEL_STATUS_OK); | ||
| 100 | + | ||
| 101 | + std::complex<float> expected[2] = {{2.0f, 1.0f}, {3.0f, 0.0f}}; | ||
| 102 | + EXPECT_EQ(CompareResult(output, expected, 2), true); | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +TEST_F(TEST_DIV_UT, InvalidBroadcastShape) | ||
| 106 | +{ | ||
| 107 | + vector<DataType> data_types = {DT_INT32, DT_INT32, DT_INT32}; | ||
| 108 | + vector<vector<int64_t>> shapes = {{2, 2, 4}, {2, 2, 3}, {2, 2, 4}}; | ||
| 109 | + int32_t input1[16] = {0}; | ||
| 110 | + int32_t input2[16] = {0}; | ||
| 111 | + int32_t output[16] = {0}; | ||
| 112 | + std::fill_n(input1, 16, 1); | ||
| 113 | + std::fill_n(input2, 16, 1); | ||
| 114 | + vector<void*> datas = {input1, input2, output}; | ||
| 115 | + | ||
| 116 | + CREATE_DIV_NODEDEF(shapes, data_types, datas); | ||
| 117 | + RUN_KERNEL(node_def, HOST, KERNEL_STATUS_PARAM_INVALID); | ||
| 118 | +} | ||
| 119 | + | ||
| 120 | +TEST_F(TEST_DIV_UT, IntegerZeroDivisorReturnsError) | ||
| 121 | +{ | ||
| 122 | + vector<DataType> data_types = {DT_UINT16, DT_UINT16, DT_UINT16}; | ||
| 123 | + vector<vector<int64_t>> shapes = {{4}, {4}, {4}}; | ||
| 124 | + uint16_t input1[4] = {1, 2, 3, 4}; | ||
| 125 | + uint16_t input2[4] = {1, 0, 2, 3}; | ||
| 126 | + uint16_t output[4] = {0}; | ||
| 127 | + vector<void*> datas = {input1, input2, output}; | ||
| 128 | + | ||
| 129 | + CREATE_DIV_NODEDEF(shapes, data_types, datas); | ||
| 130 | + RUN_KERNEL(node_def, HOST, KERNEL_STATUS_INNER_ERROR); | ||
| 131 | +} | ||
| 132 | + | ||
| 133 | +TEST_F(TEST_DIV_UT, IntegerMinDivMinusOneReturnsErrorSameShape) | ||
| 134 | +{ | ||
| 135 | + vector<DataType> data_types = {DT_INT32, DT_INT32, DT_INT32}; | ||
| 136 | + vector<vector<int64_t>> shapes = {{2}, {2}, {2}}; | ||
| 137 | + int32_t input1[2] = {std::numeric_limits<int32_t>::min(), 8}; | ||
| 138 | + int32_t input2[2] = {-1, 2}; | ||
| 139 | + int32_t output[2] = {0}; | ||
| 140 | + vector<void*> datas = {input1, input2, output}; | ||
| 141 | + | ||
| 142 | + CREATE_DIV_NODEDEF(shapes, data_types, datas); | ||
| 143 | + RUN_KERNEL(node_def, HOST, KERNEL_STATUS_INNER_ERROR); | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +TEST_F(TEST_DIV_UT, IntegerMinDivMinusOneReturnsErrorBroadcastScalar) | ||
| 147 | +{ | ||
| 148 | + vector<DataType> data_types = {DT_INT32, DT_INT32, DT_INT32}; | ||
| 149 | + vector<vector<int64_t>> shapes = {{4}, {1}, {4}}; | ||
| 150 | + int32_t input1[4] = {1, std::numeric_limits<int32_t>::min(), 3, 4}; | ||
| 151 | + int32_t input2[1] = {-1}; | ||
| 152 | + int32_t output[4] = {0}; | ||
| 153 | + vector<void*> datas = {input1, input2, output}; | ||
| 154 | + | ||
| 155 | + CREATE_DIV_NODEDEF(shapes, data_types, datas); | ||
| 156 | + RUN_KERNEL(node_def, HOST, KERNEL_STATUS_INNER_ERROR); | ||
| 157 | +} | ||
| 158 | + | ||
| 159 | +TEST_F(TEST_DIV_UT, FloatZeroDivisorKeepsOkStatus) | ||
| 160 | +{ | ||
| 161 | + vector<DataType> data_types = {DT_FLOAT, DT_FLOAT, DT_FLOAT}; | ||
| 162 | + vector<vector<int64_t>> shapes = {{1}, {1}, {1}}; | ||
| 163 | + float input1[1] = {1.0f}; | ||
| 164 | + float input2[1] = {0.0f}; | ||
| 165 | + float output[1] = {0.0f}; | ||
| 166 | + vector<void*> datas = {input1, input2, output}; | ||
| 167 | + | ||
| 168 | + CREATE_DIV_NODEDEF(shapes, data_types, datas); | ||
| 169 | + RUN_KERNEL(node_def, HOST, KERNEL_STATUS_OK); | ||
| 170 | + | ||
| 171 | + EXPECT_TRUE(std::isinf(output[0])); | ||
| 172 | + EXPECT_GT(output[0], 0.0f); | ||
| 173 | +} | ||
| @@ -89,4 +89,5 @@ | |||
| 89 | 89 | ||
| 90 | | 调用方式 | 样例代码 | 说明 | | 90 | | 调用方式 | 样例代码 | 说明 | |
| 91 | | ---------------- |------------------------------------------------------------------------------|--------------------------------------------------| | 91 | | ---------------- |------------------------------------------------------------------------------|--------------------------------------------------| |
| 92 | -| aclnn接口 | [test_aclnn_all.cpp](examples/test_aclnn_all.cpp) | 通过[aclnnAll](docs/aclnnAll.md)接口方式调用ReduceAll算子。 | | 92 | +| 图模式调用 | [test_geir_reduce_all.cpp](examples/test_geir_reduce_all.cpp) | 通过GE IR图模式调用ReduceAll算子。 | |
| 93 | +| aclnn调用 | [test_aclnn_all.cpp](examples/test_aclnn_all.cpp) | 通过[aclnnAll](docs/aclnnAll.md)接口方式调用ReduceAll算子。 | | ||
| @@ -0,0 +1,282 @@ | |||
| 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 | + * \file test_geir_reduce_all.cpp | ||
| 13 | + * \brief Test ReduceAll via GE IR graph mode | ||
| 14 | + */ | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + | ||
| 39 | +using namespace ge; | ||
| 40 | +using std::map; | ||
| 41 | +using std::string; | ||
| 42 | +using std::vector; | ||
| 43 | + | ||
| 44 | + | ||
| 45 | + do { \ | ||
| 46 | + printf(message, ##__VA_ARGS__); \ | ||
| 47 | + } while (0) | ||
| 48 | + | ||
| 49 | +string GetTime() | ||
| 50 | +{ | ||
| 51 | + time_t timep; | ||
| 52 | + time(&timep); | ||
| 53 | + char tmp[64]; | ||
| 54 | + struct tm tm_info; | ||
| 55 | + strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S,000", localtime_r(&timep, &tm_info)); | ||
| 56 | + return tmp; | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +uint32_t GetDataTypeSize(DataType dt) | ||
| 60 | +{ | ||
| 61 | + if (dt == ge::DT_BOOL || dt == ge::DT_INT8 || dt == ge::DT_UINT8) { | ||
| 62 | + return 1; | ||
| 63 | + } | ||
| 64 | + if (dt == ge::DT_INT16 || dt == ge::DT_UINT16 || dt == ge::DT_FLOAT16 || dt == ge::DT_BF16) { | ||
| 65 | + return 2; | ||
| 66 | + } | ||
| 67 | + if (dt == ge::DT_INT32 || dt == ge::DT_UINT32 || dt == ge::DT_FLOAT) { | ||
| 68 | + return 4; | ||
| 69 | + } | ||
| 70 | + if (dt == ge::DT_INT64 || dt == ge::DT_UINT64 || dt == ge::DT_DOUBLE) { | ||
| 71 | + return 8; | ||
| 72 | + } | ||
| 73 | + return 0; | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +int32_t WriteDataToFile(const string &bin_file, uint64_t data_size, const uint8_t *input_data) | ||
| 77 | +{ | ||
| 78 | + FILE *fp = fopen(bin_file.c_str(), "wb"); | ||
| 79 | + if (fp == nullptr) { | ||
| 80 | + printf("Failed to open file: %s\n", bin_file.c_str()); | ||
| 81 | + return FAILED; | ||
| 82 | + } | ||
| 83 | + size_t written = fwrite(input_data, sizeof(uint8_t), data_size, fp); | ||
| 84 | + fclose(fp); | ||
| 85 | + if (written != data_size) { | ||
| 86 | + printf("Failed to write file: %s\n", bin_file.c_str()); | ||
| 87 | + return FAILED; | ||
| 88 | + } | ||
| 89 | + return SUCCESS; | ||
| 90 | +} | ||
| 91 | + | ||
| 92 | +int32_t GenBoolData(const vector<int64_t> &shapes, Tensor &input_tensor, TensorDesc &input_tensor_desc, | ||
| 93 | + const vector<bool> &values) | ||
| 94 | +{ | ||
| 95 | + input_tensor_desc.SetRealDimCnt(shapes.size()); | ||
| 96 | + size_t size = 1; | ||
| 97 | + for (size_t i = 0; i < shapes.size(); ++i) { | ||
| 98 | + size *= static_cast<size_t>(shapes[i]); | ||
| 99 | + } | ||
| 100 | + if (values.size() != size) { | ||
| 101 | + return FAILED; | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + vector<uint8_t> raw_data(size); | ||
| 105 | + for (size_t i = 0; i < size; ++i) { | ||
| 106 | + raw_data[i] = static_cast<uint8_t>(values[i]); | ||
| 107 | + } | ||
| 108 | + input_tensor = Tensor(input_tensor_desc, raw_data.data(), raw_data.size()); | ||
| 109 | + return SUCCESS; | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | +int32_t GenInt32Data(const vector<int64_t> &shapes, Tensor &input_tensor, TensorDesc &input_tensor_desc, | ||
| 113 | + const vector<int32_t> &values) | ||
| 114 | +{ | ||
| 115 | + input_tensor_desc.SetRealDimCnt(shapes.size()); | ||
| 116 | + size_t size = 1; | ||
| 117 | + for (size_t i = 0; i < shapes.size(); ++i) { | ||
| 118 | + size *= static_cast<size_t>(shapes[i]); | ||
| 119 | + } | ||
| 120 | + if (values.size() != size) { | ||
| 121 | + return FAILED; | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + input_tensor = Tensor(input_tensor_desc, reinterpret_cast<const uint8_t *>(values.data()), | ||
| 125 | + values.size() * sizeof(int32_t)); | ||
| 126 | + return SUCCESS; | ||
| 127 | +} | ||
| 128 | + | ||
| 129 | +int CreateOppInGraph(vector<ge::Tensor> &input, vector<Operator> &inputs, vector<Operator> &outputs, Graph &graph) | ||
| 130 | +{ | ||
| 131 | + Status ret = SUCCESS; | ||
| 132 | + auto reduce_all = op::ReduceAll("reduce_all_graph"); | ||
| 133 | + vector<int64_t> x_shape = {2, 3}; | ||
| 134 | + vector<int64_t> axes_shape = {1}; | ||
| 135 | + vector<int64_t> y_shape = {2}; | ||
| 136 | + | ||
| 137 | + auto x = op::Data("placeholder0").set_attr_index(0); | ||
| 138 | + TensorDesc x_desc(ge::Shape(x_shape), FORMAT_ND, DT_BOOL); | ||
| 139 | + x_desc.SetPlacement(ge::kPlacementHost); | ||
| 140 | + x_desc.SetFormat(FORMAT_ND); | ||
| 141 | + Tensor x_tensor; | ||
| 142 | + ret = GenBoolData(x_shape, x_tensor, x_desc, {true, true, false, true, true, true}); | ||
| 143 | + if (ret != SUCCESS) { | ||
| 144 | + printf("%s - ERROR - [XIR]: Generate input data failed\n", GetTime().c_str()); | ||
| 145 | + return FAILED; | ||
| 146 | + } | ||
| 147 | + x.update_input_desc_x(x_desc); | ||
| 148 | + x.update_output_desc_y(x_desc); | ||
| 149 | + input.push_back(x_tensor); | ||
| 150 | + graph.AddOp(x); | ||
| 151 | + reduce_all.set_input_x(x); | ||
| 152 | + inputs.push_back(x); | ||
| 153 | + | ||
| 154 | + auto axes = op::Data("placeholder1").set_attr_index(1); | ||
| 155 | + TensorDesc axes_desc(ge::Shape(axes_shape), FORMAT_ND, DT_INT32); | ||
| 156 | + axes_desc.SetPlacement(ge::kPlacementHost); | ||
| 157 | + axes_desc.SetFormat(FORMAT_ND); | ||
| 158 | + Tensor axes_tensor; | ||
| 159 | + ret = GenInt32Data(axes_shape, axes_tensor, axes_desc, {1}); | ||
| 160 | + if (ret != SUCCESS) { | ||
| 161 | + printf("%s - ERROR - [XIR]: Generate axes data failed\n", GetTime().c_str()); | ||
| 162 | + return FAILED; | ||
| 163 | + } | ||
| 164 | + axes.update_input_desc_x(axes_desc); | ||
| 165 | + axes.update_output_desc_y(axes_desc); | ||
| 166 | + input.push_back(axes_tensor); | ||
| 167 | + graph.AddOp(axes); | ||
| 168 | + reduce_all.set_input_axes(axes); | ||
| 169 | + reduce_all.update_input_desc_axes(axes_desc); | ||
| 170 | + inputs.push_back(axes); | ||
| 171 | + | ||
| 172 | + TensorDesc y_desc(ge::Shape(y_shape), FORMAT_ND, DT_BOOL); | ||
| 173 | + reduce_all.update_output_desc_y(y_desc); | ||
| 174 | + reduce_all.set_attr_keep_dims(false); | ||
| 175 | + outputs.push_back(reduce_all); | ||
| 176 | + return SUCCESS; | ||
| 177 | +} | ||
| 178 | + | ||
| 179 | +int SaveInputOutput(const vector<ge::Tensor> &input, const vector<ge::Tensor> &output) | ||
| 180 | +{ | ||
| 181 | + for (size_t i = 0; i < input.size(); ++i) { | ||
| 182 | + string input_file = "./tc_ge_irrun_reduce_all_npu_input_" + std::to_string(i) + ".bin"; | ||
| 183 | + const uint8_t *input_data = input[i].GetData(); | ||
| 184 | + int64_t input_shape = input[i].GetTensorDesc().GetShape().GetShapeSize(); | ||
| 185 | + uint32_t data_size = input_shape * GetDataTypeSize(input[i].GetTensorDesc().GetDataType()); | ||
| 186 | + if (WriteDataToFile(input_file, data_size, input_data) != SUCCESS) { | ||
| 187 | + return FAILED; | ||
| 188 | + } | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + const bool expected[] = {false, true}; | ||
| 192 | + for (size_t i = 0; i < output.size(); ++i) { | ||
| 193 | + string output_file = "./tc_ge_irrun_reduce_all_npu_output_" + std::to_string(i) + ".bin"; | ||
| 194 | + const uint8_t *output_data = output[i].GetData(); | ||
| 195 | + int64_t output_shape = output[i].GetTensorDesc().GetShape().GetShapeSize(); | ||
| 196 | + uint32_t data_size = output_shape * GetDataTypeSize(output[i].GetTensorDesc().GetDataType()); | ||
| 197 | + if (WriteDataToFile(output_file, data_size, output_data) != SUCCESS) { | ||
| 198 | + return FAILED; | ||
| 199 | + } | ||
| 200 | + const bool *result_data = reinterpret_cast<const bool *>(output_data); | ||
| 201 | + for (int64_t j = 0; j < output_shape; ++j) { | ||
| 202 | + LOG_PRINT("result[%ld] is: %u\n", j, static_cast<unsigned int>(result_data[j])); | ||
| 203 | + if (result_data[j] != expected[j]) { | ||
| 204 | + printf("ReduceAll output mismatch at index %ld\n", j); | ||
| 205 | + return FAILED; | ||
| 206 | + } | ||
| 207 | + } | ||
| 208 | + } | ||
| 209 | + return SUCCESS; | ||
| 210 | +} | ||
| 211 | + | ||
| 212 | +int main(int argc, char *argv[]) | ||
| 213 | +{ | ||
| 214 | + (void)argc; | ||
| 215 | + (void)argv; | ||
| 216 | + | ||
| 217 | + const char *graph_name = "tc_ge_irrun_reduce_all"; | ||
| 218 | + Graph graph(graph_name); | ||
| 219 | + vector<ge::Tensor> input; | ||
| 220 | + | ||
| 221 | + printf("%s - INFO - [XIR]: Start to initialize ge using ge global options\n", GetTime().c_str()); | ||
| 222 | + map<AscendString, AscendString> global_options = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}}; | ||
| 223 | + Status ret = ge::GEInitialize(global_options); | ||
| 224 | + if (ret != SUCCESS) { | ||
| 225 | + printf("%s - ERROR - [XIR]: Initialize ge using ge global options failed\n", GetTime().c_str()); | ||
| 226 | + return FAILED; | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + vector<Operator> inputs; | ||
| 230 | + vector<Operator> outputs; | ||
| 231 | + ret = CreateOppInGraph(input, inputs, outputs, graph); | ||
| 232 | + if (ret != SUCCESS) { | ||
| 233 | + ge::GEFinalize(); | ||
| 234 | + return FAILED; | ||
| 235 | + } | ||
| 236 | + | ||
| 237 | + if (!inputs.empty() && !outputs.empty()) { | ||
| 238 | + graph.SetInputs(inputs).SetOutputs(outputs); | ||
| 239 | + } | ||
| 240 | + | ||
| 241 | + map<AscendString, AscendString> build_options; | ||
| 242 | + ge::Session *session = new (std::nothrow) Session(build_options); | ||
| 243 | + if (session == nullptr) { | ||
| 244 | + printf("%s - ERROR - [XIR]: Create ir session failed\n", GetTime().c_str()); | ||
| 245 | + ge::GEFinalize(); | ||
| 246 | + return FAILED; | ||
| 247 | + } | ||
| 248 | + | ||
| 249 | + map<AscendString, AscendString> graph_options = {{"ge.exec.precision_mode", "allow_mix_precision"}}; | ||
| 250 | + uint32_t graph_id = 0; | ||
| 251 | + ret = session->AddGraph(graph_id, graph, graph_options); | ||
| 252 | + if (ret != SUCCESS) { | ||
| 253 | + printf("%s - ERROR - [XIR]: Add graph failed\n", GetTime().c_str()); | ||
| 254 | + delete session; | ||
| 255 | + ge::GEFinalize(); | ||
| 256 | + return FAILED; | ||
| 257 | + } | ||
| 258 | + | ||
| 259 | + vector<ge::Tensor> output; | ||
| 260 | + ret = session->RunGraph(graph_id, input, output); | ||
| 261 | + if (ret != SUCCESS) { | ||
| 262 | + printf("%s - ERROR - [XIR]: Run graph failed\n", GetTime().c_str()); | ||
| 263 | + delete session; | ||
| 264 | + ge::GEFinalize(); | ||
| 265 | + return FAILED; | ||
| 266 | + } | ||
| 267 | + | ||
| 268 | + ret = SaveInputOutput(input, output); | ||
| 269 | + delete session; | ||
| 270 | + if (ret != SUCCESS) { | ||
| 271 | + ge::GEFinalize(); | ||
| 272 | + return FAILED; | ||
| 273 | + } | ||
| 274 | + | ||
| 275 | + ret = ge::GEFinalize(); | ||
| 276 | + if (ret != SUCCESS) { | ||
| 277 | + printf("%s - ERROR - [XIR]: GE Finalize failed\n", GetTime().c_str()); | ||
| 278 | + return FAILED; | ||
| 279 | + } | ||
| 280 | + | ||
| 281 | + return SUCCESS; | ||
| 282 | +} | ||
| @@ -0,0 +1,254 @@ | |||
| 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 | +namespace { | ||
| 21 | +constexpr uint32_t kReduceAllInputNum = 1; | ||
| 22 | +constexpr uint32_t kReduceAllOutputNum = 1; | ||
| 23 | +const char *const kReduceAll = "ReduceAll"; | ||
| 24 | +} | ||
| 25 | + | ||
| 26 | +namespace aicpu { | ||
| 27 | +uint32_t ReduceAllCpuKernel::GenDataNoAxis(const CpuKernelContext &ctx) const | ||
| 28 | +{ | ||
| 29 | + auto x_data = reinterpret_cast<bool *>(ctx.Input(kFirstInputIndex)->GetData()); | ||
| 30 | + auto y_data = reinterpret_cast<bool *>(ctx.Output(kFirstOutputIndex)->GetData()); | ||
| 31 | + int64_t input_data_size = ctx.Input(kFirstInputIndex)->NumElements(); | ||
| 32 | + bool output_y = true; | ||
| 33 | + for (int64_t i = 0; i < input_data_size; ++i) { | ||
| 34 | + output_y = output_y && x_data[i]; | ||
| 35 | + } | ||
| 36 | + y_data[0] = output_y; | ||
| 37 | + return KERNEL_STATUS_OK; | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +template <typename T> | ||
| 41 | +uint32_t ReduceAllCpuKernel::AxisCal( | ||
| 42 | + T axis, const std::vector<int64_t> &data_dims, int64_t &head_dim, int64_t &end_dim) const | ||
| 43 | +{ | ||
| 44 | + bool axis_appear = false; | ||
| 45 | + size_t data_dims_size = data_dims.size(); | ||
| 46 | + for (size_t i = 0; i < data_dims_size; i++) { | ||
| 47 | + if (static_cast<T>(i) == axis) { | ||
| 48 | + axis_appear = true; | ||
| 49 | + continue; | ||
| 50 | + } | ||
| 51 | + if (axis_appear) { | ||
| 52 | + if (data_dims[i] != 0 && end_dim > (INT64_MAX / data_dims[i])) { | ||
| 53 | + KERNEL_LOG_ERROR("Product is overflow. multiplier 1: %ld. multiplier 2: %ld.", end_dim, data_dims[i]); | ||
| 54 | + return KERNEL_STATUS_PARAM_INVALID; | ||
| 55 | + } | ||
| 56 | + end_dim *= data_dims[i]; | ||
| 57 | + } else { | ||
| 58 | + if (data_dims[i] != 0 && head_dim > (INT64_MAX / data_dims[i])) { | ||
| 59 | + KERNEL_LOG_ERROR( | ||
| 60 | + "Product is overflow. multiplier 1: %ld. multiplier 2: %ld.", head_dim, data_dims[i]); | ||
| 61 | + return KERNEL_STATUS_PARAM_INVALID; | ||
| 62 | + } | ||
| 63 | + head_dim *= data_dims[i]; | ||
| 64 | + } | ||
| 65 | + } | ||
| 66 | + return KERNEL_STATUS_OK; | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +template <typename T> | ||
| 70 | +std::vector<int64_t> ReduceAllCpuKernel::GetOutputShape(const std::vector<int64_t> &input_shape, const T &axis) | ||
| 71 | +{ | ||
| 72 | + std::vector<int64_t> output_shape; | ||
| 73 | + for (size_t i = 0; i < input_shape.size(); ++i) { | ||
| 74 | + if (static_cast<T>(i) == axis) { | ||
| 75 | + if (keep_dims_) { | ||
| 76 | + output_shape.push_back(1); | ||
| 77 | + } | ||
| 78 | + continue; | ||
| 79 | + } | ||
| 80 | + output_shape.push_back(input_shape[i]); | ||
| 81 | + } | ||
| 82 | + return output_shape; | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +template <typename T> | ||
| 86 | +uint32_t ReduceAllCpuKernel::AxesRankCheckAndReverse( | ||
| 87 | + const CpuKernelContext &ctx, const T *axis_data, const int64_t &axes_num, std::map<T, int64_t> &axis_map, | ||
| 88 | + int32_t &rank) | ||
| 89 | +{ | ||
| 90 | + T axis_temp = 0; | ||
| 91 | + rank = static_cast<T>(rank); | ||
| 92 | + for (int64_t i = 0; i < axes_num; i++) { | ||
| 93 | + if (axis_data[i] < -rank || axis_data[i] > (rank - 1)) { | ||
| 94 | + KERNEL_LOG_ERROR( | ||
| 95 | + "[%s] the value of axes should be in [-%d, %d], axes is %ld", ctx.GetOpType().c_str(), rank, rank, | ||
| 96 | + static_cast<int64_t>(axis_data[i])); | ||
| 97 | + return KERNEL_STATUS_PARAM_INVALID; | ||
| 98 | + } | ||
| 99 | + if (axis_data[i] < 0) { | ||
| 100 | + axis_temp = axis_data[i] + rank; | ||
| 101 | + } else { | ||
| 102 | + axis_temp = axis_data[i]; | ||
| 103 | + } | ||
| 104 | + if (axis_map.find(axis_temp) != axis_map.end()) { | ||
| 105 | + KERNEL_LOG_ERROR( | ||
| 106 | + "[%s] invalid reduction arguments: axes contains duplicate dimension: %ld", ctx.GetOpType().c_str(), | ||
| 107 | + static_cast<int64_t>(axis_temp)); | ||
| 108 | + return KERNEL_STATUS_PARAM_INVALID; | ||
| 109 | + } | ||
| 110 | + axis_map.emplace(std::pair<T, int64_t>(axis_temp, i)); | ||
| 111 | + } | ||
| 112 | + return KERNEL_STATUS_OK; | ||
| 113 | +} | ||
| 114 | + | ||
| 115 | +template <typename T, typename T2> | ||
| 116 | +uint32_t ReduceAllCpuKernel::ReduceAllOneAxes( | ||
| 117 | + const T *input_data, std::vector<int64_t> &input_dims, T *output_data, const int64_t &output_num, | ||
| 118 | + std::vector<T2> &axes) | ||
| 119 | +{ | ||
| 120 | + if (axes_idx_ >= axes.size()) { | ||
| 121 | + for (int64_t i = 0; i < output_num; i++) { | ||
| 122 | + output_data[i] = input_data[i]; | ||
| 123 | + } | ||
| 124 | + return KERNEL_STATUS_OK; | ||
| 125 | + } | ||
| 126 | + int64_t head_dim = 1; | ||
| 127 | + int64_t end_dim = 1; | ||
| 128 | + uint32_t ret = AxisCal<T2>(axes[axes_idx_], input_dims, head_dim, end_dim); | ||
| 129 | + if (ret != KERNEL_STATUS_OK) { | ||
| 130 | + return KERNEL_STATUS_PARAM_INVALID; | ||
| 131 | + } | ||
| 132 | + auto *output_data_temp = new (std::nothrow) T[head_dim * end_dim]; | ||
| 133 | + KERNEL_CHECK_NULLPTR(output_data_temp, KERNEL_STATUS_INNER_ERROR, "apply memory failed."); | ||
| 134 | + bool tmp_x = true; | ||
| 135 | + bool tmp_y = true; | ||
| 136 | + auto axis_dim = input_dims[axes[axes_idx_]]; | ||
| 137 | + for (int64_t i = 0; i < head_dim; ++i) { | ||
| 138 | + for (int64_t j = 0; j < end_dim; ++j) { | ||
| 139 | + tmp_x = input_data[i * end_dim * axis_dim + j]; | ||
| 140 | + for (int64_t k = 1; k < axis_dim; ++k) { | ||
| 141 | + tmp_y = input_data[i * end_dim * axis_dim + j + k * end_dim]; | ||
| 142 | + tmp_x = tmp_x && tmp_y; | ||
| 143 | + } | ||
| 144 | + output_data_temp[i * end_dim + j] = tmp_x; | ||
| 145 | + } | ||
| 146 | + } | ||
| 147 | + input_dims = GetOutputShape<T2>(input_dims, axes[axes_idx_]); | ||
| 148 | + ++axes_idx_; | ||
| 149 | + uint32_t result = ReduceAllOneAxes<T, T2>(output_data_temp, input_dims, output_data, output_num, axes); | ||
| 150 | + delete[] output_data_temp; | ||
| 151 | + return result; | ||
| 152 | +} | ||
| 153 | + | ||
| 154 | +template <typename T, typename T2> | ||
| 155 | +uint32_t ReduceAllCpuKernel::ReduceAllCompute(const CpuKernelContext &ctx) | ||
| 156 | +{ | ||
| 157 | + axes_idx_ = 0; | ||
| 158 | + Tensor *x = ctx.Input(kFirstInputIndex); | ||
| 159 | + Tensor *axes = ctx.Input(kSecondInputIndex); | ||
| 160 | + Tensor *y = ctx.Output(kFirstInputIndex); | ||
| 161 | + | ||
| 162 | + auto *output_data = reinterpret_cast<T *>(y->GetData()); | ||
| 163 | + auto *keep_dims = ctx.GetAttr("keep_dims"); | ||
| 164 | + KERNEL_CHECK_NULLPTR(keep_dims, KERNEL_STATUS_PARAM_INVALID, "Get attr [keep_dims] failed."); | ||
| 165 | + keep_dims_ = keep_dims->GetBool(); | ||
| 166 | + int64_t output_num = y->NumElements(); | ||
| 167 | + | ||
| 168 | + if (x->GetDataSize() == 0) { | ||
| 169 | + KERNEL_LOG_INFO("[%s] Input is empty tensor.", ctx.GetOpType().c_str()); | ||
| 170 | + if (output_num > 0) { | ||
| 171 | + for (int64_t i = 0; i < output_num; ++i) { | ||
| 172 | + output_data[i] = true; | ||
| 173 | + } | ||
| 174 | + } | ||
| 175 | + return KERNEL_STATUS_OK; | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | + if (axes == nullptr || axes->GetDataSize() == 0) { | ||
| 179 | + return GenDataNoAxis(ctx); | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | + auto *input_data = reinterpret_cast<T *>(x->GetData()); | ||
| 183 | + auto *axis_data = reinterpret_cast<T2 *>(axes->GetData()); | ||
| 184 | + int64_t axes_num = axes->GetTensorShape()->NumElements(); | ||
| 185 | + std::vector<int64_t> input_dims = x->GetTensorShape()->GetDimSizes(); | ||
| 186 | + int32_t rank = x->GetTensorShape()->GetDims(); | ||
| 187 | + std::map<T2, int64_t> axis_map; | ||
| 188 | + | ||
| 189 | + if (AxesRankCheckAndReverse<T2>(ctx, axis_data, axes_num, axis_map, rank) != KERNEL_STATUS_OK) { | ||
| 190 | + return KERNEL_STATUS_PARAM_INVALID; | ||
| 191 | + } | ||
| 192 | + std::vector<T2> axes_data; | ||
| 193 | + for (auto iter = axis_map.rbegin(); iter != axis_map.rend(); iter++) { | ||
| 194 | + axes_data.push_back((*iter).first); | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + uint32_t res = ReduceAllOneAxes<T, T2>(input_data, input_dims, output_data, output_num, axes_data); | ||
| 198 | + if (res != KERNEL_STATUS_OK) { | ||
| 199 | + return KERNEL_STATUS_PARAM_INVALID; | ||
| 200 | + } | ||
| 201 | + return KERNEL_STATUS_OK; | ||
| 202 | +} | ||
| 203 | + | ||
| 204 | +uint32_t ReduceAllCpuKernel::ReduceAllCheck(const CpuKernelContext &ctx) const | ||
| 205 | +{ | ||
| 206 | + auto *x = ctx.Input(kFirstInputIndex); | ||
| 207 | + auto *axes = ctx.Input(kSecondInputIndex); | ||
| 208 | + if (x != nullptr && x->GetData() != nullptr) { | ||
| 209 | + KERNEL_CHECK_FALSE( | ||
| 210 | + (x->GetDataType() == DT_BOOL), KERNEL_STATUS_PARAM_INVALID, | ||
| 211 | + "Data type of x is not support, x data type is [%u].", static_cast<uint32_t>(x->GetDataType())); | ||
| 212 | + } | ||
| 213 | + if (axes != nullptr && axes->GetData() != nullptr) { | ||
| 214 | + KERNEL_CHECK_FALSE( | ||
| 215 | + (axes->GetDataType() == DT_INT32 || axes->GetDataType() == DT_INT64), KERNEL_STATUS_PARAM_INVALID, | ||
| 216 | + "Data type of axis is not support, axis data type is [%u].", | ||
| 217 | + static_cast<uint32_t>(axes->GetDataType())); | ||
| 218 | + } | ||
| 219 | + return KERNEL_STATUS_OK; | ||
| 220 | +} | ||
| 221 | + | ||
| 222 | +uint32_t ReduceAllCpuKernel::Compute(CpuKernelContext &ctx) | ||
| 223 | +{ | ||
| 224 | + KERNEL_HANDLE_ERROR(NormalCheck(ctx, kReduceAllInputNum, kReduceAllOutputNum), | ||
| 225 | + "[%s] check input and output failed.", kReduceAll); | ||
| 226 | + KERNEL_HANDLE_ERROR(ReduceAllCheck(ctx), "[%s] check params failed.", kReduceAll); | ||
| 227 | + | ||
| 228 | + Tensor *axes = ctx.Input(kSecondInputIndex); | ||
| 229 | + if (axes == nullptr || axes->GetDataSize() == 0) { | ||
| 230 | + return ReduceAllCompute<bool, int32_t>(ctx); | ||
| 231 | + } | ||
| 232 | + | ||
| 233 | + auto axes_data_type = axes->GetDataType(); | ||
| 234 | + uint32_t ret = KERNEL_STATUS_PARAM_INVALID; | ||
| 235 | + switch (axes_data_type) { | ||
| 236 | + case DT_INT32: | ||
| 237 | + ret = ReduceAllCompute<bool, int32_t>(ctx); | ||
| 238 | + break; | ||
| 239 | + case DT_INT64: | ||
| 240 | + ret = ReduceAllCompute<bool, int64_t>(ctx); | ||
| 241 | + break; | ||
| 242 | + default: | ||
| 243 | + KERNEL_LOG_ERROR("Data type not support[%s].", DTypeStr(axes_data_type).c_str()); | ||
| 244 | + return KERNEL_STATUS_PARAM_INVALID; | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + if (ret != KERNEL_STATUS_OK) { | ||
| 248 | + return KERNEL_STATUS_PARAM_INVALID; | ||
| 249 | + } | ||
| 250 | + return ret; | ||
| 251 | +} | ||
| 252 | + | ||
| 253 | +REGISTER_CPU_KERNEL(kReduceAll, ReduceAllCpuKernel); | ||
| 254 | +} // namespace aicpu | ||
| @@ -0,0 +1,56 @@ | |||
| 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 | +namespace aicpu { | ||
| 20 | +class ReduceAllCpuKernel : public CpuKernel { | ||
| 21 | +public: | ||
| 22 | + ReduceAllCpuKernel() = default; | ||
| 23 | + ~ReduceAllCpuKernel() override = default; | ||
| 24 | + | ||
| 25 | + uint32_t Compute(CpuKernelContext &ctx) override; | ||
| 26 | + | ||
| 27 | +private: | ||
| 28 | + template <typename T, typename T2> | ||
| 29 | + uint32_t ReduceAllCompute(const CpuKernelContext &ctx); | ||
| 30 | + | ||
| 31 | + uint32_t GenDataNoAxis(const CpuKernelContext &ctx) const; | ||
| 32 | + | ||
| 33 | + template <typename T> | ||
| 34 | + uint32_t AxisCal(T axis, const std::vector<int64_t> &data_dims, int64_t &head_dim, int64_t &end_dim) const; | ||
| 35 | + | ||
| 36 | + template <typename T, typename T2> | ||
| 37 | + uint32_t ReduceAllOneAxes( | ||
| 38 | + const T *input_data, std::vector<int64_t> &input_dims, T *output_data, const int64_t &output_num, | ||
| 39 | + std::vector<T2> &axes); | ||
| 40 | + | ||
| 41 | + template <typename T> | ||
| 42 | + std::vector<int64_t> GetOutputShape(const std::vector<int64_t> &input_shape, const T &axis); | ||
| 43 | + | ||
| 44 | + template <typename T> | ||
| 45 | + uint32_t AxesRankCheckAndReverse( | ||
| 46 | + const CpuKernelContext &ctx, const T *axis_data, const int64_t &axes_num, std::map<T, int64_t> &axis_map, | ||
| 47 | + int32_t &rank); | ||
| 48 | + | ||
| 49 | + uint32_t ReduceAllCheck(const CpuKernelContext &ctx) const; | ||
| 50 | + | ||
| 51 | + bool keep_dims_ = false; | ||
| 52 | + size_t axes_idx_ = 0; | ||
| 53 | +}; | ||
| 54 | +} // namespace aicpu | ||
| 55 | + | ||
| 56 | + | ||
| @@ -0,0 +1,29 @@ | |||
| 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 | +namespace ops { | ||
| 15 | +class ReduceAll : public OpDef { | ||
| 16 | +public: | ||
| 17 | + explicit ReduceAll(const char *name) : OpDef(name) | ||
| 18 | + { | ||
| 19 | + this->Input("x").DataType({ge::DT_BOOL}); | ||
| 20 | + this->Input("axes").DataType({ge::DT_INT32, ge::DT_INT64}); | ||
| 21 | + this->Output("y").DataType({ge::DT_BOOL}); | ||
| 22 | + | ||
| 23 | + ApplyMathAicpuDefaultCfg(*this); | ||
| 24 | + this->AICPU().ExtendCfgInfo(OP_INFO_OPS_FLAG.c_str(), OPEN_OPS_FLAG.c_str()); | ||
| 25 | + } | ||
| 26 | +}; | ||
| 27 | + | ||
| 28 | +OP_ADD(ReduceAll); | ||
| 29 | +} // namespace ops | ||
| @@ -0,0 +1,109 @@ | |||
| 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 | +using namespace std; | ||
| 23 | +using namespace aicpu; | ||
| 24 | + | ||
| 25 | +class TEST_REDUCE_ALL_UT : public testing::Test {}; | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + auto node_def = CpuKernelUtils::CreateNodeDef(); \ | ||
| 29 | + NodeDefBuilder(node_def.get(), "ReduceAll", "ReduceAll") \ | ||
| 30 | + .Input({"x", data_types[0], shapes[0], datas[0]}) \ | ||
| 31 | + .Input({"axes", data_types[1], shapes[1], datas[1]}) \ | ||
| 32 | + .Output({"y", data_types[2], shapes[2], datas[2]}) \ | ||
| 33 | + .Attr("keep_dims", keep_dims) | ||
| 34 | + | ||
| 35 | +TEST_F(TEST_REDUCE_ALL_UT, ReduceSingleAxisInt32) | ||
| 36 | +{ | ||
| 37 | + vector<DataType> data_types = {DT_BOOL, DT_INT32, DT_BOOL}; | ||
| 38 | + vector<vector<int64_t>> shapes = {{2, 3}, {1}, {2}}; | ||
| 39 | + bool input[6] = {true, true, false, true, true, true}; | ||
| 40 | + int32_t axes[1] = {1}; | ||
| 41 | + bool output[2] = {false, false}; | ||
| 42 | + vector<void *> datas = {(void *)input, (void *)axes, (void *)output}; | ||
| 43 | + | ||
| 44 | + CREATE_NODEDEF(shapes, data_types, datas, false); | ||
| 45 | + RUN_KERNEL(node_def, HOST, KERNEL_STATUS_OK); | ||
| 46 | + | ||
| 47 | + bool expected[2] = {false, true}; | ||
| 48 | + EXPECT_EQ(CompareResult<bool>(output, expected, 2), true); | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | +TEST_F(TEST_REDUCE_ALL_UT, ReduceMultiAxisInt64KeepDims) | ||
| 52 | +{ | ||
| 53 | + vector<DataType> data_types = {DT_BOOL, DT_INT64, DT_BOOL}; | ||
| 54 | + vector<vector<int64_t>> shapes = {{2, 2, 2}, {2}, {1, 2, 1}}; | ||
| 55 | + bool input[8] = {true, true, true, false, true, true, true, true}; | ||
| 56 | + int64_t axes[2] = {0, -1}; | ||
| 57 | + bool output[2] = {false, false}; | ||
| 58 | + vector<void *> datas = {(void *)input, (void *)axes, (void *)output}; | ||
| 59 | + | ||
| 60 | + CREATE_NODEDEF(shapes, data_types, datas, true); | ||
| 61 | + RUN_KERNEL(node_def, HOST, KERNEL_STATUS_OK); | ||
| 62 | + | ||
| 63 | + bool expected[2] = {true, false}; | ||
| 64 | + EXPECT_EQ(CompareResult<bool>(output, expected, 2), true); | ||
| 65 | +} | ||
| 66 | + | ||
| 67 | +TEST_F(TEST_REDUCE_ALL_UT, EmptyAxesReduceAll) | ||
| 68 | +{ | ||
| 69 | + vector<DataType> data_types = {DT_BOOL, DT_INT32, DT_BOOL}; | ||
| 70 | + vector<vector<int64_t>> shapes = {{2, 2}, {}, {}}; | ||
| 71 | + bool input[4] = {true, true, true, false}; | ||
| 72 | + bool output[1] = {true}; | ||
| 73 | + vector<void *> datas = {(void *)input, nullptr, (void *)output}; | ||
| 74 | + | ||
| 75 | + CREATE_NODEDEF(shapes, data_types, datas, false); | ||
| 76 | + RUN_KERNEL(node_def, HOST, KERNEL_STATUS_OK); | ||
| 77 | + | ||
| 78 | + bool expected[1] = {false}; | ||
| 79 | + EXPECT_EQ(CompareResult<bool>(output, expected, 1), true); | ||
| 80 | +} | ||
| 81 | + | ||
| 82 | +TEST_F(TEST_REDUCE_ALL_UT, EmptyInputKeepDims) | ||
| 83 | +{ | ||
| 84 | + vector<DataType> data_types = {DT_BOOL, DT_INT32, DT_BOOL}; | ||
| 85 | + vector<vector<int64_t>> shapes = {{2, 0, 3}, {1}, {2, 1, 3}}; | ||
| 86 | + bool input[1] = {false}; | ||
| 87 | + int32_t axes[1] = {1}; | ||
| 88 | + bool output[6] = {false, false, false, false, false, false}; | ||
| 89 | + vector<void *> datas = {(void *)input, (void *)axes, (void *)output}; | ||
| 90 | + | ||
| 91 | + CREATE_NODEDEF(shapes, data_types, datas, true); | ||
| 92 | + RUN_KERNEL(node_def, HOST, KERNEL_STATUS_OK); | ||
| 93 | + | ||
| 94 | + bool expected[6] = {true, true, true, true, true, true}; | ||
| 95 | + EXPECT_EQ(CompareResult<bool>(output, expected, 6), true); | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +TEST_F(TEST_REDUCE_ALL_UT, AxisOutOfRange) | ||
| 99 | +{ | ||
| 100 | + vector<DataType> data_types = {DT_BOOL, DT_INT32, DT_BOOL}; | ||
| 101 | + vector<vector<int64_t>> shapes = {{2, 2}, {1}, {2}}; | ||
| 102 | + bool input[4] = {true, true, false, true}; | ||
| 103 | + int32_t axes[1] = {2}; | ||
| 104 | + bool output[2] = {false, false}; | ||
| 105 | + vector<void *> datas = {(void *)input, (void *)axes, (void *)output}; | ||
| 106 | + | ||
| 107 | + CREATE_NODEDEF(shapes, data_types, datas, false); | ||
| 108 | + RUN_KERNEL(node_def, HOST, KERNEL_STATUS_PARAM_INVALID); | ||
| 109 | +} | ||