825 lines
39 KiB
C++
825 lines
39 KiB
C++
/**
|
||
* Copyright 2019-2021 Huawei Technologies Co., Ltd
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
#include "transform/graph_ir/op_adapter.h"
|
||
#include "utils/check_convert_utils.h"
|
||
|
||
namespace mindspore {
|
||
namespace transform {
|
||
// 静态函数 CustomInferFunc
|
||
// 参数:const Operator &,一个操作的引用
|
||
// 返回:uint32_t,一个无符号整数
|
||
static uint32_t CustomInferFunc(const Operator &) { return 0; } // 这个函数返回固定值 0,可以根据具体需求实现不同的逻辑来计算返回值
|
||
|
||
// 该函数用于判断给定的操作是否为自定义操作
|
||
// 参数:op,OperatorPtr类型的操作指针
|
||
// 返回:bool类型,表示给定操作是否为自定义操作
|
||
bool OpAdapterImpl::IsCustomOp(const OperatorPtr &op) {
|
||
MS_EXCEPTION_IF_NULL(op); // 检查操作指针是否为空,若为空则抛出异常
|
||
auto it = cus_input_map_->find(op->GetOpType()); // 在自定义操作映射表中查找给定操作的类型
|
||
if (it == cus_input_map_->end()) {
|
||
return false; // 若在映射表中未找到该操作类型,则返回false,表示该操作不是自定义操作
|
||
}
|
||
return true; // 若在映射表中找到了该操作类型,则返回true,表示该操作是自定义操作
|
||
}
|
||
|
||
// 该函数用于生成自定义操作的输入映射表
|
||
// 参数:op,CusOperatorPtr类型的自定义操作指针
|
||
// 参数:prim,PrimitivePtr类型的操作原语指针
|
||
// 返回:Status类型,表示操作执行的状态
|
||
Status OpAdapterImpl::GenerateCustomOpInputMap(const CusOperatorPtr &op, const PrimitivePtr &prim) {
|
||
MS_EXCEPTION_IF_NULL(op); // 检查自定义操作指针是否为空,若为空则抛出异常
|
||
MS_EXCEPTION_IF_NULL(prim); // 检查操作原语指针是否为空,若为空则抛出异常
|
||
// Create the map of custom op from input index to input name.
|
||
// 创建用于存储自定义操作输入映射的 map,键为输入索引,值为输入名称
|
||
mindspore::HashMap<int, std::string> input_map;
|
||
auto value = prim->GetAttr("input_names"); // 获取操作原语中名为 "input_names" 的属性值
|
||
if (value == nullptr) { // 如果获取的属性值为空,表示该自定义操作没有输入映射
|
||
(*cus_output_map_)[prim->name()] = input_map; //将一个空的映射存储到 cus_output_map_ 中
|
||
return NOT_FOUND; //返回 NOT_FOUND 状态
|
||
}
|
||
// 将属性值转换为 std::vector<std::string> 类型
|
||
auto input_names = GetValue<const std::vector<std::string>>(value);
|
||
for (size_t i = 0; i < input_names.size(); ++i) { // 遍历输入名称列表,将索引与输入名称的映射存储到 input_map 中,并将输入名称注册到自定义操作中
|
||
// input_map begin form 1
|
||
// 输入索引从 1 开始
|
||
input_map[i + 1] = input_names[i];
|
||
op->CustomInputRegister(input_names[i]);
|
||
}
|
||
// 如果在 cus_input_map_ 中未找到该自定义操作的输入映射,则将 input_map 存储到 cus_input_map_ 中
|
||
if (cus_input_map_->find(prim->name()) == cus_input_map_->end()) {
|
||
(*cus_input_map_)[prim->name()] = input_map;
|
||
}
|
||
return SUCCESS; // 操作执行成功,返回 SUCCESS 状态
|
||
}
|
||
|
||
// 该函数用于生成自定义操作的输出映射表
|
||
// 参数:op,CusOperatorPtr类型的自定义操作指针
|
||
// 参数:prim,PrimitivePtr类型的操作原语指针
|
||
// 返回:Status类型,表示操作执行的状态
|
||
Status OpAdapterImpl::GenerateCustomOpOutputMap(const CusOperatorPtr &op, const PrimitivePtr &prim) {
|
||
MS_EXCEPTION_IF_NULL(op); // 检查自定义操作指针是否为空,若为空则抛出异常
|
||
MS_EXCEPTION_IF_NULL(prim); // 检查操作原语指针是否为空,若为空则抛出异常
|
||
// Create the map of custom op from output index to output name.
|
||
// 创建用于存储自定义操作输出映射的 map,键为输出索引,值为输出名称
|
||
mindspore::HashMap<int, std::string> output_map;
|
||
// 获取操作原语中名为 "output_names" 的属性值
|
||
auto value = prim->GetAttr("output_names");
|
||
if (value == nullptr) { // 如果获取的属性值为空,表示该自定义操作没有输出映射
|
||
// generate a empty output_map for it
|
||
(*cus_output_map_)[prim->name()] = output_map; //将一个空的映射存储到 cus_output_map_ 中
|
||
return NOT_FOUND; //返回 NOT_FOUND 状态
|
||
}
|
||
// 将属性值转换为 std::vector<std::string> 类型
|
||
auto output_names = GetValue<const std::vector<std::string>>(value);
|
||
for (size_t i = 0; i < output_names.size(); ++i) { // 遍历输出名称列表,将索引与输出名称的映射存储到 output_map 中,并将输出名称注册到自定义操作中
|
||
// output_map begin form 0
|
||
// 输出索引从 0 开始
|
||
output_map[i] = output_names[i];
|
||
op->CustomOutputRegister(output_names[i]);
|
||
}
|
||
// 如果在 cus_output_map_ 中未找到该自定义操作的输出映射,则将 output_map 存储到 cus_output_map_ 中
|
||
if (cus_output_map_->find(prim->name()) == cus_output_map_->end()) {
|
||
(*cus_output_map_)[prim->name()] = output_map;
|
||
}
|
||
return SUCCESS; // 操作执行成功,返回 SUCCESS 状态
|
||
}
|
||
|
||
// 该函数用于生成自定义操作
|
||
// 参数:anf,AnfNodePtr类型的节点指针
|
||
// 返回:OperatorPtr类型,表示生成的自定义操作指针
|
||
OperatorPtr OpAdapterImpl::GenerateCustomOp(const AnfNodePtr anf) {
|
||
MS_EXCEPTION_IF_NULL(anf); // 检查节点指针是否为空,若为空则抛出异常
|
||
auto node = anf->cast<CNodePtr>(); // 将节点转换为 CNodePtr 类型
|
||
if (node == nullptr) { // 如果转换失败,返回空指针
|
||
return nullptr;
|
||
}
|
||
|
||
if (node->inputs().empty()) { // 如果节点输入为空,抛出异常
|
||
MS_LOG(EXCEPTION) << "length of node inputs is empty";
|
||
}
|
||
|
||
auto prim = GetValueNode<PrimitivePtr>(node->inputs()[0]); // 获取节点的原语指针
|
||
MS_EXCEPTION_IF_NULL(prim); // 检查原语指针是否为空,若为空则抛出异常
|
||
// 创建 ge::CustomOperator 类型的自定义操作,并传入节点的全名和原语的名称
|
||
auto op = std::make_shared<ge::CustomOperator>(node->fullname_with_scope(), prim->name());
|
||
// 生成自定义操作的输入映射表,并注册到自定义操作中
|
||
if (GenerateCustomOpInputMap(op, prim) != SUCCESS) {
|
||
MS_LOG(WARNING) << "Custom op node has no input_names, op[" << prim->name() << "].";
|
||
}
|
||
// 生成自定义操作的输出映射表,并注册到自定义操作中
|
||
if (GenerateCustomOpOutputMap(op, prim) != SUCCESS) {
|
||
MS_LOG(WARNING) << "Custom op node has no output_names, op[" << prim->name() << "].";
|
||
}
|
||
// 注册自定义推理函数
|
||
op->CustomInferFuncRegister(CustomInferFunc);
|
||
// 返回生成的自定义操作指针
|
||
return op;
|
||
}
|
||
|
||
// 该函数用于设置操作的子图函数
|
||
// 参数:op,OperatorPtr类型的操作指针
|
||
// 参数:index,int类型的索引值
|
||
// 参数:branches,std::shared_ptr<std::vector<DfGraph>>类型的子图指针
|
||
// 返回:Status类型,表示操作执行的状态
|
||
Status OpAdapterImpl::SetOpSubgraphFunc(const OperatorPtr &op, int index,
|
||
const std::shared_ptr<std::vector<DfGraph>> &branches) {
|
||
MS_EXCEPTION_IF_NULL(op); // 检查操作指针是否为空,若为空则抛出异常
|
||
auto it = dyn_subgraph_map_.find(index); // 在动态子图映射表中查找给定索引的映射
|
||
if (it != dyn_subgraph_map_.end()) { // 如果找到了映射
|
||
auto size = branches->size(); // 获取子图指针中的子图数量
|
||
it->second.create_dyn_subgraph(op, static_cast<unsigned int>(size)); // 创建操作的动态子图
|
||
for (size_t i = 0; i < size; i++) { // 设置操作的子图
|
||
it->second.set_subgraph(op, static_cast<unsigned int>(i), std::make_shared<DfGraph>((*branches)[i]));
|
||
}
|
||
return SUCCESS; // 操作执行成功,返回 SUCCESS 状态
|
||
}
|
||
return NOT_FOUND; // 未找到对应的映射,返回 NOT_FOUND 状态
|
||
}
|
||
|
||
// 该函数用于设置自定义操作的输入
|
||
// 参数:op,CusOperatorPtr类型的自定义操作指针
|
||
// 参数:index,int类型的输入索引
|
||
// 参数:input,OperatorPtr类型的输入操作指针
|
||
// 返回:Status类型,表示操作执行的状态
|
||
Status OpAdapterImpl::SetCustomOpInput(const CusOperatorPtr &op, int index, const OperatorPtr &input) {
|
||
MS_EXCEPTION_IF_NULL(op); // 检查自定义操作指针是否为空,若为空则抛出异常
|
||
MS_EXCEPTION_IF_NULL(input); // 检查输入操作指针是否为空,若为空则抛出异常
|
||
auto it = cus_input_map_->find(op->GetOpType()); // 在自定义操作输入映射表中查找给定自定义操作类型的映射
|
||
if (it == cus_input_map_->end()) { // 如果未找到映射,则返回 NOT_FOUND 状态
|
||
return NOT_FOUND;
|
||
}
|
||
mindspore::HashMap<int, std::string> &input_map = it->second; // 获取自定义操作输入映射表
|
||
|
||
if ((input_map.find(index) != input_map.end())) { // 如果在输入映射表中找到给定索引的映射
|
||
MS_LOG(DEBUG) << "Link op " << input->GetName() << " to " << op->GetName() << ":" << input_map[index];
|
||
(void)op->SetInput(input_map[index], *input); // 设置自定义操作的输入
|
||
return SUCCESS; // 操作执行成功,返回 SUCCESS 状态
|
||
}
|
||
return NOT_FOUND; // 未找到给定索引的映射,返回 NOT_FOUND 状态
|
||
}
|
||
|
||
// 该函数用于设置普通操作的输入
|
||
// 参数:op,OperatorPtr类型的普通操作指针
|
||
// 参数:index,int类型的输入索引
|
||
// 参数:input,OperatorPtr类型的输入操作指针
|
||
// 返回:Status类型,表示操作执行的状态
|
||
Status OpAdapterImpl::SetNormalOpInput(const OperatorPtr &op, int index, const OperatorPtr &input) {
|
||
MS_EXCEPTION_IF_NULL(op); // 检查普通操作指针是否为空,若为空则抛出异常
|
||
auto it = input_map_.find(index); // 在输入映射表中查找给定索引的映射
|
||
if (input != nullptr && it != input_map_.end()) { // 如果输入操作指针不为空,并且找到了索引的映射
|
||
MS_LOG(DEBUG) << "Link op " << input->GetName() << " to " << op->GetName() << ":" << it->second.name;
|
||
it->second.set_op(op, input); // 设置普通操作的输入
|
||
return SUCCESS; // 操作执行成功,返回 SUCCESS 状态
|
||
}
|
||
return NOT_FOUND; // 输入操作指针为空或未找到给定索引的映射,返回 NOT_FOUND 状态
|
||
}
|
||
|
||
// 该函数用于设置操作的输入
|
||
// 参数:op,OperatorPtr类型的操作指针
|
||
// 参数:index,int类型的输入索引
|
||
// 参数:input,OperatorPtr类型的输入操作指针
|
||
// 返回:int类型,表示操作执行的状态,其中非负数为成功状态,负数为失败状态
|
||
int OpAdapterImpl::setInput(const OperatorPtr &op, int index, const OperatorPtr &input) {
|
||
if (IsCustomOp(op)) { // 如果是自定义操作
|
||
auto cus_op = std::dynamic_pointer_cast<CustomOperator>(op); // 将操作指针转换为自定义操作指针
|
||
return static_cast<int>(SetCustomOpInput(cus_op, index, input)); // 调用 SetCustomOpInput 设置自定义操作的输入,并返回状态
|
||
} else { // 如果是普通操作
|
||
return static_cast<int>(SetNormalOpInput(op, index, input));// 调用 SetNormalOpInput 设置普通操作的输入,并返回状态
|
||
}
|
||
}
|
||
|
||
// 该函数用于设置自定义操作的输入
|
||
// 参数:op,CusOperatorPtr类型的自定义操作指针
|
||
// 参数:index,int类型的输入索引
|
||
// 参数:handle,OutHandler类型的输出处理器
|
||
// 返回:Status类型,表示操作执行的状态
|
||
Status OpAdapterImpl::SetCustomOpInput(const CusOperatorPtr &op, int index, const OutHandler &handle) {
|
||
MS_EXCEPTION_IF_NULL(op); // 检查自定义操作指针是否为空,若为空则抛出异常
|
||
auto it = cus_input_map_->find(op->GetOpType()); // 在输入映射表中查找给定自定义操作类型的映射
|
||
if (it == cus_input_map_->end()) { // 如果未找到给定自定义操作类型的映射
|
||
return NOT_FOUND; // 返回 NOT_FOUND 状态,表示未找到该类型的自定义操作
|
||
}
|
||
|
||
mindspore::HashMap<int, std::string> &input_map = it->second; // 获取输入映射表中该自定义操作类型的映射
|
||
if ((handle.op != nullptr) && (input_map.find(index) != input_map.end())) { // 如果输出处理器中的操作指针不为空,并且找到了给定索引的映射
|
||
if (handle.out.empty()) { // 如果输出处理器中输出名称为空
|
||
MS_LOG(DEBUG) << "Link op " << handle.op->GetName() << " to " << op->GetName() << ":" << input_map[index];
|
||
(void)op->SetInput(input_map[index], *(handle.op)); // 设置自定义操作的输入
|
||
} else { // 如果输出处理器中输出名称不为空
|
||
MS_LOG(DEBUG) << "Link op " << handle.op->GetName() << ":" << handle.out << " to " << op->GetName() << ":"
|
||
<< input_map[index];
|
||
(void)op->SetInput(input_map[index], *(handle.op), handle.out); // 设置自定义操作的输入和输出名称
|
||
}
|
||
return SUCCESS; // 操作执行成功,返回 SUCCESS 状态
|
||
}
|
||
return NOT_FOUND; // 输出处理器中的操作指针为空或未找到给定索引的映射,返回 NOT_FOUND 状态
|
||
}
|
||
|
||
// 该函数用于设置普通操作的输入
|
||
// 参数:op,OperatorPtr类型的操作指针
|
||
// 参数:index,int类型的输入索引
|
||
// 参数:handle,OutHandler类型的输出处理器
|
||
// 返回:Status类型,表示操作执行的状态
|
||
Status OpAdapterImpl::SetNormalOpInput(const OperatorPtr &op, int index, const OutHandler &handle) {
|
||
MS_EXCEPTION_IF_NULL(op); // 检查操作指针是否为空,若为空则抛出异常
|
||
auto it = input_map_.find(index); // 在输入映射表中查找给定索引的映射
|
||
if ((handle.op != nullptr) && (it != input_map_.end())) { // 如果输出处理器中的操作指针不为空,并且找到了给定索引的映射
|
||
if (handle.out.empty()) { // 如果输出处理器中输出名称为空
|
||
MS_LOG(DEBUG) << "Link op " << handle.op->GetName() << " to " << op->GetName() << ":" << it->second.name;
|
||
it->second.set_op(op, handle.op); // 设置普通操作的输入
|
||
} else { // 如果输出处理器中输出名称不为空
|
||
MS_LOG(DEBUG) << "Link op " << handle.op->GetName() << ":" << handle.out << " to " << op->GetName() << ":"
|
||
<< it->second.name;
|
||
it->second.set_handle(op, handle); // 设置普通操作的输入和输出处理器
|
||
}
|
||
return SUCCESS; // 操作执行成功,返回 SUCCESS 状态
|
||
}
|
||
return NOT_FOUND; // 输出处理器中的操作指针为空或未找到给定索引的映射,返回 NOT_FOUND 状态
|
||
}
|
||
|
||
// 该函数用于设置操作的输入
|
||
// 参数:op,OperatorPtr类型的操作指针
|
||
// 参数:index,int类型的输入索引
|
||
// 参数:handle,OutHandler类型的输出处理器
|
||
// 返回:int类型,表示操作执行的结果
|
||
int OpAdapterImpl::setInput(const OperatorPtr &op, int index, const OutHandler &handle) {
|
||
if (IsCustomOp(op)) { // 如果是自定义操作
|
||
auto cus_op = std::dynamic_pointer_cast<CustomOperator>(op); // 将操作指针转换为自定义操作指针
|
||
return static_cast<int>(SetCustomOpInput(cus_op, index, handle)); // 调用 SetCustomOpInput 函数设置自定义操作的输入,并将结果转换为整数返回
|
||
} else { // 如果不是自定义操作
|
||
return static_cast<int>(SetNormalOpInput(op, index, handle)); // 调用 SetNormalOpInput 函数设置普通操作的输入,并将结果转换为整数返回
|
||
}
|
||
}
|
||
|
||
// 该函数用于设置操作的动态输入
|
||
|
||
// 参数:op,OperatorPtr类型的操作指针
|
||
// 参数:index,int类型的输入索引
|
||
// 参数:handler_vec,std::shared_ptr<std::vector<OutHandler>>类型的输出处理器向量
|
||
// 返回:int类型,表示操作执行的结果
|
||
int OpAdapterImpl::setInput(const OperatorPtr &op, int index,
|
||
const std::shared_ptr<std::vector<OutHandler>> &handler_vec) {
|
||
MS_EXCEPTION_IF_NULL(handler_vec); // 检查输出处理器向量的有效性
|
||
if (IsCustomOp(op)) { // 如果是自定义操作
|
||
MS_LOG(ERROR) << "Custom Op do not support dynamic input"; // 输出错误信息,自定义操作不支持动态输入
|
||
return static_cast<int>(FAILED); // 返回执行失败的状态码
|
||
}
|
||
MS_EXCEPTION_IF_NULL(op); // 检查操作指针的有效性
|
||
auto it = dyn_input_map_.find(index); // 查找对应索引的动态输入信息
|
||
if (it != dyn_input_map_.end()) { // 如果找到了对应索引的动态输入信息
|
||
it->second.create_dyn_input(op, static_cast<unsigned int>(handler_vec->size())); // 创建动态输入
|
||
for (unsigned int i = 0; i < handler_vec->size(); ++i) { // 遍历输出处理器向量
|
||
OutHandler h = (*handler_vec)[i]; // 获取输出处理器
|
||
MS_EXCEPTION_IF_NULL(h.op); // 检查输出处理器的操作指针有效性
|
||
if (h.out.empty()) { // 如果输出处理器中没有指定输出名称
|
||
MS_LOG(DEBUG) << "Link op " << h.op->GetName() << " to " << op->GetName() << ":" << it->second.name;
|
||
// 输出调试信息,将输出处理器中的操作链接为动态输入的一部分
|
||
it->second.set_op(op, (i), h.op);
|
||
} else { // 如果输出处理器中指定了输出名称
|
||
MS_LOG(DEBUG) << "Link op " << h.op->GetName() << ":" << h.out << " to " << op->GetName() << ":"
|
||
<< it->second.name;
|
||
// 输出调试信息,将输出处理器中的输出链接为动态输入的一部分
|
||
it->second.set_handle(op, i, h);
|
||
}
|
||
}
|
||
return 0; // 返回执行成功的状态码
|
||
}
|
||
return static_cast<int>(NOT_FOUND); // 返回未找到的状态码
|
||
}
|
||
|
||
// 该函数用于获取操作的输出处理器
|
||
// 参数:op,OperatorPtr类型的操作指针
|
||
// 参数:index,int类型的输出索引
|
||
// 返回:OutHandler类型的输出处理器
|
||
OutHandler OpAdapterImpl::getOutput(const OperatorPtr &op, int index) {
|
||
MS_EXCEPTION_IF_NULL(op);// 检查操作指针的有效性
|
||
if (IsCustomOp(op)) { // 如果是自定义操作
|
||
return getCustomOutput(op, index); // 调用 getCustomOutput 函数获取自定义操作的输出处理器
|
||
}
|
||
return getNormalOutput(op, index); // 否则,调用 getNormalOutput 函数获取普通操作的输出处理器
|
||
}
|
||
|
||
// 该函数用于获取自定义操作的输出处理器
|
||
// 参数:op,OperatorPtr类型的操作指针
|
||
// 参数:index,int类型的输出索引
|
||
// 返回:OutHandler类型的输出处理器
|
||
OutHandler OpAdapterImpl::getCustomOutput(const OperatorPtr &op, int index) {
|
||
MS_EXCEPTION_IF_NULL(op); // 检查操作指针的有效性
|
||
auto it = cus_output_map_->find(op->GetOpType());
|
||
if (it == cus_output_map_->end()) { // 查找自定义操作的输出映射
|
||
MS_LOG(ERROR) << "OpAdpator(" << op->GetName() << ") has both OUTPUT is not supported!"; // 如果没有找到输出映射,输出错误日志
|
||
return OutHandler(); // 返回空的输出处理器
|
||
}
|
||
|
||
mindspore::HashMap<int, std::string> &output_map = it->second; // 获取输出映射
|
||
|
||
if ((output_map.find(index) != output_map.end())) { // 检查是否找到输出索引对应的输出名称
|
||
return OutHandler(op, output_map[index]); // 如果找到,创建并返回输出处理器
|
||
}
|
||
MS_LOG(ERROR) << "OpAdpator(" << op->GetName() << ") has no OUTPUT index(" << index << ")!"; // 如果没有找到输出索引,输出错误日志
|
||
return OutHandler(); // 返回空的输出处理器
|
||
}
|
||
|
||
// 该函数用于获取普通操作的输出处理器
|
||
// 参数:op,OperatorPtr类型的操作指针
|
||
// 参数:index,int类型的输出索引
|
||
// 返回:OutHandler类型的输出处理器
|
||
OutHandler OpAdapterImpl::getNormalOutput(const OperatorPtr &op, int index) {
|
||
MS_EXCEPTION_IF_NULL(op); // 检查操作指针的有效性
|
||
if (!dyn_output_map_.empty() && !output_map_.empty()) { // 检查是否同时存在动态输出映射和普通输出映射
|
||
MS_LOG(ERROR) << "OpAdpator(" << op->GetName() << ") has both OUTPUT and DYN_OUTPUT is not supported!"; // 如果同时存在动态输出映射和普通输出映射,输出错误日志
|
||
return OutHandler(); // 返回空的输出处理器
|
||
}
|
||
auto it = output_map_.find(index); // 在普通输出映射中查找指定的输出索引
|
||
if (it != output_map_.end()) { // 如果找到了输出索引
|
||
return OutHandler(op, it->second.name); // 创建并返回输出处理器
|
||
} else if (!dyn_output_map_.empty()) { // 如果普通输出映射为空,但动态输出映射不为空
|
||
return OutHandler(op, dyn_output_map_.begin()->second.name + std::to_string(index)); // 根据动态输出映射的第一个输出名称构造输出处理器
|
||
} else { // 如果既没有普通输出映射,也没有动态输出映射
|
||
MS_LOG(ERROR) << "OpAdpator(" << op->GetName() << ") has no OUTPUT and DYN_OUTPUT index(" << index << ")!"; // 输出错误日志
|
||
return OutHandler(); // 返回空的输出处理器
|
||
}
|
||
}
|
||
|
||
// 该函数用于更新单个输出的描述符
|
||
// 参数:op,OperatorPtr类型的操作指针
|
||
// 参数:shp,abstract::BaseShapePtr类型的输出形状指针
|
||
// 参数:type,TypePtr类型的输出数据类型
|
||
// 参数:format,string类型的输出数据格式
|
||
// 返回:Status类型的状态,SUCCESS表示成功,FAILED表示失败
|
||
Status OpAdapterImpl::UpdateSingleOutputDesc(const OperatorPtr &op, const abstract::BaseShapePtr &shp,
|
||
const TypePtr &type, const std::string &format) {
|
||
MS_EXCEPTION_IF_NULL(type); // 检查输出数据类型的有效性
|
||
|
||
auto desc = CreateOutputDesc(dyn_cast<abstract::Shape>(shp), type, format);
|
||
if (desc == nullptr) { // 检查输出描述符是否为空
|
||
MS_LOG(ERROR) << "Update output descriptor failed!"; // 输出错误日志
|
||
return FAILED; // 返回失败状态
|
||
}
|
||
|
||
if (IsCustomOp(op)) { // 检查是否为自定义操作
|
||
if (cus_output_map_->find(op->GetOpType()) == cus_output_map_->end() ||
|
||
((*cus_output_map_)[op->GetOpType()].empty())) { // 检查是否存在自定义输出映射,并且不为空
|
||
MS_LOG(ERROR) << "This op does not create custom output map"; // 输出错误日志
|
||
return FAILED; // 返回失败状态
|
||
}
|
||
auto cus_op = std::dynamic_pointer_cast<CustomOperator>(op); // 将操作指针转换为自定义操作指针
|
||
MS_EXCEPTION_IF_NULL(cus_op); // 检查自定义操作指针的有效性
|
||
mindspore::HashMap<int, std::string> output_map = (*cus_output_map_)[op->GetOpType()]; // 获取自定义输出映射
|
||
(void)cus_op->UpdateOutputDesc(output_map[0], *desc); // 更新自定义操作的输出描述符
|
||
} else { // 如果不是自定义操作
|
||
if (output_map_.empty()) { // 检查普通输出映射是否为空
|
||
MS_LOG(INFO) << "This op does not have output map"; // 输出提示信息
|
||
return FAILED; // 返回失败状态
|
||
}
|
||
output_map_.begin()->second.update_out_desc(op, *desc); // 更新普通操作的输出描述符
|
||
}
|
||
return SUCCESS; // 返回成功状态
|
||
}
|
||
|
||
// 该函数用于获取自定义操作的输出数量
|
||
// 参数:cus_op,CusOperatorPtr类型的自定义操作指针
|
||
// 返回:size_t类型的输出数量,表示自定义操作的输出数量
|
||
size_t OpAdapterImpl::GetCustomOpOutputSize(const CusOperatorPtr &cus_op) {
|
||
MS_EXCEPTION_IF_NULL(cus_op); // 检查自定义操作指针的有效性
|
||
if (cus_output_map_->find(cus_op->GetOpType()) == cus_output_map_->end()) { // 检查是否存在自定义输出映射
|
||
MS_LOG(ERROR) << "This op does not create custom output map"; // 输出错误日志
|
||
return 0; // 返回0,表示自定义操作的输出数量为0
|
||
}
|
||
size_t output_size = (*cus_output_map_)[cus_op->GetOpType()].size(); // 获取自定义操作的输出数量
|
||
return output_size; // 返回自定义操作的输出数量
|
||
}
|
||
|
||
// 该函数用于创建输出的GeTensorDesc对象,用于描述输出的形状、数据类型和格式。
|
||
// 参数:
|
||
// - shape_ptr: abstract::ShapePtr类型的形状指针,表示输出的形状。
|
||
// - type: TypePtr类型的类型指针,表示输出的数据类型。
|
||
// - format: std::string类型的格式字符串,表示输出的数据格式。
|
||
// 返回:
|
||
// - std::shared_ptr<GeTensorDesc>类型的指针,表示输出的GeTensorDesc对象。
|
||
std::shared_ptr<GeTensorDesc> OpAdapterImpl::CreateOutputDesc(const abstract::ShapePtr &shape_ptr, const TypePtr &type,
|
||
const std::string &format) {
|
||
if (type == nullptr) { // 检查输出的数据类型是否为空
|
||
MS_LOG(ERROR) << "Type ptr is nullptr"; // 输出错误日志
|
||
return nullptr; // 返回空指针,表示创建输出描述失败
|
||
}
|
||
|
||
TypeId me_type = type->type_id(); // 获取输出的数据类型ID
|
||
if (kObjectTypeTensorType == me_type) { // 如果输出类型是TensorType
|
||
me_type = dyn_cast<TensorType>(type)->element()->type_id(); // 获取Tensor元素的数据类型ID
|
||
}
|
||
// 调用TransformUtil::GetGeTensorDesc函数,创建GeTensorDesc对象并返回
|
||
return TransformUtil::GetGeTensorDesc((shape_ptr == nullptr) ? ShapeVector{} : shape_ptr->shape(), me_type, format);
|
||
}
|
||
|
||
// 该函数用于更新多输出的输出描述。
|
||
// 参数:
|
||
// - op: OperatorPtr类型的指针,表示要更新输出描述的运算符。
|
||
// - shp: abstract::BaseShapePtr类型的形状指针,表示输出的形状。
|
||
// - type: TypePtr类型的类型指针,表示输出的数据类型。
|
||
// - format: std::string类型的格式字符串,表示输出的数据格式。
|
||
// 返回:
|
||
// - Status类型,表示更新输出描述的操作状态。
|
||
Status OpAdapterImpl::UpdateMultiOutputDesc(const OperatorPtr &op, const abstract::BaseShapePtr &shp,
|
||
const TypePtr &type, const std::string &format) {
|
||
auto tuple_shp = dyn_cast<abstract::TupleShape>(shp); // 转换成TupleShape类型
|
||
MS_EXCEPTION_IF_NULL(tuple_shp);
|
||
|
||
size_t output_size = 0;
|
||
bool is_custom_op = IsCustomOp(op); // 检查是否为自定义运算符
|
||
if (is_custom_op) {
|
||
output_size = GetCustomOpOutputSize(std::dynamic_pointer_cast<CustomOperator>(op)); // 获取自定义运算符的输出数量
|
||
} else {
|
||
output_size = output_map_.size(); // 获取普通运算符的输出数量
|
||
}
|
||
|
||
if (output_size == 0) { // 如果输出数量为0,返回失败状态
|
||
MS_LOG(INFO) << "This op does not have output map";
|
||
return FAILED;
|
||
}
|
||
|
||
if (output_size != tuple_shp->shape().size()) { // 检查输出数量是否与TupleShape的大小相等
|
||
MS_LOG(ERROR) << "output_map is not equal tuple_shape size";
|
||
return FAILED; // 如果输出数量不相等,返回失败状态
|
||
}
|
||
|
||
for (size_t i = 0; i < tuple_shp->shape().size(); ++i) {
|
||
auto tuple_type = dyn_cast<Tuple>(type); // 转换成Tuple类型
|
||
MS_EXCEPTION_IF_NULL(tuple_type);
|
||
TypePtr type_elem = tuple_type->elements()[i]; // 获取Tuple中第i个元素的类型
|
||
// 调用CreateOutputDesc函数,创建GeTensorDesc对象
|
||
auto desc = CreateOutputDesc(dyn_cast<abstract::Shape>(tuple_shp->shape()[i]), type_elem, format);
|
||
if (desc == nullptr) {
|
||
MS_LOG(ERROR) << "Create output descriptor failed!";
|
||
return FAILED; // 如果创建输出描述失败,返回失败状态
|
||
}
|
||
|
||
if (is_custom_op) { // 如果是自定义运算符
|
||
// 调用CustomOperator的UpdateOutputDesc函数,更新输出描述
|
||
(void)std::dynamic_pointer_cast<CustomOperator>(op)->UpdateOutputDesc((*cus_output_map_)[op->GetOpType()][i],
|
||
*desc);
|
||
} else {
|
||
auto it = output_map_.find(i);
|
||
if (it != output_map_.end()) {
|
||
it->second.update_out_desc(op, *desc); // 更新普通运算符的输出描述
|
||
}
|
||
}
|
||
}
|
||
return SUCCESS; // 返回成功状态
|
||
}
|
||
|
||
// 该函数用于创建GeTensorDesc对象,表示给定AnfNode的描述。
|
||
// 参数:
|
||
// - node: AnfNodePtr类型的指针,表示要创建描述的节点。
|
||
// - format: std::string类型的格式字符串,表示描述的数据格式。
|
||
// 返回:
|
||
// - std::shared_ptr<GeTensorDesc>类型的指针,表示创建的GeTensorDesc对象。
|
||
std::shared_ptr<GeTensorDesc> OpAdapterImpl::CreateNodeDesc(const AnfNodePtr &node, const std::string &format) {
|
||
MS_EXCEPTION_IF_NULL(node);
|
||
TypeId me_type = node->Type()->type_id(); // 获取节点的数据类型
|
||
if (kObjectTypeTensorType == me_type) {
|
||
me_type = dyn_cast<TensorType>(node->Type())->element()->type_id();
|
||
}
|
||
// 检查数据类型是否有效,如果无效则返回nullptr
|
||
if (me_type <= kNumberTypeBegin || me_type >= kNumberTypeEnd) {
|
||
return nullptr;
|
||
}
|
||
|
||
std::vector<int64_t> shape;
|
||
auto shape_ptr = dyn_cast<abstract::Shape>(node->Shape());
|
||
if (shape_ptr != nullptr) { // 获取节点的形状
|
||
shape = shape_ptr->shape();
|
||
}
|
||
// 调用TransformUtil的GetGeTensorDesc函数,创建GeTensorDesc对象
|
||
auto desc = TransformUtil::GetGeTensorDesc(shape, me_type, format);
|
||
// 检查是否成功创建GeTensorDesc对象,如果失败返回nullptr
|
||
if (desc == nullptr) {
|
||
MS_LOG(ERROR) << "Update output descriptor failed!";
|
||
return nullptr;
|
||
}
|
||
return desc; // 返回创建的GeTensorDesc对象的指针
|
||
}
|
||
|
||
// 该函数用于更新普通算子的输入描述。
|
||
// 参数:
|
||
// - op: OperatorPtr类型的指针,表示要更新输入描述的算子。
|
||
// - node: AnfNodePtr类型的指针,表示算子对应的CNode节点。
|
||
// - format: std::string类型的格式字符串,表示描述的数据格式。
|
||
void OpAdapterImpl::UpdateNormalOpInputDesc(const OperatorPtr &op, const AnfNodePtr &node, const std::string format) {
|
||
if (op == nullptr) { //检查算子是否为空,如果为空则打印错误日志并返回
|
||
MS_LOG(ERROR) << "op is nullptr";
|
||
return;
|
||
}
|
||
MS_EXCEPTION_IF_NULL(node);
|
||
|
||
auto inputs = node->cast<CNodePtr>()->inputs(); //遍历CNode节点的输入,从第二个输入开始,因为第一个输入是算子本身
|
||
for (size_t i = 1; i < inputs.size(); ++i) { // 对于输入节点,查找是否有对应的输入描述
|
||
auto it = input_map_.find(i);
|
||
if (it != input_map_.end()) { //如果找到,则调用CreateNodeDesc函数创建新的输入描述,并使用该描述更新输入节点的描述。
|
||
auto desc = CreateNodeDesc(inputs[i], format);
|
||
if (desc == nullptr) { // 如果创建描述失败,则继续处理下一个输入节点。
|
||
continue;
|
||
}
|
||
|
||
it->second.update_input_desc(op, *desc);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 该函数用于更新自定义算子的输入描述。
|
||
// 参数:
|
||
// - op: CusOperatorPtr类型的指针,表示要更新输入描述的自定义算子。
|
||
// - node: AnfNodePtr类型的指针,表示自定义算子对应的CNode节点。
|
||
// - format: std::string类型的格式字符串,表示描述的数据格式。
|
||
void OpAdapterImpl::UpdateCustomOpInputDesc(const CusOperatorPtr &op, const AnfNodePtr &node,
|
||
const std::string format) {
|
||
if (op == nullptr) { //检查算子是否为空,如果为空则打印错误日志并返回
|
||
MS_LOG(ERROR) << "op is nullptr";
|
||
return;
|
||
}
|
||
MS_EXCEPTION_IF_NULL(node);
|
||
//检查自定义算子是否创建了输入映射,如果未创建则打印错误日志并返回
|
||
if (cus_input_map_->find(op->GetOpType()) == cus_input_map_->end() || ((*cus_input_map_)[op->GetOpType()].empty())) {
|
||
MS_LOG(ERROR) << "This op does not create custom input map";
|
||
return;
|
||
}
|
||
//遍历CNode节点的输入,从第二个输入开始,因为第一个输入是算子本身
|
||
mindspore::HashMap<int, std::string> &input_map = (*cus_input_map_)[op->GetOpType()];
|
||
auto inputs = node->cast<CNodePtr>()->inputs();
|
||
for (size_t i = 1; i < inputs.size(); ++i) { // 对于输入节点,查找是否有对应的输入描述
|
||
if (input_map.find(i) != input_map.end()) { //如果找到,则调用CreateNodeDesc函数创建新的输入描述,并使用该描述更新自定义算子的输入描述。
|
||
auto desc = CreateNodeDesc(inputs[i], format);
|
||
if (desc == nullptr) { // 如果创建描述失败,则继续处理下一个输入节点。
|
||
continue;
|
||
}
|
||
(void)op->UpdateInputDesc(input_map[i], *desc);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 该函数用于更新算子的输入描述。
|
||
// 参数:
|
||
// - op: OperatorPtr类型的指针,表示要更新输入描述的算子。
|
||
// - node: AnfNodePtr类型的指针,表示算子对应的CNode节点。
|
||
void OpAdapterImpl::updateInputDesc(const OperatorPtr &op, const AnfNodePtr &node) {
|
||
MS_EXCEPTION_IF_NULL(op); //检查算子和节点是否为空,如果为空则抛出异常。
|
||
MS_EXCEPTION_IF_NULL(node);
|
||
std::string format = GetOpIOFormat(node); //获取节点的数据格式(即IO格式)通过调用GetOpIOFormat函数。
|
||
if (IsCustomOp(op)) { //检查算子是否为自定义算子
|
||
auto cus_op = std::dynamic_pointer_cast<CustomOperator>(op); //如果是,则将算子转换为CustomOperator类型
|
||
UpdateCustomOpInputDesc(cus_op, node, format); //调用UpdateCustomOpInputDesc函数来更新输入描述
|
||
} else { //如果不是自定义算子,则调用UpdateNormalOpInputDesc函数来更新输入描述。
|
||
UpdateNormalOpInputDesc(op, node, format);
|
||
}
|
||
}
|
||
|
||
// 该函数用于根据输出的形状和数据类型信息更新运算符的输出描述。
|
||
// 参数:
|
||
// - op: 指向OperatorPtr的指针,表示需要更新输出描述的运算符。
|
||
// - shp: 指向BaseShapePtr的指针,表示输出的形状信息。
|
||
// - type: 指向TypePtr的指针,表示输出的数据类型信息。
|
||
// - node: 指向AnfNodePtr的指针,表示与运算符对应的CNode节点。
|
||
void OpAdapterImpl::updateOutputDesc(const OperatorPtr &op, const abstract::BaseShapePtr &shp, const TypePtr &type,
|
||
const AnfNodePtr &node) {
|
||
if (op == nullptr) { //检查运算符和节点指针是否为空,如果为空,则会引发异常
|
||
MS_LOG(ERROR) << "op is nullptr";
|
||
return;
|
||
}
|
||
MS_EXCEPTION_IF_NULL(node);
|
||
MS_LOG(INFO) << "Op name is " << op->GetName() << " anf is " << node->DebugString();
|
||
|
||
auto normal_shape_ptr = dyn_cast<abstract::Shape>(shp);
|
||
auto no_shape_ptr = dyn_cast<abstract::NoShape>(shp);
|
||
std::string format = GetOpIOFormat(node); //通过调用GetOpIOFormat函数来获取节点的数据格式(IO格式)
|
||
//函数检查形状信息的类型
|
||
if ((normal_shape_ptr != nullptr) || (no_shape_ptr != nullptr)) { //如果形状类型为Shape或NoShape(单个输出)
|
||
if (UpdateSingleOutputDesc(op, shp, type, format) != SUCCESS) { //则调用UpdateSingleOutputDesc函数来更新输出描述。
|
||
return;
|
||
}
|
||
} else if (dyn_cast<abstract::TupleShape>(shp) != nullptr) { // 如果形状类型为TupleShape(多个输出)
|
||
if (UpdateMultiOutputDesc(op, shp, type, format) != SUCCESS) { //调用UpdateMultiOutputDesc函数来更新输出描述
|
||
return;
|
||
}
|
||
} else { //如果形状类型未知,则记录警告并返回。
|
||
MS_LOG(WARNING) << "Update output desc failed, unknown output shape type";
|
||
return;
|
||
}
|
||
MS_EXCEPTION_IF_NULL(node);
|
||
if (!node->isa<CNode>()) { //更新输出描述后,函数检查节点是否为CNode(即计算节点)
|
||
return; //如果节点不是CNode,则函数返回,因为非计算节点不需要更新输入描述。
|
||
}
|
||
|
||
// Need to update input_desc while the output_desc is updated
|
||
// 如果节点是CNode,则函数调用updateInputDesc函数来同时更新输入描述。
|
||
updateInputDesc(op, node);
|
||
}
|
||
|
||
// 该函数用于为运算符设置属性。
|
||
// 参数:
|
||
// - op: 指向OperatorPtr的指针,表示要设置属性的运算符。
|
||
// - attr_key: 属性的键,表示要设置的属性名称。
|
||
// - attr_value: 指向ValuePtr的指针,表示要设置的属性值。
|
||
int OpAdapterImpl::setAttr(const OperatorPtr &op, const std::string &attr_key, const ValuePtr &attr_value) {
|
||
auto it = attr_map_.find(attr_key); //通过在attr_map_中查找给定的attr_key来检查是否有对应的属性信息
|
||
if (it != attr_map_.end()) { // 如果找到了匹配的属性信息,则会调用相应的set_attr函数来设置属性。
|
||
// switch case for each avalilable attribute type
|
||
// 在设置属性之前,函数会打印属性名称和属性值,并将属性信息添加到adpt_对象以绘制图形。
|
||
MS_LOG(INFO) << "Set attr: " << attr_key << "(" << it->second.name << "), value: " << attr_value->ToString();
|
||
adpt_->AddAttrToDrawGraph(attr_key + std::string("=") + attr_value->ToString());
|
||
it->second.set_attr(op, attr_value);
|
||
return 0; // 成功则设置属性返回0。
|
||
}
|
||
return static_cast<int>(NOT_FOUND); // 如果未找到匹配的属性信息,则函数返回NOT_FOUND。
|
||
}
|
||
|
||
//该函数用于设置自定义操作的属性。
|
||
int OpAdapterImpl::SetCustomOpAttr(const CusOperatorPtr &op, const PrimitivePtr &prim) {
|
||
enum ValueType { //定义一个枚举类型ValueType,包括三个枚举值:SINGLE_VALUE、SEQUEUE_VALUE和UNKNOWN_VALUE。
|
||
SINGLE_VALUE = 0,
|
||
SEQUEUE_VALUE,
|
||
UNKNOWN_VALUE,
|
||
};
|
||
|
||
MS_EXCEPTION_IF_NULL(prim); //使用断言(MS_EXCEPTION_IF_NULL)确保prim和op指针不为空
|
||
MS_EXCEPTION_IF_NULL(op);
|
||
|
||
ValueType value_type = SINGLE_VALUE; //初始化一个变量value_type为SINGLE_VALUE
|
||
for (auto item : prim->attrs()) { //通过遍历prim对象的属性来设置操作的属性
|
||
//对于每个属性,它首先检查其类型,然后根据类型调用适当的函数将属性值设置到操作中。
|
||
//支持的属性类型包括Int32Imm、StringImm、BoolImm和FP32Imm。
|
||
/* 如果属性的类型是ValueSequence,则将value_type设置为SEQUEUE_VALUE,并根据序列中第一个元素的类型设置属性值。
|
||
如果属性的类型不在支持的类型列表中,则抛出异常。*/
|
||
if (item.second->isa<Int32Imm>()) {
|
||
(void)op->SetAttr(item.first, GetValue<int64_t>(item.second));
|
||
} else if (item.second->isa<StringImm>()) {
|
||
(void)op->SetAttr(item.first, GetValue<std::string>(item.second));
|
||
} else if (item.second->isa<BoolImm>()) {
|
||
(void)op->SetAttr(item.first, GetValue<bool>(item.second));
|
||
} else if (item.second->isa<FP32Imm>()) {
|
||
(void)op->SetAttr(item.first, GetValue<float>(item.second));
|
||
} else if (item.second->isa<ValueSequence>()) {
|
||
value_type = SEQUEUE_VALUE;
|
||
auto val_seq = item.second->cast<ValueSequencePtr>();
|
||
if ((*val_seq)[0]->isa<StringImm>()) {
|
||
(void)op->SetAttr(item.first, GetValue<const std::vector<std::string>>(item.second));
|
||
} else if ((*val_seq)[0]->isa<FP32Imm>()) {
|
||
(void)op->SetAttr(item.first, GetValue<const std::vector<float>>(item.second));
|
||
} else if ((*val_seq)[0]->isa<Int64Imm>()) {
|
||
(void)op->SetAttr(item.first, GetValue<const std::vector<int64_t>>(item.second));
|
||
} else if ((*val_seq)[0]->isa<BoolImm>()) {
|
||
(void)op->SetAttr(item.first, GetValue<const std::vector<bool>>(item.second));
|
||
} else {
|
||
MS_LOG(EXCEPTION) << "Unsupported custom attribute type in adaptor, prim name: " << prim->name()
|
||
<< ", attr name: " << item.first << ", value: " << item.second->ToString();
|
||
}
|
||
} else {
|
||
MS_LOG(WARNING) << "Unsupported custom attribute type in adaptor, prim name: " << prim->name()
|
||
<< ", attr name: " << item.first << ", value: " << item.second->ToString();
|
||
return static_cast<int>(NOT_FOUND);
|
||
}
|
||
/*在设置属性值之后,根据value_type的值,函数使用适当的字符串表示将属性添加到绘制图中。
|
||
如果value_type为SINGLE_VALUE,则将属性名和属性值以等号连接并添加到绘制图中;
|
||
如果value_type为SEQUEUE_VALUE,则将属性名和省略号添加到绘制图中。*/
|
||
if (value_type == SINGLE_VALUE) {
|
||
adpt_->AddAttrToDrawGraph(item.first + std::string("=") + item.second->ToString());
|
||
} else if (value_type == SEQUEUE_VALUE) {
|
||
adpt_->AddAttrToDrawGraph(item.first + std::string("=") + "[...]");
|
||
}
|
||
}
|
||
return 0; //函数返回0表示成功设置属性。
|
||
}
|
||
|
||
// 该函数用于为普通运算符设置属性。
|
||
// 参数:
|
||
// - op: 指向OperatorPtr的指针,表示要设置属性的运算符。
|
||
// - prim: 指向PrimitivePtr的指针,表示运算符的原语(Primitive)信息。
|
||
int OpAdapterImpl::SetNormalOpAttr(const OperatorPtr &op, const PrimitivePtr &prim) {
|
||
MS_EXCEPTION_IF_NULL(prim); //检查prim和op是否为空,如果为空,则会抛出异常
|
||
MS_EXCEPTION_IF_NULL(op);
|
||
for (auto &it : attr_map_) { //函数遍历attr_map_
|
||
auto value = prim->GetAttr(it.first); //对于每个属性(attr_map_中的每个键值对),它会先从prim中获取对应的属性值。
|
||
if (value != nullptr) { //如果在prim中找到了匹配的属性值,则会对属性值进行一系列转换(如将部分属性转换为字符串形式,或将IR属性转换为Op属性)
|
||
// convert parts of attr to str eg. data_format or change ir attr to op attr eg. axis[0]
|
||
(void)CheckAndConvertUtils::ConvertAttrValueToString(prim->name(), it.first, &value);
|
||
(void)CheckAndConvertUtils::CheckIrAttrtoOpAttr(prim->name(), it.first, &value);
|
||
// set attr from primitive
|
||
int ret = setAttr(op, it.first, value); //然后调用setAttr函数设置运算符的属性
|
||
if (ret) {
|
||
return ret;
|
||
}
|
||
} else { //// 如果在prim中未找到匹配的属性值,则会检查是否存在extra_attr_(额外的属性信息)
|
||
// set attr from extra_attr
|
||
auto it_extra = extra_attr_->find(it.first);
|
||
if (it_extra != extra_attr_->end()) { // 如果在extra_attr_中找到了匹配的属性值,则同样调用setAttr函数设置运算符的属性
|
||
int ret = setAttr(op, it.first, it_extra->second);
|
||
if (ret) { // 如果遍历过程中发现设置属性时返回了非零值(即设置属性失败),则函数会立即返回该错误码。
|
||
return ret;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return 0; //成功设置属性返回0。
|
||
}
|
||
|
||
// 该函数用于为运算符设置属性。
|
||
// 参数:
|
||
// - op: 指向OperatorPtr的指针,表示要设置属性的运算符。
|
||
// - prim: 指向PrimitivePtr的指针,表示运算符的原语(Primitive)信息。
|
||
int OpAdapterImpl::setAttr(const OperatorPtr &op, const PrimitivePtr &prim) {
|
||
int ret = 0;
|
||
if (IsCustomPrim(prim)) { //判断prim是否为自定义原语(CustomPrimitive)
|
||
auto cus_op = std::dynamic_pointer_cast<CustomOperator>(op);
|
||
ret = SetCustomOpAttr(cus_op, prim); //如果是自定义原语,则调用SetCustomOpAttr函数为运算符设置属性。
|
||
} else { // 否则,调用SetNormalOpAttr函数为运算符设置属性
|
||
ret = SetNormalOpAttr(op, prim);
|
||
}
|
||
return ret; //返回值为非零表示设置属性失败,返回对应的错误码。
|
||
}
|
||
|
||
// 该函数用于为运算符设置属性。
|
||
// 参数:
|
||
// - op: 指向OperatorPtr的指针,表示要设置属性的运算符。
|
||
// - node: 指向AnfNodePtr的指针,表示运算符对应的图节点(AnfNode)。
|
||
int OpAdapterImpl::setAttr(const OperatorPtr &op, const AnfNodePtr &node) {
|
||
// no attribute for lonely node
|
||
MS_EXCEPTION_IF_NULL(node);
|
||
if (!node->isa<CNode>()) { //判断node是否为CNode(计算节点)。
|
||
return 0; //如果不是CNode,则表示该节点没有属性,直接返回0。
|
||
}
|
||
// 将节点转换为CNodePtr(计算节点的表示)。
|
||
auto cnode = node->cast<CNodePtr>();
|
||
if (cnode == nullptr) {
|
||
return 0;
|
||
}
|
||
// 获取计算节点的输入。
|
||
auto &inputs = cnode->inputs();
|
||
if (inputs.empty()) {
|
||
return 0;
|
||
}
|
||
|
||
// get Attr T from abstract of anfnode first,
|
||
// if attr "T" appears in primitive, the primitive T will cover this one
|
||
// 首先从AnfNode的抽象信息中获取属性"T"。
|
||
// 如果原语(primitive)中有属性"T",则原语中的"T"将覆盖此处的"T"属性。
|
||
if (attr_map_.find("T") != attr_map_.end()) {
|
||
// get dtype from inputs[1], if the node has no inputs, set the attr T with output dtype
|
||
// 从inputs[1]中获取数据类型(dtype)。如果节点没有输入,则使用输出的数据类型来设置属性"T"。
|
||
TypePtr type;
|
||
if (inputs.size() > 1) {
|
||
type = inputs[1]->Type();
|
||
} else {
|
||
type = node->Type();
|
||
}
|
||
if (type != nullptr) { // 使用上面获得的数据类型来设置属性"T"
|
||
(void)setAttr(op, "T", MakeValue(type));
|
||
}
|
||
}
|
||
|
||
// set attr from primitive and ExtraAttr
|
||
// 从原语和ExtraAttr中设置属性。
|
||
if (IsValueNode<Primitive>(inputs[0])) {
|
||
// set attr from primitive
|
||
// 从原语中设置属性。
|
||
PrimitivePtr prim = GetValueNode<PrimitivePtr>(inputs[0]);
|
||
int ret = setAttr(op, prim);
|
||
if (ret != 0) {
|
||
return ret;
|
||
}
|
||
}
|
||
|
||
// set attr from const input
|
||
// 从常量输入中设置属性。
|
||
for (auto &it : input_attr_map_) {
|
||
// 检查输入索引是否在范围内,并且输入是否为ValueNode(常量节点)。
|
||
if (inputs.size() <= it.first || !inputs[it.first]->isa<ValueNode>()) {
|
||
continue;
|
||
}
|
||
// 从输入中获取常量值。
|
||
auto const_value = GetValueNode(inputs[it.first]);
|
||
MS_LOG(INFO) << "Set attr: input_" << it.first << "(" << it.second.name << "), value: " << const_value->ToString();
|
||
if (const_value->isa<None>()) { // 如果常量值为None,则跳过设置属性。
|
||
continue;
|
||
}
|
||
// 将属性信息添加到绘图图形中。
|
||
adpt_->AddAttrToDrawGraph(it.second.name + std::string("=") + const_value->ToString());
|
||
// 使用提供的设置器函数(it.second.set_attr)和常量值来设置属性。
|
||
it.second.set_attr(op, const_value);
|
||
}
|
||
return 0;
|
||
}
|
||
} // namespace transform
|
||
} // namespace mindspore
|