已合并
贡献Ascend C实现的Equal算子 #2890
fulltower创建于 5月21日
贡献Ascend C实现的Equal算子 #2890
已合并
fulltower创建于 5月21日
25 个文件变更+4711-16
@@ -1,18 +1,18 @@
1repos:1repos:
2- - repo: https://github.com/pre-commit/mirrors-clang-format2+ - repo: https://github.com/pre-commit/mirrors-clang-format
3- rev: v16.0.03+ rev: v16.0.0
4- hooks:4+ hooks:
5- - id: clang-format5+ - id: clang-format
6- types_or: [c++, c]6+ types_or: [c++, c]
7 7 
8- # OAT compliance check (Open Source Audit Tool)8+ # OAT compliance check (Open Source Audit Tool)
9- - repo: local9+ - repo: local
10- hooks:10+ hooks:
11- - id: oat-check11+ - id: oat-check
12- name: OAT Compliance Check12+ name: OAT Compliance Check
13- entry: bash scripts/oat_check.sh13+ entry: bash scripts/oat_check.sh
14- language: system14+ language: system
15- pass_filenames: true15+ pass_filenames: true
16- types: [file]16+ types: [file]
17- stages: [commit]17+ stages: [commit]
18- verbose: true18+ verbose: true
@@ -0,0 +1,11 @@
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+add_all_modules_sources(OPTYPE equal ACLNNTYPE aclnn_exclude)
@@ -0,0 +1,43 @@
1+# Equal
2+ 
3+## 贡献说明
4+ 
5+| 贡献者 | 贡献方 | 贡献算子 | 贡献时间 | 贡献内容 |
6+|-------|------------------|-------|-----------|-----------|
7+| skywang2 | 个人开发者 | Equal | 2026/6/21 | 新增Equal算子 |
8+ 
9+## 支持的产品型号
10+- Atlas A2训练系列产品
11+ 
12+产品形态详细说明请参见[昇腾产品形态说明](http://www.hiascend.com/document/redirect/CannCommunityProductForm)
13+ 
14+## 算子描述
15+ 
16+- 功能描述
17+ 
18+ `Equal`算子将输入的两个向量数据进行各对应位置的判等运算,返回结果向量。
19+ 
20+- 原型信息
21+ 
22+ <table>
23+ <tr><th align="center">算子类型(OpType)</th><th colspan="4" align="center">Equal</th></tr>
24+ <tr><td align="center"> </td><td align="center">name</td><td align="center">Type</td><td align="center">data type</td><td align="center">format</td></tr>
25+ <tr><td rowspan="2" align="center">算子输入</td><td align="center">x1</td><td align="center">tensor</td><td align="center">float32,float16,bfloat16,uint8,int8,uint32,int32</td><td align="center">ND</td></tr>
26+ <tr><td align="center">x2</td><td align="center">tensor</td><td align="center">float32,float16,bfloat16,uint8,int8,uint32,int32</td><td align="center">ND</td></tr>
27+ <tr><td align="center">算子输出</td><td align="center">y</td><td align="center">tensor</td><td align="center">bool</td><td align="center">ND</td></tr>
28+ <tr><td align="center">核函数名</td><td colspan="4" align="center">equal</td></tr>
29+ </table>
30+ 
31+## 约束与限制
32+ 
33+- x,y,out的数据类型仅支持float32,float16,bfloat16,uint8,int8,uint32,int32,数据格式仅支持ND
34+ 
35+### 运行验证
36+ 
37+测试命令调用方式:[build.sh](../../../docs/zh/invocation/quick_op_invocation.md)
38+<table>
39+ <tr><th>目录</th><th>描述</th></tr>
40+ <tr>
41+ <td><a href="./examples/test_aclnn_equal.cpp">test_aclnn_equal.cpp</a></td><td>通过aclnn调用的方式调用Equal算子。</td>
42+ </tr>
43+</table>
@@ -0,0 +1,758 @@
1+# aclnnEqScalar&aclnnInplaceEqScalar
2+ 
3+ 
4+## 产品支持情况
5+ 
6+| 产品 | 是否支持 |
7+| :----------------------------------------------------------- | :------: |
8+| <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term> | √ |
9+ 
10+## 功能说明
11+ 
12+- 接口功能:计算self中的元素的值与other的值是否相等,将self每个元素与other的值的比较结果写入out中。
13+- 计算公式:
14+ 
15+ $$
16+ out_i = (self_i == \mathit{other} ) ? [True] : [False]
17+ $$
18+ 
19+## 函数原型
20+ 
21+- aclnnEqScalar和aclnnInplaceEqScalar实现相同的功能,使用区别如下,请根据自身实际场景选择合适的算子。
22+ 
23+ - aclnnEqScalar:需新建一个输出张量对象存储计算结果。
24+ - aclnnInplaceEqScalar:无需新建输出张量对象,直接在输入张量的内存中存储计算结果。
25+ 
26+- 每个算子分为[两段式接口](../../../../docs/zh/context/两段式接口.md),必须先调用“aclnnEqScalarGetWorkspaceSize”或者“aclnnInplaceEqScalarGetWorkspaceSize”接口获取计算所需workspace大小以及包含了算子计算流程的执行器,再调用“aclnnEqScalar”或者“aclnnInplaceEqScalar”接口执行计算。
27+ 
28+```Cpp
29+aclnnStatus aclnnEqScalarGetWorkspaceSize(
30+ const aclTensor *self,
31+ const aclScalar *other,
32+ aclTensor *out,
33+ uint64_t *workspaceSize,
34+ aclOpExecutor **executor)
35+```
36+ 
37+```Cpp
38+aclnnStatus aclnnEqScalar(
39+ void* workspace,
40+ uint64_t workspaceSize,
41+ aclOpExecutor* executor,
42+ const aclrtStream stream)
43+```
44+ 
45+```Cpp
46+aclnnStatus aclnnInplaceEqScalarGetWorkspaceSize(
47+ const aclTensor *selfRef,
48+ const aclScalar *other,
49+ uint64_t *workspaceSize,
50+ aclOpExecutor **executor)
51+```
52+ 
53+```Cpp
54+aclnnStatus aclnnInplaceEqScalar(
55+ void *workspace,
56+ uint64_t workspaceSize,
57+ aclOpExecutor *executor,
58+ aclrtStream stream)
59+```
60+ 
61+## aclnnEqScalarGetWorkspaceSize
62+ 
63+- **参数说明:**
64+ 
65+ <table style="undefined;table-layout: fixed; width: 1494px"><colgroup>
66+ <col style="width: 168px">
67+ <col style="width: 136px">
68+ <col style="width: 213px">
69+ <col style="width: 200px">
70+ <col style="width: 373px">
71+ <col style="width: 115px">
72+ <col style="width: 142px">
73+ <col style="width: 147px">
74+ </colgroup>
75+ <thead>
76+ <tr>
77+ <th>参数名</th>
78+ <th>输入/输出</th>
79+ <th>描述</th>
80+ <th>使用说明</th>
81+ <th>数据类型</th>
82+ <th>数据格式</th>
83+ <th>维度(shape)</th>
84+ <th>非连续Tensor</th>
85+ </tr></thead>
86+ <tbody>
87+ <tr>
88+ <td>self</td>
89+ <td>输入</td>
90+ <td>公式中的self。</td>
91+ <td>-</td>
92+ <td>DOUBLE、FLOAT16、FLOAT、BFLOAT16、INT64、INT32、INT8、UINT8、BOOL、INT16、COMPLEX64、COMPLEX128、UINT32、UINT64</td>
93+ <td>ND</td>
94+ <td>不高于8维</td>
95+ <td>√</td>
96+ </tr>
97+ <tr>
98+ <td>other</td>
99+ <td>输入</td>
100+ <td>公式中的other。</td>
101+ <td>-</td>
102+ <td>DOUBLE、FLOAT16、FLOAT、BFLOAT16、INT64、INT32、INT8、UINT8、BOOL、INT16、COMPLEX64、COMPLEX128、UINT32、UINT64</td>
103+ <td>-</td>
104+ <td>-</td>
105+ <td>-</td>
106+ </tr>
107+ <tr>
108+ <td>out</td>
109+ <td>输出</td>
110+ <td>公式中的out。</td>
111+ <td>数据类型BOOL可转换的数据类型(参见<a href="../../../../docs/zh/context/互转换关系.md" target="_blank">互转换关系</a>)。</td>
112+ <td>DOUBLE、FLOAT16、FLOAT、BFLOAT16、INT64、INT32、INT8、UINT8、BOOL、INT16、COMPLEX64、COMPLEX128、UINT64、UINT32、UINT16</td>
113+ <td>ND</td>
114+ <td>与self的shape一致</td>
115+ <td>√</td>
116+ </tr>
117+ <tr>
118+ <td>workspaceSize</td>
119+ <td>输出</td>
120+ <td>返回需要在Device侧申请的workspace大小。</td>
121+ <td>-</td>
122+ <td>-</td>
123+ <td>-</td>
124+ <td>-</td>
125+ <td>-</td>
126+ </tr>
127+ <tr>
128+ <td>executor</td>
129+ <td>输出</td>
130+ <td>返回op执行器,包含了算子计算流程。</td>
131+ <td>-</td>
132+ <td>-</td>
133+ <td>-</td>
134+ <td>-</td>
135+ <td>-</td>
136+ </tr>
137+ </tbody>
138+ </table>
139+
140+ - <term>Ascend 950PR/Ascend 950DT</term>
141+ - self与other满足[TensorScalar互推导关系](../../../../docs/zh/context/TensorScalar互推导关系.md)。
142+ - self、other不支持UINT32数据类型。
143+ - <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term><term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term>
144+ - self与other满足[互推导关系](../../../../docs/zh/context/互推导关系.md)。
145+ - self、other不支持UINT32、UINT64数据类型
146+ - out不支持UINT64、UINT32、UINT16数据类型。
147+ - <term>Atlas 训练系列产品</term>
148+ - self与other满足[互推导关系](../../../../docs/zh/context/互推导关系.md)。
149+ - 不支持BFLOAT16数据类型。
150+ - out不支持UINT64、UINT32、UINT16、BFLOAT16数据类型。
151+ 
152+- **返回值:**
153+ 
154+ aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../../docs/zh/context/aclnn返回码.md)。
155+
156+ 第一段接口完成入参校验,出现以下场景时报错:
157+
158+ <table style="undefined;table-layout: fixed; width: 1150px"><colgroup>
159+ <col style="width: 272px">
160+ <col style="width: 114px">
161+ <col style="width: 764px">
162+ </colgroup>
163+ <thead>
164+ <tr>
165+ <th>返回码</th>
166+ <th>错误码</th>
167+ <th>描述</th>
168+ </tr></thead>
169+ <tbody>
170+ <tr>
171+ <td>ACLNN_ERR_PARAM_NULLPTR</td>
172+ <td>161001</td>
173+ <td>传入的self、other、out是空指针时。</td>
174+ </tr>
175+ <tr>
176+ <td rowspan="4">ACLNN_ERR_PARAM_INVALID</td>
177+ <td rowspan="4">161002</td>
178+ <td>self,other或out的数据类型不在支持的范围之内。</td>
179+ </tr>
180+ <tr>
181+ <td>self和other数据类型不满足数据类型推导规则。</td>
182+ </tr>
183+ <tr>
184+ <td>self和out的shape不同。</td>
185+ </tr>
186+ <tr>
187+ <td>self和out的维度大于8。</td>
188+ </tr>
189+ </tbody>
190+ </table>
191+ 
192+## aclnnEqScalar
193+ 
194+- **参数说明:**
195+ 
196+ <table style="undefined;table-layout: fixed; width: 1150px"><colgroup>
197+ <col style="width: 153px">
198+ <col style="width: 124px">
199+ <col style="width: 873px">
200+ </colgroup>
201+ <thead>
202+ <tr>
203+ <th>参数名</th>
204+ <th>输入/输出</th>
205+ <th>描述</th>
206+ </tr></thead>
207+ <tbody>
208+ <tr>
209+ <td>workspace</td>
210+ <td>输入</td>
211+ <td>在Device侧申请的workspace内存地址。</td>
212+ </tr>
213+ <tr>
214+ <td>workspaceSize</td>
215+ <td>输入</td>
216+ <td>在Device侧申请的workspace大小,由第一段接口aclnnEqScalarGetWorkspaceSize获取。</td>
217+ </tr>
218+ <tr>
219+ <td>executor</td>
220+ <td>输入</td>
221+ <td>op执行器,包含了算子计算流程。</td>
222+ </tr>
223+ <tr>
224+ <td>stream</td>
225+ <td>输入</td>
226+ <td>指定执行任务的Stream。</td>
227+ </tr>
228+ </tbody>
229+ </table>
230+ 
231+- **返回值:**
232+ 
233+ aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../../docs/zh/context/aclnn返回码.md)。
234+ 
235+## aclnnInplaceEqScalarGetWorkspaceSize
236+ 
237+- **参数说明:**
238+ 
239+ <table style="undefined;table-layout: fixed; width: 1502px"><colgroup>
240+ <col style="width: 167px">
241+ <col style="width: 135px">
242+ <col style="width: 267px">
243+ <col style="width: 159px">
244+ <col style="width: 372px">
245+ <col style="width: 115px">
246+ <col style="width: 141px">
247+ <col style="width: 146px">
248+ </colgroup>
249+ <thead>
250+ <tr>
251+ <th>参数名</th>
252+ <th>输入/输出</th>
253+ <th>描述</th>
254+ <th>使用说明</th>
255+ <th>数据类型</th>
256+ <th>数据格式</th>
257+ <th>维度(shape)</th>
258+ <th>非连续Tensor</th>
259+ </tr></thead>
260+ <tbody>
261+ <tr>
262+ <td>selfRef</td>
263+ <td>输入/输出</td>
264+ <td>公式中的selfRef。</td>
265+ <td>-</td>
266+ <td>DOUBLE、FLOAT16、FLOAT、BFLOAT16、INT64、INT32、INT8、UINT8、BOOL、INT16、COMPLEX64、COMPLEX128、UINT32、UINT64</td>
267+ <td>ND</td>
268+ <td>不高于8维</td>
269+ <td>√</td>
270+ </tr>
271+ <tr>
272+ <td>other</td>
273+ <td>输入</td>
274+ <td>公式中的other。</td>
275+ <td>-</td>
276+ <td>DOUBLE、FLOAT16、FLOAT、BFLOAT16、INT64、INT32、INT8、UINT8、BOOL、INT16、COMPLEX64、COMPLEX128、UINT32、UINT64</td>
277+ <td>-</td>
278+ <td>-</td>
279+ <td>-</td>
280+ </tr>
281+ <tr>
282+ <td>workspaceSize</td>
283+ <td>输出</td>
284+ <td>返回需要在Device侧申请的workspace大小。</td>
285+ <td>-</td>
286+ <td>-</td>
287+ <td>-</td>
288+ <td>-</td>
289+ <td>-</td>
290+ </tr>
291+ <tr>
292+ <td>executor</td>
293+ <td>输出</td>
294+ <td>返回op执行器,包含了算子计算流程。</td>
295+ <td>-</td>
296+ <td>-</td>
297+ <td>-</td>
298+ <td>-</td>
299+ <td>-</td>
300+ </tr>
301+ </tbody>
302+ </table>
303+ 
304+ - <term>Ascend 950PR/Ascend 950DT</term>
305+ - selfRef与other满足[TensorScalar互推导关系](../../../../docs/zh/context/TensorScalar互推导关系.md)。
306+ - selfRef、other不支持UINT32数据类型。
307+ - <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term><term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term>
308+ - selfRef与other满足[互推导关系](../../../../docs/zh/context/互推导关系.md)。
309+ - selfRef、other不支持UINT32、UINT64数据类型。
310+ - <term>Atlas 训练系列产品</term>
311+ - selfRef与other满足[互推导关系](../../../../docs/zh/context/互推导关系.md)。
312+ - 不支持BFLOAT16数据类型。
313+ 
314+- **返回值:**
315+ 
316+ aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../../docs/zh/context/aclnn返回码.md)。
317+ 
318+ 第一段接口完成入参校验,出现以下场景时报错:
319+ 
320+ <table style="undefined;table-layout: fixed; width: 1150px"><colgroup>
321+ <col style="width: 272px">
322+ <col style="width: 114px">
323+ <col style="width: 764px">
324+ </colgroup>
325+ <thead>
326+ <tr>
327+ <th>返回码</th>
328+ <th>错误码</th>
329+ <th>描述</th>
330+ </tr></thead>
331+ <tbody>
332+ <tr>
333+ <td>ACLNN_ERR_PARAM_NULLPTR</td>
334+ <td>161001</td>
335+ <td>传入的selfRef和other是空指针时。</td>
336+ </tr>
337+ <tr>
338+ <td rowspan="3">ACLNN_ERR_PARAM_INVALID</td>
339+ <td rowspan="3">161002</td>
340+ <td>selfRef和other的数据类型不在支持的范围之内。</td>
341+ </tr>
342+ <tr>
343+ <td>selfRef和other的数据类型不满足数据类型推导规则。</td>
344+ </tr>
345+ <tr>
346+ <td>selfRef的维度大于8。</td>
347+ </tr>
348+ </tbody>
349+ </table>
350+ 
351+## aclnnInplaceEqScalar
352+ 
353+- **参数说明:**
354+ 
355+ <table style="undefined;table-layout: fixed; width: 1150px"><colgroup>
356+ <col style="width: 153px">
357+ <col style="width: 124px">
358+ <col style="width: 873px">
359+ </colgroup>
360+ <thead>
361+ <tr>
362+ <th>参数名</th>
363+ <th>输入/输出</th>
364+ <th>描述</th>
365+ </tr></thead>
366+ <tbody>
367+ <tr>
368+ <td>workspace</td>
369+ <td>输入</td>
370+ <td>在Device侧申请的workspace内存地址。</td>
371+ </tr>
372+ <tr>
373+ <td>workspaceSize</td>
374+ <td>输入</td>
375+ <td>在Device侧申请的workspace大小,由第一段接口aclnnInplaceEqScalarGetWorkspaceSize获取。</td>
376+ </tr>
377+ <tr>
378+ <td>executor</td>
379+ <td>输入</td>
380+ <td>op执行器,包含了算子计算流程。</td>
381+ </tr>
382+ <tr>
383+ <td>stream</td>
384+ <td>输入</td>
385+ <td>指定执行任务的Stream。</td>
386+ </tr>
387+ </tbody>
388+ </table>
389+ 
390+- **返回值:**
391+ 
392+ aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../../docs/zh/context/aclnn返回码.md)。
393+ 
394+## 约束说明
395+ 
396+- 确定性计算:
397+ - aclnnEqScalar&aclnnInplaceEqScalar默认确定性实现。
398+ 
399+## 调用示例
400+ 
401+示例代码如下,仅供参考,具体编译和执行过程请参考[编译与运行样例](../../../../docs/zh/context/编译与运行样例.md)。
402+ 
403+**aclnnEqScalar示例代码:**
404+ 
405+```Cpp
406+#include <iostream>
407+#include <vector>
408+#include "acl/acl.h"
409+#include "aclnnop/aclnn_eq_scalar.h"
410+ 
411+#define CHECK_RET(cond, return_expr) \
412+ do { \
413+ if (!(cond)) { \
414+ return_expr; \
415+ } \
416+ } while (0)
417+ 
418+#define LOG_PRINT(message, ...) \
419+ do { \
420+ printf(message, ##__VA_ARGS__); \
421+ } while (0)
422+ 
423+int64_t GetShapeSize(const std::vector<int64_t>& shape)
424+{
425+ int64_t shape_size = 1;
426+ for (auto i : shape) {
427+ shape_size *= i;
428+ }
429+ return shape_size;
430+}
431+ 
432+int Init(int32_t deviceId, aclrtStream* stream)
433+{
434+ // 固定写法,资源初始化
435+ auto ret = aclInit(nullptr);
436+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
437+ ret = aclrtSetDevice(deviceId);
438+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
439+ ret = aclrtCreateStream(stream);
440+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
441+ return 0;
442+}
443+ 
444+template <typename T>
445+int CreateAclTensor(
446+ const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType,
447+ aclTensor** tensor)
448+{
449+ auto size = GetShapeSize(shape) * sizeof(T);
450+ // 调用aclrtMalloc申请device侧内存
451+ auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
452+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
453+ 
454+ // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
455+ ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
456+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
457+ 
458+ // 计算连续tensor的strides
459+ std::vector<int64_t> strides(shape.size(), 1);
460+ for (int64_t i = shape.size() - 2; i >= 0; i--) {
461+ strides[i] = shape[i + 1] * strides[i + 1];
462+ }
463+ 
464+ // 调用aclCreateTensor接口创建aclTensor
465+ *tensor = aclCreateTensor(
466+ shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(),
467+ *deviceAddr);
468+ return 0;
469+}
470+ 
471+aclError InitAcl(int32_t deviceId, aclrtStream* stream)
472+{
473+ auto ret = Init(deviceId, stream);
474+ CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
475+ return ACL_SUCCESS;
476+}
477+ 
478+aclError CreateInputs(
479+ std::vector<int64_t>& selfShape, std::vector<int64_t>& outShape, void** selfDeviceAddr, void** outDeviceAddr,
480+ aclTensor** self, aclScalar** other, aclTensor** out)
481+{
482+ std::vector<double> selfHostData = {0, 1, 1.2, 0.3, 4.1, 5, 1.6, 7};
483+ std::vector<char> outHostData = {0, 0, 0, 0, 0, 0, 0, 0};
484+ double otherValue = 1.2;
485+ 
486+ // 创建 self tensor
487+ auto ret = CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_DOUBLE, self);
488+ CHECK_RET(ret == ACL_SUCCESS, return ret);
489+ 
490+ // 创建 other scalar
491+ *other = aclCreateScalar(&otherValue, aclDataType::ACL_DOUBLE);
492+ CHECK_RET(*other != nullptr, return ret);
493+ 
494+ // 创建 out tensor
495+ ret = CreateAclTensor(outHostData, outShape, &(*outDeviceAddr), aclDataType::ACL_BOOL, out);
496+ CHECK_RET(ret == ACL_SUCCESS, return ret);
497+ 
498+ return ACL_SUCCESS;
499+}
500+ 
501+aclError ExecOpApi(
502+ aclTensor* self, aclScalar* other, aclTensor* out, void** workspaceAddrOut, uint64_t& workspaceSize,
503+ void* outDeviceAddr, std::vector<int64_t>& outShape, aclrtStream stream)
504+{
505+ aclOpExecutor* executor;
506+ 
507+ // 获取 workspace 大小
508+ auto ret = aclnnEqScalarGetWorkspaceSize(self, other, out, &workspaceSize, &executor);
509+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnEqScalarGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
510+ 
511+ // 申请 workspace(释放放在 main 里)
512+ void* workspaceAddr = nullptr;
513+ if (workspaceSize > 0) {
514+ ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
515+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
516+ }
517+ *workspaceAddrOut = workspaceAddr;
518+ 
519+ // 调用算子
520+ ret = aclnnEqScalar(workspaceAddr, workspaceSize, executor, stream);
521+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnEqScalar failed. ERROR: %d\n", ret); return ret);
522+ 
523+ // 同步
524+ ret = aclrtSynchronizeStream(stream);
525+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
526+ 
527+ // 拷贝输出
528+ auto size = GetShapeSize(outShape);
529+ std::vector<char> resultData(size, 0);
530+ 
531+ ret = aclrtMemcpy(
532+ resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(char),
533+ ACL_MEMCPY_DEVICE_TO_HOST);
534+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
535+ 
536+ for (int64_t i = 0; i < size; i++) {
537+ LOG_PRINT("result[%ld] is: %d\n", i, resultData[i]);
538+ }
539+ 
540+ return ACL_SUCCESS;
541+}
542+ 
543+int main()
544+{
545+ int32_t deviceId = 0;
546+ aclrtStream stream;
547+ auto ret = InitAcl(deviceId, &stream);
548+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("InitAcl failed. ERROR: %d\n", ret); return ret);
549+ 
550+ std::vector<int64_t> selfShape = {4, 2};
551+ std::vector<int64_t> outShape = {4, 2};
552+ 
553+ void* selfDeviceAddr = nullptr;
554+ void* outDeviceAddr = nullptr;
555+ aclTensor* self = nullptr;
556+ aclScalar* other = nullptr;
557+ aclTensor* out = nullptr;
558+ 
559+ ret = CreateInputs(selfShape, outShape, &selfDeviceAddr, &outDeviceAddr, &self, &other, &out);
560+ CHECK_RET(ret == ACL_SUCCESS, return ret);
561+ 
562+ uint64_t workspaceSize = 0;
563+ void* workspaceAddr = nullptr;
564+ 
565+ ret = ExecOpApi(self, other, out, &workspaceAddr, workspaceSize, outDeviceAddr, outShape, stream);
566+ CHECK_RET(ret == ACL_SUCCESS, return ret);
567+ 
568+ // 释放 Tensor / Scalar
569+ aclDestroyTensor(self);
570+ aclDestroyScalar(other);
571+ aclDestroyTensor(out);
572+ 
573+ // 释放 device 内存
574+ aclrtFree(selfDeviceAddr);
575+ aclrtFree(outDeviceAddr);
576+ if (workspaceSize > 0) {
577+ aclrtFree(workspaceAddr);
578+ }
579+ 
580+ aclrtDestroyStream(stream);
581+ aclrtResetDevice(deviceId);
582+ aclFinalize();
583+ 
584+ return 0;
585+}
586+```
587+ 
588+**aclnnInplaceEqScalar示例代码:**
589+ 
590+```Cpp
591+#include <iostream>
592+#include <vector>
593+#include "acl/acl.h"
594+#include "aclnnop/aclnn_eq_scalar.h"
595+ 
596+#define CHECK_RET(cond, return_expr) \
597+ do { \
598+ if (!(cond)) { \
599+ return_expr; \
600+ } \
601+ } while (0)
602+ 
603+#define LOG_PRINT(message, ...) \
604+ do { \
605+ printf(message, ##__VA_ARGS__); \
606+ } while (0)
607+ 
608+int64_t GetShapeSize(const std::vector<int64_t>& shape)
609+{
610+ int64_t shapeSize = 1;
611+ for (auto i : shape) {
612+ shapeSize *= i;
613+ }
614+ return shapeSize;
615+}
616+ 
617+int Init(int32_t deviceId, aclrtStream* stream)
618+{
619+ // 固定写法,资源初始化
620+ auto ret = aclInit(nullptr);
621+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
622+ ret = aclrtSetDevice(deviceId);
623+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
624+ ret = aclrtCreateStream(stream);
625+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
626+ return 0;
627+}
628+ 
629+template <typename T>
630+int CreateAclTensor(
631+ const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType,
632+ aclTensor** tensor)
633+{
634+ auto size = GetShapeSize(shape) * sizeof(T);
635+ // 调用aclrtMalloc申请device侧内存
636+ auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
637+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
638+ // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
639+ ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
640+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
641+ 
642+ // 计算连续tensor的strides
643+ std::vector<int64_t> strides(shape.size(), 1);
644+ for (int64_t i = shape.size() - 2; i >= 0; i--) {
645+ strides[i] = shape[i + 1] * strides[i + 1];
646+ }
647+ 
648+ // 调用aclCreateTensor接口创建aclTensor
649+ *tensor = aclCreateTensor(
650+ shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(),
651+ *deviceAddr);
652+ return 0;
653+}
654+ 
655+aclError InitAcl(int32_t deviceId, aclrtStream* stream)
656+{
657+ auto ret = Init(deviceId, stream);
658+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
659+ return ACL_SUCCESS;
660+}
661+ 
662+aclError CreateInputs(std::vector<int64_t>& selfShape, void** selfDeviceAddr, aclTensor** self, aclScalar** other)
663+{
664+ std::vector<double> selfHostData = {0, 1, 2, 3, 4, 5, 6, 7};
665+ double otherValue = 2.0;
666+ 
667+ // 创建self aclTensor
668+ auto ret = CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_DOUBLE, self);
669+ CHECK_RET(ret == ACL_SUCCESS, return ret);
670+ 
671+ // 创建other aclScalar
672+ *other = aclCreateScalar(&otherValue, aclDataType::ACL_DOUBLE);
673+ CHECK_RET(*other != nullptr, return ACL_ERROR_INVALID_PARAM);
674+ 
675+ return ACL_SUCCESS;
676+}
677+ 
678+aclError ExecOpApi(
679+ aclTensor* self, aclScalar* other, void* selfDeviceAddr, std::vector<int64_t>& selfShape, aclrtStream stream,
680+ void** workspaceAddrOut)
681+{
682+ uint64_t workspaceSize = 0;
683+ aclOpExecutor* executor;
684+ 
685+ auto ret = aclnnInplaceEqScalarGetWorkspaceSize(self, other, &workspaceSize, &executor);
686+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceEqScalarGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
687+ 
688+ // workspace 分配
689+ void* workspaceAddr = nullptr;
690+ if (workspaceSize > 0) {
691+ ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
692+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
693+ }
694+ 
695+ *workspaceAddrOut = workspaceAddr;
696+ 
697+ // 执行
698+ ret = aclnnInplaceEqScalar(workspaceAddr, workspaceSize, executor, stream);
699+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceEqScalar failed. ERROR: %d\n", ret); return ret);
700+ 
701+ // 同步
702+ ret = aclrtSynchronizeStream(stream);
703+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
704+ 
705+ // 拷贝输出
706+ auto size = GetShapeSize(selfShape);
707+ std::vector<double> resultData(size);
708+ 
709+ ret = aclrtMemcpy(
710+ resultData.data(), resultData.size() * sizeof(resultData[0]), selfDeviceAddr, size * sizeof(resultData[0]),
711+ ACL_MEMCPY_DEVICE_TO_HOST);
712+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
713+ 
714+ for (int64_t i = 0; i < size; i++) {
715+ LOG_PRINT("result[%ld] is: %lf\n", i, resultData[i]);
716+ }
717+ 
718+ return ACL_SUCCESS;
719+}
720+ 
721+int main()
722+{
723+ int32_t deviceId = 0;
724+ aclrtStream stream;
725+ 
726+ auto ret = InitAcl(deviceId, &stream);
727+ CHECK_RET(ret == ACL_SUCCESS, return ret);
728+ 
729+ std::vector<int64_t> selfShape = {4, 2};
730+ void* selfDeviceAddr = nullptr;
731+ 
732+ aclTensor* self = nullptr;
733+ aclScalar* other = nullptr;
734+ 
735+ ret = CreateInputs(selfShape, &selfDeviceAddr, &self, &other);
736+ CHECK_RET(ret == ACL_SUCCESS, return ret);
737+ 
738+ void* workspaceAddr = nullptr;
739+ ret = ExecOpApi(self, other, selfDeviceAddr, selfShape, stream, &workspaceAddr);
740+ CHECK_RET(ret == ACL_SUCCESS, return ret);
741+ 
742+ // 销毁
743+ aclDestroyTensor(self);
744+ aclDestroyScalar(other);
745+ 
746+ aclrtFree(selfDeviceAddr);
747+ if (workspaceAddr != nullptr) {
748+ aclrtFree(workspaceAddr);
749+ }
750+ // 释放
751+ aclrtDestroyStream(stream);
752+ aclrtResetDevice(deviceId);
753+ aclFinalize();
754+ 
755+ return 0;
756+}
757+ 
758+```
@@ -0,0 +1,766 @@
1+# aclnnEqTensor&aclnnInplaceEqTensor
2+ 
3+ 
4+## 产品支持情况
5+ 
6+| 产品 | 是否支持 |
7+| :----------------------------------------------------------- | :------: |
8+| <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term> | √ |
9+ 
10+## 功能说明
11+ 
12+- 接口功能:计算两个Tensor中的元素是否相等,返回一个Tensor,self=other的为True(1.),否则为False(0.)。
13+- 计算表达式:
14+ 
15+ $$
16+ out = (self_i == other_i) ? [True] : [False]
17+ $$
18+ 
19+## 函数原型
20+ 
21+- aclnnEqTensor和aclnnInplaceEqTensor实现相同的功能,使用区别如下,请根据自身实际场景选择合适的算子。
22+ 
23+ - aclnnEqTensor:需新建一个输出张量对象存储计算结果。
24+ - aclnnInplaceEqTensor:无需新建输出张量对象,直接在输入张量的内存中存储计算结果。
25+ 
26+- 每个算子分为[两段式接口](../../../../docs/zh/context/两段式接口.md),必须先调用“aclnnEqTensorGetWorkspaceSize”或者“aclnnInplaceEqTensorGetWorkspaceSize”接口获取计算所需workspace大小以及包含了算子计算流程的执行器,再调用“aclnnEqTensor”或者“aclnnInplaceEqTensor”接口执行计算aclnnEqTensor。
27+ 
28+```Cpp
29+aclnnStatus aclnnEqTensorGetWorkspaceSize(
30+ const aclTensor *self,
31+ const aclTensor *other,
32+ aclTensor *out,
33+ uint64_t *workspaceSize,
34+ aclOpExecutor **executor)
35+```
36+ 
37+```Cpp
38+aclnnStatus aclnnEqTensor(
39+ void *workspace,
40+ uint64_t workspaceSize,
41+ aclOpExecutor *executor,
42+ aclrtStream stream)
43+```
44+ 
45+```Cpp
46+aclnnStatus aclnnInplaceEqTensorGetWorkspaceSize(
47+ const aclTensor *selfRef,
48+ const aclTensor *other,
49+ uint64_t *workspaceSize,
50+ aclOpExecutor **executor)
51+```
52+ 
53+```Cpp
54+aclnnStatus aclnnInplaceEqTensor(
55+ void *workspace,
56+ uint64_t workspaceSize,
57+ aclOpExecutor *executor,
58+ aclrtStream stream)
59+```
60+ 
61+## aclnnEqTensorGetWorkspaceSize
62+ 
63+- **参数说明:**
64+ 
65+ <table style="undefined;table-layout: fixed; width: 1550px"><colgroup>
66+ <col style="width: 168px">
67+ <col style="width: 136px">
68+ <col style="width: 215px">
69+ <col style="width: 286px">
70+ <col style="width: 340px">
71+ <col style="width: 116px">
72+ <col style="width: 142px">
73+ <col style="width: 147px">
74+ </colgroup>
75+ <thead>
76+ <tr>
77+ <th>参数名</th>
78+ <th>输入/输出</th>
79+ <th>描述</th>
80+ <th>使用说明</th>
81+ <th>数据类型</th>
82+ <th>数据格式</th>
83+ <th>维度(shape)</th>
84+ <th>非连续Tensor</th>
85+ </tr></thead>
86+ <tbody>
87+ <tr>
88+ <td>self(aclTensor*)</td>
89+ <td>输入</td>
90+ <td>输入tensor。</td>
91+ <td><ul><li>数据类型需要与other满足数据类型推导规则(参见<a href="../../../../docs/zh/context/互推导关系.md" target="_blank">互推导关系</a>)。</li><li>shape需要与other满足<a href="../../../../docs/zh/context/broadcast关系.md" target="_blank">broadcast关系</a>。</li></ul></td>
92+ <td>DOUBLE、FLOAT16、FLOAT、BFLOAT16、INT64、INT32、INT8、UINT8、BOOL、INT16、COMPLEX64、COMPLEX128、UINT32、UINT64</td>
93+ <td>ND</td>
94+ <td>不高于8维</td>
95+ <td>√</td>
96+ </tr>
97+ <tr>
98+ <td>other(aclTensor*)</td>
99+ <td>输入</td>
100+ <td>输入tensor。</td>
101+ <td><ul><li>数据类型需要与self满足数据类型推导规则(参见<a href="../../../../docs/zh/context/互推导关系.md" target="_blank">互推导关系</a>)。</li><li>shape需要与self的shape满足<a href="../../../../docs/zh/context/broadcast关系.md" target="_blank">broadcast关系</a>。</li></ul></td>
102+ <td>DOUBLE、FLOAT16、FLOAT、BFLOAT16、INT64、INT32、INT8、UINT8、BOOL、INT16、COMPLEX64、COMPLEX128、UINT32、UINT64</td>
103+ <td>ND</td>
104+ <td>不高于8维</td>
105+ <td>√</td>
106+ </tr>
107+ <tr>
108+ <td>out(aclTensor*)</td>
109+ <td>输出</td>
110+ <td>输出tensor。</td>
111+ <td><ul><li>数据类型需要是BOOL可转换的数据类型<a href="../../../../docs/zh/context/互转换关系.md" target="_blank">互转换关系</a>。</li><li>shape与self、other广播之后的shape(参见<a href="../../../../docs/zh/context/broadcast关系.md" target="_blank">broadcast关系</a>)一致。</li></ul></td>
112+ <td>DOUBLE、FLOAT16、FLOAT、BFLOAT16、INT64、INT32、INT8、UINT8、BOOL、INT16、COMPLEX64、COMPLEX128、UINT64、UINT32、UINT16</td>
113+ <td>ND</td>
114+ <td>不高于8维</td>
115+ <td>√</td>
116+ </tr>
117+ <tr>
118+ <td>workspaceSize(uint64_t*)</td>
119+ <td>输出</td>
120+ <td>返回需要在Device侧申请的workspace大小。</td>
121+ <td>-</td>
122+ <td>-</td>
123+ <td>-</td>
124+ <td>-</td>
125+ <td>-</td>
126+ </tr>
127+ <tr>
128+ <td>executor(aclOpExecutor**)</td>
129+ <td>输出</td>
130+ <td>返回op执行器,包含了算子计算流程。</td>
131+ <td>-</td>
132+ <td>-</td>
133+ <td>-</td>
134+ <td>-</td>
135+ <td>-</td>
136+ </tr>
137+ </tbody>
138+ </table>
139+ 
140+ - <term>Ascend 950PR/Ascend 950DT</term>
141+ - self、other不支持UINT32数据类型。
142+ - <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term><term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term>
143+ - self、other不支持UINT32、UINT64数据类型。
144+ - out不支持UINT64、UINT32、UINT16数据类型。
145+ - <term>Atlas 训练系列产品</term>
146+ - 不支持BFLOAT16数据类型。
147+ - out不支持BFLOAT16、UINT64、UINT32、UINT16数据类型。
148+ 
149+- **返回值:**
150+ 
151+ aclnnStatus: 返回状态码,具体参见[aclnn返回码](../../../../docs/zh/context/aclnn返回码.md)。
152+
153+ 第一段接口完成入参校验,出现以下场景时报错:
154+ 
155+ <table style="undefined;table-layout: fixed; width: 1150px"><colgroup>
156+ <col style="width: 281px">
157+ <col style="width: 119px">
158+ <col style="width: 750px">
159+ </colgroup>
160+ <thead>
161+ <tr>
162+ <th>返回码</th>
163+ <th>错误码</th>
164+ <th>描述</th>
165+ </tr></thead>
166+ <tbody>
167+ <tr>
168+ <td>ACLNN_ERR_PARAM_NULLPTR</td>
169+ <td>161001</td>
170+ <td>传入的self、other、out是空指针时。</td>
171+ </tr>
172+ <tr>
173+ <td rowspan="5">ACLNN_ERR_PARAM_INVALID</td>
174+ <td rowspan="5">161002</td>
175+ <td>传入的self、other、out是空指针时。</td>
176+ </tr>
177+ <tr>
178+ <td>self、other或out的维度大于8。</td>
179+ </tr>
180+ <tr>
181+ <td>self和other的数据类型无法进行推导。</td>
182+ </tr>
183+ <tr>
184+ <td>self和other的shape无法进行broadcast。</td>
185+ </tr>
186+ <tr>
187+ <td>out的shape与broadcast后的shape不一致。</td>
188+ </tr>
189+ </tbody>
190+ </table>
191+ 
192+## aclnnEqTensor
193+ 
194+- **参数说明:**
195+ 
196+ <table style="undefined;table-layout: fixed; width: 1150px"><colgroup>
197+ <col style="width: 168px">
198+ <col style="width: 128px">
199+ <col style="width: 854px">
200+ </colgroup>
201+ <thead>
202+ <tr>
203+ <th>参数名</th>
204+ <th>输入/输出</th>
205+ <th>描述</th>
206+ </tr></thead>
207+ <tbody>
208+ <tr>
209+ <td>workspace</td>
210+ <td>输入</td>
211+ <td>在Device侧申请的workspace内存地址。</td>
212+ </tr>
213+ <tr>
214+ <td>workspaceSize</td>
215+ <td>输入</td>
216+ <td>在Device侧申请的workspace大小,由第一段接口aclnnEqTensorGetWorkspaceSize获取。</td>
217+ </tr>
218+ <tr>
219+ <td>executor</td>
220+ <td>输入</td>
221+ <td>op执行器,包含了算子计算流程。</td>
222+ </tr>
223+ <tr>
224+ <td>stream</td>
225+ <td>输入</td>
226+ <td>指定执行任务的Stream。</td>
227+ </tr>
228+ </tbody>
229+ </table>
230+ 
231+- **返回值:**
232+ 
233+ aclnnStatus: 返回状态码,具体参见[aclnn返回码](../../../../docs/zh/context/aclnn返回码.md)。
234+ 
235+## aclnnInplaceEqTensorGetWorkspaceSize
236+ 
237+- **参数说明:**
238+ 
239+ <table style="undefined;table-layout: fixed; width: 1542px"><colgroup>
240+ <col style="width: 166px">
241+ <col style="width: 134px">
242+ <col style="width: 212px">
243+ <col style="width: 283px">
244+ <col style="width: 348px">
245+ <col style="width: 114px">
246+ <col style="width: 140px">
247+ <col style="width: 145px">
248+ </colgroup>
249+ <thead>
250+ <tr>
251+ <th>参数名</th>
252+ <th>输入/输出</th>
253+ <th>描述</th>
254+ <th>使用说明</th>
255+ <th>数据类型</th>
256+ <th>数据格式</th>
257+ <th>维度(shape)</th>
258+ <th>非连续Tensor</th>
259+ </tr></thead>
260+ <tbody>
261+ <tr>
262+ <td>selfRef</td>
263+ <td>输入/输出</td>
264+ <td>输入输出tensor,即公式中的self与out。</td>
265+ <td><ul><li>数据类型与other的数据类型需满足数据类型推导规则(参见<a href="../../../../docs/zh/context/互推导关系.md" target="_blank">互推导关系</a>)。</li><li>shape需要与other满足<a href="../../../../docs/zh/context/broadcast关系.md" target="_blank">broadcast关系</a>。</li></ul></td>
266+ <td>DOUBLE、FLOAT16、FLOAT、BFLOAT16、INT64、INT32、INT8、UINT8、BOOL、INT16、COMPLEX64、COMPLEX128、UINT32、UINT64</td>
267+ <td>ND</td>
268+ <td>-</td>
269+ <td>√</td>
270+ </tr>
271+ <tr>
272+ <td>other</td>
273+ <td>输入</td>
274+ <td>公式中的other。</td>
275+ <td><ul><li>数据类型与selfRef的数据类型需满足数据类型推导规则(参见<a href="../../../../docs/zh/context/互推导关系.md" target="_blank">互推导关系</a>)。</li><li>shape需要与selfRef满足<a href="../../../../docs/zh/context/broadcast关系.md" target="_blank">broadcast关系</a>。</li></ul></td>
276+ <td>DOUBLE、FLOAT16、FLOAT、BFLOAT16、INT64、INT32、INT8、UINT8、BOOL、INT16、COMPLEX64、COMPLEX128、UINT32、UINT64</td>
277+ <td>ND</td>
278+ <td>-</td>
279+ <td>√</td>
280+ </tr>
281+ <tr>
282+ <td>workspaceSize</td>
283+ <td>输出</td>
284+ <td>返回需要在Device侧申请的workspace大小。</td>
285+ <td>-</td>
286+ <td>-</td>
287+ <td>-</td>
288+ <td>-</td>
289+ <td>-</td>
290+ </tr>
291+ <tr>
292+ <td>executor</td>
293+ <td>输出</td>
294+ <td>返回op执行器,包含了算子计算流程。</td>
295+ <td>-</td>
296+ <td>-</td>
297+ <td>-</td>
298+ <td>-</td>
299+ <td>-</td>
300+ </tr>
301+ </tbody>
302+ </table>
303+ 
304+ - <term>Ascend 950PR/Ascend 950DT</term>
305+ - selfRef、other不支持UINT32数据类型。
306+ - <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term><term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term>
307+ - selfRef、other不支持UINT32、UINT64数据类型。
308+ - <term>Atlas 训练系列产品</term>
309+ - 不支持BFLOAT16数据类型。
310+ 
311+- **返回值:**
312+ 
313+ aclnnStatus: 返回状态码,具体参见[aclnn返回码](../../../../docs/zh/context/aclnn返回码.md)。
314+ 
315+ 第一段接口完成入参校验,出现以下场景时报错:
316+ 
317+ <table style="undefined;table-layout: fixed; width: 1150px"><colgroup>
318+ <col style="width: 281px">
319+ <col style="width: 119px">
320+ <col style="width: 750px">
321+ </colgroup>
322+ <thead>
323+ <tr>
324+ <th>返回码</th>
325+ <th>错误码</th>
326+ <th>描述</th>
327+ </tr></thead>
328+ <tbody>
329+ <tr>
330+ <td>ACLNN_ERR_PARAM_NULLPTR</td>
331+ <td>161001</td>
332+ <td>传入的selfRef和other是空指针时。</td>
333+ </tr>
334+ <tr>
335+ <td rowspan="6">ACLNN_ERR_PARAM_INVALID</td>
336+ <td rowspan="6">161002</td>
337+ <td>selfRef和other的数据类型不在支持的范围之内。</td>
338+ </tr>
339+ <tr>
340+ <td>selfRef和other的数据类型无法进行推导。</td>
341+ </tr>
342+ <tr>
343+ <td>selfRef和other的shape无法做broadcast。</td>
344+ </tr>
345+ <tr>
346+ <td>selfRef和other做broadcast后的shape不等于selfRef的shape。</td>
347+ </tr>
348+ <tr>
349+ <td>selfRef、other的维度大于8。</td>
350+ </tr>
351+ <tr>
352+ <td>selfRef和other有且仅有一个为空Tensor。</td>
353+ </tr>
354+ </tbody>
355+ </table>
356+ 
357+## aclnnInplaceEqTensor
358+ 
359+- **参数说明:**
360+ 
361+ <table style="undefined;table-layout: fixed; width: 1150px"><colgroup>
362+ <col style="width: 168px">
363+ <col style="width: 128px">
364+ <col style="width: 854px">
365+ </colgroup>
366+ <thead>
367+ <tr>
368+ <th>参数名</th>
369+ <th>输入/输出</th>
370+ <th>描述</th>
371+ </tr></thead>
372+ <tbody>
373+ <tr>
374+ <td>workspace</td>
375+ <td>输入</td>
376+ <td>在Device侧申请的workspace内存地址。</td>
377+ </tr>
378+ <tr>
379+ <td>workspaceSize</td>
380+ <td>输入</td>
381+ <td>在Device侧申请的workspace大小,由第一段接口aclnnInplaceEqTensorGetWorkspaceSize获取。</td>
382+ </tr>
383+ <tr>
384+ <td>executor</td>
385+ <td>输入</td>
386+ <td>op执行器,包含了算子计算流程。</td>
387+ </tr>
388+ <tr>
389+ <td>stream</td>
390+ <td>输入</td>
391+ <td>指定执行任务的Stream。</td>
392+ </tr>
393+ </tbody>
394+ </table>
395+ 
396+- **返回值:**
397+ 
398+ aclnnStatus: 返回状态码,具体参见[aclnn返回码](../../../../docs/zh/context/aclnn返回码.md)。
399+ 
400+## 约束说明
401+ 
402+- 确定性计算:
403+ - aclnnEqTensor&aclnnInplaceEqTensor默认确定性实现。
404+ 
405+## 调用示例
406+ 
407+示例代码如下,仅供参考,具体编译和执行过程请参考[编译与运行样例](../../../../docs/zh/context/编译与运行样例.md)。
408+ 
409+**aclnnEqTensor示例代码:**
410+ 
411+```Cpp
412+#include <iostream>
413+#include <vector>
414+#include "acl/acl.h"
415+#include "aclnnop/aclnn_eq_tensor.h"
416+ 
417+#define CHECK_RET(cond, return_expr) \
418+ do { \
419+ if (!(cond)) { \
420+ return_expr; \
421+ } \
422+ } while (0)
423+ 
424+#define LOG_PRINT(message, ...) \
425+ do { \
426+ printf(message, ##__VA_ARGS__); \
427+ } while (0)
428+ 
429+int64_t GetShapeSize(const std::vector<int64_t>& shape)
430+{
431+ int64_t shape_size = 1;
432+ for (auto i : shape) {
433+ shape_size *= i;
434+ }
435+ return shape_size;
436+}
437+ 
438+int Init(int32_t deviceId, aclrtStream* stream)
439+{
440+ // 固定写法,资源初始化
441+ auto ret = aclInit(nullptr);
442+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
443+ ret = aclrtSetDevice(deviceId);
444+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
445+ ret = aclrtCreateStream(stream);
446+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
447+ return 0;
448+}
449+ 
450+template <typename T>
451+int CreateAclTensor(
452+ const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType,
453+ aclTensor** tensor)
454+{
455+ auto size = GetShapeSize(shape) * sizeof(T);
456+ // 调用aclrtMalloc申请device侧内存
457+ auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
458+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
459+ 
460+ // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
461+ ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
462+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
463+ 
464+ // 计算连续tensor的strides
465+ std::vector<int64_t> strides(shape.size(), 1);
466+ for (int64_t i = shape.size() - 2; i >= 0; i--) {
467+ strides[i] = shape[i + 1] * strides[i + 1];
468+ }
469+ 
470+ // 调用aclCreateTensor接口创建aclTensor
471+ *tensor = aclCreateTensor(
472+ shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(),
473+ *deviceAddr);
474+ return 0;
475+}
476+ 
477+aclError InitAcl(int32_t deviceId, aclrtStream* stream)
478+{
479+ auto ret = Init(deviceId, stream);
480+ CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
481+ return ACL_SUCCESS;
482+}
483+ 
484+aclError CreateInputs(
485+ std::vector<int64_t>& selfShape, std::vector<int64_t>& otherShape, std::vector<int64_t>& outShape,
486+ void** selfDeviceAddr, void** otherDeviceAddr, void** outDeviceAddr, aclTensor** self, aclTensor** other,
487+ aclTensor** out)
488+{
489+ std::vector<double> selfHostData = {0, 1, 2, 3, 4, 5, 6, 7};
490+ std::vector<double> otherHostData = {0, 1, 9, 9, 9, 9, 9, 9};
491+ std::vector<char> outHostData = {0, 0, 0, 0, 0, 0, 0, 0};
492+ 
493+ auto ret = CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_DOUBLE, self);
494+ CHECK_RET(ret == ACL_SUCCESS, return ret);
495+ 
496+ ret = CreateAclTensor(otherHostData, otherShape, otherDeviceAddr, aclDataType::ACL_DOUBLE, other);
497+ CHECK_RET(ret == ACL_SUCCESS, return ret);
498+ 
499+ ret = CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_BOOL, out);
500+ CHECK_RET(ret == ACL_SUCCESS, return ret);
501+ 
502+ return ACL_SUCCESS;
503+}
504+ 
505+aclError ExecOpApi(
506+ aclTensor* self, aclTensor* other, aclTensor* out, void** workspaceAddrOut, uint64_t& workspaceSize,
507+ void* outDeviceAddr, std::vector<int64_t>& outShape, aclrtStream stream)
508+{
509+ aclOpExecutor* executor;
510+ 
511+ auto ret = aclnnEqTensorGetWorkspaceSize(self, other, out, &workspaceSize, &executor);
512+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnEqTensorGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
513+ 
514+ void* workspaceAddr = nullptr;
515+ if (workspaceSize > 0) {
516+ ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
517+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
518+ }
519+ *workspaceAddrOut = workspaceAddr;
520+ 
521+ ret = aclnnEqTensor(workspaceAddr, workspaceSize, executor, stream);
522+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnEqTensor failed. ERROR: %d\n", ret); return ret);
523+ 
524+ ret = aclrtSynchronizeStream(stream);
525+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
526+ 
527+ auto size = GetShapeSize(outShape);
528+ std::vector<char> resultData(size, 0);
529+ 
530+ ret = aclrtMemcpy(
531+ resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(char),
532+ ACL_MEMCPY_DEVICE_TO_HOST);
533+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
534+ 
535+ for (int64_t i = 0; i < size; i++) {
536+ LOG_PRINT("result[%ld] is: %d\n", i, resultData[i]);
537+ }
538+ 
539+ return ACL_SUCCESS;
540+}
541+ 
542+int main()
543+{
544+ int32_t deviceId = 0;
545+ aclrtStream stream;
546+ 
547+ auto ret = InitAcl(deviceId, &stream);
548+ CHECK_RET(ret == ACL_SUCCESS, return ret);
549+ 
550+ std::vector<int64_t> selfShape = {4, 2};
551+ std::vector<int64_t> otherShape = {4, 2};
552+ std::vector<int64_t> outShape = {4, 2};
553+ 
554+ void* selfDeviceAddr = nullptr;
555+ void* otherDeviceAddr = nullptr;
556+ void* outDeviceAddr = nullptr;
557+ 
558+ aclTensor* self = nullptr;
559+ aclTensor* other = nullptr;
560+ aclTensor* out = nullptr;
561+ 
562+ ret = CreateInputs(
563+ selfShape, otherShape, outShape, &selfDeviceAddr, &otherDeviceAddr, &outDeviceAddr, &self, &other, &out);
564+ CHECK_RET(ret == ACL_SUCCESS, return ret);
565+ 
566+ uint64_t workspaceSize = 0;
567+ void* workspaceAddr = nullptr;
568+ 
569+ ret = ExecOpApi(self, other, out, &workspaceAddr, workspaceSize, outDeviceAddr, outShape, stream);
570+ CHECK_RET(ret == ACL_SUCCESS, return ret);
571+ 
572+ // 释放资源
573+ aclDestroyTensor(self);
574+ aclDestroyTensor(other);
575+ aclDestroyTensor(out);
576+ 
577+ aclrtFree(selfDeviceAddr);
578+ aclrtFree(otherDeviceAddr);
579+ aclrtFree(outDeviceAddr);
580+ 
581+ if (workspaceSize > 0) {
582+ aclrtFree(workspaceAddr);
583+ }
584+ 
585+ aclrtDestroyStream(stream);
586+ aclrtResetDevice(deviceId);
587+ aclFinalize();
588+ return 0;
589+}
590+```
591+ 
592+**aclnnInplaceEqTensor示例代码:**
593+ 
594+```Cpp
595+#include <iostream>
596+#include <vector>
597+#include "acl/acl.h"
598+#include "aclnnop/aclnn_eq_tensor.h"
599+ 
600+#define CHECK_RET(cond, return_expr) \
601+ do { \
602+ if (!(cond)) { \
603+ return_expr; \
604+ } \
605+ } while (0)
606+ 
607+#define LOG_PRINT(message, ...) \
608+ do { \
609+ printf(message, ##__VA_ARGS__); \
610+ } while (0)
611+ 
612+int64_t GetShapeSize(const std::vector<int64_t>& shape)
613+{
614+ int64_t shapeSize = 1;
615+ for (auto i : shape) {
616+ shapeSize *= i;
617+ }
618+ return shapeSize;
619+}
620+ 
621+int Init(int32_t deviceId, aclrtStream* stream)
622+{
623+ // 固定写法,资源初始化
624+ auto ret = aclInit(nullptr);
625+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
626+ ret = aclrtSetDevice(deviceId);
627+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
628+ ret = aclrtCreateStream(stream);
629+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
630+ return 0;
631+}
632+ 
633+template <typename T>
634+int CreateAclTensor(
635+ const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType,
636+ aclTensor** tensor)
637+{
638+ auto size = GetShapeSize(shape) * sizeof(T);
639+ // 调用aclrtMalloc申请device侧内存
640+ auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
641+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
642+ // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
643+ ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
644+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
645+ 
646+ // 计算连续tensor的strides
647+ std::vector<int64_t> strides(shape.size(), 1);
648+ for (int64_t i = shape.size() - 2; i >= 0; i--) {
649+ strides[i] = shape[i + 1] * strides[i + 1];
650+ }
651+ 
652+ // 调用aclCreateTensor接口创建aclTensor
653+ *tensor = aclCreateTensor(
654+ shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(),
655+ *deviceAddr);
656+ return 0;
657+}
658+ 
659+aclError InitAcl(int32_t deviceId, aclrtStream* stream)
660+{
661+ auto ret = Init(deviceId, stream);
662+ CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
663+ return ACL_SUCCESS;
664+}
665+ 
666+aclError CreateInputs(
667+ std::vector<int64_t>& selfShape, std::vector<int64_t>& otherShape, void** selfDeviceAddr, void** otherDeviceAddr,
668+ aclTensor** self, aclTensor** other)
669+{
670+ std::vector<double> selfHostData = {0, 1, 2, 3, 4, 5, 6, 7};
671+ std::vector<double> otherHostData = {1, 1, 3, 3, 5, 5, 7, 7};
672+ 
673+ // 创建 self
674+ auto ret = CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_DOUBLE, self);
675+ CHECK_RET(ret == ACL_SUCCESS, return ret);
676+ 
677+ // 创建 other
678+ ret = CreateAclTensor(otherHostData, otherShape, otherDeviceAddr, aclDataType::ACL_DOUBLE, other);
679+ CHECK_RET(ret == ACL_SUCCESS, return ret);
680+ 
681+ return ACL_SUCCESS;
682+}
683+ 
684+aclError ExecOpApi(
685+ aclTensor* self, aclTensor* other, void** workspaceAddrOut, uint64_t& workspaceSize, void* selfDeviceAddr,
686+ std::vector<int64_t>& selfShape, aclrtStream stream)
687+{
688+ aclOpExecutor* executor;
689+ 
690+ // 获取 workspace 大小
691+ auto ret = aclnnInplaceEqTensorGetWorkspaceSize(self, other, &workspaceSize, &executor);
692+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceEqTensorGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
693+ 
694+ // 分配 workspace
695+ void* workspaceAddr = nullptr;
696+ if (workspaceSize > 0) {
697+ ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
698+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
699+ }
700+ *workspaceAddrOut = workspaceAddr;
701+ 
702+ // 执行算子
703+ ret = aclnnInplaceEqTensor(workspaceAddr, workspaceSize, executor, stream);
704+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceEqTensor failed. ERROR: %d\n", ret); return ret);
705+ 
706+ // 同步
707+ ret = aclrtSynchronizeStream(stream);
708+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
709+ 
710+ // 拷贝输出
711+ auto size = GetShapeSize(selfShape);
712+ std::vector<double> resultData(size);
713+ 
714+ ret = aclrtMemcpy(
715+ resultData.data(), resultData.size() * sizeof(resultData[0]), selfDeviceAddr, size * sizeof(double),
716+ ACL_MEMCPY_DEVICE_TO_HOST);
717+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
718+ 
719+ for (int64_t i = 0; i < size; i++) {
720+ LOG_PRINT("result[%ld] is: %lf\n", i, resultData[i]);
721+ }
722+ 
723+ return ACL_SUCCESS;
724+}
725+ 
726+int main()
727+{
728+ int32_t deviceId = 0;
729+ aclrtStream stream;
730+ 
731+ auto ret = InitAcl(deviceId, &stream);
732+ CHECK_RET(ret == ACL_SUCCESS, return ret);
733+ 
734+ std::vector<int64_t> selfShape = {4, 2};
735+ std::vector<int64_t> otherShape = {4, 2};
736+ void* selfDeviceAddr = nullptr;
737+ void* otherDeviceAddr = nullptr;
738+ aclTensor* self = nullptr;
739+ aclTensor* other = nullptr;
740+ 
741+ ret = CreateInputs(selfShape, otherShape, &selfDeviceAddr, &otherDeviceAddr, &self, &other);
742+ CHECK_RET(ret == ACL_SUCCESS, return ret);
743+ 
744+ uint64_t workspaceSize = 0;
745+ void* workspaceAddr = nullptr;
746+ 
747+ ret = ExecOpApi(self, other, &workspaceAddr, workspaceSize, selfDeviceAddr, selfShape, stream);
748+ CHECK_RET(ret == ACL_SUCCESS, return ret);
749+ 
750+ // 销毁
751+ aclDestroyTensor(self);
752+ aclDestroyTensor(other);
753+ 
754+ // 释放
755+ aclrtFree(selfDeviceAddr);
756+ aclrtFree(otherDeviceAddr);
757+ if (workspaceSize > 0) {
758+ aclrtFree(workspaceAddr);
759+ }
760+ 
761+ aclrtDestroyStream(stream);
762+ aclrtResetDevice(deviceId);
763+ aclFinalize();
764+ return 0;
765+}
766+```
@@ -0,0 +1,408 @@
1+# aclnnIsInScalarTensor
2+ 
3+ 
4+## 产品支持情况
5+ 
6+| 产品 | 是否支持 |
7+| :----------------------------------------------------------- | :------: |
8+| <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term> | √ |
9+ 
10+## 功能说明
11+ 
12+检查element中的元素是否等于testElement。
13+ 
14+## 函数原型
15+ 
16+每个算子分为[两段式接口](../../../../docs/zh/context/两段式接口.md),必须先调用“aclnnIsInScalarTensorGetWorkspaceSize”接口获取计算所需workspace大小以及包含了算子计算流程的执行器,再调用“aclnnIsInScalarTensor”接口执行计算。
17+ 
18+```Cpp
19+aclnnStatus aclnnIsInScalarTensorGetWorkspaceSize(
20+ const aclTensor* self,
21+ const aclScalar* element,
22+ bool assumeUnique,
23+ bool invert,
24+ aclTensor* out,
25+ uint64_t* workspaceSize,
26+ aclOpExecutor** executor)
27+```
28+ 
29+```Cpp
30+aclnnStatus aclnnIsInScalarTensor(
31+ void* workspace,
32+ uint64_t workspaceSize,
33+ aclOpExecutor* executor,
34+ aclrtStream stream)
35+```
36+ 
37+## aclnnIsInScalarTensorGetWorkspaceSize
38+ 
39+- **参数说明:**
40+ 
41+ <table style="undefined;table-layout: fixed; width: 1555px"><colgroup>
42+ <col style="width: 217px">
43+ <col style="width: 125px">
44+ <col style="width: 247px">
45+ <col style="width: 317px">
46+ <col style="width: 233px">
47+ <col style="width: 126px">
48+ <col style="width: 144px">
49+ <col style="width: 146px">
50+ </colgroup>
51+ <thead>
52+ <tr>
53+ <th>参数名</th>
54+ <th>输入/输出</th>
55+ <th>描述</th>
56+ <th>使用说明</th>
57+ <th>数据类型</th>
58+ <th>数据格式</th>
59+ <th>维度(shape)</th>
60+ <th>非连续Tensor</th>
61+ </tr></thead>
62+ <tbody>
63+ <tr>
64+ <td>self(aclTensor*)</td>
65+ <td>输入</td>
66+ <td>输入张量,公式中的self。</td>
67+ <td>-</td>
68+ <td>FLOAT、FLOAT16、DOUBLE、BFLOAT16、INT8、INT16、INT32、INT64、UINT8、UINT16、BOOL、COMPLEX64、COMPLEX128</td>
69+ <td>ND</td>
70+ <td>0-8</td>
71+ <td>√</td>
72+ </tr>
73+ <tr>
74+ <td>element(aclScalar*)</td>
75+ <td>输入</td>
76+ <td>输入标量,公式中的element。</td>
77+ <td>数据类型需要与self的数据类型满足<a href="../../../../docs/zh/context/互推导关系.md" target="_blank">数据类型推导规则</a>。</td>
78+ <td>FLOAT、FLOAT16、DOUBLE、BFLOAT16、INT8、INT16、INT32、INT64、UINT8、UINT16、BOOL、COMPLEX64、COMPLEX128</td>
79+ <td>-</td>
80+ <td>-</td>
81+ <td>-</td>
82+ </tr>
83+ <tr>
84+ <td>assumeUnique(bool)</td>
85+ <td>输入</td>
86+ <td>是否假设element唯一,公式中的assumeUnique。</td>
87+ <td>-</td>
88+ <td>-</td>
89+ <td>-</td>
90+ <td>-</td>
91+ <td>-</td>
92+ </tr>
93+ <tr>
94+ <td>invert(bool)</td>
95+ <td>输入</td>
96+ <td>是否取反,公式中的invert。</td>
97+ <td>-</td>
98+ <td>-</td>
99+ <td>-</td>
100+ <td>-</td>
101+ <td>-</td>
102+ </tr>
103+ <tr>
104+ <td>out(aclTensor*)</td>
105+ <td>输出</td>
106+ <td>输出张量,公式中的out。</td>
107+ <td>数据类型为BOOL。shape与self相同。</td>
108+ <td>BOOL</td>
109+ <td>ND</td>
110+ <td>0-8</td>
111+ <td>√</td>
112+ </tr>
113+ <tr>
114+ <td>workspaceSize(uint64_t*)</td>
115+ <td>输出</td>
116+ <td>返回需要在Device侧申请的workspace大小。</td>
117+ <td>-</td>
118+ <td>-</td>
119+ <td>-</td>
120+ <td>-</td>
121+ <td>-</td>
122+ </tr>
123+ <tr>
124+ <td>executor(aclOpExecutor**)</td>
125+ <td>输出</td>
126+ <td>返回op执行器,包含了算子计算流程。</td>
127+ <td>-</td>
128+ <td>-</td>
129+ <td>-</td>
130+ <td>-</td>
131+ <td>-</td>
132+ </tr>
133+ </tbody></table>
134+ 
135+ - <term>Atlas 训练系列产品</term><term>Atlas 推理系列产品</term>:不支持BFLOAT16、COMPLEX64、COMPLEX128。
136+ 
137+- **返回值:**
138+ 
139+ aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../../docs/zh/context/aclnn返回码.md)。
140+ 
141+ 第一段接口完成入参校验,出现以下场景时报错:
142+ 
143+ <table style="undefined;table-layout: fixed; width: 1150px"><colgroup>
144+ <col style="width: 300px">
145+ <col style="width: 134px">
146+ <col style="width: 716px">
147+ </colgroup>
148+ <thead>
149+ <tr>
150+ <th>返回码</th>
151+ <th>错误码</th>
152+ <th>描述</th>
153+ </tr></thead>
154+ <tbody>
155+ <tr>
156+ <td>ACLNN_ERR_PARAM_NULLPTR</td>
157+ <td>161001</td>
158+ <td>传入的self、element或out是空指针。</td>
159+ </tr>
160+ <tr>
161+ <td rowspan="3">ACLNN_ERR_PARAM_INVALID</td>
162+ <td rowspan="3">161002</td>
163+ <td>self或out的数据类型不在支持范围之内。</td>
164+ </tr>
165+ <tr>
166+ <td>self和out的维度超过8维。</td>
167+ </tr>
168+ <tr>
169+ <td>self和out的shape不一致。</td>
170+ </tr>
171+ </tbody></table>
172+ 
173+## aclnnIsInScalarTensor
174+ 
175+- **参数说明:**
176+ 
177+ <table style="undefined;table-layout: fixed; width: 1151px"><colgroup>
178+ <col style="width: 184px">
179+ <col style="width: 134px">
180+ <col style="width: 833px">
181+ </colgroup>
182+ <thead>
183+ <tr>
184+ <th>参数名</th>
185+ <th>输入/输出</th>
186+ <th>描述</th>
187+ </tr></thead>
188+ <tbody>
189+ <tr>
190+ <td>workspace</td>
191+ <td>输入</td>
192+ <td>在Device侧申请的workspace内存地址。</td>
193+ </tr>
194+ <tr>
195+ <td>workspaceSize</td>
196+ <td>输入</td>
197+ <td>在Device侧申请的workspace大小,由第一段接口aclnnIsInScalarTensorGetWorkspaceSize获取。</td>
198+ </tr>
199+ <tr>
200+ <td>executor</td>
201+ <td>输入</td>
202+ <td>op执行器,包含了算子计算流程。</td>
203+ </tr>
204+ <tr>
205+ <td>stream</td>
206+ <td>输入</td>
207+ <td>指定执行任务的Stream。</td>
208+ </tr>
209+ </tbody></table>
210+ 
211+- **返回值:**
212+ 
213+ aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../../docs/zh/context/aclnn返回码.md)。
214+ 
215+## 约束说明
216+ 
217+- 确定性计算:
218+ - aclnnIsInScalarTensor默认确定性实现。
219+ 
220+## 调用示例
221+ 
222+示例代码如下,仅供参考,具体编译和执行过程请参考[编译与运行样例](../../../../docs/zh/context/编译与运行样例.md)。
223+ 
224+```Cpp
225+#include <iostream>
226+#include <vector>
227+#include "acl/acl.h"
228+#include "aclnnop/aclnn_isin.h"
229+ 
230+#define CHECK_RET(cond, return_expr) \
231+ do { \
232+ if (!(cond)) { \
233+ return_expr; \
234+ } \
235+ } while (0)
236+ 
237+#define LOG_PRINT(message, ...) \
238+ do { \
239+ printf(message, ##__VA_ARGS__); \
240+ } while (0)
241+ 
242+int64_t GetShapeSize(const std::vector<int64_t>& shape)
243+{
244+ int64_t shape_size = 1;
245+ for (auto i : shape) {
246+ shape_size *= i;
247+ }
248+ return shape_size;
249+}
250+ 
251+int Init(int32_t deviceId, aclrtStream* stream)
252+{
253+ // 固定写法,资源初始化
254+ auto ret = aclInit(nullptr);
255+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
256+ ret = aclrtSetDevice(deviceId);
257+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
258+ ret = aclrtCreateStream(stream);
259+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
260+ return 0;
261+}
262+ 
263+template <typename T>
264+int CreateAclTensor(
265+ const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType,
266+ aclTensor** tensor)
267+{
268+ auto size = GetShapeSize(shape) * sizeof(T);
269+ // 调用aclrtMalloc申请device侧内存
270+ auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
271+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
272+ 
273+ // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
274+ ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
275+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
276+ 
277+ // 计算连续tensor的strides
278+ std::vector<int64_t> strides(shape.size(), 1);
279+ for (int64_t i = shape.size() - 2; i >= 0; i--) {
280+ strides[i] = shape[i + 1] * strides[i + 1];
281+ }
282+ 
283+ // 调用aclCreateTensor接口创建aclTensor
284+ *tensor = aclCreateTensor(
285+ shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(),
286+ *deviceAddr);
287+ return 0;
288+}
289+ 
290+aclError InitAcl(int32_t deviceId, aclrtStream* stream)
291+{
292+ auto ret = Init(deviceId, stream);
293+ CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
294+ return ACL_SUCCESS;
295+}
296+ 
297+aclError CreateInputs(
298+ std::vector<int64_t>& testElementsShape, std::vector<int64_t>& outShape, void** testElementsDeviceAddr,
299+ void** outDeviceAddr, aclTensor** testElements, aclTensor** out, aclScalar** element, bool& assumeUnique,
300+ bool& invert)
301+{
302+ std::vector<double> testElementsHostData = {1.0, 2.0, 3.0};
303+ std::vector<char> outHostData = {0};
304+ double elementValue = 4.0;
305+ 
306+ // 创建 testElements Tensor
307+ auto ret = CreateAclTensor(
308+ testElementsHostData, testElementsShape, testElementsDeviceAddr, aclDataType::ACL_DOUBLE, testElements);
309+ CHECK_RET(ret == ACL_SUCCESS, return ret);
310+ 
311+ // 创建 element Scalar
312+ *element = aclCreateScalar(&elementValue, aclDataType::ACL_DOUBLE);
313+ CHECK_RET(*element != nullptr, return ACL_ERROR_INVALID_PARAM);
314+ 
315+ // 创建 out Tensor
316+ ret = CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_BOOL, out);
317+ CHECK_RET(ret == ACL_SUCCESS, return ret);
318+ 
319+ return ACL_SUCCESS;
320+}
321+ 
322+aclError ExecOpApi(
323+ aclScalar* element, aclTensor* testElements, bool assumeUnique, bool invert, aclTensor* out,
324+ void** workspaceAddrOut, uint64_t& workspaceSize, void* outDeviceAddr, aclrtStream stream)
325+{
326+ aclOpExecutor* executor;
327+ 
328+ // 第一段接口
329+ auto ret = aclnnIsInScalarTensorGetWorkspaceSize(
330+ element, testElements, assumeUnique, invert, out, &workspaceSize, &executor);
331+ CHECK_RET(
332+ ret == ACL_SUCCESS, LOG_PRINT("aclnnIsInScalarTensorGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
333+ 
334+ // 分配 workspace
335+ void* workspaceAddr = nullptr;
336+ if (workspaceSize > 0) {
337+ ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
338+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
339+ }
340+ *workspaceAddrOut = workspaceAddr;
341+ 
342+ // 第二段接口
343+ ret = aclnnIsInScalarTensor(workspaceAddr, workspaceSize, executor, stream);
344+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnIsInScalarTensor failed. ERROR: %d\n", ret); return ret);
345+ 
346+ // 同步
347+ ret = aclrtSynchronizeStream(stream);
348+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
349+ 
350+ // 输出拷贝
351+ char resultData = 0;
352+ ret = aclrtMemcpy(&resultData, sizeof(char), outDeviceAddr, sizeof(char), ACL_MEMCPY_DEVICE_TO_HOST);
353+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
354+ 
355+ LOG_PRINT("result is: %d\n", static_cast<bool>(resultData));
356+ 
357+ return ACL_SUCCESS;
358+}
359+ 
360+int main()
361+{
362+ int32_t deviceId = 0;
363+ aclrtStream stream;
364+ 
365+ auto ret = InitAcl(deviceId, &stream);
366+ CHECK_RET(ret == ACL_SUCCESS, return ret);
367+ 
368+ std::vector<int64_t> testElementsShape = {3};
369+ std::vector<int64_t> outShape = {};
370+ void* testElementsDeviceAddr = nullptr;
371+ void* outDeviceAddr = nullptr;
372+ aclTensor* testElements = nullptr;
373+ aclTensor* out = nullptr;
374+ aclScalar* element = nullptr;
375+ 
376+ bool assumeUnique = false;
377+ bool invert = true;
378+ 
379+ ret = CreateInputs(
380+ testElementsShape, outShape, &testElementsDeviceAddr, &outDeviceAddr, &testElements, &out, &element, assumeUnique,
381+ invert);
382+ CHECK_RET(ret == ACL_SUCCESS, return ret);
383+ 
384+ uint64_t workspaceSize = 0;
385+ void* workspaceAddr = nullptr;
386+ 
387+ ret =
388+ ExecOpApi(element, testElements, assumeUnique, invert, out, &workspaceAddr, workspaceSize, outDeviceAddr, stream);
389+ CHECK_RET(ret == ACL_SUCCESS, return ret);
390+ 
391+ // 释放
392+ aclDestroyScalar(element);
393+ aclDestroyTensor(testElements);
394+ aclDestroyTensor(out);
395+ 
396+ aclrtFree(testElementsDeviceAddr);
397+ aclrtFree(outDeviceAddr);
398+ 
399+ if (workspaceSize > 0) {
400+ aclrtFree(workspaceAddr);
401+ }
402+ 
403+ aclrtDestroyStream(stream);
404+ aclrtResetDevice(deviceId);
405+ aclFinalize();
406+ return 0;
407+}
408+```
@@ -0,0 +1,407 @@
1+# aclnnIsInTensorScalar
2+ 
3+ 
4+## 产品支持情况
5+ 
6+| 产品 | 是否支持 |
7+| :----------------------------------------------------------- | :------: |
8+| <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term> | √ |
9+ 
10+## 功能说明
11+ 
12+检查element中的元素是否等于testElement。
13+ 
14+## 函数原型
15+ 
16+每个算子分为[两段式接口](../../../../docs/zh/context/两段式接口.md),必须先调用“aclnnIsInTensorScalarGetWorkspaceSize”接口获取计算所需workspace大小以及包含了算子计算流程的执行器,再调用“aclnnIsInTensorScalar”接口执行计算。
17+ 
18+```Cpp
19+aclnnStatus aclnnIsInTensorScalarGetWorkspaceSize(
20+ const aclTensor* self,
21+ const aclScalar* element,
22+ bool assumeUnique,
23+ bool invert,
24+ aclScalar* out,
25+ uint64_t* workspaceSize,
26+ aclOpExecutor** executor)
27+```
28+ 
29+```Cpp
30+aclnnStatus aclnnIsInTensorScalar(
31+ void* workspace,
32+ uint64_t workspaceSize,
33+ aclOpExecutor* executor,
34+ aclrtStream stream)
35+```
36+ 
37+## aclnnIsInTensorScalarGetWorkspaceSize
38+ 
39+- **参数说明:**
40+ 
41+ <table style="undefined;table-layout: fixed; width: 1555px"><colgroup>
42+ <col style="width: 217px">
43+ <col style="width: 125px">
44+ <col style="width: 247px">
45+ <col style="width: 317px">
46+ <col style="width: 233px">
47+ <col style="width: 126px">
48+ <col style="width: 144px">
49+ <col style="width: 146px">
50+ </colgroup>
51+ <thead>
52+ <tr>
53+ <th>参数名</th>
54+ <th>输入/输出</th>
55+ <th>描述</th>
56+ <th>使用说明</th>
57+ <th>数据类型</th>
58+ <th>数据格式</th>
59+ <th>维度(shape)</th>
60+ <th>非连续Tensor</th>
61+ </tr></thead>
62+ <tbody>
63+ <tr>
64+ <td>self(aclTensor*)</td>
65+ <td>输入</td>
66+ <td>输入张量,公式中的self。</td>
67+ <td>-</td>
68+ <td>FLOAT、FLOAT16、DOUBLE、BFLOAT16、INT8、INT16、INT32、INT64、UINT8、UINT16、BOOL、COMPLEX64、COMPLEX128</td>
69+ <td>ND</td>
70+ <td>0-8</td>
71+ <td>√</td>
72+ </tr>
73+ <tr>
74+ <td>element(aclScalar*)</td>
75+ <td>输入</td>
76+ <td>输入标量,公式中的element。</td>
77+ <td>数据类型需要与self的数据类型满足<a href="../../../../docs/zh/context/互推导关系.md" target="_blank">数据类型推导规则</a>。</td>
78+ <td>FLOAT、FLOAT16、DOUBLE、BFLOAT16、INT8、INT16、INT32、INT64、UINT8、UINT16、BOOL、COMPLEX64、COMPLEX128</td>
79+ <td>-</td>
80+ <td>-</td>
81+ <td>-</td>
82+ </tr>
83+ <tr>
84+ <td>assumeUnique(bool)</td>
85+ <td>输入</td>
86+ <td>是否假设element唯一,公式中的assumeUnique。</td>
87+ <td>-</td>
88+ <td>-</td>
89+ <td>-</td>
90+ <td>-</td>
91+ <td>-</td>
92+ </tr>
93+ <tr>
94+ <td>invert(bool)</td>
95+ <td>输入</td>
96+ <td>是否取反,公式中的invert。</td>
97+ <td>-</td>
98+ <td>-</td>
99+ <td>-</td>
100+ <td>-</td>
101+ <td>-</td>
102+ </tr>
103+ <tr>
104+ <td>out(aclScalar*)</td>
105+ <td>输出</td>
106+ <td>输出标量,公式中的out。</td>
107+ <td>数据类型为BOOL。</td>
108+ <td>BOOL</td>
109+ <td>-</td>
110+ <td>-</td>
111+ <td>-</td>
112+ </tr>
113+ <tr>
114+ <td>workspaceSize(uint64_t*)</td>
115+ <td>输出</td>
116+ <td>返回需要在Device侧申请的workspace大小。</td>
117+ <td>-</td>
118+ <td>-</td>
119+ <td>-</td>
120+ <td>-</td>
121+ <td>-</td>
122+ </tr>
123+ <tr>
124+ <td>executor(aclOpExecutor**)</td>
125+ <td>输出</td>
126+ <td>返回op执行器,包含了算子计算流程。</td>
127+ <td>-</td>
128+ <td>-</td>
129+ <td>-</td>
130+ <td>-</td>
131+ <td>-</td>
132+ </tr>
133+ </tbody></table>
134+ 
135+ - <term>Atlas 训练系列产品</term><term>Atlas 推理系列产品</term>:不支持BFLOAT16、COMPLEX64、COMPLEX128。
136+ 
137+- **返回值:**
138+ 
139+ aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../../docs/zh/context/aclnn返回码.md)。
140+ 
141+ 第一段接口完成入参校验,出现以下场景时报错:
142+ 
143+ <table style="undefined;table-layout: fixed; width: 1150px"><colgroup>
144+ <col style="width: 300px">
145+ <col style="width: 134px">
146+ <col style="width: 716px">
147+ </colgroup>
148+ <thead>
149+ <tr>
150+ <th>返回码</th>
151+ <th>错误码</th>
152+ <th>描述</th>
153+ </tr></thead>
154+ <tbody>
155+ <tr>
156+ <td>ACLNN_ERR_PARAM_NULLPTR</td>
157+ <td>161001</td>
158+ <td>传入的self、element或out是空指针。</td>
159+ </tr>
160+ <tr>
161+ <td rowspan="2">ACLNN_ERR_PARAM_INVALID</td>
162+ <td rowspan="2">161002</td>
163+ <td>self的数据类型不在支持范围之内。</td>
164+ </tr>
165+ <tr>
166+ <td>self的维度超过8维。</td>
167+ </tr>
168+ </tbody></table>
169+ 
170+## aclnnIsInTensorScalar
171+ 
172+- **参数说明:**
173+ 
174+ <table style="undefined;table-layout: fixed; width: 1151px"><colgroup>
175+ <col style="width: 184px">
176+ <col style="width: 134px">
177+ <col style="width: 833px">
178+ </colgroup>
179+ <thead>
180+ <tr>
181+ <th>参数名</th>
182+ <th>输入/输出</th>
183+ <th>描述</th>
184+ </tr></thead>
185+ <tbody>
186+ <tr>
187+ <td>workspace</td>
188+ <td>输入</td>
189+ <td>在Device侧申请的workspace内存地址。</td>
190+ </tr>
191+ <tr>
192+ <td>workspaceSize</td>
193+ <td>输入</td>
194+ <td>在Device侧申请的workspace大小,由第一段接口aclnnIsInTensorScalarGetWorkspaceSize获取。</td>
195+ </tr>
196+ <tr>
197+ <td>executor</td>
198+ <td>输入</td>
199+ <td>op执行器,包含了算子计算流程。</td>
200+ </tr>
201+ <tr>
202+ <td>stream</td>
203+ <td>输入</td>
204+ <td>指定执行任务的Stream。</td>
205+ </tr>
206+ </tbody></table>
207+ 
208+- **返回值:**
209+ 
210+ aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../../docs/zh/context/aclnn返回码.md)。
211+ 
212+## 约束说明
213+ 
214+- 确定性计算:
215+ - aclnnIsInTensorScalar默认确定性实现。
216+ 
217+## 调用示例
218+ 
219+示例代码如下,仅供参考,具体编译和执行过程请参考[编译与运行样例](../../../../docs/zh/context/编译与运行样例.md)。
220+ 
221+```Cpp
222+#include <iostream>
223+#include <vector>
224+#include "acl/acl.h"
225+#include "aclnnop/aclnn_isin_tensor_scalar.h"
226+ 
227+#define CHECK_RET(cond, return_expr) \
228+ do { \
229+ if (!(cond)) { \
230+ return_expr; \
231+ } \
232+ } while (0)
233+ 
234+#define LOG_PRINT(message, ...) \
235+ do { \
236+ printf(message, ##__VA_ARGS__); \
237+ } while (0)
238+ 
239+int64_t GetShapeSize(const std::vector<int64_t>& shape)
240+{
241+ int64_t shape_size = 1;
242+ for (auto i : shape) {
243+ shape_size *= i;
244+ }
245+ return shape_size;
246+}
247+ 
248+int Init(int32_t deviceId, aclrtStream* stream)
249+{
250+ // 固定写法,资源初始化
251+ auto ret = aclInit(nullptr);
252+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
253+ ret = aclrtSetDevice(deviceId);
254+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
255+ ret = aclrtCreateStream(stream);
256+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
257+ return 0;
258+}
259+ 
260+template <typename T>
261+int CreateAclTensor(
262+ const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType,
263+ aclTensor** tensor)
264+{
265+ auto size = GetShapeSize(shape) * sizeof(T);
266+ // 调用aclrtMalloc申请device侧内存
267+ auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
268+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
269+ 
270+ // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
271+ ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
272+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
273+ 
274+ // 计算连续tensor的strides
275+ std::vector<int64_t> strides(shape.size(), 1);
276+ for (int64_t i = shape.size() - 2; i >= 0; i--) {
277+ strides[i] = shape[i + 1] * strides[i + 1];
278+ }
279+ 
280+ // 调用aclCreateTensor接口创建aclTensor
281+ *tensor = aclCreateTensor(
282+ shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(),
283+ *deviceAddr);
284+ return 0;
285+}
286+ 
287+aclError InitAcl(int32_t deviceId, aclrtStream* stream)
288+{
289+ auto ret = Init(deviceId, stream);
290+ CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
291+ return ACL_SUCCESS;
292+}
293+ 
294+aclError CreateInputs(
295+ std::vector<int64_t>& elementShape, std::vector<int64_t>& outShape, void** elementDeviceAddr, void** outDeviceAddr,
296+ aclTensor** element, aclTensor** out, aclScalar** testElement, bool& assumeUnique, bool& invert)
297+{
298+ std::vector<double> elementHostData = {0, 1, 2, 3, 2};
299+ std::vector<char> outHostData = {5, 0};
300+ double testElementValue = 2;
301+ 
302+ // 创建 testElement scalar
303+ *testElement = aclCreateScalar(&testElementValue, aclDataType::ACL_DOUBLE);
304+ CHECK_RET(*testElement != nullptr, return ACL_ERROR_INVALID_PARAM);
305+ 
306+ // 创建 element tensor
307+ auto ret = CreateAclTensor(elementHostData, elementShape, elementDeviceAddr, aclDataType::ACL_DOUBLE, element);
308+ CHECK_RET(ret == ACL_SUCCESS, return ret);
309+ 
310+ // 创建 out tensor
311+ ret = CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_BOOL, out);
312+ CHECK_RET(ret == ACL_SUCCESS, return ret);
313+ 
314+ return ACL_SUCCESS;
315+}
316+ 
317+aclError ExecOpApi(
318+ aclTensor* element, aclScalar* testElement, bool assumeUnique, bool invert, aclTensor* out, void** workspaceAddrOut,
319+ uint64_t& workspaceSize, void* outDeviceAddr, std::vector<int64_t>& outShape, aclrtStream stream)
320+{
321+ aclOpExecutor* executor;
322+ 
323+ // 第一段接口
324+ auto ret =
325+ aclnnIsInTensorScalarGetWorkspaceSize(element, testElement, assumeUnique, invert, out, &workspaceSize, &executor);
326+ CHECK_RET(
327+ ret == ACL_SUCCESS, LOG_PRINT("aclnnIsInTensorScalarGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
328+ 
329+ // 分配 workspace
330+ void* workspaceAddr = nullptr;
331+ if (workspaceSize > 0) {
332+ ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
333+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
334+ }
335+ *workspaceAddrOut = workspaceAddr;
336+ 
337+ // 第二段接口
338+ ret = aclnnIsInTensorScalar(workspaceAddr, workspaceSize, executor, stream);
339+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnIsInTensorScalar failed. ERROR: %d\n", ret); return ret);
340+ 
341+ // 同步
342+ ret = aclrtSynchronizeStream(stream);
343+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
344+ 
345+ // 拷贝输出
346+ auto size = GetShapeSize(outShape);
347+ std::vector<char> resultData(size, 0);
348+ 
349+ ret = aclrtMemcpy(
350+ resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(resultData[0]),
351+ ACL_MEMCPY_DEVICE_TO_HOST);
352+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
353+ 
354+ for (int64_t i = 0; i < size; i++) {
355+ LOG_PRINT("result[%ld] is: %d\n", i, resultData[i]);
356+ }
357+ 
358+ return ACL_SUCCESS;
359+}
360+ 
361+int main()
362+{
363+ int32_t deviceId = 0;
364+ aclrtStream stream;
365+ 
366+ auto ret = InitAcl(deviceId, &stream);
367+ CHECK_RET(ret == ACL_SUCCESS, return ret);
368+ 
369+ std::vector<int64_t> elementShape = {5};
370+ std::vector<int64_t> outShape = {5};
371+ void* elementDeviceAddr = nullptr;
372+ void* outDeviceAddr = nullptr;
373+ aclTensor* element = nullptr;
374+ aclScalar* testElement = nullptr;
375+ aclTensor* out = nullptr;
376+ 
377+ bool assumeUnique = false;
378+ bool invert = false;
379+ 
380+ ret = CreateInputs(
381+ elementShape, outShape, &elementDeviceAddr, &outDeviceAddr, &element, &out, &testElement, assumeUnique, invert);
382+ CHECK_RET(ret == ACL_SUCCESS, return ret);
383+ 
384+ uint64_t workspaceSize = 0;
385+ void* workspaceAddr = nullptr;
386+ 
387+ ret = ExecOpApi(
388+ element, testElement, assumeUnique, invert, out, &workspaceAddr, workspaceSize, outDeviceAddr, outShape, stream);
389+ CHECK_RET(ret == ACL_SUCCESS, return ret);
390+ 
391+ // 释放
392+ aclDestroyScalar(testElement);
393+ aclDestroyTensor(element);
394+ aclDestroyTensor(out);
395+ 
396+ aclrtFree(elementDeviceAddr);
397+ aclrtFree(outDeviceAddr);
398+ if (workspaceSize > 0) {
399+ aclrtFree(workspaceAddr);
400+ }
401+ 
402+ aclrtDestroyStream(stream);
403+ aclrtResetDevice(deviceId);
404+ aclFinalize();
405+ return 0;
406+}
407+```
@@ -0,0 +1,189 @@
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+#include <iostream>
11+#include <vector>
12+#include "acl/acl.h"
13+#include "aclnnop/aclnn_eq_scalar.h"
14+ 
15+#define CHECK_RET(cond, return_expr) \
16+ do { \
17+ if (!(cond)) { \
18+ return_expr; \
19+ } \
20+ } while (0)
21+ 
22+#define LOG_PRINT(message, ...) \
23+ do { \
24+ printf(message, ##__VA_ARGS__); \
25+ } while (0)
26+ 
27+int64_t GetShapeSize(const std::vector<int64_t>& shape)
28+{
29+ int64_t shape_size = 1;
30+ for (auto i : shape) {
31+ shape_size *= i;
32+ }
33+ return shape_size;
34+}
35+ 
36+int Init(int32_t deviceId, aclrtStream* stream)
37+{
38+ // 固定写法,资源初始化
39+ auto ret = aclInit(nullptr);
40+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
41+ ret = aclrtSetDevice(deviceId);
42+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
43+ ret = aclrtCreateStream(stream);
44+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
45+ return 0;
46+}
47+ 
48+template <typename T>
49+int CreateAclTensor(
50+ const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType,
51+ aclTensor** tensor)
52+{
53+ auto size = GetShapeSize(shape) * sizeof(T);
54+ // 调用aclrtMalloc申请device侧内存
55+ auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
56+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
57+ 
58+ // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
59+ ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
60+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
61+ 
62+ // 计算连续tensor的strides
63+ std::vector<int64_t> strides(shape.size(), 1);
64+ for (int64_t i = shape.size() - 2; i >= 0; i--) {
65+ strides[i] = shape[i + 1] * strides[i + 1];
66+ }
67+ 
68+ // 调用aclCreateTensor接口创建aclTensor
69+ *tensor = aclCreateTensor(
70+ shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(),
71+ *deviceAddr);
72+ return 0;
73+}
74+ 
75+aclError InitAcl(int32_t deviceId, aclrtStream* stream)
76+{
77+ auto ret = Init(deviceId, stream);
78+ CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
79+ return ACL_SUCCESS;
80+}
81+ 
82+aclError CreateInputs(
83+ std::vector<int64_t>& selfShape, std::vector<int64_t>& outShape, void** selfDeviceAddr, void** outDeviceAddr,
84+ aclTensor** self, aclScalar** other, aclTensor** out)
85+{
86+ std::vector<double> selfHostData = {0, 1, 1.2, 0.3, 4.1, 5, 1.6, 7};
87+ std::vector<char> outHostData = {0, 0, 0, 0, 0, 0, 0, 0};
88+ double otherValue = 1.2;
89+ 
90+ // 创建 self tensor
91+ auto ret = CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_DOUBLE, self);
92+ CHECK_RET(ret == ACL_SUCCESS, return ret);
93+ 
94+ // 创建 other scalar
95+ *other = aclCreateScalar(&otherValue, aclDataType::ACL_DOUBLE);
96+ CHECK_RET(*other != nullptr, return ret);
97+ 
98+ // 创建 out tensor
99+ ret = CreateAclTensor(outHostData, outShape, &(*outDeviceAddr), aclDataType::ACL_BOOL, out);
100+ CHECK_RET(ret == ACL_SUCCESS, return ret);
101+ 
102+ return ACL_SUCCESS;
103+}
104+ 
105+aclError ExecOpApi(
106+ aclTensor* self, aclScalar* other, aclTensor* out, void** workspaceAddrOut, uint64_t& workspaceSize,
107+ void* outDeviceAddr, std::vector<int64_t>& outShape, aclrtStream stream)
108+{
109+ aclOpExecutor* executor;
110+ 
111+ // 获取 workspace 大小
112+ auto ret = aclnnEqScalarGetWorkspaceSize(self, other, out, &workspaceSize, &executor);
113+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnEqScalarGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
114+ 
115+ // 申请 workspace(释放放在 main 里)
116+ void* workspaceAddr = nullptr;
117+ if (workspaceSize > 0) {
118+ ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
119+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
120+ }
121+ *workspaceAddrOut = workspaceAddr;
122+ 
123+ // 调用算子
124+ ret = aclnnEqScalar(workspaceAddr, workspaceSize, executor, stream);
125+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnEqScalar failed. ERROR: %d\n", ret); return ret);
126+ 
127+ // 同步
128+ ret = aclrtSynchronizeStream(stream);
129+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
130+ 
131+ // 拷贝输出
132+ auto size = GetShapeSize(outShape);
133+ std::vector<char> resultData(size, 0);
134+ 
135+ ret = aclrtMemcpy(
136+ resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(char),
137+ ACL_MEMCPY_DEVICE_TO_HOST);
138+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
139+ 
140+ for (int64_t i = 0; i < size; i++) {
141+ LOG_PRINT("result[%ld] is: %d\n", i, resultData[i]);
142+ }
143+ 
144+ return ACL_SUCCESS;
145+}
146+ 
147+int main()
148+{
149+ int32_t deviceId = 0;
150+ aclrtStream stream;
151+ auto ret = InitAcl(deviceId, &stream);
152+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("InitAcl failed. ERROR: %d\n", ret); return ret);
153+ 
154+ std::vector<int64_t> selfShape = {4, 2};
155+ std::vector<int64_t> outShape = {4, 2};
156+ 
157+ void* selfDeviceAddr = nullptr;
158+ void* outDeviceAddr = nullptr;
159+ aclTensor* self = nullptr;
160+ aclScalar* other = nullptr;
161+ aclTensor* out = nullptr;
162+ 
163+ ret = CreateInputs(selfShape, outShape, &selfDeviceAddr, &outDeviceAddr, &self, &other, &out);
164+ CHECK_RET(ret == ACL_SUCCESS, return ret);
165+ 
166+ uint64_t workspaceSize = 0;
167+ void* workspaceAddr = nullptr;
168+ 
169+ ret = ExecOpApi(self, other, out, &workspaceAddr, workspaceSize, outDeviceAddr, outShape, stream);
170+ CHECK_RET(ret == ACL_SUCCESS, return ret);
171+ 
172+ // 释放 Tensor / Scalar
173+ aclDestroyTensor(self);
174+ aclDestroyScalar(other);
175+ aclDestroyTensor(out);
176+ 
177+ // 释放 device 内存
178+ aclrtFree(selfDeviceAddr);
179+ aclrtFree(outDeviceAddr);
180+ if (workspaceSize > 0) {
181+ aclrtFree(workspaceAddr);
182+ }
183+ 
184+ aclrtDestroyStream(stream);
185+ aclrtResetDevice(deviceId);
186+ aclFinalize();
187+ 
188+ return 0;
189+}
@@ -0,0 +1,188 @@
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+#include "acl/acl.h"
14+#include "aclnnop/aclnn_eq_tensor.h"
15+ 
16+#define CHECK_RET(cond, return_expr) \
17+ do { \
18+ if (!(cond)) { \
19+ return_expr; \
20+ } \
21+ } while (0)
22+ 
23+#define LOG_PRINT(message, ...) \
24+ do { \
25+ printf(message, ##__VA_ARGS__); \
26+ } while (0)
27+ 
28+int64_t GetShapeSize(const std::vector<int64_t>& shape)
29+{
30+ int64_t shape_size = 1;
31+ for (auto i : shape) {
32+ shape_size *= i;
33+ }
34+ return shape_size;
35+}
36+ 
37+int Init(int32_t deviceId, aclrtStream* stream)
38+{
39+ // 固定写法,资源初始化
40+ auto ret = aclInit(nullptr);
41+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
42+ ret = aclrtSetDevice(deviceId);
43+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
44+ ret = aclrtCreateStream(stream);
45+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
46+ return 0;
47+}
48+ 
49+template <typename T>
50+int CreateAclTensor(
51+ const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType,
52+ aclTensor** tensor)
53+{
54+ auto size = GetShapeSize(shape) * sizeof(T);
55+ // 调用aclrtMalloc申请device侧内存
56+ auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
57+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
58+ 
59+ // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
60+ ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
61+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
62+ 
63+ // 计算连续tensor的strides
64+ std::vector<int64_t> strides(shape.size(), 1);
65+ for (int64_t i = shape.size() - 2; i >= 0; i--) {
66+ strides[i] = shape[i + 1] * strides[i + 1];
67+ }
68+ 
69+ // 调用aclCreateTensor接口创建aclTensor
70+ *tensor = aclCreateTensor(
71+ shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(),
72+ *deviceAddr);
73+ return 0;
74+}
75+ 
76+aclError InitAcl(int32_t deviceId, aclrtStream* stream)
77+{
78+ auto ret = Init(deviceId, stream);
79+ CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
80+ return ACL_SUCCESS;
81+}
82+ 
83+aclError CreateInputs(
84+ std::vector<int64_t>& selfShape, std::vector<int64_t>& otherShape, std::vector<int64_t>& outShape,
85+ void** selfDeviceAddr, void** otherDeviceAddr, void** outDeviceAddr, aclTensor** self, aclTensor** other,
86+ aclTensor** out)
87+{
88+ std::vector<double> selfHostData = {0, 1, 2, 3, 4, 5, 6, 7};
89+ std::vector<double> otherHostData = {0, 1, 9, 9, 9, 9, 9, 9};
90+ std::vector<char> outHostData = {0, 0, 0, 0, 0, 0, 0, 0};
91+ 
92+ auto ret = CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_DOUBLE, self);
93+ CHECK_RET(ret == ACL_SUCCESS, return ret);
94+ 
95+ ret = CreateAclTensor(otherHostData, otherShape, otherDeviceAddr, aclDataType::ACL_DOUBLE, other);
96+ CHECK_RET(ret == ACL_SUCCESS, return ret);
97+ 
98+ ret = CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_BOOL, out);
99+ CHECK_RET(ret == ACL_SUCCESS, return ret);
100+ 
101+ return ACL_SUCCESS;
102+}
103+ 
104+aclError ExecOpApi(
105+ aclTensor* self, aclTensor* other, aclTensor* out, void** workspaceAddrOut, uint64_t& workspaceSize,
106+ void* outDeviceAddr, std::vector<int64_t>& outShape, aclrtStream stream)
107+{
108+ aclOpExecutor* executor;
109+ 
110+ auto ret = aclnnEqTensorGetWorkspaceSize(self, other, out, &workspaceSize, &executor);
111+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnEqTensorGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
112+ 
113+ void* workspaceAddr = nullptr;
114+ if (workspaceSize > 0) {
115+ ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
116+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
117+ }
118+ *workspaceAddrOut = workspaceAddr;
119+ 
120+ ret = aclnnEqTensor(workspaceAddr, workspaceSize, executor, stream);
121+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnEqTensor failed. ERROR: %d\n", ret); return ret);
122+ 
123+ ret = aclrtSynchronizeStream(stream);
124+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
125+ 
126+ auto size = GetShapeSize(outShape);
127+ std::vector<char> resultData(size, 0);
128+ 
129+ ret = aclrtMemcpy(
130+ resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(char),
131+ ACL_MEMCPY_DEVICE_TO_HOST);
132+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
133+ 
134+ for (int64_t i = 0; i < size; i++) {
135+ LOG_PRINT("result[%ld] is: %d\n", i, resultData[i]);
136+ }
137+ 
138+ return ACL_SUCCESS;
139+}
140+ 
141+int main()
142+{
143+ int32_t deviceId = 0;
144+ aclrtStream stream;
145+ 
146+ auto ret = InitAcl(deviceId, &stream);
147+ CHECK_RET(ret == ACL_SUCCESS, return ret);
148+ 
149+ std::vector<int64_t> selfShape = {4, 2};
150+ std::vector<int64_t> otherShape = {4, 2};
151+ std::vector<int64_t> outShape = {4, 2};
152+ 
153+ void* selfDeviceAddr = nullptr;
154+ void* otherDeviceAddr = nullptr;
155+ void* outDeviceAddr = nullptr;
156+ 
157+ aclTensor* self = nullptr;
158+ aclTensor* other = nullptr;
159+ aclTensor* out = nullptr;
160+ 
161+ ret = CreateInputs(
162+ selfShape, otherShape, outShape, &selfDeviceAddr, &otherDeviceAddr, &outDeviceAddr, &self, &other, &out);
163+ CHECK_RET(ret == ACL_SUCCESS, return ret);
164+ 
165+ uint64_t workspaceSize = 0;
166+ void* workspaceAddr = nullptr;
167+ 
168+ ret = ExecOpApi(self, other, out, &workspaceAddr, workspaceSize, outDeviceAddr, outShape, stream);
169+ CHECK_RET(ret == ACL_SUCCESS, return ret);
170+ 
171+ // 释放资源
172+ aclDestroyTensor(self);
173+ aclDestroyTensor(other);
174+ aclDestroyTensor(out);
175+ 
176+ aclrtFree(selfDeviceAddr);
177+ aclrtFree(otherDeviceAddr);
178+ aclrtFree(outDeviceAddr);
179+ 
180+ if (workspaceSize > 0) {
181+ aclrtFree(workspaceAddr);
182+ }
183+ 
184+ aclrtDestroyStream(stream);
185+ aclrtResetDevice(deviceId);
186+ aclFinalize();
187+ return 0;
188+}
@@ -0,0 +1,188 @@
1+/**
2+ * Copyright (c) 2025 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+#include "acl/acl.h"
14+#include "aclnnop/aclnn_eq_tensor.h"
15+ 
16+#define CHECK_RET(cond, return_expr) \
17+ do { \
18+ if (!(cond)) { \
19+ return_expr; \
20+ } \
21+ } while (0)
22+ 
23+#define LOG_PRINT(message, ...) \
24+ do { \
25+ printf(message, ##__VA_ARGS__); \
26+ } while (0)
27+ 
28+int64_t GetShapeSize(const std::vector<int64_t>& shape)
29+{
30+ int64_t shape_size = 1;
31+ for (auto i : shape) {
32+ shape_size *= i;
33+ }
34+ return shape_size;
35+}
36+ 
37+int Init(int32_t deviceId, aclrtStream* stream)
38+{
39+ // 固定写法,资源初始化
40+ auto ret = aclInit(nullptr);
41+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
42+ ret = aclrtSetDevice(deviceId);
43+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
44+ ret = aclrtCreateStream(stream);
45+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
46+ return 0;
47+}
48+ 
49+template <typename T>
50+int CreateAclTensor(
51+ const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType,
52+ aclTensor** tensor)
53+{
54+ auto size = GetShapeSize(shape) * sizeof(T);
55+ // 调用aclrtMalloc申请device侧内存
56+ auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
57+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
58+ 
59+ // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
60+ ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
61+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
62+ 
63+ // 计算连续tensor的strides
64+ std::vector<int64_t> strides(shape.size(), 1);
65+ for (int64_t i = shape.size() - 2; i >= 0; i--) {
66+ strides[i] = shape[i + 1] * strides[i + 1];
67+ }
68+ 
69+ // 调用aclCreateTensor接口创建aclTensor
70+ *tensor = aclCreateTensor(
71+ shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(),
72+ *deviceAddr);
73+ return 0;
74+}
75+ 
76+aclError InitAcl(int32_t deviceId, aclrtStream* stream)
77+{
78+ auto ret = Init(deviceId, stream);
79+ CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
80+ return ACL_SUCCESS;
81+}
82+ 
83+aclError CreateInputs(
84+ std::vector<int64_t>& selfShape, std::vector<int64_t>& otherShape, std::vector<int64_t>& outShape,
85+ void** selfDeviceAddr, void** otherDeviceAddr, void** outDeviceAddr, aclTensor** self, aclTensor** other,
86+ aclTensor** out)
87+{
88+ std::vector<double> selfHostData = {0, 1, 2, 3, 4, 5, 6, 7};
89+ std::vector<double> otherHostData = {0, 1, 9, 9, 9, 9, 9, 9};
90+ std::vector<char> outHostData = {0, 0, 0, 0, 0, 0, 0, 0};
91+ 
92+ auto ret = CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_DOUBLE, self);
93+ CHECK_RET(ret == ACL_SUCCESS, return ret);
94+ 
95+ ret = CreateAclTensor(otherHostData, otherShape, otherDeviceAddr, aclDataType::ACL_DOUBLE, other);
96+ CHECK_RET(ret == ACL_SUCCESS, return ret);
97+ 
98+ ret = CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_BOOL, out);
99+ CHECK_RET(ret == ACL_SUCCESS, return ret);
100+ 
101+ return ACL_SUCCESS;
102+}
103+ 
104+aclError ExecOpApi(
105+ aclTensor* self, aclTensor* other, aclTensor* out, void** workspaceAddrOut, uint64_t& workspaceSize,
106+ void* outDeviceAddr, std::vector<int64_t>& outShape, aclrtStream stream)
107+{
108+ aclOpExecutor* executor;
109+ 
110+ auto ret = aclnnEqTensorGetWorkspaceSize(self, other, out, &workspaceSize, &executor);
111+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnEqTensorGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
112+ 
113+ void* workspaceAddr = nullptr;
114+ if (workspaceSize > 0) {
115+ ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
116+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
117+ }
118+ *workspaceAddrOut = workspaceAddr;
119+ 
120+ ret = aclnnEqTensor(workspaceAddr, workspaceSize, executor, stream);
121+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnEqTensor failed. ERROR: %d\n", ret); return ret);
122+ 
123+ ret = aclrtSynchronizeStream(stream);
124+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
125+ 
126+ auto size = GetShapeSize(outShape);
127+ std::vector<char> resultData(size, 0);
128+ 
129+ ret = aclrtMemcpy(
130+ resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(char),
131+ ACL_MEMCPY_DEVICE_TO_HOST);
132+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
133+ 
134+ for (int64_t i = 0; i < size; i++) {
135+ LOG_PRINT("result[%ld] is: %d\n", i, resultData[i]);
136+ }
137+ 
138+ return ACL_SUCCESS;
139+}
140+ 
141+int main()
142+{
143+ int32_t deviceId = 0;
144+ aclrtStream stream;
145+ 
146+ auto ret = InitAcl(deviceId, &stream);
147+ CHECK_RET(ret == ACL_SUCCESS, return ret);
148+ 
149+ std::vector<int64_t> selfShape = {4, 2};
150+ std::vector<int64_t> otherShape = {4, 2};
151+ std::vector<int64_t> outShape = {4, 2};
152+ 
153+ void* selfDeviceAddr = nullptr;
154+ void* otherDeviceAddr = nullptr;
155+ void* outDeviceAddr = nullptr;
156+ 
157+ aclTensor* self = nullptr;
158+ aclTensor* other = nullptr;
159+ aclTensor* out = nullptr;
160+ 
161+ ret = CreateInputs(
162+ selfShape, otherShape, outShape, &selfDeviceAddr, &otherDeviceAddr, &outDeviceAddr, &self, &other, &out);
163+ CHECK_RET(ret == ACL_SUCCESS, return ret);
164+ 
165+ uint64_t workspaceSize = 0;
166+ void* workspaceAddr = nullptr;
167+ 
168+ ret = ExecOpApi(self, other, out, &workspaceAddr, workspaceSize, outDeviceAddr, outShape, stream);
169+ CHECK_RET(ret == ACL_SUCCESS, return ret);
170+ 
171+ // 释放资源
172+ aclDestroyTensor(self);
173+ aclDestroyTensor(other);
174+ aclDestroyTensor(out);
175+ 
176+ aclrtFree(selfDeviceAddr);
177+ aclrtFree(otherDeviceAddr);
178+ aclrtFree(outDeviceAddr);
179+ 
180+ if (workspaceSize > 0) {
181+ aclrtFree(workspaceAddr);
182+ }
183+ 
184+ aclrtDestroyStream(stream);
185+ aclrtResetDevice(deviceId);
186+ aclFinalize();
187+ return 0;
188+}