* @file test_aclnn_bitwise_not.cpp
*
* 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.
*
* ============================================================================
* BitwiseNot (experimental/math/bitwise_not) eager 调用示例 —— 单输入按位/逻辑取反。
*
* 调用路径:
* 本示例走 aclnn 两段式接口:
* aclnnBitwiseNotGetWorkspaceSize(self, out, &workspaceSize, &executor);
* aclnnBitwiseNot(workspace, workspaceSize, executor, stream);
*
* 算子语义(详见 docs/aclnnBitwiseNot.md):
* - 整型 (int8/int16/int32/int64/uint8): 按位取反 out = ~self
* (对有符号等价 -self-1,与 numpy.invert / torch.bitwise_not 严格一致)
* - bool: 逻辑非 out = (self == 0) ? 1 : 0 (0<->1,非裸位翻转)
* 单输入单输出,out.shape == self.shape,out.dtype == self.dtype,ND,无 broadcast,无属性。
*
* 精度判据: bitwise exact —— atol=0, rtol=0,逐元素严格相等(== numpy.invert)。
*
* 运行说明:
* 两段式 aclnn 入口直接命中自定义算子包安装的预编译 binary kernel,无需在线 JIT,
* 故不调用 aclSetCompileopt(避免引入 libacl_op_compiler 依赖、破坏仓库统一
* build.sh --run_example 链接约定);编译运行流程由配套 run.sh 自动封装。
* ============================================================================
*/
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <vector>
#include "acl/acl.h"
#include "aclnn_bitwise_not.h"
#define CHECK_RET(cond, return_expr) \
do { \
if (!(cond)) { \
return_expr; \
} \
} while (0)
#define LOG_PRINT(message, ...) \
do { \
printf(message, ##__VA_ARGS__); \
} while (0)
static int64_t GetShapeSize(const std::vector<int64_t> &shape)
{
int64_t shapeSize = 1;
for (auto i : shape) {
shapeSize *= i;
}
return shapeSize;
}
static int Init(int32_t deviceId, aclrtStream *stream)
{
auto ret = aclInit(nullptr);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
ret = aclrtSetDevice(deviceId);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
ret = aclrtCreateStream(stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
return 0;
}
template <typename T>
static int CreateAclTensor(const std::vector<T> &hostData, const std::vector<int64_t> &shape, void **deviceAddr,
aclDataType dataType, aclTensor **tensor)
{
auto size = GetShapeSize(shape) * static_cast<int64_t>(sizeof(T));
if (size <= 0) {
size = 1;
}
auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
if (!hostData.empty()) {
ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), hostData.size() * sizeof(T), ACL_MEMCPY_HOST_TO_DEVICE);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
}
std::vector<int64_t> strides(shape.size(), 1);
for (int64_t i = static_cast<int64_t>(shape.size()) - 2; i >= 0; i--) {
strides[i] = shape[i + 1] * strides[i + 1];
}
*tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND,
shape.data(), shape.size(), *deviceAddr);
CHECK_RET(*tensor != nullptr, LOG_PRINT("aclCreateTensor failed\n"); return -1);
return 0;
}
template <typename T>
static int RunBitwiseNotOnNpu(aclrtStream stream, aclDataType dtype, const std::vector<int64_t> &shape,
const std::vector<T> &selfHost, std::vector<T> &outHost)
{
void *selfDev = nullptr;
void *outDev = nullptr;
aclTensor *self = nullptr;
aclTensor *out = nullptr;
void *workspaceAddr = nullptr;
uint64_t workspaceSize = 0;
aclOpExecutor *executor = nullptr;
int rc = -1;
int ret = CreateAclTensor(selfHost, shape, &selfDev, dtype, &self);
CHECK_RET(ret == 0, goto cleanup);
ret = CreateAclTensor(outHost, shape, &outDev, dtype, &out);
CHECK_RET(ret == 0, goto cleanup);
{
aclError aclRet = aclnnBitwiseNotGetWorkspaceSize(self, out, &workspaceSize, &executor);
CHECK_RET(aclRet == ACL_SUCCESS,
LOG_PRINT("aclnnBitwiseNotGetWorkspaceSize failed. ERROR: %d msg=%s\n", aclRet,
aclGetRecentErrMsg());
goto cleanup);
if (workspaceSize > 0) {
aclRet = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
CHECK_RET(aclRet == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", aclRet); goto cleanup);
}
aclRet = aclnnBitwiseNot(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(aclRet == ACL_SUCCESS,
LOG_PRINT("aclnnBitwiseNot failed. ERROR: %d msg=%s\n", aclRet, aclGetRecentErrMsg());
goto cleanup);
}
{
aclError aclRet = aclrtSynchronizeStream(stream);
CHECK_RET(aclRet == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", aclRet); goto cleanup);
}
if (!outHost.empty()) {
aclError aclRet = aclrtMemcpy(outHost.data(), outHost.size() * sizeof(T), outDev, outHost.size() * sizeof(T),
ACL_MEMCPY_DEVICE_TO_HOST);
CHECK_RET(aclRet == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy D2H failed. ERROR: %d\n", aclRet); goto cleanup);
}
rc = 0;
cleanup:
if (self != nullptr) aclDestroyTensor(self);
if (out != nullptr) aclDestroyTensor(out);
if (selfDev != nullptr) aclrtFree(selfDev);
if (outDev != nullptr) aclrtFree(outDev);
if (workspaceAddr != nullptr) aclrtFree(workspaceAddr);
return rc;
}
int main()
{
int32_t deviceId = 0;
aclrtStream stream = nullptr;
int initRet = Init(deviceId, &stream);
CHECK_RET(initRet == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", initRet); return initRet);
bool allPass = true;
{
std::vector<int64_t> shape = {2, 4};
std::vector<int32_t> self = {0, 1, -1, 2, 7, -8, 2147483647, -2147483648};
std::vector<int32_t> actual(self.size(), 0);
std::vector<int32_t> golden(self.size(), 0);
for (size_t i = 0; i < self.size(); ++i) {
golden[i] = static_cast<int32_t>(~self[i]);
}
int ret = RunBitwiseNotOnNpu(stream, ACL_INT32, shape, self, actual);
CHECK_RET(ret == 0, LOG_PRINT("[FAIL] int32 case run failed\n"); allPass = false);
if (ret == 0) {
bool ok = (memcmp(actual.data(), golden.data(), self.size() * sizeof(int32_t)) == 0);
LOG_PRINT("[%s] int32 ~self bitwise-exact\n", ok ? "PASS" : "FAIL");
for (size_t i = 0; i < self.size(); ++i) {
LOG_PRINT(" self[%zu]=%d out=%d golden=%d\n", i, self[i], actual[i], golden[i]);
}
allPass = allPass && ok;
}
}
{
std::vector<int64_t> shape = {5};
std::vector<int8_t> self = {-128, -1, 0, 1, 127};
std::vector<int8_t> actual(self.size(), 0);
std::vector<int8_t> golden(self.size(), 0);
for (size_t i = 0; i < self.size(); ++i) {
golden[i] = static_cast<int8_t>(~self[i]);
}
int ret = RunBitwiseNotOnNpu(stream, ACL_INT8, shape, self, actual);
CHECK_RET(ret == 0, LOG_PRINT("[FAIL] int8 case run failed\n"); allPass = false);
if (ret == 0) {
bool ok = (memcmp(actual.data(), golden.data(), self.size() * sizeof(int8_t)) == 0);
LOG_PRINT("[%s] int8 ~self bitwise-exact (boundary ~(-128)=%d)\n", ok ? "PASS" : "FAIL", actual[0]);
allPass = allPass && ok;
}
}
{
std::vector<int64_t> shape = {4};
std::vector<uint8_t> self = {0, 1, 128, 255};
std::vector<uint8_t> actual(self.size(), 0);
std::vector<uint8_t> golden(self.size(), 0);
for (size_t i = 0; i < self.size(); ++i) {
golden[i] = static_cast<uint8_t>(~self[i]);
}
int ret = RunBitwiseNotOnNpu(stream, ACL_UINT8, shape, self, actual);
CHECK_RET(ret == 0, LOG_PRINT("[FAIL] uint8 case run failed\n"); allPass = false);
if (ret == 0) {
bool ok = (memcmp(actual.data(), golden.data(), self.size() * sizeof(uint8_t)) == 0);
LOG_PRINT("[%s] uint8 ~self bitwise-exact (~0=%u ~255=%u)\n", ok ? "PASS" : "FAIL", actual[0], actual[3]);
allPass = allPass && ok;
}
}
{
std::vector<int64_t> shape = {4};
std::vector<uint8_t> self = {0, 1, 0, 1};
std::vector<uint8_t> actual(self.size(), 0);
std::vector<uint8_t> golden(self.size(), 0);
for (size_t i = 0; i < self.size(); ++i) {
golden[i] = (self[i] == 0) ? 1 : 0;
}
int ret = RunBitwiseNotOnNpu(stream, ACL_BOOL, shape, self, actual);
CHECK_RET(ret == 0, LOG_PRINT("[FAIL] bool case run failed\n"); allPass = false);
if (ret == 0) {
bool ok = (memcmp(actual.data(), golden.data(), self.size() * sizeof(uint8_t)) == 0);
LOG_PRINT("[%s] bool !self (0<->1) bitwise-exact\n", ok ? "PASS" : "FAIL");
allPass = allPass && ok;
}
}
LOG_PRINT("==== BitwiseNot eager example: %s ====\n", allPass ? "ALL PASS" : "SOME FAILED");
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return allPass ? 0 : 1;
}