transform/op_adapter.cc

825 lines
39 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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可以根据具体需求实现不同的逻辑来计算返回值
// 该函数用于判断给定的操作是否为自定义操作
// 参数opOperatorPtr类型的操作指针
// 返回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表示该操作是自定义操作
}
// 该函数用于生成自定义操作的输入映射表
// 参数opCusOperatorPtr类型的自定义操作指针
// 参数primPrimitivePtr类型的操作原语指针
// 返回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 状态
}
// 该函数用于生成自定义操作的输出映射表
// 参数opCusOperatorPtr类型的自定义操作指针
// 参数primPrimitivePtr类型的操作原语指针
// 返回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 状态
}
// 该函数用于生成自定义操作
// 参数anfAnfNodePtr类型的节点指针
// 返回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;
}
// 该函数用于设置操作的子图函数
// 参数opOperatorPtr类型的操作指针
// 参数indexint类型的索引值
// 参数branchesstd::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 状态
}
// 该函数用于设置自定义操作的输入
// 参数opCusOperatorPtr类型的自定义操作指针
// 参数indexint类型的输入索引
// 参数inputOperatorPtr类型的输入操作指针
// 返回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 状态
}
// 该函数用于设置普通操作的输入
// 参数opOperatorPtr类型的普通操作指针
// 参数indexint类型的输入索引
// 参数inputOperatorPtr类型的输入操作指针
// 返回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 状态
}
// 该函数用于设置操作的输入
// 参数opOperatorPtr类型的操作指针
// 参数indexint类型的输入索引
// 参数inputOperatorPtr类型的输入操作指针
// 返回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 设置普通操作的输入,并返回状态
}
}
// 该函数用于设置自定义操作的输入
// 参数opCusOperatorPtr类型的自定义操作指针
// 参数indexint类型的输入索引
// 参数handleOutHandler类型的输出处理器
// 返回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 状态
}
// 该函数用于设置普通操作的输入
// 参数opOperatorPtr类型的操作指针
// 参数indexint类型的输入索引
// 参数handleOutHandler类型的输出处理器
// 返回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 状态
}
// 该函数用于设置操作的输入
// 参数opOperatorPtr类型的操作指针
// 参数indexint类型的输入索引
// 参数handleOutHandler类型的输出处理器
// 返回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 函数设置普通操作的输入,并将结果转换为整数返回
}
}
// 该函数用于设置操作的动态输入
// 参数opOperatorPtr类型的操作指针
// 参数indexint类型的输入索引
// 参数handler_vecstd::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); // 返回未找到的状态码
}
// 该函数用于获取操作的输出处理器
// 参数opOperatorPtr类型的操作指针
// 参数indexint类型的输出索引
// 返回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 函数获取普通操作的输出处理器
}
// 该函数用于获取自定义操作的输出处理器
// 参数opOperatorPtr类型的操作指针
// 参数indexint类型的输出索引
// 返回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(); // 返回空的输出处理器
}
// 该函数用于获取普通操作的输出处理器
// 参数opOperatorPtr类型的操作指针
// 参数indexint类型的输出索引
// 返回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(); // 返回空的输出处理器
}
}
// 该函数用于更新单个输出的描述符
// 参数opOperatorPtr类型的操作指针
// 参数shpabstract::BaseShapePtr类型的输出形状指针
// 参数typeTypePtr类型的输出数据类型
// 参数formatstring类型的输出数据格式
// 返回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_opCusOperatorPtr类型的自定义操作指针
// 返回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