* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
* @file test_geir_xlog1py.cpp
* @brief GE IR 图模式调用示例 - Xlog1py 算子
*
* z = x * log1p(y), x==0 -> z=0
* 测试流程: GEInitialize -> 建图 -> AddGraph -> RunGraph -> 比对精度 -> GEFinalize
*/
#include <iostream>
#include <fstream>
#include <string.h>
#include <stdint.h>
#include <vector>
#include <string>
#include <map>
#include <cmath>
#include "assert.h"
#include "graph.h"
#include "types.h"
#include "tensor.h"
#include "ge_error_codes.h"
#include "ge_api_types.h"
#include "ge_api.h"
#include "ge_ir_build.h"
#include "array_ops.h"
#include "../op_graph/xlog1py_proto.h"
#define FAILED -1
#define SUCCESS 0
using namespace ge;
using std::map;
using std::string;
using std::vector;
static std::string GetTime()
{
time_t timep;
time(&timep);
char tmp[64];
strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S,000", localtime(&timep));
return tmp;
}
static uint32_t GetDtypeSize(DataType dt)
{
switch (dt) {
case ge::DT_FLOAT: case ge::DT_INT32: case ge::DT_UINT32: return 4;
case ge::DT_FLOAT16: case ge::DT_BF16: case ge::DT_INT16: case ge::DT_UINT16: return 2;
case ge::DT_INT64: case ge::DT_UINT64: case ge::DT_DOUBLE: return 8;
default: return 1;
}
}
static Status GenFloatData(const vector<int64_t>& shapes, Tensor& tensor,
TensorDesc& desc, const vector<float>& values)
{
desc.SetRealDimCnt(shapes.size());
int64_t size = 1;
for (auto s : shapes) size *= s;
uint32_t dataLen = size * sizeof(float);
float* pData = new (std::nothrow) float[size];
for (int64_t i = 0; i < size; i++) pData[i] = values[i % values.size()];
tensor = Tensor(desc, reinterpret_cast<uint8_t*>(pData), dataLen);
return SUCCESS;
}
static Status GenScalarData(const vector<int64_t>& shapes, Tensor& tensor,
TensorDesc& desc, float value)
{
desc.SetRealDimCnt(shapes.size());
int64_t size = 1;
for (auto s : shapes) size *= s;
uint32_t dataLen = size * sizeof(float);
float* pData = new (std::nothrow) float[size];
for (int64_t i = 0; i < size; i++) pData[i] = value;
tensor = Tensor(desc, reinterpret_cast<uint8_t*>(pData), dataLen);
return SUCCESS;
}
static std::vector<float> ComputeGolden(const vector<float>& x, const vector<int64_t>& shapeX,
const vector<float>& y, const vector<int64_t>& shapeY, const vector<int64_t>& outShape)
{
auto broadcastIdx = [](int64_t flat, const vector<int64_t>& inShape, const vector<int64_t>& outShape) {
int inRank = (int)inShape.size(), outRank = (int)outShape.size();
int64_t outIdx = 0, outStride = 1;
for (int d = 0; d < outRank; d++) {
int dimIdx = outRank - 1 - d;
int64_t dim = outShape[dimIdx];
int64_t coord = (flat / outStride) % dim;
int inDimIdx = dimIdx - (outRank - inRank);
int64_t inDim = (inDimIdx >= 0) ? inShape[inDimIdx] : 1;
int64_t inCoord = (inDim == 1) ? 0 : coord;
int inStride = 1;
for (int dd = inRank - 1; dd > inDimIdx; dd--) inStride *= inShape[dd];
outIdx += inCoord * inStride;
outStride *= dim;
}
return outIdx;
};
int64_t n = 1;
for (auto d : outShape) n *= d;
vector<float> result(n);
for (int64_t i = 0; i < n; i++) {
int64_t ix = broadcastIdx(i, shapeX, outShape);
int64_t iy = broadcastIdx(i, shapeY, outShape);
float fx = x[ix], fy = y[iy];
result[i] = (fx == 0.0f) ? 0.0f : fx * std::log1p(fy);
}
return result;
}
static bool CompareOutput(const vector<float>& golden, const vector<float>& output,
int64_t n, const string& tag)
{
bool allPass = true;
double maxMere = 0.0;
for (int64_t i = 0; i < n; i++) {
float g = golden[i], r = output[i];
double mere = (std::fabs(g) > 1e-6) ? std::fabs(r - g) / std::fabs(g) : std::fabs(r - g);
if (mere > maxMere) maxMere = mere;
if (mere > 0.001) {
printf(" [FAIL][%s][%ld] golden=%.6f geir=%.6f mere=%.6e\n",
tag.c_str(), i, g, r, mere);
allPass = false;
}
}
if (allPass) {
printf(" [PASS][%s] all %ld elems OK, max_mere=%.6e\n", tag.c_str(), n, maxMere);
}
return allPass;
}
static int RunGEIRTest(const string& tag,
const vector<int64_t>& shapeX, const vector<float>& dataX,
const vector<int64_t>& shapeY, const vector<float>& dataY)
{
printf("--- Test GEIR %s ---\n", tag.c_str());
int rank = std::max((int)shapeX.size(), (int)shapeY.size());
vector<int64_t> outShape(rank);
for (int d = 0; d < rank; d++) {
int dx = d - (rank - shapeX.size());
int dy = d - (rank - shapeY.size());
outShape[d] = std::max(dx >= 0 ? shapeX[dx] : 1, dy >= 0 ? shapeY[dy] : 1);
}
int64_t outSize = 1;
for (auto d : outShape) outSize *= d;
auto golden = ComputeGolden(dataX, shapeX, dataY, shapeY, outShape);
Graph graph("tc_ge_irrun_xlog1py");
vector<ge::Tensor> input;
vector<Operator> inputs;
vector<Operator> outputs;
Status ret;
auto opX = op::Data("inputX").set_attr_index(0);
TensorDesc descX(ge::Shape(shapeX), FORMAT_ND, DT_FLOAT);
descX.SetPlacement(ge::kPlacementHost);
descX.SetFormat(FORMAT_ND);
Tensor tensorX;
ret = GenFloatData(shapeX, tensorX, descX, dataX);
if (ret != SUCCESS) { printf("Gen input X data failed\n"); return FAILED; }
opX.update_input_desc_x(descX);
input.push_back(tensorX);
graph.AddOp(opX);
inputs.push_back(opX);
auto opY = op::Data("inputY").set_attr_index(1);
TensorDesc descY(ge::Shape(shapeY), FORMAT_ND, DT_FLOAT);
descY.SetPlacement(ge::kPlacementHost);
descY.SetFormat(FORMAT_ND);
Tensor tensorY;
ret = GenFloatData(shapeY, tensorY, descY, dataY);
if (ret != SUCCESS) { printf("Gen input Y data failed\n"); return FAILED; }
opY.update_input_desc_x(descY);
input.push_back(tensorY);
graph.AddOp(opY);
inputs.push_back(opY);
auto xlog1py = op::Xlog1py("xlog1py_op");
xlog1py.set_input_x(opX);
xlog1py.set_input_y(opY);
TensorDesc descZ(ge::Shape(outShape), FORMAT_ND, DT_FLOAT);
xlog1py.update_output_desc_z(descZ);
outputs.push_back(xlog1py);
graph.SetInputs(inputs).SetOutputs(outputs);
map<AscendString, AscendString> build_options;
ge::Session* session = new Session(build_options);
if (session == nullptr) { printf("Create session failed\n"); return FAILED; }
map<AscendString, AscendString> graph_options;
uint32_t graphId = 0;
ret = session->AddGraph(graphId, graph, graph_options);
if (ret != SUCCESS) {
printf("AddGraph failed, ret=%d\n", (int)ret);
ge::AscendString errMsg = ge::GEGetErrorMsgV2();
std::string errStr(errMsg.GetString());
printf("GE Error: %s\n", errStr.c_str());
delete session;
return FAILED;
}
vector<ge::Tensor> output;
ret = session->RunGraph(graphId, input, output);
if (ret != SUCCESS) {
ge::AscendString errMsg = ge::GEGetErrorMsgV2();
printf("RunGraph failed, ret=%d\n", (int)ret);
printf("GE Error: %s\n", errMsg.GetString());
delete session;
return FAILED;
}
printf("RunGraph success\n");
if (output.empty()) { printf("No output\n"); delete session; return FAILED; }
int64_t outputShapeSize = output[0].GetTensorDesc().GetShape().GetShapeSize();
uint32_t dataSize = outputShapeSize * GetDtypeSize(output[0].GetTensorDesc().GetDataType());
uint8_t* outputData = output[0].GetData();
vector<float> npuResult(outputShapeSize);
memcpy(npuResult.data(), outputData, dataSize);
bool pass = CompareOutput(golden, npuResult, outSize, tag);
delete session;
return pass ? SUCCESS : FAILED;
}
int main(int argc, char* argv[])
{
printf("%s - INFO - [XIR]: Start to initialize GE\n", GetTime().c_str());
map<AscendString, AscendString> globalOptions = {
{"ge.exec.deviceId", "0"},
{"ge.graphRunMode", "1"}
};
Status ret = ge::GEInitialize(globalOptions);
if (ret != SUCCESS) {
printf("%s - ERROR - [XIR]: GEInitialize failed\n", GetTime().c_str());
return FAILED;
}
printf("%s - INFO - [XIR]: GEInitialize success\n", GetTime().c_str());
int numPass = 0, numFail = 0;
{
vector<int64_t> shape = {1, 2, 4, 4};
vector<float> x(32, 2.0f), y(32, 1.0f);
if (RunGEIRTest("same_shape", shape, x, shape, y) == SUCCESS) numPass++; else numFail++;
}
{
vector<int64_t> shapeX = {1, 2, 1, 4};
vector<int64_t> shapeY = {1, 2, 4, 4};
vector<float> x(8, 3.0f), y(32, 2.0f);
if (RunGEIRTest("broadcast", shapeX, x, shapeY, y) == SUCCESS) numPass++; else numFail++;
}
{
vector<int64_t> shape = {1, 1, 8, 8};
vector<float> x(64, 0.0f), y(64, 100.0f);
if (RunGEIRTest("x_eq_0", shape, x, shape, y) == SUCCESS) numPass++; else numFail++;
}
{
vector<int64_t> shapeX = {1};
vector<int64_t> shapeY = {4, 8, 16, 16};
vector<float> x(1, 2.5f);
int64_t n = 8192;
vector<float> y(n);
for (int64_t i = 0; i < n; i++) y[i] = 1.0f + 0.1f * (i % 5);
if (RunGEIRTest("scalar_broadcast", shapeX, x, shapeY, y) == SUCCESS) numPass++; else numFail++;
}
printf("========================================\n");
printf("GEIR Xlog1py NPU results: PASS=%d FAIL=%d\n", numPass, numFail);
printf("========================================\n");
printf("%s - INFO - [XIR]: Finalize GE\n", GetTime().c_str());
ret = ge::GEFinalize();
if (ret != SUCCESS) {
printf("%s - ERROR - [XIR]: GEFinalize failed\n", GetTime().c_str());
return FAILED;
}
printf("%s - INFO - [XIR]: GEFinalize success\n", GetTime().c_str());
return (numFail == 0) ? SUCCESS : FAILED;
}