已合并
fix: 完善 Relu6D 和 SmoothL1LossGrad 的异常输入 Tiling 校验及 GE IR 验证覆盖 #8281
zhongheng创建于 8月5日
fix: 完善 Relu6D 和 SmoothL1LossGrad 的异常输入 Tiling 校验及 GE IR 验证覆盖 #8281
已合并
共 10 个文件变更+1702-102
| @@ -86,6 +86,61 @@ static std::vector<float> MakeInput(int64_t n) | |||
| 86 | return v; | 86 | return v; |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | +static bool VerifyOutput(const std::vector<ge::Tensor>& output, const std::vector<int64_t>& expectedShape, | ||
| 90 | + ge::DataType expectedDtype, const std::vector<float>& input, const std::vector<float>& golden) | ||
| 91 | +{ | ||
| 92 | + if (output.size() != 1U) { | ||
| 93 | + std::cout << "[FAIL] Expected exactly 1 output, got " << output.size() << std::endl; | ||
| 94 | + return false; | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + const ge::Tensor& result = output[0]; | ||
| 98 | + const ge::TensorDesc resultDesc = result.GetTensorDesc(); | ||
| 99 | + const auto actualShape = resultDesc.GetShape().GetDims(); | ||
| 100 | + if (actualShape != expectedShape) { | ||
| 101 | + std::cout << "[FAIL] Output shape mismatch, actual=["; | ||
| 102 | + for (size_t i = 0; i < actualShape.size(); ++i) { | ||
| 103 | + std::cout << (i == 0U ? "" : ",") << actualShape[i]; | ||
| 104 | + } | ||
| 105 | + std::cout << "], expected=["; | ||
| 106 | + for (size_t i = 0; i < expectedShape.size(); ++i) { | ||
| 107 | + std::cout << (i == 0U ? "" : ",") << expectedShape[i]; | ||
| 108 | + } | ||
| 109 | + std::cout << "]" << std::endl; | ||
| 110 | + return false; | ||
| 111 | + } | ||
| 112 | + if (resultDesc.GetDataType() != expectedDtype) { | ||
| 113 | + std::cout << "[FAIL] Output dtype mismatch, actual=" << resultDesc.GetDataType() | ||
| 114 | + << ", expected=" << expectedDtype << std::endl; | ||
| 115 | + return false; | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + const size_t expectedBytes = golden.size() * sizeof(float); | ||
| 119 | + if (result.GetData() == nullptr || result.GetSize() != expectedBytes) { | ||
| 120 | + std::cout << "[FAIL] Output buffer mismatch, data=" << static_cast<const void*>(result.GetData()) | ||
| 121 | + << ", actual_bytes=" << result.GetSize() << ", expected_bytes=" << expectedBytes << std::endl; | ||
| 122 | + return false; | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + const float* actual = reinterpret_cast<const float*>(result.GetData()); | ||
| 126 | + const float rtol = 1e-4F; | ||
| 127 | + const float atol = 1e-4F; | ||
| 128 | + bool passed = true; | ||
| 129 | + for (size_t i = 0; i < golden.size(); ++i) { | ||
| 130 | + const float error = std::abs(actual[i] - golden[i]); | ||
| 131 | + const float tolerance = atol + rtol * std::abs(golden[i]); | ||
| 132 | + const bool ok = error <= tolerance; | ||
| 133 | + if (!ok) { | ||
| 134 | + passed = false; | ||
| 135 | + } | ||
| 136 | + if (i < 8U || !ok) { | ||
| 137 | + std::cout << " [" << i << "] x=" << input[i] << " -> y=" << actual[i] << " (expected " << golden[i] | ||
| 138 | + << ", err " << error << (ok ? " OK" : " FAIL") << ")" << std::endl; | ||
| 139 | + } | ||
| 140 | + } | ||
| 141 | + return passed; | ||
| 142 | +} | ||
| 143 | + | ||
| 89 | int CreateRelu6DGraph(DataType inDtype, float scale, const std::vector<int64_t>& tensorShape, | 144 | int CreateRelu6DGraph(DataType inDtype, float scale, const std::vector<int64_t>& tensorShape, |
| 90 | std::vector<ge::Tensor>& input, std::vector<Operator>& inputs, std::vector<Operator>& outputs, | 145 | std::vector<ge::Tensor>& input, std::vector<Operator>& inputs, std::vector<Operator>& outputs, |
| 91 | Graph& graph) | 146 | Graph& graph) |
| @@ -130,6 +185,9 @@ int CreateRelu6DGraph(DataType inDtype, float scale, const std::vector<int64_t>& | |||
| 130 | 185 | ||
| 131 | int main(int argc, char* argv[]) | 186 | int main(int argc, char* argv[]) |
| 132 | { | 187 | { |
| 188 | + (void)argc; | ||
| 189 | + (void)argv; | ||
| 190 | + | ||
| 133 | const char* graph_name = "tc_ge_irrun_relu6_d"; | 191 | const char* graph_name = "tc_ge_irrun_relu6_d"; |
| 134 | Graph graph(graph_name); | 192 | Graph graph(graph_name); |
| 135 | std::vector<ge::Tensor> input; | 193 | std::vector<ge::Tensor> input; |
| @@ -156,6 +214,7 @@ int main(int argc, char* argv[]) | |||
| 156 | ret = CreateRelu6DGraph(inDtype, scale, tensorShape, input, inputs, outputs, graph); | 214 | ret = CreateRelu6DGraph(inDtype, scale, tensorShape, input, inputs, outputs, graph); |
| 157 | if (ret != SUCCESS) { | 215 | if (ret != SUCCESS) { |
| 158 | printf("%s - ERROR - [XIR]: Create operator in graph failed\n", GetTime().c_str()); | 216 | printf("%s - ERROR - [XIR]: Create operator in graph failed\n", GetTime().c_str()); |
| 217 | + ge::GEFinalize(); | ||
| 159 | return FAILED; | 218 | return FAILED; |
| 160 | } | 219 | } |
| 161 | 220 | ||
| @@ -168,6 +227,7 @@ int main(int argc, char* argv[]) | |||
| 168 | ge::Session* session = new Session(build_options); | 227 | ge::Session* session = new Session(build_options); |
| 169 | if (session == nullptr) { | 228 | if (session == nullptr) { |
| 170 | printf("%s - ERROR - [XIR]: Create ir session failed\n", GetTime().c_str()); | 229 | printf("%s - ERROR - [XIR]: Create ir session failed\n", GetTime().c_str()); |
| 230 | + ge::GEFinalize(); | ||
| 171 | return FAILED; | 231 | return FAILED; |
| 172 | } | 232 | } |
| 173 | printf("%s - INFO - [XIR]: Create ir session success\n", GetTime().c_str()); | 233 | printf("%s - INFO - [XIR]: Create ir session success\n", GetTime().c_str()); |
| @@ -195,53 +255,31 @@ int main(int argc, char* argv[]) | |||
| 195 | } | 255 | } |
| 196 | printf("%s - INFO - [XIR]: Run graph success\n", GetTime().c_str()); | 256 | printf("%s - INFO - [XIR]: Run graph success\n", GetTime().c_str()); |
| 197 | 257 | ||
| 198 | - // ---- 结果验证:GE IR 输出 vs CPU golden(fp32 双万分之一) | 258 | + // ---- 结果验证:严格检查输出数量、完整 Shape、dtype 和全部数值 |
| 199 | - bool passed = true; | 259 | + std::vector<float> hX = MakeInput(numElements); |
| 200 | - if (output.empty()) { | 260 | + std::vector<float> hGolden(static_cast<size_t>(numElements)); |
| 201 | - printf("%s - ERROR - [XIR]: No output tensor produced\n", GetTime().c_str()); | 261 | + ComputeGolden(hX.data(), hGolden.data(), numElements, scale); |
| 202 | - passed = false; | 262 | + std::cout << "=== Relu6D GE IR Test (y = min(max(x,0), 6*scale)) ===" << std::endl; |
| 203 | - } else { | 263 | + std::cout << "dtype: float32, shape: [4, 8], scale=" << scale << " (上界 " << 6.0 * scale << ")" << std::endl; |
| 204 | - std::vector<float> hX = MakeInput(numElements); | 264 | + bool passed = VerifyOutput(output, tensorShape, inDtype, hX, hGolden); |
| 205 | - std::vector<float> hGolden(static_cast<size_t>(numElements)); | ||
| 206 | - ComputeGolden(hX.data(), hGolden.data(), numElements, scale); | ||
| 207 | - | ||
| 208 | - const float* outData = reinterpret_cast<const float*>(output[0].GetData()); | ||
| 209 | - int64_t outNum = output[0].GetTensorDesc().GetShape().GetShapeSize(); | ||
| 210 | - std::cout << "=== Relu6D GE IR Test (y = min(max(x,0), 6*scale)) ===" << std::endl; | ||
| 211 | - std::cout << "dtype: float32, shape: [4, 8], scale=" << scale << " (上界 " << 6.0 * scale << ")" << std::endl; | ||
| 212 | - std::cout << "output shape size = " << outNum << std::endl; | ||
| 213 | - | ||
| 214 | - const float rtol = 1e-4f; | ||
| 215 | - const float atol = 1e-4f; | ||
| 216 | - int64_t cmpNum = std::min<int64_t>(outNum, numElements); | ||
| 217 | - for (int64_t i = 0; i < cmpNum; ++i) { | ||
| 218 | - float err = std::abs(outData[i] - hGolden[i]); | ||
| 219 | - float tol = atol + rtol * std::abs(hGolden[i]); | ||
| 220 | - bool ok = (err <= tol); | ||
| 221 | - if (!ok) | ||
| 222 | - passed = false; | ||
| 223 | - if (i < 8) { // 打印首 8 个(一个完整段模式) | ||
| 224 | - std::cout << " [" << i << "] x=" << hX[i] << " -> y=" << outData[i] << " (expected " << hGolden[i] | ||
| 225 | - << ", err " << err << (ok ? " OK" : " FAIL") << ")" << std::endl; | ||
| 226 | - } | ||
| 227 | - } | ||
| 228 | - } | ||
| 229 | - | ||
| 230 | - std::cout << std::endl; | ||
| 231 | - if (passed) { | ||
| 232 | - printf("%s - INFO - [XIR]: GE IR pathway verification PASSED\n", GetTime().c_str()); | ||
| 233 | - std::cout << "[PASS] GE IR results match golden reference." << std::endl; | ||
| 234 | - } else { | ||
| 235 | - std::cout << "[FAIL] GE IR results do not match golden reference." << std::endl; | ||
| 236 | - } | ||
| 237 | 265 | ||
| 266 | + delete session; | ||
| 238 | printf("%s - INFO - [XIR]: Start to finalize ge\n", GetTime().c_str()); | 267 | printf("%s - INFO - [XIR]: Start to finalize ge\n", GetTime().c_str()); |
| 239 | Status finRet = ge::GEFinalize(); | 268 | Status finRet = ge::GEFinalize(); |
| 240 | if (finRet != SUCCESS) { | 269 | if (finRet != SUCCESS) { |
| 241 | printf("%s - ERROR - [XIR]: Finalize ge failed\n", GetTime().c_str()); | 270 | printf("%s - ERROR - [XIR]: Finalize ge failed\n", GetTime().c_str()); |
| 271 | + passed = false; | ||
| 272 | + } else { | ||
| 273 | + printf("%s - INFO - [XIR]: Finalize ge success\n", GetTime().c_str()); | ||
| 274 | + } | ||
| 275 | + std::cout << std::endl; | ||
| 276 | + if (passed) { | ||
| 277 | + std::cout << "Shape, dtype and values PASSED for [4,8]" << std::endl; | ||
| 278 | + std::cout << "Relu6D static GEIR verification PASSED" << std::endl; | ||
| 279 | + std::cout << "[PASS] GE IR results match golden reference." << std::endl; | ||
| 280 | + } else { | ||
| 281 | + std::cout << "[FAIL] Relu6D static GEIR verification failed" << std::endl; | ||
| 242 | } | 282 | } |
| 243 | - delete session; | ||
| 244 | - printf("%s - INFO - [XIR]: Finalize ge success\n", GetTime().c_str()); | ||
| 245 | 283 | ||
| 246 | return passed ? SUCCESS : FAILED; | 284 | return passed ? SUCCESS : FAILED; |
| 247 | } | 285 | } |
| @@ -0,0 +1,284 @@ | |||
| 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 | +/* Generated By CANNBot */ | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * @file test_geir_relu6_d_dynamic.cpp | ||
| 15 | + * @brief Relu6D dynamic-shape and dynamic-rank GE IR verification. | ||
| 16 | + * | ||
| 17 | + * Both scenarios reuse one GE Session. Each scenario adds one dynamic graph and | ||
| 18 | + * runs that graph three times with distinct concrete shapes. Every run checks | ||
| 19 | + * the only output's shape, dtype, and all values against the CPU formula | ||
| 20 | + * y = min(max(x, 0), 6 * scale). | ||
| 21 | + */ | ||
| 22 | + | ||
| 23 | +#include <algorithm> | ||
| 24 | +#include <cmath> | ||
| 25 | +#include <cstdint> | ||
| 26 | +#include <iostream> | ||
| 27 | +#include <map> | ||
| 28 | +#include <new> | ||
| 29 | +#include <string> | ||
| 30 | +#include <vector> | ||
| 31 | + | ||
| 32 | +#include "array_ops.h" | ||
| 33 | +#include "ge_api.h" | ||
| 34 | +#include "ge_api_types.h" | ||
| 35 | +#include "ge_error_codes.h" | ||
| 36 | +#include "ge_ir_build.h" | ||
| 37 | +#include "graph.h" | ||
| 38 | +#include "tensor.h" | ||
| 39 | +#include "types.h" | ||
| 40 | + | ||
| 41 | +#include "../../op_graph/relu6_d_proto.h" | ||
| 42 | + | ||
| 43 | +namespace { | ||
| 44 | + | ||
| 45 | +constexpr int kSuccess = 0; | ||
| 46 | +constexpr int kFailed = -1; | ||
| 47 | +constexpr float kScale = 1.0F; | ||
| 48 | +constexpr float kAtol = 1.0e-4F; | ||
| 49 | +constexpr float kRtol = 1.0e-4F; | ||
| 50 | + | ||
| 51 | +using ShapeVector = std::vector<int64_t>; | ||
| 52 | +using ShapeCases = std::vector<ShapeVector>; | ||
| 53 | + | ||
| 54 | +std::string ShapeToString(const ShapeVector& shape) | ||
| 55 | +{ | ||
| 56 | + std::string result = "["; | ||
| 57 | + for (size_t index = 0; index < shape.size(); ++index) { | ||
| 58 | + if (index != 0U) { | ||
| 59 | + result += ","; | ||
| 60 | + } | ||
| 61 | + result += std::to_string(shape[index]); | ||
| 62 | + } | ||
| 63 | + result += "]"; | ||
| 64 | + return result; | ||
| 65 | +} | ||
| 66 | + | ||
| 67 | +int64_t ElementCount(const ShapeVector& shape) | ||
| 68 | +{ | ||
| 69 | + int64_t count = 1; | ||
| 70 | + for (int64_t dim : shape) { | ||
| 71 | + count *= dim; | ||
| 72 | + } | ||
| 73 | + return count; | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +std::vector<float> MakeInput(int64_t count) | ||
| 77 | +{ | ||
| 78 | + const std::vector<float> pattern = {-8.0F, -1.0F, 0.0F, 0.5F, 3.0F, 6.0F, 7.0F, 1000.0F}; | ||
| 79 | + std::vector<float> input(static_cast<size_t>(count)); | ||
| 80 | + for (int64_t index = 0; index < count; ++index) { | ||
| 81 | + input[static_cast<size_t>(index)] = pattern[static_cast<size_t>(index) % pattern.size()]; | ||
| 82 | + } | ||
| 83 | + return input; | ||
| 84 | +} | ||
| 85 | + | ||
| 86 | +std::vector<float> ComputeGolden(const std::vector<float>& input) | ||
| 87 | +{ | ||
| 88 | + const double upper = 6.0 * static_cast<double>(kScale); | ||
| 89 | + std::vector<float> golden(input.size()); | ||
| 90 | + for (size_t index = 0; index < input.size(); ++index) { | ||
| 91 | + const double value = static_cast<double>(input[index]); | ||
| 92 | + golden[index] = static_cast<float>(std::min(std::max(value, 0.0), upper)); | ||
| 93 | + } | ||
| 94 | + return golden; | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +ge::TensorDesc MakeTensorDesc(const ShapeVector& shape, bool hostPlacement) | ||
| 98 | +{ | ||
| 99 | + ge::TensorDesc desc(ge::Shape(shape), ge::FORMAT_ND, ge::DT_FLOAT); | ||
| 100 | + if (hostPlacement) { | ||
| 101 | + desc.SetPlacement(ge::kPlacementHost); | ||
| 102 | + } | ||
| 103 | + desc.SetFormat(ge::FORMAT_ND); | ||
| 104 | + desc.SetRealDimCnt(shape.size()); | ||
| 105 | + return desc; | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +struct ReluFeeds { | ||
| 109 | + std::vector<float> input; | ||
| 110 | + std::vector<ge::Tensor> tensors; | ||
| 111 | +}; | ||
| 112 | + | ||
| 113 | +ReluFeeds MakeFeeds(const ShapeVector& shape) | ||
| 114 | +{ | ||
| 115 | + ReluFeeds feeds; | ||
| 116 | + feeds.input = MakeInput(ElementCount(shape)); | ||
| 117 | + const ge::TensorDesc desc = MakeTensorDesc(shape, true); | ||
| 118 | + const uint32_t byteSize = static_cast<uint32_t>(feeds.input.size() * sizeof(float)); | ||
| 119 | + feeds.tensors.emplace_back(desc, reinterpret_cast<uint8_t*>(feeds.input.data()), byteSize); | ||
| 120 | + return feeds; | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +int BuildGraph(const std::string& scenarioName, const ShapeVector& declaredShape, ge::Graph& graph) | ||
| 124 | +{ | ||
| 125 | + const ge::TensorDesc inputDesc = MakeTensorDesc(declaredShape, false); | ||
| 126 | + | ||
| 127 | + const std::string dataName = scenarioName + "_x"; | ||
| 128 | + auto data = ge::op::Data(dataName.c_str()).set_attr_index(0); | ||
| 129 | + data.update_input_desc_x(inputDesc); | ||
| 130 | + data.update_output_desc_y(inputDesc); | ||
| 131 | + graph.AddOp(data); | ||
| 132 | + | ||
| 133 | + const std::string opName = "relu6_d_" + scenarioName; | ||
| 134 | + auto relu6d = ge::op::Relu6D(opName.c_str()); | ||
| 135 | + relu6d.set_input_x(data); | ||
| 136 | + relu6d.set_attr_scale(kScale); | ||
| 137 | + relu6d.update_input_desc_x(inputDesc); | ||
| 138 | + | ||
| 139 | + const ge::TensorDesc outputDesc = MakeTensorDesc(declaredShape, false); | ||
| 140 | + relu6d.update_output_desc_y(outputDesc); | ||
| 141 | + graph.AddOp(relu6d); | ||
| 142 | + | ||
| 143 | + std::vector<ge::Operator> inputs = {data}; | ||
| 144 | + std::vector<ge::Operator> outputs = {relu6d}; | ||
| 145 | + graph.SetInputs(inputs).SetOutputs(outputs); | ||
| 146 | + return kSuccess; | ||
| 147 | +} | ||
| 148 | + | ||
| 149 | +bool HasExpectedShape(const ge::Shape& actual, const ShapeVector& expected) | ||
| 150 | +{ | ||
| 151 | + if (actual.GetDimNum() != expected.size()) { | ||
| 152 | + return false; | ||
| 153 | + } | ||
| 154 | + for (size_t index = 0; index < expected.size(); ++index) { | ||
| 155 | + if (actual.GetDim(index) != expected[index]) { | ||
| 156 | + return false; | ||
| 157 | + } | ||
| 158 | + } | ||
| 159 | + return true; | ||
| 160 | +} | ||
| 161 | + | ||
| 162 | +bool VerifyOutput(const std::vector<ge::Tensor>& outputs, const ShapeVector& shape, const std::vector<float>& input) | ||
| 163 | +{ | ||
| 164 | + if (outputs.size() != 1U) { | ||
| 165 | + std::cerr << "Expected exactly one Relu6D output, got " << outputs.size() << std::endl; | ||
| 166 | + return false; | ||
| 167 | + } | ||
| 168 | + | ||
| 169 | + const ge::Tensor& output = outputs[0]; | ||
| 170 | + const ge::TensorDesc outputDesc = output.GetTensorDesc(); | ||
| 171 | + if (!HasExpectedShape(outputDesc.GetShape(), shape)) { | ||
| 172 | + std::cerr << "Relu6D output shape mismatch for " << ShapeToString(shape) << std::endl; | ||
| 173 | + return false; | ||
| 174 | + } | ||
| 175 | + if (outputDesc.GetDataType() != ge::DT_FLOAT) { | ||
| 176 | + std::cerr << "Relu6D output dtype mismatch: expected DT_FLOAT, got " << outputDesc.GetDataType() << std::endl; | ||
| 177 | + return false; | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + const std::vector<float> golden = ComputeGolden(input); | ||
| 181 | + const size_t expectedBytes = golden.size() * sizeof(float); | ||
| 182 | + const float* actual = reinterpret_cast<const float*>(output.GetData()); | ||
| 183 | + if (actual == nullptr || output.GetSize() != expectedBytes) { | ||
| 184 | + std::cerr << "Relu6D output buffer mismatch: actual bytes=" << output.GetSize() | ||
| 185 | + << ", expected bytes=" << expectedBytes << std::endl; | ||
| 186 | + return false; | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + for (size_t index = 0; index < golden.size(); ++index) { | ||
| 190 | + const float error = std::fabs(actual[index] - golden[index]); | ||
| 191 | + const float tolerance = kAtol + kRtol * std::fabs(golden[index]); | ||
| 192 | + if (!std::isfinite(actual[index]) || error > tolerance) { | ||
| 193 | + std::cerr << "Relu6D value mismatch at index " << index << ": actual=" << actual[index] | ||
| 194 | + << ", expected=" << golden[index] << ", tolerance=" << tolerance << std::endl; | ||
| 195 | + return false; | ||
| 196 | + } | ||
| 197 | + } | ||
| 198 | + return true; | ||
| 199 | +} | ||
| 200 | + | ||
| 201 | +int RunScenario(ge::Session& session, uint32_t graphId, const std::string& scenarioName, | ||
| 202 | + const ShapeVector& declaredShape, const ShapeCases& concreteShapes) | ||
| 203 | +{ | ||
| 204 | + std::cout << "Scenario " << scenarioName << ", declared shape " << ShapeToString(declaredShape) << std::endl; | ||
| 205 | + | ||
| 206 | + const std::string graphName = "relu6_d_" + scenarioName + "_graph"; | ||
| 207 | + ge::Graph graph(graphName.c_str()); | ||
| 208 | + if (BuildGraph(scenarioName, declaredShape, graph) != kSuccess) { | ||
| 209 | + std::cerr << "ERROR: unable to build graph for scenario " << scenarioName << std::endl; | ||
| 210 | + return kFailed; | ||
| 211 | + } | ||
| 212 | + | ||
| 213 | + std::map<ge::AscendString, ge::AscendString> graphOptions; | ||
| 214 | + ge::Status status = session.AddGraph(graphId, graph, graphOptions); | ||
| 215 | + if (status != kSuccess) { | ||
| 216 | + std::cerr << "ERROR: AddGraph failed for scenario " << scenarioName << ": " << ge::GEGetErrorMsgV2().GetString() | ||
| 217 | + << std::endl; | ||
| 218 | + return kFailed; | ||
| 219 | + } | ||
| 220 | + | ||
| 221 | + size_t passed = 0U; | ||
| 222 | + for (const ShapeVector& shape : concreteShapes) { | ||
| 223 | + std::cout << "Run concrete shape " << ShapeToString(shape) << std::endl; | ||
| 224 | + ReluFeeds feeds = MakeFeeds(shape); | ||
| 225 | + std::vector<ge::Tensor> outputs; | ||
| 226 | + status = session.RunGraph(graphId, feeds.tensors, outputs); | ||
| 227 | + if (status != kSuccess) { | ||
| 228 | + std::cerr << "ERROR: RunGraph failed for scenario " << scenarioName << ", shape " << ShapeToString(shape) | ||
| 229 | + << ": " << ge::GEGetErrorMsgV2().GetString() << std::endl; | ||
| 230 | + continue; | ||
| 231 | + } | ||
| 232 | + if (!VerifyOutput(outputs, shape, feeds.input)) { | ||
| 233 | + std::cerr << "ERROR: output verification failed for scenario " << scenarioName << ", shape " | ||
| 234 | + << ShapeToString(shape) << std::endl; | ||
| 235 | + continue; | ||
| 236 | + } | ||
| 237 | + | ||
| 238 | + ++passed; | ||
| 239 | + std::cout << "Shape, dtype and values PASSED for " << ShapeToString(shape) << std::endl; | ||
| 240 | + } | ||
| 241 | + | ||
| 242 | + std::cout << "Scenario " << scenarioName << " summary: " << passed << "/" << concreteShapes.size() << " passed" | ||
| 243 | + << std::endl; | ||
| 244 | + return passed == concreteShapes.size() ? kSuccess : kFailed; | ||
| 245 | +} | ||
| 246 | + | ||
| 247 | +} // namespace | ||
| 248 | + | ||
| 249 | +int main() | ||
| 250 | +{ | ||
| 251 | + std::map<ge::AscendString, ge::AscendString> globalOptions = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}}; | ||
| 252 | + ge::Status status = ge::GEInitialize(globalOptions); | ||
| 253 | + if (status != kSuccess) { | ||
| 254 | + std::cerr << "ERROR: GEInitialize failed: " << ge::GEGetErrorMsgV2().GetString() << std::endl; | ||
| 255 | + return kFailed; | ||
| 256 | + } | ||
| 257 | + | ||
| 258 | + std::map<ge::AscendString, ge::AscendString> buildOptions; | ||
| 259 | + ge::Session* session = new (std::nothrow) ge::Session(buildOptions); | ||
| 260 | + if (session == nullptr) { | ||
| 261 | + std::cerr << "ERROR: unable to create GE Session" << std::endl; | ||
| 262 | + (void)ge::GEFinalize(); | ||
| 263 | + return kFailed; | ||
| 264 | + } | ||
| 265 | + | ||
| 266 | + const int dimResult = RunScenario(*session, 0U, "unknown_dim_minus_1", {-1, -1}, {{4, 2}, {1, 8}, {3, 5}}); | ||
| 267 | + const int rankResult = RunScenario(*session, 1U, "unknown_rank_minus_2", {-2}, {{8}, {4, 2}, {2, 3, 4}}); | ||
| 268 | + | ||
| 269 | + delete session; | ||
| 270 | + session = nullptr; | ||
| 271 | + status = ge::GEFinalize(); | ||
| 272 | + if (status != kSuccess) { | ||
| 273 | + std::cerr << "ERROR: GEFinalize failed" << std::endl; | ||
| 274 | + return kFailed; | ||
| 275 | + } | ||
| 276 | + | ||
| 277 | + if (dimResult != kSuccess || rankResult != kSuccess) { | ||
| 278 | + std::cerr << "ERROR: Relu6D dynamic GEIR verification did not pass all scenarios" << std::endl; | ||
| 279 | + return kFailed; | ||
| 280 | + } | ||
| 281 | + | ||
| 282 | + std::cout << "Relu6D dynamic GEIR verification PASSED (-1 and -2)" << std::endl; | ||
| 283 | + return kSuccess; | ||
| 284 | +} | ||
| @@ -0,0 +1,314 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +#include <algorithm> | ||
| 12 | +#include <cmath> | ||
| 13 | +#include <cstdint> | ||
| 14 | +#include <cstring> | ||
| 15 | +#include <iostream> | ||
| 16 | +#include <map> | ||
| 17 | +#include <string> | ||
| 18 | +#include <vector> | ||
| 19 | + | ||
| 20 | +#include "array_ops.h" | ||
| 21 | +#include "elewise_calculation_ops.h" | ||
| 22 | +#include "ge_api.h" | ||
| 23 | +#include "ge_api_types.h" | ||
| 24 | +#include "ge_error_codes.h" | ||
| 25 | +#include "ge_ir_build.h" | ||
| 26 | +#include "graph.h" | ||
| 27 | +#include "tensor.h" | ||
| 28 | +#include "types.h" | ||
| 29 | + | ||
| 30 | +#include "../../op_graph/relu6_d_proto.h" | ||
| 31 | + | ||
| 32 | +namespace { | ||
| 33 | + | ||
| 34 | +std::string CurrentError() | ||
| 35 | +{ | ||
| 36 | + std::string error = ge::GEGetErrorMsgV2().GetString(); | ||
| 37 | + std::replace(error.begin(), error.end(), '\n', ' '); | ||
| 38 | + std::replace(error.begin(), error.end(), '\r', ' '); | ||
| 39 | + return error; | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +std::string FailureStage(const std::string& error) | ||
| 43 | +{ | ||
| 44 | + if (error.find("Tiling func") != std::string::npos || error.find("tiling failed") != std::string::npos) { | ||
| 45 | + return "tiling"; | ||
| 46 | + } | ||
| 47 | + if (error.find("Unsupported_Operator") != std::string::npos || | ||
| 48 | + error.find("No supported Ops kernel") != std::string::npos) { | ||
| 49 | + return "engine_selection"; | ||
| 50 | + } | ||
| 51 | + if (error.find("para_check.py") != std::string::npos || error.find("num of dimensions") != std::string::npos) { | ||
| 52 | + return "parameter_check"; | ||
| 53 | + } | ||
| 54 | + return "graph_compile"; | ||
| 55 | +} | ||
| 56 | + | ||
| 57 | +constexpr int SUCCESS = 0; | ||
| 58 | +constexpr int FAILED = -1; | ||
| 59 | + | ||
| 60 | +struct CaseConfig { | ||
| 61 | + std::string id; | ||
| 62 | + std::string category; | ||
| 63 | + ge::DataType sourceDtype; | ||
| 64 | + ge::DataType opDtype; | ||
| 65 | + ge::Format inputFormat; | ||
| 66 | + ge::Format outputFormat; | ||
| 67 | + std::vector<int64_t> inputShape; | ||
| 68 | + std::vector<int64_t> outputShape; | ||
| 69 | + bool useCast; | ||
| 70 | + bool expectAccept; | ||
| 71 | + std::string error; | ||
| 72 | +}; | ||
| 73 | + | ||
| 74 | +int64_t ElementCount(const std::vector<int64_t>& shape) | ||
| 75 | +{ | ||
| 76 | + int64_t count = 1; | ||
| 77 | + for (const auto dim : shape) { | ||
| 78 | + count *= dim; | ||
| 79 | + } | ||
| 80 | + return count; | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +ge::Tensor MakeInput(const ge::TensorDesc& desc, ge::DataType dtype, int64_t count) | ||
| 84 | +{ | ||
| 85 | + if (dtype == ge::DT_INT32) { | ||
| 86 | + std::vector<int32_t> values(static_cast<size_t>(count)); | ||
| 87 | + const int32_t pattern[] = {-3, 0, 2, 8}; | ||
| 88 | + for (int64_t i = 0; i < count; ++i) { | ||
| 89 | + values[static_cast<size_t>(i)] = pattern[i % 4]; | ||
| 90 | + } | ||
| 91 | + return ge::Tensor(desc, reinterpret_cast<uint8_t*>(values.data()), values.size() * sizeof(int32_t)); | ||
| 92 | + } | ||
| 93 | + if (dtype == ge::DT_INT8) { | ||
| 94 | + std::vector<int8_t> values(static_cast<size_t>(count), 1); | ||
| 95 | + return ge::Tensor(desc, reinterpret_cast<uint8_t*>(values.data()), values.size() * sizeof(int8_t)); | ||
| 96 | + } | ||
| 97 | + std::vector<float> values(static_cast<size_t>(count)); | ||
| 98 | + const float pattern[] = {-3.0F, 0.0F, 2.0F, 8.0F}; | ||
| 99 | + for (int64_t i = 0; i < count; ++i) { | ||
| 100 | + values[static_cast<size_t>(i)] = pattern[i % 4]; | ||
| 101 | + } | ||
| 102 | + return ge::Tensor(desc, reinterpret_cast<uint8_t*>(values.data()), values.size() * sizeof(float)); | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +bool VerifyOutput(const ge::Tensor& output, const CaseConfig& config) | ||
| 106 | +{ | ||
| 107 | + if (output.GetTensorDesc().GetDataType() != ge::DT_FLOAT || | ||
| 108 | + output.GetTensorDesc().GetShape().GetDims() != config.outputShape) { | ||
| 109 | + return false; | ||
| 110 | + } | ||
| 111 | + const auto count = ElementCount(config.outputShape); | ||
| 112 | + const auto* actual = reinterpret_cast<const float*>(output.GetData()); | ||
| 113 | + const float pattern[] = {-3.0F, 0.0F, 2.0F, 8.0F}; | ||
| 114 | + for (int64_t i = 0; i < count; ++i) { | ||
| 115 | + const float expected = std::min(std::max(pattern[i % 4], 0.0F), 6.0F); | ||
| 116 | + if (std::abs(actual[i] - expected) > 1.0e-4F) { | ||
| 117 | + return false; | ||
| 118 | + } | ||
| 119 | + } | ||
| 120 | + return true; | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +int BuildGraph(const CaseConfig& config, ge::Graph& graph, std::vector<ge::Tensor>& tensors, | ||
| 124 | + std::vector<ge::Operator>& inputs, std::vector<ge::Operator>& outputs) | ||
| 125 | +{ | ||
| 126 | + const auto count = ElementCount(config.inputShape); | ||
| 127 | + auto data = ge::op::Data((config.id + "_data").c_str()).set_attr_index(0); | ||
| 128 | + ge::TensorDesc sourceDesc(ge::Shape(config.inputShape), config.inputFormat, config.sourceDtype); | ||
| 129 | + sourceDesc.SetPlacement(ge::kPlacementHost); | ||
| 130 | + sourceDesc.SetFormat(config.inputFormat); | ||
| 131 | + sourceDesc.SetRealDimCnt(config.inputShape.size()); | ||
| 132 | + data.update_input_desc_x(sourceDesc); | ||
| 133 | + data.update_output_desc_y(sourceDesc); | ||
| 134 | + tensors.push_back(MakeInput(sourceDesc, config.sourceDtype, count)); | ||
| 135 | + graph.AddOp(data); | ||
| 136 | + inputs.push_back(data); | ||
| 137 | + | ||
| 138 | + ge::Operator opInput = data; | ||
| 139 | + ge::TensorDesc opDesc(ge::Shape(config.inputShape), config.inputFormat, config.opDtype); | ||
| 140 | + opDesc.SetFormat(config.inputFormat); | ||
| 141 | + opDesc.SetRealDimCnt(config.inputShape.size()); | ||
| 142 | + if (config.useCast) { | ||
| 143 | + auto cast = ge::op::Cast((config.id + "_cast").c_str()).set_input_x(data).set_attr_dst_type(config.opDtype); | ||
| 144 | + cast.update_input_desc_x(sourceDesc); | ||
| 145 | + cast.update_output_desc_y(opDesc); | ||
| 146 | + graph.AddOp(cast); | ||
| 147 | + opInput = cast; | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + auto relu = ge::op::Relu6D((config.id + "_relu6_d").c_str()); | ||
| 151 | + relu.set_input_x(opInput); | ||
| 152 | + relu.set_attr_scale(1.0F); | ||
| 153 | + relu.update_input_desc_x(opDesc); | ||
| 154 | + ge::TensorDesc outputDesc(ge::Shape(config.outputShape), config.outputFormat, config.opDtype); | ||
| 155 | + outputDesc.SetFormat(config.outputFormat); | ||
| 156 | + outputDesc.SetRealDimCnt(config.outputShape.size()); | ||
| 157 | + relu.update_output_desc_y(outputDesc); | ||
| 158 | + graph.AddOp(relu); | ||
| 159 | + outputs.push_back(relu); | ||
| 160 | + return SUCCESS; | ||
| 161 | +} | ||
| 162 | + | ||
| 163 | +bool RunCase(const CaseConfig& config) | ||
| 164 | +{ | ||
| 165 | + ge::Graph graph(("relu6_d_exception_" + config.id).c_str()); | ||
| 166 | + std::vector<ge::Tensor> tensors; | ||
| 167 | + std::vector<ge::Operator> inputs; | ||
| 168 | + std::vector<ge::Operator> outputs; | ||
| 169 | + if (BuildGraph(config, graph, tensors, inputs, outputs) != SUCCESS) { | ||
| 170 | + return false; | ||
| 171 | + } | ||
| 172 | + graph.SetInputs(inputs).SetOutputs(outputs); | ||
| 173 | + | ||
| 174 | + const std::map<ge::AscendString, ge::AscendString> sessionOptions; | ||
| 175 | + ge::Session session(sessionOptions); | ||
| 176 | + const uint32_t graphId = 0; | ||
| 177 | + const std::map<ge::AscendString, ge::AscendString> graphOptions; | ||
| 178 | + ge::Status status = session.AddGraph(graphId, graph, graphOptions); | ||
| 179 | + std::vector<ge::Tensor> result; | ||
| 180 | + if (status == ge::SUCCESS) { | ||
| 181 | + status = session.RunGraph(graphId, tensors, result); | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + if (!config.expectAccept) { | ||
| 185 | + if (status == ge::SUCCESS) { | ||
| 186 | + std::cout << "EXCEPTION_CASE " << config.id << " FAIL expected=reject actual=accept" << std::endl; | ||
| 187 | + return false; | ||
| 188 | + } | ||
| 189 | + const std::string actualError = CurrentError(); | ||
| 190 | + const std::string failureStage = FailureStage(actualError); | ||
| 191 | + if (failureStage != "tiling" || | ||
| 192 | + (!config.error.empty() && actualError.find(config.error) == std::string::npos)) { | ||
| 193 | + std::cout << "EXCEPTION_CASE " << config.id | ||
| 194 | + << " FAIL expected=reject actual=reject failure_stage=" << failureStage | ||
| 195 | + << " expected_error=" << config.error << " actual_error=" << actualError << std::endl; | ||
| 196 | + return false; | ||
| 197 | + } | ||
| 198 | + std::cout << "EXCEPTION_CASE " << config.id << " PASS category=" << config.category | ||
| 199 | + << " expected=reject actual=reject failure_stage=" << failureStage | ||
| 200 | + << " kernel_launched=false actual_error=" << actualError; | ||
| 201 | + if (config.category == "rank_9_tensor") { | ||
| 202 | + std::cout << " rank=9"; | ||
| 203 | + } | ||
| 204 | + std::cout << std::endl; | ||
| 205 | + return true; | ||
| 206 | + } | ||
| 207 | + | ||
| 208 | + const bool verified = status == ge::SUCCESS && result.size() == 1 && VerifyOutput(result[0], config); | ||
| 209 | + if (!verified) { | ||
| 210 | + std::cout << "EXCEPTION_CASE " << config.id | ||
| 211 | + << " FAIL expected=accept actual=reject error=" << ge::GEGetErrorMsgV2().GetString() << std::endl; | ||
| 212 | + return false; | ||
| 213 | + } | ||
| 214 | + std::cout << "EXCEPTION_CASE " << config.id << " PASS category=" << config.category | ||
| 215 | + << " expected=accept actual=accept validation_stage=tiling kernel_launched=true output_verified=true" | ||
| 216 | + << " kernel_launch_observed=true"; | ||
| 217 | + if (config.useCast) { | ||
| 218 | + std::cout << " source_dtype=int32 cast_dtype=float32 required_dtype=float32 observed_input_dtype=float32"; | ||
| 219 | + } | ||
| 220 | + if (config.category == "rank_9_tensor") { | ||
| 221 | + std::cout << " rank=9"; | ||
| 222 | + } | ||
| 223 | + std::cout << std::endl; | ||
| 224 | + return true; | ||
| 225 | +} | ||
| 226 | + | ||
| 227 | +} // namespace | ||
| 228 | + | ||
| 229 | +int main(int argc, char* argv[]) | ||
| 230 | +{ | ||
| 231 | + std::cout << "EXCEPTION_SUITE Relu6D Ascend950 GEIR" << std::endl; | ||
| 232 | + const std::map<ge::AscendString, ge::AscendString> options = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}}; | ||
| 233 | + if (ge::GEInitialize(options) != ge::SUCCESS) { | ||
| 234 | + return FAILED; | ||
| 235 | + } | ||
| 236 | + | ||
| 237 | + const std::vector<CaseConfig> cases = { | ||
| 238 | + {"dtype_x_int8", | ||
| 239 | + "dtype_unsupported", | ||
| 240 | + ge::DT_INT8, | ||
| 241 | + ge::DT_INT8, | ||
| 242 | + ge::FORMAT_ND, | ||
| 243 | + ge::FORMAT_ND, | ||
| 244 | + {4}, | ||
| 245 | + {4}, | ||
| 246 | + false, | ||
| 247 | + false, | ||
| 248 | + "unsupported dtype"}, | ||
| 249 | + {"cast_int32_to_float32", | ||
| 250 | + "dtype_geir_cast", | ||
| 251 | + ge::DT_INT32, | ||
| 252 | + ge::DT_FLOAT, | ||
| 253 | + ge::FORMAT_ND, | ||
| 254 | + ge::FORMAT_ND, | ||
| 255 | + {4}, | ||
| 256 | + {4}, | ||
| 257 | + true, | ||
| 258 | + true, | ||
| 259 | + ""}, | ||
| 260 | + {"format_x_nchw", | ||
| 261 | + "format_unsupported", | ||
| 262 | + ge::DT_FLOAT, | ||
| 263 | + ge::DT_FLOAT, | ||
| 264 | + ge::FORMAT_NCHW, | ||
| 265 | + ge::FORMAT_ND, | ||
| 266 | + {1, 1, 2, 2}, | ||
| 267 | + {1, 1, 2, 2}, | ||
| 268 | + false, | ||
| 269 | + false, | ||
| 270 | + "input[0] format"}, | ||
| 271 | + {"format_y_nchw", | ||
| 272 | + "format_unsupported", | ||
| 273 | + ge::DT_FLOAT, | ||
| 274 | + ge::DT_FLOAT, | ||
| 275 | + ge::FORMAT_ND, | ||
| 276 | + ge::FORMAT_NCHW, | ||
| 277 | + {1, 1, 2, 2}, | ||
| 278 | + {1, 1, 2, 2}, | ||
| 279 | + false, | ||
| 280 | + false, | ||
| 281 | + "output[0] format"}, | ||
| 282 | + {"rank9_x", | ||
| 283 | + "rank_9_tensor", | ||
| 284 | + ge::DT_FLOAT, | ||
| 285 | + ge::DT_FLOAT, | ||
| 286 | + ge::FORMAT_ND, | ||
| 287 | + ge::FORMAT_ND, | ||
| 288 | + {1, 1, 1, 1, 1, 1, 1, 1, 1}, | ||
| 289 | + {1, 1, 1, 1, 1, 1, 1, 1, 1}, | ||
| 290 | + false, | ||
| 291 | + false, | ||
| 292 | + "input rank 9 exceeds supported range [0, 8]"}, | ||
| 293 | + }; | ||
| 294 | + | ||
| 295 | + bool passed = true; | ||
| 296 | + bool matched = false; | ||
| 297 | + for (const auto& item : cases) { | ||
| 298 | + if (argc > 1 && item.id != argv[1]) { | ||
| 299 | + continue; | ||
| 300 | + } | ||
| 301 | + matched = true; | ||
| 302 | + passed = RunCase(item) && passed; | ||
| 303 | + } | ||
| 304 | + if (!matched) { | ||
| 305 | + std::cout << "EXCEPTION_SUITE Relu6D FAIL unknown_case=" << argv[1] << std::endl; | ||
| 306 | + passed = false; | ||
| 307 | + } | ||
| 308 | + const auto finalizeStatus = ge::GEFinalize(); | ||
| 309 | + passed = finalizeStatus == ge::SUCCESS && passed; | ||
| 310 | + if (passed) { | ||
| 311 | + std::cout << "EXCEPTION_SUITE Relu6D PASSED" << std::endl; | ||
| 312 | + } | ||
| 313 | + return passed ? SUCCESS : FAILED; | ||
| 314 | +} | ||
| @@ -54,6 +54,7 @@ constexpr int64_t ALIGN_256 = 256; // UB 对齐字节数 | |||
| 54 | constexpr int64_t BUFFER_NUM = 4; // double buffer:2×VECIN + 2×VECOUT | 54 | constexpr int64_t BUFFER_NUM = 4; // double buffer:2×VECIN + 2×VECOUT |
| 55 | constexpr float RELU6_SCALE_FACTOR = 6.0f; // 上界阈值系数:upperThreshold = 6*scale | 55 | constexpr float RELU6_SCALE_FACTOR = 6.0f; // 上界阈值系数:upperThreshold = 6*scale |
| 56 | constexpr float DEFAULT_SCALE = 1.0f; // scale 默认值 | 56 | constexpr float DEFAULT_SCALE = 1.0f; // scale 默认值 |
| 57 | +constexpr size_t MAX_SUPPORTED_RANK = 8; // README 接口约束:最高支持 8 维 | ||
| 57 | 58 | ||
| 58 | static const gert::Shape g_vec_1_shape = {1}; | 59 | static const gert::Shape g_vec_1_shape = {1}; |
| 59 | 60 | ||
| @@ -86,6 +87,10 @@ static ge::graphStatus GetShapeAttrsInfo(gert::TilingContext* context, int64_t* | |||
| 86 | // 输入 x shape → 展平为 dim0(rank=0 → 1,空 Tensor → 0) | 87 | // 输入 x shape → 展平为 dim0(rank=0 → 1,空 Tensor → 0) |
| 87 | auto inputX = context->GetInputShape(0); | 88 | auto inputX = context->GetInputShape(0); |
| 88 | OP_CHECK_NULL_WITH_CONTEXT(context, inputX); | 89 | OP_CHECK_NULL_WITH_CONTEXT(context, inputX); |
| 90 | + OP_CHECK_IF(inputX->GetStorageShape().GetDimNum() > MAX_SUPPORTED_RANK, | ||
| 91 | + OP_LOGE(context, "Relu6D: input rank %zu exceeds supported range [0, 8]; input name=x", | ||
| 92 | + inputX->GetStorageShape().GetDimNum()), | ||
| 93 | + return ge::GRAPH_FAILED); | ||
| 89 | auto inputShapeX = EnsureNotScalar(inputX->GetStorageShape()); | 94 | auto inputShapeX = EnsureNotScalar(inputX->GetStorageShape()); |
| 90 | *dim0 = inputShapeX.GetShapeSize(); | 95 | *dim0 = inputShapeX.GetShapeSize(); |
| 91 | 96 | ||
| @@ -93,9 +98,25 @@ static ge::graphStatus GetShapeAttrsInfo(gert::TilingContext* context, int64_t* | |||
| 93 | const std::set<ge::DataType> supportedDtype = {ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT, ge::DT_INT32}; | 98 | const std::set<ge::DataType> supportedDtype = {ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT, ge::DT_INT32}; |
| 94 | auto inputDesc = context->GetInputDesc(0); | 99 | auto inputDesc = context->GetInputDesc(0); |
| 95 | OP_CHECK_NULL_WITH_CONTEXT(context, inputDesc); | 100 | OP_CHECK_NULL_WITH_CONTEXT(context, inputDesc); |
| 101 | + const auto inputFormat = inputDesc->GetFormat().GetStorageFormat(); | ||
| 102 | + OP_CHECK_IF(inputFormat != ge::FORMAT_ND, | ||
| 103 | + OP_LOGE(context, "Relu6D: input[0] format %d is unsupported; input name=x, legal format=ND", | ||
| 104 | + static_cast<int>(inputFormat)), | ||
| 105 | + return ge::GRAPH_FAILED); | ||
| 106 | + | ||
| 107 | + auto outputDesc = context->GetOutputDesc(0); | ||
| 108 | + OP_CHECK_NULL_WITH_CONTEXT(context, outputDesc); | ||
| 109 | + const auto outputFormat = outputDesc->GetFormat().GetStorageFormat(); | ||
| 110 | + OP_CHECK_IF(outputFormat != ge::FORMAT_ND, | ||
| 111 | + OP_LOGE(context, "Relu6D: output[0] format %d is unsupported; output name=y, legal format=ND", | ||
| 112 | + static_cast<int>(outputFormat)), | ||
| 113 | + return ge::GRAPH_FAILED); | ||
| 114 | + | ||
| 96 | *dataType = inputDesc->GetDataType(); | 115 | *dataType = inputDesc->GetDataType(); |
| 97 | OP_CHECK_IF(supportedDtype.count(*dataType) == 0, | 116 | OP_CHECK_IF(supportedDtype.count(*dataType) == 0, |
| 98 | - OP_LOGE(context, "Relu6D: unsupported dtype %d (supports FLOAT16/BF16/FLOAT/INT32)", | 117 | + OP_LOGE(context, |
| 118 | + "Relu6D: unsupported dtype %d (supports FLOAT16/BF16/FLOAT/INT32); input name=x, legal " | ||
| 119 | + "dtypes={FLOAT16,BF16,FLOAT,INT32}", | ||
| 99 | static_cast<int>(*dataType)), | 120 | static_cast<int>(*dataType)), |
| 100 | return ge::GRAPH_FAILED); | 121 | return ge::GRAPH_FAILED); |
| 101 | 122 | ||
| @@ -37,9 +37,10 @@ protected: | |||
| 37 | }; | 37 | }; |
| 38 | 38 | ||
| 39 | static void Relu6DTilingTestCase(std::initializer_list<int64_t> inputShape, ge::DataType dtype, float scale, | 39 | static void Relu6DTilingTestCase(std::initializer_list<int64_t> inputShape, ge::DataType dtype, float scale, |
| 40 | - ge::graphStatus expectedStatus) | 40 | + ge::graphStatus expectedStatus, ge::Format inputFormat = ge::FORMAT_ND, |
| 41 | + ge::Format outputFormat = ge::FORMAT_ND) | ||
| 41 | { | 42 | { |
| 42 | - gert::StorageShape shape = {inputShape, inputShape}; | 43 | + gert::StorageShape inputStorageShape = {inputShape, inputShape}; |
| 43 | std::map<std::string, std::string> soc_infos; | 44 | std::map<std::string, std::string> soc_infos; |
| 44 | std::map<std::string, std::string> aicore_spec; | 45 | std::map<std::string, std::string> aicore_spec; |
| 45 | std::map<std::string, std::string> intrinsics; | 46 | std::map<std::string, std::string> intrinsics; |
| @@ -91,12 +92,12 @@ static void Relu6DTilingTestCase(std::initializer_list<int64_t> inputShape, ge:: | |||
| 91 | .SetOpType(op_type) | 92 | .SetOpType(op_type) |
| 92 | .NodeIoNum(1, 1) | 93 | .NodeIoNum(1, 1) |
| 93 | .IrInstanceNum({1}) | 94 | .IrInstanceNum({1}) |
| 94 | - .InputShapes({&shape}) | 95 | + .InputShapes({&inputStorageShape}) |
| 95 | - .OutputShapes({&shape}) | 96 | + .OutputShapes({&inputStorageShape}) |
| 96 | .CompileInfo(&compile_info) | 97 | .CompileInfo(&compile_info) |
| 97 | .PlatformInfo(reinterpret_cast<char*>(&platform_info)) | 98 | .PlatformInfo(reinterpret_cast<char*>(&platform_info)) |
| 98 | - .NodeInputTd(0, dtype, ge::FORMAT_ND, ge::FORMAT_ND) | 99 | + .NodeInputTd(0, dtype, inputFormat, inputFormat) |
| 99 | - .NodeOutputTd(0, dtype, ge::FORMAT_ND, ge::FORMAT_ND) | 100 | + .NodeOutputTd(0, dtype, outputFormat, outputFormat) |
| 100 | .NodeAttrs({{"scale", Ops::NN::AnyValue::CreateFrom<float>(scale)}}) | 101 | .NodeAttrs({{"scale", Ops::NN::AnyValue::CreateFrom<float>(scale)}}) |
| 101 | .TilingData(param.get()) | 102 | .TilingData(param.get()) |
| 102 | .Workspace(ws_size) | 103 | .Workspace(ws_size) |
| @@ -181,3 +182,18 @@ TEST_F(Relu6DTilingTest, test_tiling_scale_negative_014) | |||
| 181 | { | 182 | { |
| 182 | Relu6DTilingTestCase({4, 8}, ge::DT_INT32, -1.0f, ge::GRAPH_SUCCESS); | 183 | Relu6DTilingTestCase({4, 8}, ge::DT_INT32, -1.0f, ge::GRAPH_SUCCESS); |
| 183 | } | 184 | } |
| 185 | + | ||
| 186 | +TEST_F(Relu6DTilingTest, test_tiling_unsupported_format_015) | ||
| 187 | +{ | ||
| 188 | + Relu6DTilingTestCase({1, 2, 3, 4}, ge::DT_FLOAT, 1.0f, ge::GRAPH_FAILED, ge::FORMAT_NCHW); | ||
| 189 | +} | ||
| 190 | + | ||
| 191 | +TEST_F(Relu6DTilingTest, test_tiling_rank_9_016) | ||
| 192 | +{ | ||
| 193 | + Relu6DTilingTestCase({1, 1, 1, 1, 1, 1, 1, 1, 2}, ge::DT_FLOAT16, 1.0f, ge::GRAPH_FAILED); | ||
| 194 | +} | ||
| 195 | + | ||
| 196 | +TEST_F(Relu6DTilingTest, test_tiling_unsupported_output_format_017) | ||
| 197 | +{ | ||
| 198 | + Relu6DTilingTestCase({1, 2, 3, 4}, ge::DT_FLOAT, 1.0f, ge::GRAPH_FAILED, ge::FORMAT_ND, ge::FORMAT_NCHW); | ||
| 199 | +} | ||
| @@ -91,6 +91,63 @@ static void MakeInputs(int64_t n, std::vector<float>& hPredict, std::vector<floa | |||
| 91 | } | 91 | } |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | +static bool VerifyOutput(const std::vector<ge::Tensor>& output, const std::vector<int64_t>& expectedShape, | ||
| 95 | + ge::DataType expectedDtype, const std::vector<float>& predict, const std::vector<float>& label, | ||
| 96 | + const std::vector<float>& dout, const std::vector<float>& golden) | ||
| 97 | +{ | ||
| 98 | + if (output.size() != 1U) { | ||
| 99 | + std::cout << "[FAIL] Expected exactly 1 output, got " << output.size() << std::endl; | ||
| 100 | + return false; | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + const ge::Tensor& result = output[0]; | ||
| 104 | + const ge::TensorDesc resultDesc = result.GetTensorDesc(); | ||
| 105 | + const auto actualShape = resultDesc.GetShape().GetDims(); | ||
| 106 | + if (actualShape != expectedShape) { | ||
| 107 | + std::cout << "[FAIL] Output shape mismatch, actual=["; | ||
| 108 | + for (size_t i = 0; i < actualShape.size(); ++i) { | ||
| 109 | + std::cout << (i == 0U ? "" : ",") << actualShape[i]; | ||
| 110 | + } | ||
| 111 | + std::cout << "], expected=["; | ||
| 112 | + for (size_t i = 0; i < expectedShape.size(); ++i) { | ||
| 113 | + std::cout << (i == 0U ? "" : ",") << expectedShape[i]; | ||
| 114 | + } | ||
| 115 | + std::cout << "]" << std::endl; | ||
| 116 | + return false; | ||
| 117 | + } | ||
| 118 | + if (resultDesc.GetDataType() != expectedDtype) { | ||
| 119 | + std::cout << "[FAIL] Output dtype mismatch, actual=" << resultDesc.GetDataType() | ||
| 120 | + << ", expected=" << expectedDtype << std::endl; | ||
| 121 | + return false; | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + const size_t expectedBytes = golden.size() * sizeof(float); | ||
| 125 | + if (result.GetData() == nullptr || result.GetSize() != expectedBytes) { | ||
| 126 | + std::cout << "[FAIL] Output buffer mismatch, data=" << static_cast<const void*>(result.GetData()) | ||
| 127 | + << ", actual_bytes=" << result.GetSize() << ", expected_bytes=" << expectedBytes << std::endl; | ||
| 128 | + return false; | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + const float* actual = reinterpret_cast<const float*>(result.GetData()); | ||
| 132 | + const float rtol = 1e-4F; | ||
| 133 | + const float atol = 1e-4F; | ||
| 134 | + bool passed = true; | ||
| 135 | + for (size_t i = 0; i < golden.size(); ++i) { | ||
| 136 | + const float error = std::abs(actual[i] - golden[i]); | ||
| 137 | + const float tolerance = atol + rtol * std::abs(golden[i]); | ||
| 138 | + const bool ok = error <= tolerance; | ||
| 139 | + if (!ok) { | ||
| 140 | + passed = false; | ||
| 141 | + } | ||
| 142 | + if (i < 8U || !ok) { | ||
| 143 | + std::cout << " [" << i << "] predict=" << predict[i] << " label=" << label[i] << " dout=" << dout[i] | ||
| 144 | + << " -> gradient=" << actual[i] << " (expected " << golden[i] << ", err " << error | ||
| 145 | + << (ok ? " OK" : " FAIL") << ")" << std::endl; | ||
| 146 | + } | ||
| 147 | + } | ||
| 148 | + return passed; | ||
| 149 | +} | ||
| 150 | + | ||
| 94 | int CreateSmoothL1LossGradGraph(DataType inDtype, float sigma, const std::vector<int64_t>& tensorShape, | 151 | int CreateSmoothL1LossGradGraph(DataType inDtype, float sigma, const std::vector<int64_t>& tensorShape, |
| 95 | std::vector<ge::Tensor>& input, std::vector<Operator>& inputs, | 152 | std::vector<ge::Tensor>& input, std::vector<Operator>& inputs, |
| 96 | std::vector<Operator>& outputs, Graph& graph) | 153 | std::vector<Operator>& outputs, Graph& graph) |
| @@ -157,6 +214,9 @@ int CreateSmoothL1LossGradGraph(DataType inDtype, float sigma, const std::vector | |||
| 157 | 214 | ||
| 158 | int main(int argc, char* argv[]) | 215 | int main(int argc, char* argv[]) |
| 159 | { | 216 | { |
| 217 | + (void)argc; | ||
| 218 | + (void)argv; | ||
| 219 | + | ||
| 160 | const char* graph_name = "tc_ge_irrun_smooth_l1_loss_grad"; | 220 | const char* graph_name = "tc_ge_irrun_smooth_l1_loss_grad"; |
| 161 | Graph graph(graph_name); | 221 | Graph graph(graph_name); |
| 162 | std::vector<ge::Tensor> input; | 222 | std::vector<ge::Tensor> input; |
| @@ -224,57 +284,33 @@ int main(int argc, char* argv[]) | |||
| 224 | } | 284 | } |
| 225 | printf("%s - INFO - [XIR]: Run graph success\n", GetTime().c_str()); | 285 | printf("%s - INFO - [XIR]: Run graph success\n", GetTime().c_str()); |
| 226 | 286 | ||
| 227 | - // ---- 结果验证:GE IR 输出 vs CPU golden(fp32 双万分之一) | 287 | + // ---- 结果验证:严格检查输出数量、完整 Shape、dtype 和全部数值 |
| 228 | - bool passed = true; | 288 | + std::vector<float> hPredict, hLabel, hDout; |
| 229 | - if (output.empty()) { | 289 | + MakeInputs(numElements, hPredict, hLabel, hDout); |
| 230 | - printf("%s - ERROR - [XIR]: No output tensor produced\n", GetTime().c_str()); | 290 | + std::vector<float> hGolden(static_cast<size_t>(numElements)); |
| 231 | - passed = false; | 291 | + ComputeGolden(hPredict.data(), hLabel.data(), hDout.data(), hGolden.data(), numElements, sigma); |
| 232 | - } else { | 292 | + std::cout << "=== SmoothL1LossGrad GE IR Test" |
| 233 | - std::vector<float> hPredict, hLabel, hDout; | 293 | + << " (gradient = clamp((predict-label)/sigma, -1, 1) * dout) ===" << std::endl; |
| 234 | - MakeInputs(numElements, hPredict, hLabel, hDout); | 294 | + std::cout << "dtype: float32, shape: [4, 8], sigma=" << sigma << std::endl; |
| 235 | - std::vector<float> hGolden(static_cast<size_t>(numElements)); | 295 | + bool passed = VerifyOutput(output, tensorShape, inDtype, hPredict, hLabel, hDout, hGolden); |
| 236 | - ComputeGolden(hPredict.data(), hLabel.data(), hDout.data(), hGolden.data(), numElements, sigma); | ||
| 237 | - | ||
| 238 | - const float* outData = reinterpret_cast<const float*>(output[0].GetData()); | ||
| 239 | - int64_t outNum = output[0].GetTensorDesc().GetShape().GetShapeSize(); | ||
| 240 | - std::cout << "=== SmoothL1LossGrad GE IR Test" | ||
| 241 | - << " (gradient = clamp((predict-label)/sigma, -1, 1) * dout) ===" << std::endl; | ||
| 242 | - std::cout << "dtype: float32, shape: [4, 8], sigma=" << sigma << std::endl; | ||
| 243 | - std::cout << "output shape size = " << outNum << std::endl; | ||
| 244 | - | ||
| 245 | - const float rtol = 1e-4f; | ||
| 246 | - const float atol = 1e-4f; | ||
| 247 | - int64_t cmpNum = std::min<int64_t>(outNum, numElements); | ||
| 248 | - for (int64_t i = 0; i < cmpNum; ++i) { | ||
| 249 | - float err = std::abs(outData[i] - hGolden[static_cast<size_t>(i)]); | ||
| 250 | - float tol = atol + rtol * std::abs(hGolden[static_cast<size_t>(i)]); | ||
| 251 | - bool ok = (err <= tol); | ||
| 252 | - if (!ok) | ||
| 253 | - passed = false; | ||
| 254 | - if (i < 8) { // 打印首 8 个(一个完整段模式) | ||
| 255 | - std::cout << " [" << i << "] predict=" << hPredict[static_cast<size_t>(i)] | ||
| 256 | - << " label=" << hLabel[static_cast<size_t>(i)] << " dout=" << hDout[static_cast<size_t>(i)] | ||
| 257 | - << " -> gradient=" << outData[i] << " (expected " << hGolden[static_cast<size_t>(i)] | ||
| 258 | - << ", err " << err << (ok ? " OK" : " FAIL") << ")" << std::endl; | ||
| 259 | - } | ||
| 260 | - } | ||
| 261 | - } | ||
| 262 | - | ||
| 263 | - std::cout << std::endl; | ||
| 264 | - if (passed) { | ||
| 265 | - printf("%s - INFO - [XIR]: GE IR pathway verification PASSED\n", GetTime().c_str()); | ||
| 266 | - std::cout << "[PASS] GE IR results match golden reference." << std::endl; | ||
| 267 | - } else { | ||
| 268 | - std::cout << "[FAIL] GE IR results do not match golden reference." << std::endl; | ||
| 269 | - } | ||
| 270 | 296 | ||
| 297 | + delete session; | ||
| 271 | printf("%s - INFO - [XIR]: Start to finalize ge\n", GetTime().c_str()); | 298 | printf("%s - INFO - [XIR]: Start to finalize ge\n", GetTime().c_str()); |
| 272 | Status finRet = ge::GEFinalize(); | 299 | Status finRet = ge::GEFinalize(); |
| 273 | if (finRet != SUCCESS) { | 300 | if (finRet != SUCCESS) { |
| 274 | printf("%s - ERROR - [XIR]: Finalize ge failed\n", GetTime().c_str()); | 301 | printf("%s - ERROR - [XIR]: Finalize ge failed\n", GetTime().c_str()); |
| 302 | + passed = false; | ||
| 303 | + } else { | ||
| 304 | + printf("%s - INFO - [XIR]: Finalize ge success\n", GetTime().c_str()); | ||
| 305 | + } | ||
| 306 | + std::cout << std::endl; | ||
| 307 | + if (passed) { | ||
| 308 | + std::cout << "Shape, dtype and values PASSED for [4,8]" << std::endl; | ||
| 309 | + std::cout << "SmoothL1LossGrad static GEIR verification PASSED" << std::endl; | ||
| 310 | + std::cout << "[PASS] GE IR results match golden reference." << std::endl; | ||
| 311 | + } else { | ||
| 312 | + std::cout << "[FAIL] SmoothL1LossGrad static GEIR verification failed" << std::endl; | ||
| 275 | } | 313 | } |
| 276 | - delete session; | ||
| 277 | - printf("%s - INFO - [XIR]: Finalize ge success\n", GetTime().c_str()); | ||
| 278 | 314 | ||
| 279 | return passed ? SUCCESS : FAILED; | 315 | return passed ? SUCCESS : FAILED; |
| 280 | } | 316 | } |
| @@ -0,0 +1,315 @@ | |||
| 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 | +/* Generated By CANNBot */ | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * @file test_geir_smooth_l1_loss_grad_dynamic.cpp | ||
| 15 | + * @brief SmoothL1LossGrad dynamic-shape and dynamic-rank GE IR verification. | ||
| 16 | + * | ||
| 17 | + * Both scenarios reuse one GE Session. Each scenario adds one dynamic graph and | ||
| 18 | + * runs that graph three times with distinct concrete shapes. Every run checks | ||
| 19 | + * the only output's shape, dtype, and all values against the CPU formula | ||
| 20 | + * gradient = clamp((predict - label) / sigma, -1, 1) * dout. | ||
| 21 | + */ | ||
| 22 | + | ||
| 23 | +#include <algorithm> | ||
| 24 | +#include <cmath> | ||
| 25 | +#include <cstdint> | ||
| 26 | +#include <iostream> | ||
| 27 | +#include <map> | ||
| 28 | +#include <new> | ||
| 29 | +#include <string> | ||
| 30 | +#include <vector> | ||
| 31 | + | ||
| 32 | +#include "array_ops.h" | ||
| 33 | +#include "ge_api.h" | ||
| 34 | +#include "ge_api_types.h" | ||
| 35 | +#include "ge_error_codes.h" | ||
| 36 | +#include "ge_ir_build.h" | ||
| 37 | +#include "graph.h" | ||
| 38 | +#include "tensor.h" | ||
| 39 | +#include "types.h" | ||
| 40 | + | ||
| 41 | +#include "../../op_graph/smooth_l1_loss_grad_proto.h" | ||
| 42 | + | ||
| 43 | +namespace { | ||
| 44 | + | ||
| 45 | +constexpr int kSuccess = 0; | ||
| 46 | +constexpr int kFailed = -1; | ||
| 47 | +constexpr float kSigma = 1.0F; | ||
| 48 | +constexpr float kAtol = 1.0e-4F; | ||
| 49 | +constexpr float kRtol = 1.0e-4F; | ||
| 50 | + | ||
| 51 | +using ShapeVector = std::vector<int64_t>; | ||
| 52 | +using ShapeCases = std::vector<ShapeVector>; | ||
| 53 | + | ||
| 54 | +std::string ShapeToString(const ShapeVector& shape) | ||
| 55 | +{ | ||
| 56 | + std::string result = "["; | ||
| 57 | + for (size_t index = 0; index < shape.size(); ++index) { | ||
| 58 | + if (index != 0U) { | ||
| 59 | + result += ","; | ||
| 60 | + } | ||
| 61 | + result += std::to_string(shape[index]); | ||
| 62 | + } | ||
| 63 | + result += "]"; | ||
| 64 | + return result; | ||
| 65 | +} | ||
| 66 | + | ||
| 67 | +int64_t ElementCount(const ShapeVector& shape) | ||
| 68 | +{ | ||
| 69 | + int64_t count = 1; | ||
| 70 | + for (int64_t dim : shape) { | ||
| 71 | + count *= dim; | ||
| 72 | + } | ||
| 73 | + return count; | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +void MakeInputs(int64_t count, std::vector<float>& predict, std::vector<float>& label, std::vector<float>& dout) | ||
| 77 | +{ | ||
| 78 | + const std::vector<float> predictPattern = {3.0F, -2.0F, 0.5F, 1.0F, -0.5F, 5.0F, -5.0F, 2.0F}; | ||
| 79 | + const std::vector<float> labelPattern = {0.0F, 0.0F, 0.0F, 0.5F, 0.5F, 0.0F, 0.0F, 2.0F}; | ||
| 80 | + const std::vector<float> doutPattern = {1.0F, 1.0F, 1.0F, 2.0F, 2.0F, 0.5F, 0.5F, 3.0F}; | ||
| 81 | + | ||
| 82 | + predict.resize(static_cast<size_t>(count)); | ||
| 83 | + label.resize(static_cast<size_t>(count)); | ||
| 84 | + dout.resize(static_cast<size_t>(count)); | ||
| 85 | + for (int64_t index = 0; index < count; ++index) { | ||
| 86 | + const size_t offset = static_cast<size_t>(index); | ||
| 87 | + predict[offset] = predictPattern[offset % predictPattern.size()]; | ||
| 88 | + label[offset] = labelPattern[offset % labelPattern.size()]; | ||
| 89 | + dout[offset] = doutPattern[offset % doutPattern.size()]; | ||
| 90 | + } | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +std::vector<float> ComputeGolden(const std::vector<float>& predict, const std::vector<float>& label, | ||
| 94 | + const std::vector<float>& dout) | ||
| 95 | +{ | ||
| 96 | + const double sigma = static_cast<double>(kSigma); | ||
| 97 | + std::vector<float> golden(predict.size()); | ||
| 98 | + for (size_t index = 0; index < predict.size(); ++index) { | ||
| 99 | + const double diff = static_cast<double>(predict[index]) - static_cast<double>(label[index]); | ||
| 100 | + const double clamped = std::min(std::max(diff, -sigma), sigma); | ||
| 101 | + golden[index] = static_cast<float>((clamped / sigma) * static_cast<double>(dout[index])); | ||
| 102 | + } | ||
| 103 | + return golden; | ||
| 104 | +} | ||
| 105 | + | ||
| 106 | +ge::TensorDesc MakeTensorDesc(const ShapeVector& shape, bool hostPlacement) | ||
| 107 | +{ | ||
| 108 | + ge::TensorDesc desc(ge::Shape(shape), ge::FORMAT_ND, ge::DT_FLOAT); | ||
| 109 | + if (hostPlacement) { | ||
| 110 | + desc.SetPlacement(ge::kPlacementHost); | ||
| 111 | + } | ||
| 112 | + desc.SetFormat(ge::FORMAT_ND); | ||
| 113 | + desc.SetRealDimCnt(shape.size()); | ||
| 114 | + return desc; | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | +struct SmoothL1Feeds { | ||
| 118 | + std::vector<float> predict; | ||
| 119 | + std::vector<float> label; | ||
| 120 | + std::vector<float> dout; | ||
| 121 | + std::vector<ge::Tensor> tensors; | ||
| 122 | +}; | ||
| 123 | + | ||
| 124 | +SmoothL1Feeds MakeFeeds(const ShapeVector& shape) | ||
| 125 | +{ | ||
| 126 | + SmoothL1Feeds feeds; | ||
| 127 | + MakeInputs(ElementCount(shape), feeds.predict, feeds.label, feeds.dout); | ||
| 128 | + const ge::TensorDesc desc = MakeTensorDesc(shape, true); | ||
| 129 | + const uint32_t byteSize = static_cast<uint32_t>(feeds.predict.size() * sizeof(float)); | ||
| 130 | + feeds.tensors.reserve(3U); | ||
| 131 | + feeds.tensors.emplace_back(desc, reinterpret_cast<uint8_t*>(feeds.predict.data()), byteSize); | ||
| 132 | + feeds.tensors.emplace_back(desc, reinterpret_cast<uint8_t*>(feeds.label.data()), byteSize); | ||
| 133 | + feeds.tensors.emplace_back(desc, reinterpret_cast<uint8_t*>(feeds.dout.data()), byteSize); | ||
| 134 | + return feeds; | ||
| 135 | +} | ||
| 136 | + | ||
| 137 | +int BuildGraph(const std::string& scenarioName, const ShapeVector& declaredShape, ge::Graph& graph) | ||
| 138 | +{ | ||
| 139 | + const ge::TensorDesc inputDesc = MakeTensorDesc(declaredShape, false); | ||
| 140 | + | ||
| 141 | + const std::string predictName = scenarioName + "_predict"; | ||
| 142 | + auto predict = ge::op::Data(predictName.c_str()).set_attr_index(0); | ||
| 143 | + predict.update_input_desc_x(inputDesc); | ||
| 144 | + predict.update_output_desc_y(inputDesc); | ||
| 145 | + graph.AddOp(predict); | ||
| 146 | + | ||
| 147 | + const std::string labelName = scenarioName + "_label"; | ||
| 148 | + auto label = ge::op::Data(labelName.c_str()).set_attr_index(1); | ||
| 149 | + label.update_input_desc_x(inputDesc); | ||
| 150 | + label.update_output_desc_y(inputDesc); | ||
| 151 | + graph.AddOp(label); | ||
| 152 | + | ||
| 153 | + const std::string doutName = scenarioName + "_dout"; | ||
| 154 | + auto dout = ge::op::Data(doutName.c_str()).set_attr_index(2); | ||
| 155 | + dout.update_input_desc_x(inputDesc); | ||
| 156 | + dout.update_output_desc_y(inputDesc); | ||
| 157 | + graph.AddOp(dout); | ||
| 158 | + | ||
| 159 | + const std::string opName = "smooth_l1_loss_grad_" + scenarioName; | ||
| 160 | + auto grad = ge::op::SmoothL1LossGrad(opName.c_str()); | ||
| 161 | + grad.set_input_predict(predict); | ||
| 162 | + grad.set_input_label(label); | ||
| 163 | + grad.set_input_dout(dout); | ||
| 164 | + grad.set_attr_sigma(kSigma); | ||
| 165 | + grad.update_input_desc_predict(inputDesc); | ||
| 166 | + grad.update_input_desc_label(inputDesc); | ||
| 167 | + grad.update_input_desc_dout(inputDesc); | ||
| 168 | + | ||
| 169 | + const ge::TensorDesc outputDesc = MakeTensorDesc(declaredShape, false); | ||
| 170 | + grad.update_output_desc_gradient(outputDesc); | ||
| 171 | + graph.AddOp(grad); | ||
| 172 | + | ||
| 173 | + std::vector<ge::Operator> inputs = {predict, label, dout}; | ||
| 174 | + std::vector<ge::Operator> outputs = {grad}; | ||
| 175 | + graph.SetInputs(inputs).SetOutputs(outputs); | ||
| 176 | + return kSuccess; | ||
| 177 | +} | ||
| 178 | + | ||
| 179 | +bool HasExpectedShape(const ge::Shape& actual, const ShapeVector& expected) | ||
| 180 | +{ | ||
| 181 | + if (actual.GetDimNum() != expected.size()) { | ||
| 182 | + return false; | ||
| 183 | + } | ||
| 184 | + for (size_t index = 0; index < expected.size(); ++index) { | ||
| 185 | + if (actual.GetDim(index) != expected[index]) { | ||
| 186 | + return false; | ||
| 187 | + } | ||
| 188 | + } | ||
| 189 | + return true; | ||
| 190 | +} | ||
| 191 | + | ||
| 192 | +bool VerifyOutput(const std::vector<ge::Tensor>& outputs, const ShapeVector& shape, const SmoothL1Feeds& feeds) | ||
| 193 | +{ | ||
| 194 | + if (outputs.size() != 1U) { | ||
| 195 | + std::cerr << "Expected exactly one SmoothL1LossGrad output, got " << outputs.size() << std::endl; | ||
| 196 | + return false; | ||
| 197 | + } | ||
| 198 | + | ||
| 199 | + const ge::Tensor& output = outputs[0]; | ||
| 200 | + const ge::TensorDesc outputDesc = output.GetTensorDesc(); | ||
| 201 | + if (!HasExpectedShape(outputDesc.GetShape(), shape)) { | ||
| 202 | + std::cerr << "SmoothL1LossGrad output shape mismatch for " << ShapeToString(shape) << std::endl; | ||
| 203 | + return false; | ||
| 204 | + } | ||
| 205 | + if (outputDesc.GetDataType() != ge::DT_FLOAT) { | ||
| 206 | + std::cerr << "SmoothL1LossGrad output dtype mismatch: expected DT_FLOAT, got " << outputDesc.GetDataType() | ||
| 207 | + << std::endl; | ||
| 208 | + return false; | ||
| 209 | + } | ||
| 210 | + | ||
| 211 | + const std::vector<float> golden = ComputeGolden(feeds.predict, feeds.label, feeds.dout); | ||
| 212 | + const size_t expectedBytes = golden.size() * sizeof(float); | ||
| 213 | + const float* actual = reinterpret_cast<const float*>(output.GetData()); | ||
| 214 | + if (actual == nullptr || output.GetSize() != expectedBytes) { | ||
| 215 | + std::cerr << "SmoothL1LossGrad output buffer mismatch: actual bytes=" << output.GetSize() | ||
| 216 | + << ", expected bytes=" << expectedBytes << std::endl; | ||
| 217 | + return false; | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | + for (size_t index = 0; index < golden.size(); ++index) { | ||
| 221 | + const float error = std::fabs(actual[index] - golden[index]); | ||
| 222 | + const float tolerance = kAtol + kRtol * std::fabs(golden[index]); | ||
| 223 | + if (!std::isfinite(actual[index]) || error > tolerance) { | ||
| 224 | + std::cerr << "SmoothL1LossGrad value mismatch at index " << index << ": actual=" << actual[index] | ||
| 225 | + << ", expected=" << golden[index] << ", tolerance=" << tolerance << std::endl; | ||
| 226 | + return false; | ||
| 227 | + } | ||
| 228 | + } | ||
| 229 | + return true; | ||
| 230 | +} | ||
| 231 | + | ||
| 232 | +int RunScenario(ge::Session& session, uint32_t graphId, const std::string& scenarioName, | ||
| 233 | + const ShapeVector& declaredShape, const ShapeCases& concreteShapes) | ||
| 234 | +{ | ||
| 235 | + std::cout << "Scenario " << scenarioName << ", declared shape " << ShapeToString(declaredShape) << std::endl; | ||
| 236 | + | ||
| 237 | + const std::string graphName = "smooth_l1_loss_grad_" + scenarioName + "_graph"; | ||
| 238 | + ge::Graph graph(graphName.c_str()); | ||
| 239 | + if (BuildGraph(scenarioName, declaredShape, graph) != kSuccess) { | ||
| 240 | + std::cerr << "ERROR: unable to build graph for scenario " << scenarioName << std::endl; | ||
| 241 | + return kFailed; | ||
| 242 | + } | ||
| 243 | + | ||
| 244 | + std::map<ge::AscendString, ge::AscendString> graphOptions; | ||
| 245 | + ge::Status status = session.AddGraph(graphId, graph, graphOptions); | ||
| 246 | + if (status != kSuccess) { | ||
| 247 | + std::cerr << "ERROR: AddGraph failed for scenario " << scenarioName << ": " << ge::GEGetErrorMsgV2().GetString() | ||
| 248 | + << std::endl; | ||
| 249 | + return kFailed; | ||
| 250 | + } | ||
| 251 | + | ||
| 252 | + size_t passed = 0U; | ||
| 253 | + for (const ShapeVector& shape : concreteShapes) { | ||
| 254 | + std::cout << "Run concrete shape " << ShapeToString(shape) << std::endl; | ||
| 255 | + SmoothL1Feeds feeds = MakeFeeds(shape); | ||
| 256 | + std::vector<ge::Tensor> outputs; | ||
| 257 | + status = session.RunGraph(graphId, feeds.tensors, outputs); | ||
| 258 | + if (status != kSuccess) { | ||
| 259 | + std::cerr << "ERROR: RunGraph failed for scenario " << scenarioName << ", shape " << ShapeToString(shape) | ||
| 260 | + << ": " << ge::GEGetErrorMsgV2().GetString() << std::endl; | ||
| 261 | + continue; | ||
| 262 | + } | ||
| 263 | + if (!VerifyOutput(outputs, shape, feeds)) { | ||
| 264 | + std::cerr << "ERROR: output verification failed for scenario " << scenarioName << ", shape " | ||
| 265 | + << ShapeToString(shape) << std::endl; | ||
| 266 | + continue; | ||
| 267 | + } | ||
| 268 | + | ||
| 269 | + ++passed; | ||
| 270 | + std::cout << "Shape, dtype and values PASSED for " << ShapeToString(shape) << std::endl; | ||
| 271 | + } | ||
| 272 | + | ||
| 273 | + std::cout << "Scenario " << scenarioName << " summary: " << passed << "/" << concreteShapes.size() << " passed" | ||
| 274 | + << std::endl; | ||
| 275 | + return passed == concreteShapes.size() ? kSuccess : kFailed; | ||
| 276 | +} | ||
| 277 | + | ||
| 278 | +} // namespace | ||
| 279 | + | ||
| 280 | +int main() | ||
| 281 | +{ | ||
| 282 | + std::map<ge::AscendString, ge::AscendString> globalOptions = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}}; | ||
| 283 | + ge::Status status = ge::GEInitialize(globalOptions); | ||
| 284 | + if (status != kSuccess) { | ||
| 285 | + std::cerr << "ERROR: GEInitialize failed: " << ge::GEGetErrorMsgV2().GetString() << std::endl; | ||
| 286 | + return kFailed; | ||
| 287 | + } | ||
| 288 | + | ||
| 289 | + std::map<ge::AscendString, ge::AscendString> buildOptions; | ||
| 290 | + ge::Session* session = new (std::nothrow) ge::Session(buildOptions); | ||
| 291 | + if (session == nullptr) { | ||
| 292 | + std::cerr << "ERROR: unable to create GE Session" << std::endl; | ||
| 293 | + (void)ge::GEFinalize(); | ||
| 294 | + return kFailed; | ||
| 295 | + } | ||
| 296 | + | ||
| 297 | + const int dimResult = RunScenario(*session, 0U, "unknown_dim_minus_1", {-1, -1}, {{4, 2}, {1, 8}, {3, 5}}); | ||
| 298 | + const int rankResult = RunScenario(*session, 1U, "unknown_rank_minus_2", {-2}, {{8}, {4, 2}, {2, 3, 4}}); | ||
| 299 | + | ||
| 300 | + delete session; | ||
| 301 | + session = nullptr; | ||
| 302 | + status = ge::GEFinalize(); | ||
| 303 | + if (status != kSuccess) { | ||
| 304 | + std::cerr << "ERROR: GEFinalize failed" << std::endl; | ||
| 305 | + return kFailed; | ||
| 306 | + } | ||
| 307 | + | ||
| 308 | + if (dimResult != kSuccess || rankResult != kSuccess) { | ||
| 309 | + std::cerr << "ERROR: SmoothL1LossGrad dynamic GEIR verification did not pass all scenarios" << std::endl; | ||
| 310 | + return kFailed; | ||
| 311 | + } | ||
| 312 | + | ||
| 313 | + std::cout << "SmoothL1LossGrad dynamic GEIR verification PASSED (-1 and -2)" << std::endl; | ||
| 314 | + return kSuccess; | ||
| 315 | +} | ||
| @@ -0,0 +1,338 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +#include <algorithm> | ||
| 12 | +#include <cmath> | ||
| 13 | +#include <cstdint> | ||
| 14 | +#include <iostream> | ||
| 15 | +#include <map> | ||
| 16 | +#include <string> | ||
| 17 | +#include <vector> | ||
| 18 | + | ||
| 19 | +#include "array_ops.h" | ||
| 20 | +#include "elewise_calculation_ops.h" | ||
| 21 | +#include "ge_api.h" | ||
| 22 | +#include "ge_ir_build.h" | ||
| 23 | +#include "graph.h" | ||
| 24 | +#include "tensor.h" | ||
| 25 | +#include "types.h" | ||
| 26 | + | ||
| 27 | +#include "../../op_graph/smooth_l1_loss_grad_proto.h" | ||
| 28 | + | ||
| 29 | +namespace { | ||
| 30 | + | ||
| 31 | +std::string CurrentError() | ||
| 32 | +{ | ||
| 33 | + std::string error = ge::GEGetErrorMsgV2().GetString(); | ||
| 34 | + std::replace(error.begin(), error.end(), '\n', ' '); | ||
| 35 | + std::replace(error.begin(), error.end(), '\r', ' '); | ||
| 36 | + return error; | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +std::string FailureStage(const std::string& error) | ||
| 40 | +{ | ||
| 41 | + if (error.find("Tiling func") != std::string::npos || error.find("tiling failed") != std::string::npos) { | ||
| 42 | + return "tiling"; | ||
| 43 | + } | ||
| 44 | + if (error.find("Unsupported_Operator") != std::string::npos || | ||
| 45 | + error.find("No supported Ops kernel") != std::string::npos) { | ||
| 46 | + return "engine_selection"; | ||
| 47 | + } | ||
| 48 | + if (error.find("para_check.py") != std::string::npos || error.find("num of dimensions") != std::string::npos) { | ||
| 49 | + return "parameter_check"; | ||
| 50 | + } | ||
| 51 | + return "graph_compile"; | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +struct CaseConfig { | ||
| 55 | + std::string id; | ||
| 56 | + std::string category; | ||
| 57 | + ge::DataType predictDtype = ge::DT_FLOAT; | ||
| 58 | + ge::DataType labelDtype = ge::DT_FLOAT; | ||
| 59 | + ge::DataType doutDtype = ge::DT_FLOAT; | ||
| 60 | + ge::Format predictFormat = ge::FORMAT_ND; | ||
| 61 | + ge::Format labelFormat = ge::FORMAT_ND; | ||
| 62 | + ge::Format doutFormat = ge::FORMAT_ND; | ||
| 63 | + ge::Format outputFormat = ge::FORMAT_ND; | ||
| 64 | + std::vector<int64_t> predictShape = {4}; | ||
| 65 | + std::vector<int64_t> labelShape = {4}; | ||
| 66 | + std::vector<int64_t> doutShape = {4}; | ||
| 67 | + std::vector<int64_t> outputShape = {4}; | ||
| 68 | + float sigma = 1.0F; | ||
| 69 | + bool castPredict = false; | ||
| 70 | + bool expectAccept = false; | ||
| 71 | + std::string error; | ||
| 72 | +}; | ||
| 73 | + | ||
| 74 | +int64_t Count(const std::vector<int64_t>& shape) | ||
| 75 | +{ | ||
| 76 | + int64_t count = 1; | ||
| 77 | + for (const auto dim : shape) { | ||
| 78 | + count *= dim; | ||
| 79 | + } | ||
| 80 | + return count; | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +ge::Tensor MakeTensor(const ge::TensorDesc& desc, ge::DataType dtype, int64_t count) | ||
| 84 | +{ | ||
| 85 | + if (dtype == ge::DT_INT32) { | ||
| 86 | + std::vector<int32_t> values(static_cast<size_t>(count), 1); | ||
| 87 | + return ge::Tensor(desc, reinterpret_cast<uint8_t*>(values.data()), values.size() * sizeof(int32_t)); | ||
| 88 | + } | ||
| 89 | + if (dtype == ge::DT_INT8) { | ||
| 90 | + std::vector<int8_t> values(static_cast<size_t>(count), 1); | ||
| 91 | + return ge::Tensor(desc, reinterpret_cast<uint8_t*>(values.data()), values.size()); | ||
| 92 | + } | ||
| 93 | + std::vector<float> values(static_cast<size_t>(count), 1.0F); | ||
| 94 | + return ge::Tensor(desc, reinterpret_cast<uint8_t*>(values.data()), values.size() * sizeof(float)); | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +ge::Operator AddData(const std::string& name, uint32_t index, const std::vector<int64_t>& shape, ge::DataType dtype, | ||
| 98 | + ge::Format format, ge::Graph& graph, std::vector<ge::Tensor>& tensors, | ||
| 99 | + std::vector<ge::Operator>& inputs) | ||
| 100 | +{ | ||
| 101 | + auto data = ge::op::Data(name.c_str()).set_attr_index(index); | ||
| 102 | + ge::TensorDesc desc(ge::Shape(shape), format, dtype); | ||
| 103 | + desc.SetPlacement(ge::kPlacementHost); | ||
| 104 | + desc.SetFormat(format); | ||
| 105 | + desc.SetRealDimCnt(shape.size()); | ||
| 106 | + data.update_input_desc_x(desc); | ||
| 107 | + data.update_output_desc_y(desc); | ||
| 108 | + tensors.push_back(MakeTensor(desc, dtype, Count(shape))); | ||
| 109 | + graph.AddOp(data); | ||
| 110 | + inputs.push_back(data); | ||
| 111 | + return data; | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | +void BuildGraph(const CaseConfig& config, ge::Graph& graph, std::vector<ge::Tensor>& tensors, | ||
| 115 | + std::vector<ge::Operator>& inputs, std::vector<ge::Operator>& outputs) | ||
| 116 | +{ | ||
| 117 | + const auto sourceDtype = config.castPredict ? ge::DT_INT32 : config.predictDtype; | ||
| 118 | + auto predictData = AddData(config.id + "_predict", 0, config.predictShape, sourceDtype, config.predictFormat, graph, | ||
| 119 | + tensors, inputs); | ||
| 120 | + ge::Operator predict = predictData; | ||
| 121 | + ge::TensorDesc predictDesc(ge::Shape(config.predictShape), config.predictFormat, config.predictDtype); | ||
| 122 | + if (config.castPredict) { | ||
| 123 | + ge::TensorDesc sourceDesc(ge::Shape(config.predictShape), config.predictFormat, sourceDtype); | ||
| 124 | + auto cast = ge::op::Cast((config.id + "_cast").c_str()) | ||
| 125 | + .set_input_x(predictData) | ||
| 126 | + .set_attr_dst_type(config.predictDtype); | ||
| 127 | + cast.update_input_desc_x(sourceDesc); | ||
| 128 | + cast.update_output_desc_y(predictDesc); | ||
| 129 | + graph.AddOp(cast); | ||
| 130 | + predict = cast; | ||
| 131 | + } | ||
| 132 | + auto label = AddData(config.id + "_label", 1, config.labelShape, config.labelDtype, config.labelFormat, graph, | ||
| 133 | + tensors, inputs); | ||
| 134 | + auto dout = AddData(config.id + "_dout", 2, config.doutShape, config.doutDtype, config.doutFormat, graph, tensors, | ||
| 135 | + inputs); | ||
| 136 | + ge::TensorDesc labelDesc(ge::Shape(config.labelShape), config.labelFormat, config.labelDtype); | ||
| 137 | + ge::TensorDesc doutDesc(ge::Shape(config.doutShape), config.doutFormat, config.doutDtype); | ||
| 138 | + | ||
| 139 | + auto op = ge::op::SmoothL1LossGrad((config.id + "_op").c_str()); | ||
| 140 | + op.set_input_predict(predict); | ||
| 141 | + op.set_input_label(label); | ||
| 142 | + op.set_input_dout(dout); | ||
| 143 | + op.set_attr_sigma(config.sigma); | ||
| 144 | + op.update_input_desc_predict(predictDesc); | ||
| 145 | + op.update_input_desc_label(labelDesc); | ||
| 146 | + op.update_input_desc_dout(doutDesc); | ||
| 147 | + ge::TensorDesc outputDesc(ge::Shape(config.outputShape), config.outputFormat, config.predictDtype); | ||
| 148 | + op.update_output_desc_gradient(outputDesc); | ||
| 149 | + graph.AddOp(op); | ||
| 150 | + outputs.push_back(op); | ||
| 151 | +} | ||
| 152 | + | ||
| 153 | +bool VerifyOutput(const std::vector<ge::Tensor>& result, const CaseConfig& config) | ||
| 154 | +{ | ||
| 155 | + if (result.size() != 1 || result[0].GetTensorDesc().GetDataType() != ge::DT_FLOAT || | ||
| 156 | + result[0].GetTensorDesc().GetShape().GetDims() != config.outputShape) { | ||
| 157 | + return false; | ||
| 158 | + } | ||
| 159 | + const auto count = Count(config.outputShape); | ||
| 160 | + const auto* gradient = reinterpret_cast<const float*>(result[0].GetData()); | ||
| 161 | + for (int64_t i = 0; i < count; ++i) { | ||
| 162 | + if (gradient == nullptr || std::abs(gradient[i]) > 1.0e-6F) { | ||
| 163 | + return false; | ||
| 164 | + } | ||
| 165 | + } | ||
| 166 | + return true; | ||
| 167 | +} | ||
| 168 | + | ||
| 169 | +bool RunCase(const CaseConfig& config) | ||
| 170 | +{ | ||
| 171 | + ge::Graph graph(("smooth_l1_loss_grad_exception_" + config.id).c_str()); | ||
| 172 | + std::vector<ge::Tensor> tensors; | ||
| 173 | + std::vector<ge::Operator> inputs; | ||
| 174 | + std::vector<ge::Operator> outputs; | ||
| 175 | + BuildGraph(config, graph, tensors, inputs, outputs); | ||
| 176 | + graph.SetInputs(inputs).SetOutputs(outputs); | ||
| 177 | + const std::map<ge::AscendString, ge::AscendString> options; | ||
| 178 | + ge::Session session(options); | ||
| 179 | + ge::Status status = session.AddGraph(0, graph, options); | ||
| 180 | + std::vector<ge::Tensor> result; | ||
| 181 | + if (status == ge::SUCCESS) { | ||
| 182 | + status = session.RunGraph(0, tensors, result); | ||
| 183 | + } | ||
| 184 | + if (!config.expectAccept) { | ||
| 185 | + if (status == ge::SUCCESS) { | ||
| 186 | + std::cout << "EXCEPTION_CASE " << config.id << " FAIL expected=reject actual=accept" << std::endl; | ||
| 187 | + return false; | ||
| 188 | + } | ||
| 189 | + const std::string actualError = CurrentError(); | ||
| 190 | + const std::string failureStage = FailureStage(actualError); | ||
| 191 | + if (failureStage != "tiling" || | ||
| 192 | + (!config.error.empty() && actualError.find(config.error) == std::string::npos)) { | ||
| 193 | + std::cout << "EXCEPTION_CASE " << config.id | ||
| 194 | + << " FAIL expected=reject actual=reject failure_stage=" << failureStage | ||
| 195 | + << " expected_error=" << config.error << " actual_error=" << actualError << std::endl; | ||
| 196 | + return false; | ||
| 197 | + } | ||
| 198 | + std::cout << "EXCEPTION_CASE " << config.id << " PASS category=" << config.category | ||
| 199 | + << " expected=reject actual=reject failure_stage=" << failureStage | ||
| 200 | + << " kernel_launched=false actual_error=" << actualError; | ||
| 201 | + if (config.category == "rank_9_tensor") { | ||
| 202 | + std::cout << " rank=9"; | ||
| 203 | + } | ||
| 204 | + std::cout << std::endl; | ||
| 205 | + return true; | ||
| 206 | + } | ||
| 207 | + if (status != ge::SUCCESS || !VerifyOutput(result, config)) { | ||
| 208 | + std::cout << "EXCEPTION_CASE " << config.id | ||
| 209 | + << " FAIL expected=accept actual=reject error=" << ge::GEGetErrorMsgV2().GetString() << std::endl; | ||
| 210 | + return false; | ||
| 211 | + } | ||
| 212 | + std::cout << "EXCEPTION_CASE " << config.id << " PASS category=" << config.category | ||
| 213 | + << " expected=accept actual=accept validation_stage=tiling kernel_launched=true output_verified=true" | ||
| 214 | + << " kernel_launch_observed=true"; | ||
| 215 | + if (config.castPredict) { | ||
| 216 | + std::cout << " source_dtype=int32 cast_dtype=float32 required_dtype=float32 observed_input_dtype=float32"; | ||
| 217 | + } | ||
| 218 | + std::cout << std::endl; | ||
| 219 | + return true; | ||
| 220 | +} | ||
| 221 | + | ||
| 222 | +CaseConfig MakeCase(const std::string& id, const std::string& category, const std::string& error) | ||
| 223 | +{ | ||
| 224 | + CaseConfig config; | ||
| 225 | + config.id = id; | ||
| 226 | + config.category = category; | ||
| 227 | + config.error = error; | ||
| 228 | + return config; | ||
| 229 | +} | ||
| 230 | + | ||
| 231 | +void SetAllShapes(CaseConfig& config, const std::vector<int64_t>& shape) | ||
| 232 | +{ | ||
| 233 | + config.predictShape = shape; | ||
| 234 | + config.labelShape = shape; | ||
| 235 | + config.doutShape = shape; | ||
| 236 | + config.outputShape = shape; | ||
| 237 | +} | ||
| 238 | + | ||
| 239 | +std::vector<CaseConfig> MakeCases() | ||
| 240 | +{ | ||
| 241 | + std::vector<CaseConfig> cases; | ||
| 242 | + | ||
| 243 | + auto config = MakeCase("dtype_predict_int8", "dtype_unsupported", "unsupported dtype"); | ||
| 244 | + config.predictDtype = ge::DT_INT8; | ||
| 245 | + cases.push_back(config); | ||
| 246 | + | ||
| 247 | + config = MakeCase("dtype_label_int8", "dtype_unsupported", "input[1] unsupported dtype"); | ||
| 248 | + config.labelDtype = ge::DT_INT8; | ||
| 249 | + cases.push_back(config); | ||
| 250 | + | ||
| 251 | + config = MakeCase("dtype_dout_int8", "dtype_unsupported", "input[2] unsupported dtype"); | ||
| 252 | + config.doutDtype = ge::DT_INT8; | ||
| 253 | + cases.push_back(config); | ||
| 254 | + | ||
| 255 | + config = MakeCase("dtype_predict_label_mismatch", "dtype_combination_mismatch", | ||
| 256 | + "dtype mismatch between predict and label"); | ||
| 257 | + config.labelDtype = ge::DT_FLOAT16; | ||
| 258 | + cases.push_back(config); | ||
| 259 | + | ||
| 260 | + config = MakeCase("dtype_predict_dout_mismatch", "dtype_combination_mismatch", | ||
| 261 | + "dtype mismatch between predict and dout"); | ||
| 262 | + config.doutDtype = ge::DT_FLOAT16; | ||
| 263 | + cases.push_back(config); | ||
| 264 | + | ||
| 265 | + config = MakeCase("cast_predict_int32_to_float32", "dtype_geir_cast", ""); | ||
| 266 | + config.castPredict = true; | ||
| 267 | + config.expectAccept = true; | ||
| 268 | + cases.push_back(config); | ||
| 269 | + | ||
| 270 | + const std::vector<int64_t> nchwShape = {1, 1, 1, 1}; | ||
| 271 | + config = MakeCase("format_predict_nchw", "format_unsupported", "input[0] format"); | ||
| 272 | + SetAllShapes(config, nchwShape); | ||
| 273 | + config.predictFormat = ge::FORMAT_NCHW; | ||
| 274 | + cases.push_back(config); | ||
| 275 | + | ||
| 276 | + config = MakeCase("format_label_nchw", "format_unsupported", "input[1] format"); | ||
| 277 | + SetAllShapes(config, nchwShape); | ||
| 278 | + config.labelFormat = ge::FORMAT_NCHW; | ||
| 279 | + cases.push_back(config); | ||
| 280 | + | ||
| 281 | + config = MakeCase("format_dout_nchw", "format_unsupported", "input[2] format"); | ||
| 282 | + SetAllShapes(config, nchwShape); | ||
| 283 | + config.doutFormat = ge::FORMAT_NCHW; | ||
| 284 | + cases.push_back(config); | ||
| 285 | + | ||
| 286 | + config = MakeCase("format_gradient_nchw", "format_unsupported", "output[0] format"); | ||
| 287 | + SetAllShapes(config, nchwShape); | ||
| 288 | + config.outputFormat = ge::FORMAT_NCHW; | ||
| 289 | + cases.push_back(config); | ||
| 290 | + | ||
| 291 | + config = MakeCase("rank9_predict", "rank_9_tensor", "predict rank 9 exceeds supported range [0, 8]"); | ||
| 292 | + SetAllShapes(config, {1, 1, 1, 1, 1, 1, 1, 1, 1}); | ||
| 293 | + cases.push_back(config); | ||
| 294 | + | ||
| 295 | + config = MakeCase("sigma_zero", "attribute_out_of_range", "sigma must be > 0"); | ||
| 296 | + config.sigma = 0.0F; | ||
| 297 | + cases.push_back(config); | ||
| 298 | + | ||
| 299 | + config = MakeCase("shape_label_mismatch", "shape_dimension_violation", "shape mismatch between predict and label"); | ||
| 300 | + config.labelShape = {2, 2}; | ||
| 301 | + cases.push_back(config); | ||
| 302 | + | ||
| 303 | + config = MakeCase("shape_dout_mismatch", "shape_dimension_violation", "shape mismatch between predict and dout"); | ||
| 304 | + config.doutShape = {2, 2}; | ||
| 305 | + cases.push_back(config); | ||
| 306 | + | ||
| 307 | + return cases; | ||
| 308 | +} | ||
| 309 | + | ||
| 310 | +} // namespace | ||
| 311 | + | ||
| 312 | +int main(int argc, char* argv[]) | ||
| 313 | +{ | ||
| 314 | + std::cout << "EXCEPTION_SUITE SmoothL1LossGrad Ascend950 GEIR" << std::endl; | ||
| 315 | + const std::map<ge::AscendString, ge::AscendString> options = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}}; | ||
| 316 | + if (ge::GEInitialize(options) != ge::SUCCESS) { | ||
| 317 | + return -1; | ||
| 318 | + } | ||
| 319 | + const auto cases = MakeCases(); | ||
| 320 | + bool passed = true; | ||
| 321 | + bool matched = false; | ||
| 322 | + for (const auto& item : cases) { | ||
| 323 | + if (argc > 1 && item.id != argv[1]) { | ||
| 324 | + continue; | ||
| 325 | + } | ||
| 326 | + matched = true; | ||
| 327 | + passed = RunCase(item) && passed; | ||
| 328 | + } | ||
| 329 | + if (!matched) { | ||
| 330 | + std::cout << "EXCEPTION_SUITE SmoothL1LossGrad FAIL unknown_case=" << argv[1] << std::endl; | ||
| 331 | + passed = false; | ||
| 332 | + } | ||
| 333 | + passed = ge::GEFinalize() == ge::SUCCESS && passed; | ||
| 334 | + if (passed) { | ||
| 335 | + std::cout << "EXCEPTION_SUITE SmoothL1LossGrad PASSED" << std::endl; | ||
| 336 | + } | ||
| 337 | + return passed ? 0 : -1; | ||
| 338 | +} | ||
| @@ -51,6 +51,7 @@ constexpr int64_t ELEM_ALIGN_FACTOR = 512; // 多核切分元素 | |||
| 51 | constexpr int64_t ALIGN_256 = 256; // UB 对齐字节数(亦作 extraSize 预留) | 51 | constexpr int64_t ALIGN_256 = 256; // UB 对齐字节数(亦作 extraSize 预留) |
| 52 | constexpr int64_t BUFFER_NUM = 8; // double buffer:(3 VECIN + 1 VECOUT) × 2 | 52 | constexpr int64_t BUFFER_NUM = 8; // double buffer:(3 VECIN + 1 VECOUT) × 2 |
| 53 | constexpr float DEFAULT_SIGMA = 1.0f; // sigma 默认值 | 53 | constexpr float DEFAULT_SIGMA = 1.0f; // sigma 默认值 |
| 54 | +constexpr size_t MAX_SUPPORTED_RANK = 8; // README 接口约束:支持 0-8 维 | ||
| 54 | 55 | ||
| 55 | static const gert::Shape g_vec_1_shape = {1}; | 56 | static const gert::Shape g_vec_1_shape = {1}; |
| 56 | 57 | ||
| @@ -83,6 +84,10 @@ static ge::graphStatus GetShapeAttrsInfo(gert::TilingContext* context, int64_t* | |||
| 83 | // 输入 predict shape → 展平为 totalLength(rank=0 → 1,空 Tensor → 0) | 84 | // 输入 predict shape → 展平为 totalLength(rank=0 → 1,空 Tensor → 0) |
| 84 | auto inputPredict = context->GetInputShape(0); | 85 | auto inputPredict = context->GetInputShape(0); |
| 85 | OP_CHECK_NULL_WITH_CONTEXT(context, inputPredict); | 86 | OP_CHECK_NULL_WITH_CONTEXT(context, inputPredict); |
| 87 | + OP_CHECK_IF(inputPredict->GetStorageShape().GetDimNum() > MAX_SUPPORTED_RANK, | ||
| 88 | + OP_LOGE(context, "SmoothL1LossGrad: predict rank %zu exceeds supported range [0, 8]", | ||
| 89 | + inputPredict->GetStorageShape().GetDimNum()), | ||
| 90 | + return ge::GRAPH_FAILED); | ||
| 86 | auto predictShape = EnsureNotScalar(inputPredict->GetStorageShape()); | 91 | auto predictShape = EnsureNotScalar(inputPredict->GetStorageShape()); |
| 87 | *totalLength = predictShape.GetShapeSize(); | 92 | *totalLength = predictShape.GetShapeSize(); |
| 88 | 93 | ||
| @@ -90,9 +95,17 @@ static ge::graphStatus GetShapeAttrsInfo(gert::TilingContext* context, int64_t* | |||
| 90 | const std::set<ge::DataType> supportedDtype = {ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT}; | 95 | const std::set<ge::DataType> supportedDtype = {ge::DT_FLOAT16, ge::DT_BF16, ge::DT_FLOAT}; |
| 91 | auto inputDesc = context->GetInputDesc(0); | 96 | auto inputDesc = context->GetInputDesc(0); |
| 92 | OP_CHECK_NULL_WITH_CONTEXT(context, inputDesc); | 97 | OP_CHECK_NULL_WITH_CONTEXT(context, inputDesc); |
| 98 | + const auto predictFormat = inputDesc->GetFormat().GetStorageFormat(); | ||
| 99 | + OP_CHECK_IF( | ||
| 100 | + predictFormat != ge::FORMAT_ND, | ||
| 101 | + OP_LOGE(context, "SmoothL1LossGrad: input[0] format %d is unsupported; input name=predict, legal format=ND", | ||
| 102 | + static_cast<int>(predictFormat)), | ||
| 103 | + return ge::GRAPH_FAILED); | ||
| 93 | *dataType = inputDesc->GetDataType(); | 104 | *dataType = inputDesc->GetDataType(); |
| 94 | OP_CHECK_IF(supportedDtype.count(*dataType) == 0, | 105 | OP_CHECK_IF(supportedDtype.count(*dataType) == 0, |
| 95 | - OP_LOGE(context, "SmoothL1LossGrad: unsupported dtype %d (supports FLOAT16/BF16/FLOAT)", | 106 | + OP_LOGE(context, |
| 107 | + "SmoothL1LossGrad: unsupported dtype %d (supports FLOAT16/BF16/FLOAT); input name=predict, " | ||
| 108 | + "legal dtypes={FLOAT16,BF16,FLOAT}", | ||
| 96 | static_cast<int>(*dataType)), | 109 | static_cast<int>(*dataType)), |
| 97 | return ge::GRAPH_FAILED); | 110 | return ge::GRAPH_FAILED); |
| 98 | 111 | ||
| @@ -101,25 +114,76 @@ static ge::graphStatus GetShapeAttrsInfo(gert::TilingContext* context, int64_t* | |||
| 101 | auto inputLabel = context->GetInputShape(1); | 114 | auto inputLabel = context->GetInputShape(1); |
| 102 | OP_CHECK_NULL_WITH_CONTEXT(context, inputLabel); | 115 | OP_CHECK_NULL_WITH_CONTEXT(context, inputLabel); |
| 103 | OP_CHECK_IF(inputLabel->GetStorageShape() != inputPredict->GetStorageShape(), | 116 | OP_CHECK_IF(inputLabel->GetStorageShape() != inputPredict->GetStorageShape(), |
| 104 | - OP_LOGE(context, "SmoothL1LossGrad: shape mismatch between predict and label"), | 117 | + OP_LOGE(context, |
| 118 | + "SmoothL1LossGrad: shape mismatch between predict and label; actual predict shape=%s, " | ||
| 119 | + "actual label shape=%s, legal relation=label shape must equal predict shape", | ||
| 120 | + Ops::Base::ToString(inputPredict->GetStorageShape()).c_str(), | ||
| 121 | + Ops::Base::ToString(inputLabel->GetStorageShape()).c_str()), | ||
| 105 | return ge::GRAPH_FAILED); | 122 | return ge::GRAPH_FAILED); |
| 106 | 123 | ||
| 107 | auto inputDout = context->GetInputShape(2); | 124 | auto inputDout = context->GetInputShape(2); |
| 108 | OP_CHECK_NULL_WITH_CONTEXT(context, inputDout); | 125 | OP_CHECK_NULL_WITH_CONTEXT(context, inputDout); |
| 109 | OP_CHECK_IF(inputDout->GetStorageShape() != inputPredict->GetStorageShape(), | 126 | OP_CHECK_IF(inputDout->GetStorageShape() != inputPredict->GetStorageShape(), |
| 110 | - OP_LOGE(context, "SmoothL1LossGrad: shape mismatch between predict and dout"), return ge::GRAPH_FAILED); | 127 | + OP_LOGE(context, |
| 128 | + "SmoothL1LossGrad: shape mismatch between predict and dout; actual predict shape=%s, actual " | ||
| 129 | + "dout shape=%s, legal relation=dout shape must equal predict shape", | ||
| 130 | + Ops::Base::ToString(inputPredict->GetStorageShape()).c_str(), | ||
| 131 | + Ops::Base::ToString(inputDout->GetStorageShape()).c_str()), | ||
| 132 | + return ge::GRAPH_FAILED); | ||
| 111 | 133 | ||
| 112 | // dtype 一致性校验 | 134 | // dtype 一致性校验 |
| 113 | auto labelDesc = context->GetInputDesc(1); | 135 | auto labelDesc = context->GetInputDesc(1); |
| 114 | OP_CHECK_NULL_WITH_CONTEXT(context, labelDesc); | 136 | OP_CHECK_NULL_WITH_CONTEXT(context, labelDesc); |
| 115 | - OP_CHECK_IF(labelDesc->GetDataType() != *dataType, | 137 | + const auto labelFormat = labelDesc->GetFormat().GetStorageFormat(); |
| 116 | - OP_LOGE(context, "SmoothL1LossGrad: dtype mismatch between predict and label"), | 138 | + OP_CHECK_IF( |
| 139 | + labelFormat != ge::FORMAT_ND, | ||
| 140 | + OP_LOGE(context, "SmoothL1LossGrad: input[1] format %d is unsupported; input name=label, legal format=ND", | ||
| 141 | + static_cast<int>(labelFormat)), | ||
| 142 | + return ge::GRAPH_FAILED); | ||
| 143 | + const auto labelDtype = labelDesc->GetDataType(); | ||
| 144 | + OP_CHECK_IF(supportedDtype.count(labelDtype) == 0, | ||
| 145 | + OP_LOGE(context, | ||
| 146 | + "SmoothL1LossGrad: input[1] unsupported dtype %d (supports FLOAT16/BF16/FLOAT); input " | ||
| 147 | + "name=label, legal dtypes={FLOAT16,BF16,FLOAT}", | ||
| 148 | + static_cast<int>(labelDtype)), | ||
| 149 | + return ge::GRAPH_FAILED); | ||
| 150 | + OP_CHECK_IF(labelDtype != *dataType, | ||
| 151 | + OP_LOGE(context, | ||
| 152 | + "SmoothL1LossGrad: dtype mismatch between predict and label; actual predict dtype=%d, actual " | ||
| 153 | + "label dtype=%d, legal relation=label dtype must equal predict dtype", | ||
| 154 | + static_cast<int>(*dataType), static_cast<int>(labelDtype)), | ||
| 117 | return ge::GRAPH_FAILED); | 155 | return ge::GRAPH_FAILED); |
| 118 | 156 | ||
| 119 | auto doutDesc = context->GetInputDesc(2); | 157 | auto doutDesc = context->GetInputDesc(2); |
| 120 | OP_CHECK_NULL_WITH_CONTEXT(context, doutDesc); | 158 | OP_CHECK_NULL_WITH_CONTEXT(context, doutDesc); |
| 121 | - OP_CHECK_IF(doutDesc->GetDataType() != *dataType, | 159 | + const auto doutFormat = doutDesc->GetFormat().GetStorageFormat(); |
| 122 | - OP_LOGE(context, "SmoothL1LossGrad: dtype mismatch between predict and dout"), return ge::GRAPH_FAILED); | 160 | + OP_CHECK_IF( |
| 161 | + doutFormat != ge::FORMAT_ND, | ||
| 162 | + OP_LOGE(context, "SmoothL1LossGrad: input[2] format %d is unsupported; input name=dout, legal format=ND", | ||
| 163 | + static_cast<int>(doutFormat)), | ||
| 164 | + return ge::GRAPH_FAILED); | ||
| 165 | + const auto doutDtype = doutDesc->GetDataType(); | ||
| 166 | + OP_CHECK_IF(supportedDtype.count(doutDtype) == 0, | ||
| 167 | + OP_LOGE(context, | ||
| 168 | + "SmoothL1LossGrad: input[2] unsupported dtype %d (supports FLOAT16/BF16/FLOAT); input " | ||
| 169 | + "name=dout, legal dtypes={FLOAT16,BF16,FLOAT}", | ||
| 170 | + static_cast<int>(doutDtype)), | ||
| 171 | + return ge::GRAPH_FAILED); | ||
| 172 | + OP_CHECK_IF(doutDtype != *dataType, | ||
| 173 | + OP_LOGE(context, | ||
| 174 | + "SmoothL1LossGrad: dtype mismatch between predict and dout; actual predict dtype=%d, actual " | ||
| 175 | + "dout dtype=%d, legal relation=dout dtype must equal predict dtype", | ||
| 176 | + static_cast<int>(*dataType), static_cast<int>(doutDtype)), | ||
| 177 | + return ge::GRAPH_FAILED); | ||
| 178 | + | ||
| 179 | + auto outputDesc = context->GetOutputDesc(0); | ||
| 180 | + OP_CHECK_NULL_WITH_CONTEXT(context, outputDesc); | ||
| 181 | + const auto outputFormat = outputDesc->GetFormat().GetStorageFormat(); | ||
| 182 | + OP_CHECK_IF( | ||
| 183 | + outputFormat != ge::FORMAT_ND, | ||
| 184 | + OP_LOGE(context, "SmoothL1LossGrad: output[0] format %d is unsupported; output name=gradient, legal format=ND", | ||
| 185 | + static_cast<int>(outputFormat)), | ||
| 186 | + return ge::GRAPH_FAILED); | ||
| 123 | 187 | ||
| 124 | // 属性 sigma(Float,index 0,默认 1.0) | 188 | // 属性 sigma(Float,index 0,默认 1.0) |
| 125 | const auto* attrs = context->GetAttrs(); | 189 | const auto* attrs = context->GetAttrs(); |
| @@ -131,8 +195,11 @@ static ge::graphStatus GetShapeAttrsInfo(gert::TilingContext* context, int64_t* | |||
| 131 | } | 195 | } |
| 132 | 196 | ||
| 133 | // sigma > 0 校验 | 197 | // sigma > 0 校验 |
| 134 | - OP_CHECK_IF(*sigma <= 0.0f, OP_LOGE(context, "SmoothL1LossGrad: sigma must be > 0, got %f", *sigma), | 198 | + OP_CHECK_IF( |
| 135 | - return ge::GRAPH_FAILED); | 199 | + *sigma <= 0.0f, |
| 200 | + OP_LOGE(context, "SmoothL1LossGrad: sigma must be > 0, got %f; attribute name=sigma, legal range=(0, +inf)", | ||
| 201 | + *sigma), | ||
| 202 | + return ge::GRAPH_FAILED); | ||
| 136 | 203 | ||
| 137 | return ge::GRAPH_SUCCESS; | 204 | return ge::GRAPH_SUCCESS; |
| 138 | } | 205 | } |
| @@ -0,0 +1,171 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +#include <iostream> | ||
| 12 | +#include <vector> | ||
| 13 | + | ||
| 14 | +#include <gtest/gtest.h> | ||
| 15 | +#include "exe_graph/runtime/storage_format.h" | ||
| 16 | +#include "exe_graph/runtime/storage_shape.h" | ||
| 17 | +#include "kernel_run_context_facker.h" | ||
| 18 | +#include "platform/platform_infos_def.h" | ||
| 19 | +#include "test_cube_util.h" | ||
| 20 | +#include "ut_op_util.h" | ||
| 21 | + | ||
| 22 | +using namespace ge; | ||
| 23 | +using namespace ut_util; | ||
| 24 | + | ||
| 25 | +namespace optiling { | ||
| 26 | +struct SmoothL1LossGradCompileInfo {}; | ||
| 27 | +} // namespace optiling | ||
| 28 | + | ||
| 29 | +class SmoothL1LossGradTilingTest : public testing::Test { | ||
| 30 | +protected: | ||
| 31 | + static void SetUpTestCase() { std::cout << "SmoothL1LossGradTilingTest SetUp" << std::endl; } | ||
| 32 | + static void TearDownTestCase() { std::cout << "SmoothL1LossGradTilingTest TearDown" << std::endl; } | ||
| 33 | +}; | ||
| 34 | + | ||
| 35 | +static void DoTilingTest(std::initializer_list<int64_t> predictDims, std::initializer_list<int64_t> labelDims, | ||
| 36 | + std::initializer_list<int64_t> doutDims, ge::DataType predictDtype, float sigma, | ||
| 37 | + ge::graphStatus expectedStatus, ge::DataType labelDtype = ge::DT_UNDEFINED, | ||
| 38 | + ge::DataType doutDtype = ge::DT_UNDEFINED, ge::Format predictFormat = ge::FORMAT_ND, | ||
| 39 | + ge::Format labelFormat = ge::FORMAT_ND, ge::Format doutFormat = ge::FORMAT_ND, | ||
| 40 | + ge::Format outputFormat = ge::FORMAT_ND) | ||
| 41 | +{ | ||
| 42 | + std::string opType("SmoothL1LossGrad"); | ||
| 43 | + ASSERT_NE(gert::OpImplRegistry::GetInstance().GetOpImpl(opType.c_str()), nullptr); | ||
| 44 | + auto tilingFunc = gert::OpImplRegistry::GetInstance().GetOpImpl(opType.c_str())->tiling; | ||
| 45 | + | ||
| 46 | + std::string compileInfoStr = R"({ | ||
| 47 | + "hardware_info": {"BT_SIZE": 0, "load3d_constraints": "1", | ||
| 48 | + "Intrinsic_fix_pipe_l0c2out": false, "Intrinsic_data_move_l12ub": true, | ||
| 49 | + "Intrinsic_data_move_l0c2ub": true, "Intrinsic_data_move_out2l1_nd2nz": false, | ||
| 50 | + "UB_SIZE": 245760, "L2_SIZE": 33554432, "L1_SIZE": 524288, | ||
| 51 | + "L0A_SIZE": 65536, "L0B_SIZE": 65536, "L0C_SIZE": 131072, "CORE_NUM": 64} | ||
| 52 | + })"; | ||
| 53 | + std::map<std::string, std::string> socInfos, aicoreSpec, intrinsics; | ||
| 54 | + GetPlatFormInfos(compileInfoStr.c_str(), socInfos, aicoreSpec, intrinsics); | ||
| 55 | + | ||
| 56 | + fe::PlatFormInfos platformInfo; | ||
| 57 | + platformInfo.Init(); | ||
| 58 | + optiling::SmoothL1LossGradCompileInfo compileInfo; | ||
| 59 | + auto param = gert::TilingData::CreateCap(8192); | ||
| 60 | + ASSERT_NE(param, nullptr); | ||
| 61 | + auto wsHolder = gert::ContinuousVector::Create<size_t>(32); | ||
| 62 | + auto wsSize = reinterpret_cast<gert::ContinuousVector*>(wsHolder.get()); | ||
| 63 | + | ||
| 64 | + gert::StorageShape predictShape = {predictDims, predictDims}; | ||
| 65 | + gert::StorageShape labelShape = {labelDims, labelDims}; | ||
| 66 | + gert::StorageShape doutShape = {doutDims, doutDims}; | ||
| 67 | + ge::DataType effectiveLabelDtype = labelDtype == ge::DT_UNDEFINED ? predictDtype : labelDtype; | ||
| 68 | + ge::DataType effectiveDoutDtype = doutDtype == ge::DT_UNDEFINED ? predictDtype : doutDtype; | ||
| 69 | + | ||
| 70 | + auto holder = gert::TilingContextFaker() | ||
| 71 | + .SetOpType(opType) | ||
| 72 | + .NodeIoNum(3, 1) | ||
| 73 | + .IrInstanceNum({1, 1, 1}) | ||
| 74 | + .InputShapes({&predictShape, &labelShape, &doutShape}) | ||
| 75 | + .OutputShapes({&predictShape}) | ||
| 76 | + .CompileInfo(&compileInfo) | ||
| 77 | + .PlatformInfo(reinterpret_cast<char*>(&platformInfo)) | ||
| 78 | + .NodeInputTd(0, predictDtype, predictFormat, predictFormat) | ||
| 79 | + .NodeInputTd(1, effectiveLabelDtype, labelFormat, labelFormat) | ||
| 80 | + .NodeInputTd(2, effectiveDoutDtype, doutFormat, doutFormat) | ||
| 81 | + .NodeOutputTd(0, predictDtype, outputFormat, outputFormat) | ||
| 82 | + .NodeAttrs({{"sigma", Ops::NN::AnyValue::CreateFrom<float>(sigma)}}) | ||
| 83 | + .TilingData(param.get()) | ||
| 84 | + .Workspace(wsSize) | ||
| 85 | + .Build(); | ||
| 86 | + | ||
| 87 | + gert::TilingContext* ctx = holder.GetContext<gert::TilingContext>(); | ||
| 88 | + ASSERT_NE(ctx, nullptr); | ||
| 89 | + ASSERT_NE(ctx->GetPlatformInfo(), nullptr); | ||
| 90 | + ctx->GetPlatformInfo()->SetPlatformRes("SoCInfo", socInfos); | ||
| 91 | + ctx->GetPlatformInfo()->SetPlatformRes("AICoreSpec", aicoreSpec); | ||
| 92 | + ctx->GetPlatformInfo()->SetCoreNumByCoreType("AICore"); | ||
| 93 | + ctx->GetPlatformInfo()->SetPlatformRes("AICoreintrinsicDtypeMap", intrinsics); | ||
| 94 | + | ||
| 95 | + EXPECT_EQ(tilingFunc(ctx), expectedStatus); | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +TEST_F(SmoothL1LossGradTilingTest, tiling_fp32_accepted) | ||
| 99 | +{ | ||
| 100 | + DoTilingTest({4, 8}, {4, 8}, {4, 8}, ge::DT_FLOAT, 1.0f, ge::GRAPH_SUCCESS); | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +TEST_F(SmoothL1LossGradTilingTest, tiling_unsupported_dtype_rejected) | ||
| 104 | +{ | ||
| 105 | + DoTilingTest({4, 8}, {4, 8}, {4, 8}, ge::DT_INT8, 1.0f, ge::GRAPH_FAILED); | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +TEST_F(SmoothL1LossGradTilingTest, tiling_dtype_mismatch_rejected) | ||
| 109 | +{ | ||
| 110 | + DoTilingTest({4, 8}, {4, 8}, {4, 8}, ge::DT_FLOAT, 1.0f, ge::GRAPH_FAILED, ge::DT_FLOAT16); | ||
| 111 | +} | ||
| 112 | + | ||
| 113 | +TEST_F(SmoothL1LossGradTilingTest, tiling_exact_shape_mismatch_rejected) | ||
| 114 | +{ | ||
| 115 | + DoTilingTest({2, 6}, {3, 4}, {2, 6}, ge::DT_FLOAT, 1.0f, ge::GRAPH_FAILED); | ||
| 116 | +} | ||
| 117 | + | ||
| 118 | +TEST_F(SmoothL1LossGradTilingTest, tiling_sigma_zero_rejected) | ||
| 119 | +{ | ||
| 120 | + DoTilingTest({4, 8}, {4, 8}, {4, 8}, ge::DT_FLOAT, 0.0f, ge::GRAPH_FAILED); | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +TEST_F(SmoothL1LossGradTilingTest, tiling_unsupported_format_rejected) | ||
| 124 | +{ | ||
| 125 | + DoTilingTest({1, 2, 3, 4}, {1, 2, 3, 4}, {1, 2, 3, 4}, ge::DT_FLOAT, 1.0f, ge::GRAPH_FAILED, ge::DT_UNDEFINED, | ||
| 126 | + ge::DT_UNDEFINED, ge::FORMAT_NCHW); | ||
| 127 | +} | ||
| 128 | + | ||
| 129 | +TEST_F(SmoothL1LossGradTilingTest, tiling_rank_9_rejected) | ||
| 130 | +{ | ||
| 131 | + DoTilingTest({1, 1, 1, 1, 1, 1, 1, 1, 2}, {1, 1, 1, 1, 1, 1, 1, 1, 2}, {1, 1, 1, 1, 1, 1, 1, 1, 2}, ge::DT_FLOAT16, | ||
| 132 | + 1.0f, ge::GRAPH_FAILED); | ||
| 133 | +} | ||
| 134 | + | ||
| 135 | +TEST_F(SmoothL1LossGradTilingTest, tiling_label_unsupported_dtype_rejected) | ||
| 136 | +{ | ||
| 137 | + DoTilingTest({4, 8}, {4, 8}, {4, 8}, ge::DT_FLOAT, 1.0f, ge::GRAPH_FAILED, ge::DT_INT8); | ||
| 138 | +} | ||
| 139 | + | ||
| 140 | +TEST_F(SmoothL1LossGradTilingTest, tiling_dout_unsupported_dtype_rejected) | ||
| 141 | +{ | ||
| 142 | + DoTilingTest({4, 8}, {4, 8}, {4, 8}, ge::DT_FLOAT, 1.0f, ge::GRAPH_FAILED, ge::DT_UNDEFINED, ge::DT_INT8); | ||
| 143 | +} | ||
| 144 | + | ||
| 145 | +TEST_F(SmoothL1LossGradTilingTest, tiling_dout_dtype_mismatch_rejected) | ||
| 146 | +{ | ||
| 147 | + DoTilingTest({4, 8}, {4, 8}, {4, 8}, ge::DT_FLOAT, 1.0f, ge::GRAPH_FAILED, ge::DT_UNDEFINED, ge::DT_FLOAT16); | ||
| 148 | +} | ||
| 149 | + | ||
| 150 | +TEST_F(SmoothL1LossGradTilingTest, tiling_label_unsupported_format_rejected) | ||
| 151 | +{ | ||
| 152 | + DoTilingTest({1, 2, 3, 4}, {1, 2, 3, 4}, {1, 2, 3, 4}, ge::DT_FLOAT, 1.0f, ge::GRAPH_FAILED, ge::DT_UNDEFINED, | ||
| 153 | + ge::DT_UNDEFINED, ge::FORMAT_ND, ge::FORMAT_NCHW); | ||
| 154 | +} | ||
| 155 | + | ||
| 156 | +TEST_F(SmoothL1LossGradTilingTest, tiling_dout_unsupported_format_rejected) | ||
| 157 | +{ | ||
| 158 | + DoTilingTest({1, 2, 3, 4}, {1, 2, 3, 4}, {1, 2, 3, 4}, ge::DT_FLOAT, 1.0f, ge::GRAPH_FAILED, ge::DT_UNDEFINED, | ||
| 159 | + ge::DT_UNDEFINED, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_NCHW); | ||
| 160 | +} | ||
| 161 | + | ||
| 162 | +TEST_F(SmoothL1LossGradTilingTest, tiling_output_unsupported_format_rejected) | ||
| 163 | +{ | ||
| 164 | + DoTilingTest({1, 2, 3, 4}, {1, 2, 3, 4}, {1, 2, 3, 4}, ge::DT_FLOAT, 1.0f, ge::GRAPH_FAILED, ge::DT_UNDEFINED, | ||
| 165 | + ge::DT_UNDEFINED, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_NCHW); | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | +TEST_F(SmoothL1LossGradTilingTest, tiling_dout_shape_mismatch_rejected) | ||
| 169 | +{ | ||
| 170 | + DoTilingTest({4}, {4}, {2, 2}, ge::DT_FLOAT, 1.0f, ge::GRAPH_FAILED); | ||
| 171 | +} | ||