news 2026/9/19 11:53:17

CANN ops-math 中的 aclnnLogdet 接口:矩阵行列式自然对数两段式 API 详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN ops-math 中的 aclnnLogdet 接口:矩阵行列式自然对数两段式 API 详解

CANN ops-math 中的 aclnnLogdet 接口:矩阵行列式自然对数两段式 API 详解

【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math

在科学计算、概率图模型和自回归变换(如 Normalizing Flow)等场景中,经常需要对输入方阵或方阵 batch 计算log(det(self))。CANN 算子库 ops-math 提供了Logdet算子的 ACLNN 两段式接口,支持对(*, n, n)形状的 float32 输入批量计算行列式自然对数,并在 Device 侧基于带部分主元选取的 LU 分解实现。读完本篇,你可以掌握该接口的完整参数语义、错误码、调用流程,以及从 Host 校验链、L0 主链到 Ascend C Kernel 的源码级实现原理。

产品支持情况

产品是否支持
Ascend 950PR/Ascend 950DT×
Atlas A3 训练系列产品/Atlas A3 推理系列产品×
Atlas A2 训练系列产品/Atlas A2 推理系列产品
Atlas 200I/500 A2 推理产品×
Atlas 推理系列产品×
Atlas 训练系列产品×

从仓库结构看,当前算子仅针对 Ascend 910B 平台提供了编译配置(ascend910b/logdet_binary.json),与上表中仅 Atlas A2 系列(910B)支持的情况一致。

功能说明:计算公式与特殊值语义

接口功能:计算输入方阵或方阵 batchself的行列式自然对数,并将结果写入out

设 $\det(\text{self})$ 表示对self最后两维对应的每个方阵分别计算行列式,公式分解为:

$$ \text{signValue} = \text{sign}(\det(\text{self})) $$

$$ \text{logAbsValue} = \log(|\det(\text{self})|) $$

$$ \text{out_tmp} = \log(\text{signValue}) + \text{logAbsValue} $$

$$ \text{out} = \text{cast}(\text{out_tmp}) $$

其中三种取值情形定义了接口的完整输出语义:

  • 当 $\det(\text{self}) > 0$ 时,$\text{out} = \log(\det(\text{self}))$。
  • 当 $\det(\text{self}) = 0$ 时(奇异矩阵),$\text{out} = -\infty$。
  • 当 $\det(\text{self}) < 0$ 时,$\text{out} = \text{NaN}$(对负数取自然对数无定义)。

输入约束为self的最后两维必须构成方阵,整体 shape 为(*, n, n)out的 shape 为self.shape[:-2](去掉最后两维的 batch shape),当self为 2 维方阵时,out是 0 维标量。

两段式接口设计

每个算子分为两段式接口(参见 两段式 API 说明):必须先调用aclnnLogdetGetWorkspaceSize接口获取计算所需 workspace 大小以及包含了算子计算流程的执行器(aclOpExecutor),再调用aclnnLogdet接口执行计算。

函数原型如下:

aclnnStatus aclnnLogdetGetWorkspaceSize( const aclTensor *self, aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor)
aclnnStatus aclnnLogdet( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)

接口声明位于 aclnn_logdet.h,并以 C 链接(extern "C")导出,头文件中用 mermaid 计算图注释给出了主链:self → Contiguous → Logdet → ViewCopy → out

aclnnLogdetGetWorkspaceSize 参数说明

参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续Tensor
self(aclTensor*)输入待计算行列式自然对数的输入方阵或方阵 batch,对应公式中 self支持空 Tensor(空 Tensor 时直接返回成功,workspaceSize 为 0);最后两维必须构成方阵;维度数不少于 2FLOATND至少 2 维,shape 为(*, n, n)
out(aclTensor*)输出self 的行列式自然对数结果,对应公式中 out不支持空 Tensor;数据类型需为 FLOAT;shape 需严格等于 self 去掉最后两维后的 batch shape,不支持 broadcast;当 self 为 2 维时,out 为 0 维标量FLOATNDself 去掉最后两维后的 batch shape
workspaceSize(uint64_t*)输出返回需要在 Device 侧申请的 workspace 大小-----
executor(aclOpExecutor**)输出返回 op 执行器,包含了算子计算流程-----

返回值aclnnStatus,具体参见 aclnn 返回码。

第一段接口完成入参校验,出现以下场景时报错:

返回值错误码描述
ACLNN_ERR_PARAM_NULLPTR161001self 或 out 是空指针。
ACLNN_ERR_PARAM_INVALID161002self 或 out 的数据类型不在支持的范围之内。
ACLNN_ERR_PARAM_INVALID161002self 的维度数小于 2。
ACLNN_ERR_PARAM_INVALID161002self 的最后两维不相等(不是方阵)。
ACLNN_ERR_PARAM_INVALID161002out 的 shape 不等于 self 去掉最后两维后的 batch shape。

aclnnLogdet 参数说明

参数名输入/输出描述
workspace输入在 Device 侧申请的 workspace 内存地址。
workspaceSize输入在 Device 侧申请的 workspace 大小,由第一段接口aclnnLogdetGetWorkspaceSize获取。
executor输入op 执行器,包含了算子计算流程。
stream输入指定执行任务的 Stream。

返回值aclnnStatus,具体参见 aclnn 返回码。

约束说明与边界行为

  • 确定性说明:aclnnLogdet 默认确定性实现。
  • 仅支持 float32 数据类型。
  • 非连续 self 会先转为连续 Tensor 后计算;非连续 out 通过 ViewCopy 回写。
  • 支持大于 8 维的 self,内部先 reshape 到 3 维计算后再 reshape 回原 batch shape。
  • 使用带部分主元选取的 LU 分解算法。

源码级实现剖析

以下分析基于 aclnn_logdet.cpp,帮助读者理解文档中各约束在代码中的落点。

Host 侧校验链

第一段接口aclnnLogdetGetWorkspaceSize的执行顺序为:

  1. CheckParams依次执行CheckNotNullCheckDtypeValidCheckShape
    • CheckNotNull校验 self/out 非空,对应ACLNN_ERR_PARAM_NULLPTR(161001);
    • CheckDtypeValid中数据类型支持列表DTYPE_SUPPORT_LIST = {op::DataType::DT_FLOAT}仅含 float32,并额外校验out的 dtype 与self一致,对应ACLNN_ERR_PARAM_INVALID(161002);
    • CheckShape校验self->GetViewShape().GetDimNum() >= 2、最后两维相等(mDim == nDim),以及out的 view shape 严格等于self.shape[:-2],对应文档错误码表中的后三条 161002 场景。
  2. 空 Tensor 快速路径:若self->IsEmpty(),直接令*workspaceSize = 0并释放执行器返回成功,不再构建任何子算子图——这正是文档“空 Tensor 时直接返回成功”约束的实现。
  3. 非空时调用LogdetExecImpl构建完整计算图并统计 workspace 大小,最后uniqueExecutor->GetWorkspaceSize()回填。

L0 主链:Contiguous → Logdet → Cast → ViewCopy

LogdetExecImpl构建的算子链为:

auto selfContiguous = l0op::Contiguous(self, executor); // 非连续 self 连续化 // 8 维以上:l0op::Reshape 到 (numel/(n*n), n, n) auto logdetOut = l0op::Logdet(selfReshapeOut, executor); // L0 启动 AiCore Logdet kernel // 8 维以上:l0op::Reshape 回原 batch shape auto logdetCastOut = l0op::Cast(logdetReshapeOut, out->GetDataType(), executor); auto logdetCopyResult = l0op::ViewCopy(logdetCastOut, out, executor); // 非连续 out 回写

其中:

  • l0op::Logdet是 L0 级接口,声明在 logdet.h,负责按 batch shape 创建输出 tensor 并 launch AiCore Kernel;
  • 当输入维度数dim > MAX_SUPPORT_DIMS_NUMS(8)时,ReshapeDim将 batch 折叠为一维,即{numel/(n*n), n, n},Kernel 只面对 3 维输入,计算完成后再 reshape 回原 batch shape。这与文档“支持大于 8 维的 self”约束一一对应;
  • l0op::Contiguousl0op::ViewCopy分别处理非连续输入与回写,因此调用方可以直接传入非连续的 self/out。

Graph 侧 InferShape

logdet_infershape.cpp 中,InferShape4Logdet实现与文档一致的形状推导规则:输入 rank 必须 ≥ 2,输出dimNum = inputRank - 2,且逐维复制前rank-2维;InferDataType4Logdet则直接令输出 dtype 等于输入 dtype。

Kernel 侧:带部分主元选取的 LU 分解

AiCore 侧实现位于 logdet.cpp 与 logdet.h,Kernel 入口是模板函数logdet<D_T, MEM_STRATEGY>,其中D_T为数据类型(仅 fp32),MEM_STRATEGY取值 0/1 对应两种存储策略:

  • MEM_STRATEGY = 0(FULL_RESIDENT,全驻留):整个 n×n 矩阵一次性加载进 Unified Buffer(UB),适合中小规模矩阵;
  • MEM_STRATEGY = 1(BLOCKED,核内分块):U 矩阵常驻 GM workspace,UB 中仅保留 O(n) 的行/列缓冲(pivot 整行、子列、行交换中转、ReduceMax 输出等),适合大 n 场景。

算法流程(以ProcessFull路径为例):batch 按核切分,每个 AiCore 按blockIdx_ + blockDim_*loop步长处理多个无依赖的矩阵(保证确定性);对单个 n×n 矩阵逐列执行部分主元 LU——先用ArgMaxAbsColumn在当前子列中找绝对值最大的元素作为主元(严格大于比较,命中首个最大值的行号,保证确定性),主元为负时翻转符号计数(对应公式中的 signValue),累计logabs += LogScalar(pivAbs)(对应 logAbsValue),再用EliminateFull对下方行做 Axpy 消元;当主元绝对值低于奇异阈值时(CheckSingular),判定矩阵奇异。该实现与文档“使用带部分主元选取的 LU 分解算法”以及det=0 → -infdet<0 → NaN的输出语义相符:符号累计为负时最终对数结果无定义返回 NaN,奇异时返回 -inf。

调用示例

示例代码如下(来自 aclnnLogdet.md),仅供参考,具体编译和执行过程请参考 编译与运行样例:

#include <cstdint> #include <vector> #include "acl/acl.h" #include "aclnn_logdet.h" int RunLogdetExample(aclrtStream stream, aclTensor* self, aclTensor* out) { uint64_t workspaceSize = 0; aclOpExecutor* executor = nullptr; // 第一段接口:做参数校验并返回 workspace 大小与执行器 aclnnStatus ret = aclnnLogdetGetWorkspaceSize(self, out, &workspaceSize, &executor); if (ret != ACLNN_SUCCESS) { return ret; } void* workspace = nullptr; if (workspaceSize > 0) { aclError aclRet = aclrtMalloc(&workspace, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); if (aclRet != ACL_SUCCESS) { return static_cast<int>(aclRet); } } // 第二段接口:在指定 stream 上执行 ret = aclnnLogdet(workspace, workspaceSize, executor, stream); if (ret != ACLNN_SUCCESS) { if (workspace != nullptr) { aclrtFree(workspace); } return ret; } aclrtSynchronizeStream(stream); if (workspace != nullptr) { aclrtFree(workspace); } return ACLNN_SUCCESS; }

要点:仅在workspaceSize > 0时才申请 Device 内存,空 Tensor 场景下第一段接口会直接返回 0 大小,因此aclrtMalloc可以被跳过;第二段接口失败时应释放已分配的 workspace 再返回错误码。

示例说明

  • self为输入 Tensor,shape 需满足(*, n, n),dtype 必须为FLOAT
  • out为输出 Tensor,shape 必须等于self.shape[:-2],当self为 2 维时,out为 0 维标量。
  • self为空 Tensor 时,aclnnLogdetGetWorkspaceSize会直接返回成功,且workspaceSize=0
  • 非连续self/out可以直接传入,接口内部会分别处理连续化和回写。

一个 2x2 输入示例

若输入矩阵为:

[[2.0, 0.0], [0.0, 3.0]]

det(self) = 6.0 > 0,输出为标量:

log(det(self)) = log(6.0)

测试与工程组织

算子工程的目录与测试布局如下(以 README.md 为准):

目录 / 文件说明
docs/ACLNN 接口文档(本文主体)
op_api/ACLNN 两段式接口实现(aclnn_logdet.cpp/h、L0logdet.cpp/h
op_host/Host 侧注册(logdet_def.cpp)、InferShape(logdet_infershape.cpp)、Tiling(logdet_tiling.cpp)
op_kernel/Ascend C Kernel 实现(logdet.cpp、logdet.h)
tests/st/ST 测试工程
tests/ut/UT 测试工程

ST 测试用例定义在 aclnnLogdet_l1_test_cases.csv 等 CSV 文件中,每条用例声明tensor_view_shapestensor_dtypesinput_data_ranges。例如:

aclnnLogdet_L1_001,aclnnLogdet,"((4,4,),(,),)","('float32','float32',)",,,(1,),,,((-100,100,)),, aclnnLogdet_L1_005,aclnnLogdet,"((3,8,8,),(3,))",('float32','float32',),,,(1,),,,((-100,100,)),,

其中((4,4,),(,),)表示 2 维方阵输入 → 0 维标量输出,((3,8,8,),(3,))表示 batch 为 3、每块 8×8 的方阵 batch →[3]输出,正好覆盖了文档中“2 维输入输出标量、batch 输入输出 batch shape”两种典型形态;另有 L0/L2 用例 扩展取值范围。UT 侧则对 InferShape 与 Tiling 做白盒覆盖(test_logdet_infershape.cpp、test_logdet_tiling.cpp),另有 Kernel 单测 logdet_kernel.asc。

小结

aclnnLogdet是 ops-math 库中面向方阵行列式自然对数的 ACLNN 两段式接口:第一段接口完成空指针/dtype/shape 三重校验、空 Tensor 快速返回与 workspace 统计,第二段接口在指定 stream 上按Contiguous → Logdet → Cast → ViewCopy主链执行;Device 侧 Kernel 采用带部分主元选取的 LU 分解,通过主元符号与log|pivot|累加同时得到 signValue 与 logAbsValue,并按全驻留/核内分块两种内存策略适配不同规模的矩阵。该实现默认确定性、仅支持 float32,对大于 8 维输入通过 reshape 归一化处理,可直接用于 NPU 上需要log(det(·))的模型推理与训练场景。

【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 11:51:27

多角色关系Transformer与强化学习:剧情冲突设计的双重机制

简介&#xff1a;一份面向自然语言处理与影视智能化创作研究者的PDF技术文档&#xff0c;系统探讨多角色关系Transformer在影视剧本生成中的应用&#xff0c;尤其聚焦于剧情冲突设计的强化学习机制。文档共27页&#xff0c;以单一PDF文件呈现&#xff0c;压缩包大小2.11MB&…

作者头像 李华
网站建设 2026/9/19 11:49:34

开源数字人DUIX端侧部署实战:从架构拆解到对话定制

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 11:47:37

50 行 Python AI Agent 跑 ReAct 循环,模型通道改到 TaoToken 通道行不行

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华