代码评注赛:aabbccd队PR #15

Open
hyf152 wants to merge 18 commits from hyf152/mindspore2022:master into master
13 changed files with 2771 additions and 407 deletions

View File

@ -75,26 +75,44 @@ bool EnableTupleBroaden(const abstract::AbstractBasePtr &abs) {
return abs->isa<abstract::AbstractTuple>() && abs->cast<abstract::AbstractTuplePtr>()->ContainsAllBroadenTensors();
}
// Update the parameters of a function graph
void UpdateFuncGraphParameter(const FuncGraphPtr &func_graph) {
MS_EXCEPTION_IF_NULL(func_graph);
// Initialize a vector to store the new parameters
std::vector<AnfNodePtr> new_paras;
// Iterate through the existing parameters of the function graph
for (const auto &param : func_graph->parameters()) {
// Cast the parameter to a ParameterPtr
auto param_node = param->cast<ParameterPtr>();
MS_EXCEPTION_IF_NULL(param_node);
// Check if the parameter has a default value
if (param_node->has_default()) {
// If it has a default value, add it to the new parameters vector and continue to the next parameter
new_paras.push_back(param_node);
continue;
}
// Get the abstract value of the parameter
AbstractBasePtr par_abs = param_node->abstract();
MS_EXCEPTION_IF_NULL(par_abs);
// Check if the parameter's abstract type is undetermined, or if it can have any value,
// or if it is a scalar that should be enabled for gradient, or if it should be enabled for tuple broaden
if (par_abs->isa<abstract::AbstractUndetermined>() || par_abs->BuildValue() == kAnyValue ||
EnableGradForScalar(par_abs) || EnableTupleBroaden(par_abs)) {
// If any of the above conditions are met, add the parameter to the new parameters vector
new_paras.push_back(param_node);
}
}
// Set the parameters of the function graph to the new parameters vector
func_graph->set_parameters(new_paras);
}
bool IsDynamicShapeGraph(const FuncGraphPtr &func_graph) {
MS_EXCEPTION_IF_NULL(func_graph);
std::vector<AnfNodePtr> node_list = TopoSort(func_graph->get_return());
@ -103,32 +121,57 @@ bool IsDynamicShapeGraph(const FuncGraphPtr &func_graph) {
}
// Disable mindRT in the heterogeneous scenario + dynamic_shape scenario.
// Disable the MindRT runtime in specific scenarios
void DisableMindRT(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
// Get the current context
auto context_ptr = MsContext::GetInstance();
MS_EXCEPTION_IF_NULL(context_ptr);
// Check if MindRT is already disabled in the context, if so, return
if (context_ptr->get_param<bool>(MS_CTX_ENABLE_MINDRT) == false) {
return;
}
// Get the function graph from the resource
auto func_graph = res->func_graph();
MS_EXCEPTION_IF_NULL(func_graph);
// Get the parallel context
auto parallel_context = parallel::ParallelContext::GetInstance();
MS_EXCEPTION_IF_NULL(parallel_context);
// Get the current parallel mode
auto parallel_mode = parallel_context->parallel_mode();
// Check if the parallel mode is SemiAutoParallel or AutoParallel
bool is_parallel_mode = parallel_mode == parallel::kSemiAutoParallel || parallel_mode == parallel::kAutoParallel;
// Check if the environment variable MS_DEV_ENABLE_CLOSURE is set to "0"
bool enable_old_runtime = (common::GetEnv("MS_DEV_ENABLE_CLOSURE") == "0");
// Check if the function graph contains control flow nodes and is in parallel mode
bool use_old_vm_for_control_parallel =
func_graph->exist_multi_target() && ExistControlFlow(func_graph) && is_parallel_mode;
// If any of the conditions for disabling MindRT are met, perform the following steps
if (enable_old_runtime || use_old_vm_for_control_parallel) {
MS_LOG(INFO) << "Disable mindRT in the heterogeneous + control flow + parallel scenario.";
MS_LOG(INFO) << "Disable MindRT in the heterogeneous + control flow + parallel scenario.";
// Set the ENABLE_MINDRT context parameter to false, effectively disabling MindRT
context_ptr->set_param<bool>(MS_CTX_ENABLE_MINDRT, false);
// Update the backend.
// Update the backend to use the old runtime
auto new_backend = compile::CreateBackend();
new_backend->SetDebugger();
// Set the new backend as the result in the resource
res->SetResult(kBackend, new_backend);
}
}
void TaskEmitActionForMindRT(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
// Get the mindRT backend.
@ -141,42 +184,58 @@ void TaskEmitActionForMindRT(const ResourcePtr &res) {
res->SetResult(kOutput, actor_info);
}
// Execute an action using MindRT
void ExecuteActionForMindRT(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
// Retrieve the actor information from the resource
const auto actor_info = res->GetResult(kOutput).cast<compile::ActorInfo>();
// Get the mindRT backend.
// Get the MindRT backend from the resource
std::shared_ptr<compile::Backend> bc_ptr = res->GetResult(kBackend).cast<std::shared_ptr<compile::Backend>>();
auto mindrt_bc_ptr = (std::dynamic_pointer_cast<compile::MindRTBackend>(bc_ptr)).get();
MS_EXCEPTION_IF_NULL(mindrt_bc_ptr);
// Construct the graph run function ptr.
// Construct a graph run function pointer
compile::VmEvalFuncPtr run =
std::make_shared<compile::VmEvalFunc>([mindrt_bc_ptr, actor_info](const VectorRef &args) -> BaseRef {
MS_LOG(DEBUG) << "Execute args size " << args.size();
VectorRef outputs;
// Run the graph using the MindRT backend
mindrt_bc_ptr->RunGraph(actor_info, args, &outputs);
MS_LOG(DEBUG) << "out size " << outputs.size();
// Check if the outputs are empty, and return an empty VectorRef if they are
if (outputs.empty()) {
return VectorRef();
} else {
// Return the first element of the outputs
return outputs[0];
}
});
// Set the result in the resource with the key kOutput
res->SetResult(kOutput, run);
}
// Modify the output node of func_graph to add forward nodes used in bprop graph.
// Modify the output node of a function graph
void ModifyOutputNode(const FuncGraphPtr &func_graph) {
MS_EXCEPTION_IF_NULL(func_graph);
// Get the set of forward nodes used in the function graph
const auto &used_forward_nodes = func_graph->used_forward_nodes();
// Get original output node and abstract
// Get the original output node and its abstract
auto original_output_node = func_graph->output();
MS_EXCEPTION_IF_NULL(original_output_node);
auto original_output_abs = original_output_node->abstract();
MS_EXCEPTION_IF_NULL(original_output_abs);
// Create a new make tuple node to hold all forward used nodes.
// Create a new make tuple node to hold all forward used nodes
abstract::AbstractBasePtrList added_abs_list;
std::vector<AnfNodePtr> added_node_list{NewValueNode(prim::kPrimMakeTuple)};
std::for_each(used_forward_nodes.begin(), used_forward_nodes.end(),
@ -185,30 +244,43 @@ void ModifyOutputNode(const FuncGraphPtr &func_graph) {
added_node_list.push_back(node);
added_abs_list.push_back(node->abstract());
});
AnfNodePtr added_output_node = nullptr;
AbstractBasePtr added_output_abs = nullptr;
// If there are no forward used nodes, create a constant node with the value 1
if (added_abs_list.empty()) {
added_output_node = NewValueNode(MakeValue<int32_t>(1));
added_output_abs = std::make_shared<abstract::AbstractScalar>(std::make_shared<Int32Imm>(1));
} else {
// Create a new node that makes a tuple from the list of forward used nodes
added_output_node = func_graph->NewCNode(std::move(added_node_list));
added_output_abs = std::make_shared<abstract::AbstractTuple>(added_abs_list);
}
// Set the abstract for the added output node
added_output_node->set_abstract(added_output_abs);
MS_LOG(DEBUG) << "Added output node info: " << added_output_node->DebugString();
// Merge original output node and used forward nodes to return node.
// Create a new make tuple node to merge the original output node and the added output node
std::vector<AnfNodePtr> new_output_nodes{NewValueNode(prim::kPrimMakeTuple), original_output_node, added_output_node};
auto merge_node = func_graph->NewCNode(std::move(new_output_nodes));
// Create a new abstract that represents the merged output
abstract::AbstractBasePtrList new_output_abs{original_output_abs, added_output_abs};
merge_node->set_abstract(std::make_shared<abstract::AbstractTuple>(new_output_abs));
MS_LOG(DEBUG) << "Merge node info: " << merge_node->DebugString();
// Set the merge node as the new output of the function graph
func_graph->set_output(merge_node);
// Clear
// Mark that the output has been modified
func_graph->set_modify_output(true);
// Clear the set of used forward nodes
func_graph->ClearUsedForwardNodes();
}
} // namespace
using CompileGraphs = compile::CompileGraphs;
using abstract::AnalysisResult;
@ -260,161 +332,260 @@ abstract::AnalysisResult AbstractAnalyze(const ResourcePtr &resource, const Func
return res;
}
// Program specialization for a given function graph
FuncGraphPtr ProgramSpecialize(const ResourcePtr &res, const FuncGraphPtr &func_graph,
const abstract::AnalysisContextPtr &context) {
MS_EXCEPTION_IF_NULL(res);
// Log a debug message indicating the start of program specialization
MS_LOG(DEBUG) << "ProgramSpecialize start";
// Create a program specializer using the engine from the resource
abstract::ProgramSpecializer specializer(res->engine());
// Run the program specializer on the input function graph with the specified analysis context
FuncGraphPtr result = specializer.Run(func_graph, context);
// Get the manager from the resource
auto manager = res->manager();
MS_EXCEPTION_IF_NULL(manager);
// Keep the specialized result graph as a root in the manager
manager->KeepRoots({result});
// Perform specialized handling for CNodes with input 0 as a function graph
specializer.SpecializeCNodeInput0FuncGraph();
// Log a debug message indicating the end of program specialization
MS_LOG(DEBUG) << "ProgramSpecialize end";
// Return the specialized result function graph
return result;
}
// Renormalize a function graph with new arguments and return the specialized function graph
FuncGraphPtr Renormalize(const ResourcePtr &res, const FuncGraphPtr &func_graph,
const abstract::AbstractBasePtrList &args_spec) {
MS_EXCEPTION_IF_NULL(res);
// Log a debug message indicating the start of renormalization
MS_LOG(DEBUG) << "Renormalize start";
#ifdef ENABLE_PROFILE
// If profiling is enabled, record the start time
double t1 = GetTime();
#endif
// Analyze the function graph with the new arguments and return the analysis result
abstract::AnalysisResult result = AbstractAnalyze(res, func_graph, args_spec, true);
#ifdef ENABLE_PROFILE
// If profiling is enabled, record the end time of analysis
double t2 = GetTime();
#endif
// Perform program specialization on the function graph using the analysis result
auto ret = ProgramSpecialize(res, func_graph, result.context);
// Set the specialized function graph as the new function graph in the resource
res->set_func_graph(ret);
#ifdef ENABLE_PROFILE
// If profiling is enabled, record the time spent on analysis and specialization
double t3 = GetTime();
MsProfile::StatTime("renormalize.infer", t2 - t1);
MsProfile::StatTime("renormalize.specialize", t3 - t2);
#endif
// Log a debug message indicating the end of renormalization
MS_LOG(DEBUG) << "Renormalize end";
// Return the specialized function graph
return ret;
}
// Get the loaded graph from the manager in the resource
const FuncGraphPtr GetLoadedGraph(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
// Get the manager from the resource
auto manager = res->manager();
MS_EXCEPTION_IF_NULL(manager);
// Initialize variables to store the loaded graph and count
FuncGraphPtr loaded_graph = nullptr;
size_t loaded_graph_num = 0;
// Get all function graphs managed by the manager
auto all_graphs = manager->func_graphs();
// Iterate through all function graphs
for (auto &graph : all_graphs) {
MS_EXCEPTION_IF_NULL(graph);
// Check if the graph has an "is_load" attribute
if (graph->has_attr("is_load")) {
// If the attribute is present, set the loaded graph and increment the count
loaded_graph = graph;
loaded_graph_num += 1;
// Set the "is_load" flag in the resource to indicate that a loaded graph is found
res->set_is_load(true);
}
}
// If no loaded graph is found, return nullptr
if (loaded_graph_num == 0) {
return nullptr;
}
// If only one loaded graph is found, return it
if (loaded_graph_num == 1) {
return loaded_graph;
}
MS_LOG(EXCEPTION) << "The loaded sub graph currently should be less than 2, but got " << loaded_graph_num;
// If more than one loaded graph is found, log an exception with the count
MS_LOG(EXCEPTION) << "The loaded subgraph should be less than 2, but got " << loaded_graph_num;
}
// Check the shape and type of root graph inputs against the loaded graph inputs
void CheckRootInputShapeAndType(const ResourcePtr &res, const FuncGraphPtr &loaded_graph) {
MS_EXCEPTION_IF_NULL(res);
auto manager = res->manager();
MS_EXCEPTION_IF_NULL(manager);
// Get the root graph from the manager
FuncGraphPtr root_graph = *(manager->roots().begin());
// Get inputs of both root and loaded graphs
auto root_inputs = root_graph->get_inputs();
auto loaded_inputs = loaded_graph->get_inputs();
MS_LOG(DEBUG) << "root_graph: " << root_graph->ToString();
MS_LOG(DEBUG) << "loaded_graph: " << loaded_graph->ToString();
// Get the number of inputs for both graphs
size_t root_inputs_num = root_inputs.size();
size_t loaded_inputs_num = loaded_inputs.size();
// Check if the number of inputs matches between the root and loaded graphs
if (root_inputs_num != loaded_inputs_num) {
MS_LOG(EXCEPTION) << "The inputs number " << root_inputs_num << " not equal to the inputs number of loaded graph "
<< loaded_inputs_num;
MS_LOG(EXCEPTION) << "The number of inputs in the root graph (" << root_inputs_num
<< ") does not match the number of inputs in the loaded graph (" << loaded_inputs_num << ")";
}
// Iterate through the inputs and compare their shapes and types
for (size_t index = 0; index < root_inputs_num; index++) {
auto root_input = root_inputs[index];
auto loaded_input = loaded_inputs[index];
// Debug log to display information about the inputs
MS_LOG(DEBUG) << "root_input[" << index << "]: " << root_input->DebugString(1);
MS_LOG(DEBUG) << "loaded_input[" << index << "]: " << loaded_input->DebugString(1);
MS_LOG(DEBUG) << "root_input abstract[" << index
<< "]: " << (root_input->abstract() ? root_input->abstract()->ToString() : "NULL");
MS_LOG(DEBUG) << "loaded_input abstract [" << index
<< "]: " << (loaded_input->abstract() ? loaded_input->abstract()->ToString() : "NULL");
MS_LOG(DEBUG) << "root_input abstract[" << index << "]: " << (root_input->abstract() ? root_input->abstract()->ToString() : "NULL");
MS_LOG(DEBUG) << "loaded_input abstract[" << index << "]: " << (loaded_input->abstract() ? loaded_input->abstract()->ToString() : "NULL");
auto root_shape = root_input->Shape() == nullptr ? nullptr : dyn_cast<abstract::Shape>(root_input->Shape());
auto loaded_shape = loaded_input->Shape() == nullptr ? nullptr : dyn_cast<abstract::Shape>(loaded_input->Shape());
auto root_type = root_input->Type() == nullptr ? nullptr : dyn_cast<Type>(root_input->Type());
auto loaded_type = loaded_input->Type() == nullptr ? nullptr : dyn_cast<Type>(loaded_input->Type());
// Get the shape and type of the inputs
auto root_shape = dyn_cast<abstract::Shape>(root_input->Shape());
auto loaded_shape = dyn_cast<abstract::Shape>(loaded_input->Shape());
auto root_type = dyn_cast<Type>(root_input->Type());
auto loaded_type = dyn_cast<Type>(loaded_input->Type());
// Check if the shape and type are not null
MS_EXCEPTION_IF_NULL(root_shape);
MS_EXCEPTION_IF_NULL(loaded_shape);
MS_EXCEPTION_IF_NULL(root_type);
MS_EXCEPTION_IF_NULL(loaded_type);
auto shapeEqu = (root_shape->shape() == loaded_shape->shape()) ||
(root_shape->shape().size() <= 1 && loaded_shape->shape().size() <= 1);
if (!shapeEqu) {
MS_EXCEPTION(ValueError) << "The " << index
<< " th input shape differ from loaded graph. Input shape: " << root_shape->ToString()
<< ", input shape of loaded graph: " << loaded_shape->ToString();
// Compare the shapes of the inputs, allowing for scalar inputs
auto shape_equal = (root_shape->shape() == loaded_shape->shape()) ||
(root_shape->shape().size() <= 1 && loaded_shape->shape().size() <= 1);
if (!shape_equal) {
MS_EXCEPTION(ValueError) << "The shape of the " << index << "th input differs between the root graph and the loaded graph."
<< " Input shape: " << root_shape->ToString() << ", input shape of loaded graph: " << loaded_shape->ToString();
}
// Compare the types of the inputs
if (root_type->type_id() != loaded_type->type_id()) {
MS_EXCEPTION(TypeError) << "The " << std::to_string(index)
<< " th input type differ from loaded graph. Input type: " << root_type->ToString()
<< ", input type of loaded graph: " << loaded_type->ToString();
MS_EXCEPTION(TypeError) << "The type of the " << index << "th input differs between the root graph and the loaded graph."
<< " Input type: " << root_type->ToString() << ", input type of loaded graph: " << loaded_type->ToString();
}
}
}
// Parse an action using the provided resource
bool ParseAction(const ResourcePtr &res) {
MS_EXCEPTION_IF_NULL(res);
// Enable recording of debug information
TraceManager::OpenRecordDebugInfoFlag();
// Check if the source input exists; if not, raise an exception
if (!res->source_input()) {
MS_LOG(EXCEPTION) << "Parse error";
MS_LOG(EXCEPTION) << "Parse error: Source input is null.";
}
// Retrieve the source input
py::object input = res->source_input();
// Initialize the parser environment with the input
parse::Parser::InitParserEnvironment(input);
// Import the 'os.path' module and retrieve the directory of the source file
py::module path = py::module::import("os.path");
std::string dir = path.attr("dirname")(py::globals()["__file__"]).cast<std::string>();
// Set Python environment flags and update Python path
python_adapter::set_python_env_flag(true);
python_adapter::SetPythonPath(dir);
// Convert the input data to a ValuePtr
ValuePtr converted_ret = nullptr;
bool converted = parse::ConvertData(input, &converted_ret, true);
// Check if the conversion was successful; if not, raise an exception
if (!converted) {
MS_LOG(EXCEPTION) << "Attribute convert error with type:" << std::string(py::str(input));
MS_LOG(EXCEPTION) << "Attribute convert error with type: " << std::string(py::str(input));
}
FuncGraphPtr top_graph = nullptr;
// Check if the input is an instance of Cell
if (py::isinstance<Cell>(input)) {
// If it is a Cell, create a top-level graph using the input and converted result
top_graph = parse::MakeTopGraph(input, converted_ret);
} else if (converted_ret->isa<FuncGraph>()) {
// If the converted result is a FuncGraph, use it as the top-level graph
top_graph = converted_ret->cast<FuncGraphPtr>();
} else {
MS_LOG(EXCEPTION) << "Object to parse " << std::string(py::str(input)) << " is not function or cell.";
// If the input is neither a Cell nor a FuncGraph, raise an exception
MS_LOG(EXCEPTION) << "Object to parse " << std::string(py::str(input)) << " is not a function or cell.";
}
// Update the top-level FuncGraph in the parser
parse::Parser::UpdateTopFuncGraph(top_graph);
// Set the top-level FuncGraph in the resource
res->set_func_graph(top_graph);
// Retrieve the FuncGraph manager from the resource
FuncGraphManagerPtr manager = res->manager();
// Check if the manager is not null; if it is null, raise an exception
if (manager == nullptr) {
MS_LOG(EXCEPTION) << "Manager is nullptr.";
}
// Add the top-level FuncGraph to the manager
manager->AddFuncGraph(top_graph);
// Return true to indicate successful parsing
return true;
}
// obj_map's graphs have the same construct, these graphs can be optimized to one graph.
// This step do this optimize: graph1(x){xx(fv1),xxx(fv2)}, graph2(x){xxx(fv3),xxx(fv4)}->
// graph1(x){base_graph(x, fv1, fv2)}, graph1(x){base_graph(x, fv3, fv4)}, base_graph(x, fv...){xxx,xxx}

File diff suppressed because it is too large Load Diff

View File

@ -39,6 +39,7 @@
namespace mindspore {
// Helper struct and function objects for handling parameter pointers
struct ParamPtrEqual {
bool operator()(AnfNodePtr const &t1, AnfNodePtr const &t2) const {
const ParameterPtr param1 = dyn_cast<Parameter>(t1);
@ -53,6 +54,7 @@ struct ParamPtrEqual {
};
struct ParamPtrHasher {
// Compute a hash for a parameter pointer
std::size_t operator()(AnfNodePtr const &param) const {
const ParameterPtr parameter = dyn_cast<Parameter>(param);
if (parameter == nullptr) {
@ -67,6 +69,7 @@ using ParamIndexMap = OrderedMap<AnfNodePtr, int, ParamPtrHasher, ParamPtrEqual,
class AnfExporter {
public:
// Constructor with optional parameters for exporting and integrity checking
explicit AnfExporter(bool export_used = true, bool check_integrity = false)
: param_index(1), export_used_(export_used), check_integrity_(check_integrity) {
func_graph_set.clear();

View File

@ -43,117 +43,206 @@
#include "utils/file_utils.h"
namespace mindspore {
// namespace to support debug trace information
// Namespace to support debug trace information
namespace trace {
using abstract::AbstractBasePtr;
using abstract::AnalysisContextPtr;
using abstract::AnalysisEnginePtr;
using abstract::AnfNodeConfigPtr;
// Helper function to get a string representation of an AbstractBasePtr
// Function definition for GetAbstractStr
std::string GetAbstractStr(const abstract::AbstractBasePtr &abs) {
// Check if the abstract pointer is null.
if (abs == nullptr) {
return "NullAbstract";
}
// Attempt to extract the shape and type information from the abstract.
auto shape = abs->BuildShape()->cast<abstract::ShapePtr>();
TypePtr type = abs->BuildType();
// Create an output string stream to construct the result.
std::ostringstream oss;
// Check if both shape and type information are available.
if ((shape != nullptr) && (type != nullptr)) {
oss << type->DumpText() << shape->DumpText();
} else if (type != nullptr) {
}
// Check if only type information is available.
else if (type != nullptr) {
oss << type->DumpText();
} else {
}
// If neither shape nor type information is available, label it as "Undefined."
else {
oss << "Undefined";
}
// Return the constructed string representation.
return oss.str();
}
// Helper function to get a string representation of a function graph and its arguments
std::string GetGraphParamString(const FuncGraphPtr &graph, const abstract::AbstractBasePtrList &args_spec_list) {
// Check if the function graph pointer is null.
MS_EXCEPTION_IF_NULL(graph);
// Create an output string stream to construct the result.
std::ostringstream oss;
// Append the function graph's name and indicate it is a graph.
oss << "graph:" << graph->ToString() << " with args[";
// Get the list of parameters from the function graph.
auto params = graph->parameters();
// Check if the number of parameters is less than the size of args_spec_list.
if (params.size() < args_spec_list.size()) {
MS_EXCEPTION(TypeError) << "The size of parameters less than args_spec_list's size.";
MS_EXCEPTION(TypeError) << "The size of parameters is less than args_spec_list's size.";
}
// Iterate over the args_spec_list and parameters, and construct a string representation.
for (size_t i = 0; i < args_spec_list.size(); i++) {
auto parameter = params[i];
MS_EXCEPTION_IF_NULL(parameter);
// Append the parameter's name and its corresponding abstract type.
oss << parameter->ToString() << ":<" << GetAbstractStr(args_spec_list[i]) << ">,";
}
// Close the argument list and append debug information.
oss << "]";
oss << GetDebugInfo(graph->debug_info(), kSourceLineTipDiscard);
// Return the constructed string representation.
return oss.str();
}
// Dump the inference stack
void DumpInferStack(std::ostringstream &oss) {
// Get the current graph evaluation stack.
auto &graph_stack = GetCurrentGraphEvalStack();
// Check if the stack is empty.
if (graph_stack.empty()) {
return;
}
// Create a vector to store pairs of AnalysisContextPtr and AnfNodeConfigPtr.
std::vector<std::pair<abstract::AnalysisContextPtr, abstract::AnfNodeConfigPtr>> infer_vec;
// Transfer the contents of the graph stack to infer_vec and reverse the order.
while (!graph_stack.empty()) {
auto top = graph_stack.back();
infer_vec.push_back(top);
graph_stack.pop_back();
}
std::reverse(infer_vec.begin(), infer_vec.end());
// Initialize an index for labeling.
int index = 0;
// Iterate through the inference stack and extract information.
for (const auto &item : infer_vec) {
auto context = item.first;
// Check if the context is null.
if (context == nullptr) {
MS_LOG(EXCEPTION) << "DumpInferStack failed, got null graph context";
}
auto graph = context->func_graph();
if (graph == nullptr) { // Top context.
auto graph = context->func_graph;
// Skip the top context, which may not have a valid graph.
if (graph == nullptr) {
continue;
}
auto args_spec_list = context->args_spec_list();
auto args_spec_list = context->args_spec_list;
// Check if the number of parameters in the graph is less than the size of args_spec_list.
if (graph->parameters().size() < args_spec_list.size()) {
continue;
}
// Append the graph information to the output stream.
oss << " #" << index++ << " " << GetGraphParamString(graph, args_spec_list) << "\n";
}
}
// Trace the evaluation of a graph
void TraceGraphEval() {
// Get the current graph evaluation stack.
auto &graph_stack = GetCurrentGraphEvalStack();
// Check if the stack is empty.
if (graph_stack.empty()) {
MS_LOG(INFO) << "Length of analysis graph stack is empty.";
return;
}
// Create a string stream to store the trace output.
std::ostringstream oss;
// Add a header to the trace output.
oss << "\n*******************************graph evaluate stack**********************************";
oss << std::endl;
// Dump the information from the inference stack into the output stream.
DumpInferStack(oss);
// Add a footer to the trace output.
oss << "\n*************************************************************************************";
// Log the trace output as INFO.
MS_LOG(INFO) << oss.str();
}
// Class for exporting analyzed function graphs
class AnalyzeFailExporter : public AnfExporter {
public:
AnalyzeFailExporter() : AnfExporter(true, false) {}
~AnalyzeFailExporter() override = default;
AnalyzeFailExporter() : AnfExporter(true, false) {} // Constructor
~AnalyzeFailExporter() override = default; // Destructor
// Export a function graph given a filename and a TraceCNodeEvalStack.
bool ExportFuncGraph(const std::string &filename, const TraceCNodeEvalStack &node_config_stack);
protected:
// Override function to output a CNode and its inputs.
void OutputCNode(std::ostringstream &oss, const CNodePtr &cnode, const FuncGraphPtr &func_graph, int *idx,
std::map<AnfNodePtr, int> *const apply_map) override;
// Override function to get the type of an AnfNode.
std::string GetNodeType(const AnfNodePtr &nd) override;
// Get the abstract type of an AnfNode.
AbstractBasePtr GetNodeAbstract(const AnfNodePtr &nd);
// Get the forward configuration for an AnfNode.
AnfNodeConfigPtr GetForwardConfig(const AnfNodeConfigPtr &cfg);
// Process a function graph call and update the operation comment.
void ProcessFuncGraphCall(const CNodePtr &node, std::string *const op_comment);
// Create a tagged node map for function graphs.
mindspore::HashMap<FuncGraphPtr, TaggedNodeMap> CreateTaggedNodeMap(
const std::vector<abstract::AnfNodeConfigPtr> &node_config_stack);
private:
AnalysisContextPtr current_context_ = nullptr;
AnalysisEnginePtr engine_ = nullptr;
AnalysisContextPtr current_context_ = nullptr; // Current analysis context
AnalysisEnginePtr engine_ = nullptr; // Analysis engine
};
// Helper function to create a mapping of function graphs to tagged nodes
mindspore::HashMap<FuncGraphPtr, TaggedNodeMap> AnalyzeFailExporter::CreateTaggedNodeMap(
const std::vector<abstract::AnfNodeConfigPtr> &node_config_stack) {
mindspore::HashSet<abstract::AnfNodeConfigPtr> forwarded_configs; // Check if config. is forwarded.
mindspore::HashSet<abstract::AnfNodeConfigPtr> forwarded_configs; // Check if config is forwarded.
mindspore::HashMap<FuncGraphPtr, TaggedNodeMap> tagged_func_graphs;
size_t index = 0;
for (auto &node_config : node_config_stack) {
@ -188,6 +277,7 @@ bool OutputAnalyzedGraphWithType(const string &file_path) {
std::string AnalyzeFailExporter::GetNodeType(const AnfNodePtr &node) {
if (current_context_ == nullptr) {
// If the current context is null, use the base class AnfExporter's GetNodeType.
return AnfExporter::GetNodeType(node);
}
@ -195,48 +285,72 @@ std::string AnalyzeFailExporter::GetNodeType(const AnfNodePtr &node) {
try {
FuncGraphPtr dummy_call_func_graph = nullptr;
auto cfg = engine_->MakeConfig(node, current_context_, dummy_call_func_graph);
// Attempt to retrieve the analysis result from the cache.
auto res = abstract::AnalysisResultCacheMgr::GetInstance().GetValue(cfg);
if (res != nullptr) {
// If the analysis result is available, return the type information.
return GetAbstractStr(res->abstract());
}
} catch (const std::exception &e) {
MS_LOG(INFO) << "Exception: " << e.what();
}
// Return "Undefined" if the analysis result is not available or if an exception occurs.
return "Undefined";
}
AbstractBasePtr AnalyzeFailExporter::GetNodeAbstract(const AnfNodePtr &node) {
if (current_context_ == nullptr) {
// If the current context is null, return nullptr (no abstract information available).
return nullptr;
}
MS_EXCEPTION_IF_NULL(engine_);
try {
FuncGraphPtr dummy_call_func_graph = nullptr;
auto cfg = engine_->MakeConfig(node, current_context_, dummy_call_func_graph);
// Attempt to retrieve the analysis result from the cache.
auto res = abstract::AnalysisResultCacheMgr::GetInstance().GetValue(cfg);
return res == nullptr ? nullptr : res->abstract();
// If the result is found, return its abstract; otherwise, return nullptr.
return (res == nullptr) ? nullptr : res->abstract();
} catch (const std::exception &e) {
MS_LOG(INFO) << "Exception: " << e.what();
}
// Return nullptr if the analysis result is not available or if an exception occurs.
return nullptr;
}
AnfNodeConfigPtr AnalyzeFailExporter::GetForwardConfig(const AnfNodeConfigPtr &cfg) {
MS_EXCEPTION_IF_NULL(cfg);
MS_EXCEPTION_IF_NULL(engine_);
AnfNodeConfigPtr cur_cfg = cfg;
// Look for the forward configuration in the engine's map.
auto iter = engine_->anfnode_config_map().find(cur_cfg);
while (iter != engine_->anfnode_config_map().end()) {
auto node = cur_cfg->node();
cur_cfg = iter->second;
MS_LOG(DEBUG) << "Get forward node: " << node << "[" << node->DebugString() << "] --> " << cur_cfg->node() << "["
<< cur_cfg->node()->DebugString() << "]";
// Continue searching for the next forward configuration.
iter = engine_->anfnode_config_map().find(cur_cfg);
}
// Return the ultimate configuration that is not forwarded.
return cur_cfg;
}
void AnalyzeFailExporter::ProcessFuncGraphCall(const CNodePtr &node, std::string *const op_comment) {
// Check if the input node is nullptr
if (node == nullptr) {
MS_LOG(ERROR) << "Node is nullptr";
return;
@ -244,30 +358,41 @@ void AnalyzeFailExporter::ProcessFuncGraphCall(const CNodePtr &node, std::string
CNodePtr cnode = nullptr;
try {
FuncGraphPtr dummy_call_func_graph = nullptr;
// Create a configuration for the node using the engine
auto cfg = engine_->MakeConfig(node, current_context_, dummy_call_func_graph);
// Get the forward configuration
cfg = GetForwardConfig(cfg);
MS_EXCEPTION_IF_NULL(cfg);
// Attempt to cast the configuration node to a CNode
cnode = dyn_cast<CNode>(cfg->node());
} catch (const std::exception &e) {
// Handle exceptions by logging the error message
MS_LOG(INFO) << "Exception: " << e.what();
}
// Check if the CNode is nullptr
if (cnode == nullptr) {
MS_LOG(INFO) << "CNode is nullptr";
return;
}
// Get the inputs of the CNode
const auto &inputs = cnode->inputs();
for (size_t i = 0; i < inputs.size(); ++i) {
// Get the abstract value of the input
auto op_abs = GetNodeAbstract(inputs[i]);
if (op_abs == nullptr) {
MS_LOG(DEBUG) << "Abstract of inputs[" << i << "] of cnode " << cnode->ToString() << " is nullptr";
// Log a message if the abstract value is nullptr
MS_LOG(DEBUG) << "Abstract of inputs[" << i << "] of cnode " << cnode->ToString() << " is nullptr";
continue;
}
// Check if the abstract value is not a function
if (!op_abs->isa<abstract::FuncGraphAbstractClosure>() && !op_abs->isa<abstract::MetaFuncGraphAbstractClosure>()) {
// Log a message indicating that the input is not a function
MS_LOG(DEBUG) << "Inputs[" << i << "] of cnode " << cnode->ToString() << " is of type " << op_abs->type_name()
<< ", not function, ignore it";
// Get prototype of VirtualEvaluator for printing
<< ", not a function, ignore it";
// Get the prototype of VirtualEvaluator for printing
if (i == 0 && op_abs->isa<abstract::VirtualAbstractClosure>()) {
auto func = dyn_cast<abstract::VirtualAbstractClosure>(op_abs);
std::ostringstream oss;
@ -282,50 +407,74 @@ void AnalyzeFailExporter::ProcessFuncGraphCall(const CNodePtr &node, std::string
oss << GetAbstractStr(arg);
}
oss << ") -> " << GetAbstractStr(func->output()) << " ";
// Store the prototype string in op_comment
*op_comment = oss.str();
}
}
}
}
void AnalyzeFailExporter::OutputCNode(std::ostringstream &oss, const CNodePtr &cnode, const FuncGraphPtr &func_graph,
int *idx, std::map<AnfNodePtr, int> *const apply_map) {
// Output the text representation of the CNode to the provided output stream
OutputCNodeText(oss, cnode, func_graph, idx, apply_map);
// Process function graph call
// Process the function graph call and generate a prototype comment
std::string op_comment;
ProcessFuncGraphCall(cnode, &op_comment);
// Check if op_comment is not empty (i.e., a prototype comment was generated)
if (!op_comment.empty()) {
auto &inputs = cnode->inputs();
// Output a comment with the prototype information
oss << " #" << GetAnfNodeText(func_graph, inputs[0], *apply_map) << ".prototype = " << op_comment;
}
// Output comment
// Output any additional comments associated with the CNode
OutputStatementComment(oss, cnode);
// Add a newline character to separate this CNode from the next one
oss << "\n";
}
// Export analyzed function graphs to a file
bool AnalyzeFailExporter::ExportFuncGraph(const std::string &filename, const TraceCNodeEvalStack &node_config_stack) {
// Check if the node_config_stack is empty
if (node_config_stack.empty()) {
MS_LOG(DEBUG) << "Node configs is empty";
return false;
}
// Create the real filepath for the export file
auto real_filepath = Common::CreatePrefixPath(filename);
if (!real_filepath.has_value()) {
MS_LOG(ERROR) << "The export ir path: " << filename << " is not illegal.";
MS_LOG(ERROR) << "The export ir path: " << filename << " is not legal.";
return false;
}
// Change the file mode to make it writable
ChangeFileMode(real_filepath.value(), S_IWUSR);
// Open the export file for writing
std::ofstream ofs(real_filepath.value());
if (!ofs.is_open()) {
MS_LOG(ERROR) << "Open file '" << real_filepath.value() << "' failed!" << ErrnoToString(errno);
return false;
}
// Set the engine if it is not already set
if (engine_ == nullptr) {
engine_ = node_config_stack.front()->engine();
}
// Create a map of tagged function graphs based on node_config_stack
auto tagged_func_graphs = CreateTaggedNodeMap(node_config_stack);
mindspore::HashSet<FuncGraphPtr> printed_func_graphs; // Check if func graph has been printed.
// Create a HashSet to keep track of printed function graphs
mindspore::HashSet<FuncGraphPtr> printed_func_graphs;
// Output graph on the analysis stack
for (const auto &node_config : node_config_stack) {
MS_EXCEPTION_IF_NULL(node_config);
@ -334,21 +483,33 @@ bool AnalyzeFailExporter::ExportFuncGraph(const std::string &filename, const Tra
<< ", FV: " << (node_config->func_graph() != node_config->context()->func_graph())
<< ", calling func graph: " << node_config->func_graph()->ToString()
<< ", context func graph: " << node_config->context()->func_graph()->ToString();
// Check if the function graph is null
if (fg == nullptr) {
MS_LOG(ERROR) << "FuncGraph is null, context: " << node_config->ToString();
continue;
}
// Check if the function graph has already been printed
if (printed_func_graphs.find(fg) != printed_func_graphs.end()) {
continue;
}
// Add the function graph to the printed set
(void)printed_func_graphs.emplace(fg);
current_context_ = node_config->context(); // Set current context.
// Set the current context
current_context_ = node_config->context();
// Create a buffer to store the export content for the function graph
std::ostringstream buffer;
ExportOneFuncGraph(buffer, fg, tagged_func_graphs[fg]);
// Write the content to the export file
ofs << buffer.str() << "\n\n";
}
// Output a separator and the number of function graphs processed
ofs << "#===============================================================================\n";
ofs << "# num of function graphs in stack: ";
auto ignored_num = (node_config_stack.size() - printed_func_graphs.size());
@ -358,70 +519,115 @@ bool AnalyzeFailExporter::ExportFuncGraph(const std::string &filename, const Tra
ofs << printed_func_graphs.size() << "/" << node_config_stack.size() << " (Ignored " << ignored_num
<< " internal frames).\n";
}
// Close the export file
ofs.close();
// Change the file mode to make it readable
ChangeFileMode(real_filepath.value(), S_IRUSR);
return true;
}
// Get the path for the "analyze_fail.dat" file
std::string GetEvalFailDatPath() {
std::string path;
// Try to get the "MS_OM_PATH" environment variable
auto ms_om_path = common::GetEnv("MS_OM_PATH");
// Check if the environment variable is set
if (!ms_om_path.empty()) {
// If set, use its value as the path
path = ms_om_path;
} else {
// If not set, use the current directory as the path
path = ".";
}
// Append the rank and the "/om/analyze_fail.dat" to the path
path += "/rank_" + std::to_string(GetRank()) + "/om/analyze_fail.dat";
// Support "../" in path.
// Support "../" in the path by creating a real path
auto realpath = Common::CreatePrefixPath(path, true);
// Check if the real path was successfully created
if (!realpath.has_value()) {
// If not, raise an exception with an error message
MS_EXCEPTION(ValueError) << "Get real path failed. path=" << path;
}
// Return the real path as the result
return realpath.value();
}
// Get evaluation stack information and write it to the provided output stream
void GetEvalStackInfo(std::ostringstream &oss) {
MS_LOG(INFO) << "Get graph analysis information begin";
// Get the CNode debug stack
auto stack = GetCNodeDebugStack();
// Check if the stack is empty
if (stack.empty()) {
MS_LOG(INFO) << "Length of analysis information stack is empty.";
return;
}
// Write a header to the output stream
oss << "\nThe function call stack";
// If security is not enabled, obtain the file name for "analyze_fail.dat" using GetEvalFailDatPath()
#ifndef ENABLE_SECURITY
std::string file_name = GetEvalFailDatPath();
auto ret = OutputAnalyzedGraphWithType(file_name);
// If writing to the file is successful, add a message to the output stream with the file name
if (ret) {
oss << " (See file '" << file_name << "' for more details)";
}
#endif
// Add a colon to the output stream to separate the header from the stack information
oss << ":\n";
int index = 0;
std::string last_location_info = "";
// Iterate over the stack elements
for (size_t i = 0; i < stack.size(); ++i) {
auto node_config = stack[i];
MS_EXCEPTION_IF_NULL(node_config);
auto cnode = dyn_cast<CNode>(node_config->node());
// Check if the CNode is nullptr
if (cnode == nullptr) {
MS_LOG(DEBUG) << "CNode of elements[" << i << "] is nullptr.";
continue;
}
// Get the debug information for the CNode
auto debug_info = cnode->debug_info();
auto this_location_info = trace::GetDebugInfo(debug_info, std::string(""));
// Check if the debug information is empty or identical to the previous one
if (this_location_info.empty() || this_location_info == last_location_info) {
continue;
}
// Update the last location information and add it to the output stream
last_location_info = this_location_info;
oss << "# " << index++ << " " << this_location_info;
}
// Clear the stack and log the end of graph analysis information
stack.clear();
MS_LOG(INFO) << "Get graph analysis information *end*";
}
// Trace the graph evaluator stack
thread_local TraceGraphEvalStack graph_infer_stack;
// Trace the cnode infer debug info
@ -468,23 +674,40 @@ void ClearTraceStack() {
cnode_debug_stack.clear();
}
// Get trace stack information and write it to the provided output stream
void GetTraceStackInfo(std::ostringstream &oss) {
// Trace the current graph evaluation
TraceGraphEval();
// Create an empty ostringstream to store evaluation stack information
std::ostringstream trace_info;
// Get evaluation stack information and store it in trace_info
GetEvalStackInfo(trace_info);
// Check if trace_info is empty
if (trace_info.str().empty()) {
// If it's empty, attempt to retrieve debug information using TraceManager
DebugInfoPtr debug_info = TraceManager::record_debug_info();
// Check if debug_info is not nullptr and the record_debug_info_flag is true
if (debug_info != nullptr && TraceManager::record_debug_info_flag() == true) {
// Get the debug information as a string
auto debug_str = trace::GetDebugInfo(debug_info);
// Check if the debug_str is not empty
if (!debug_str.empty()) {
// Add the debug_str to the output stream
oss << "\n\n# " << debug_str;
}
}
} else {
// If trace_info is not empty, add it to the output stream
oss << trace_info.str();
}
}
// Register trace provider to LogWriter.
struct TraceProviderRegister {
TraceProviderRegister() { LogWriter::set_trace_provider(GetTraceStackInfo); }

View File

@ -37,10 +37,19 @@
namespace mindspore {
namespace parse {
namespace {
// This structure is used to register a callback function for converting Python data to MindSpore values.
struct PyDataToValueRegister {
PyDataToValueRegister() { python_adapter::PyAdapterCallback::SetPyDataToValueHandler(data_converter::PyDataToValue); }
// Constructor of the structure.
PyDataToValueRegister() {
// Register the PyDataToValue handler from the Python adapter module.
python_adapter::PyAdapterCallback::SetPyDataToValueHandler(data_converter::PyDataToValue);
}
} callback_register;
} // namespace
// Define common data types and aliases for better readability.
using Tensor = mindspore::tensor::Tensor;
using TensorPtr = mindspore::tensor::TensorPtr;
using MetaTensor = mindspore::tensor::MetaTensor;
@ -50,21 +59,28 @@ using CSRTensorPtr = mindspore::tensor::CSRTensorPtr;
using COOTensor = mindspore::tensor::COOTensor;
using COOTensorPtr = mindspore::tensor::COOTensorPtr;
// Define function types for instance checking and conversion.
using InstanceCheckFunc = std::function<bool(const py::object &)>;
using InstanceConvertFunc = std::function<ValuePtr(const py::object &, bool, const TypePtr &)>;
// Constants for bit sizes.
static constexpr int kBit8 = 8;
static constexpr int kBit16 = 16;
static constexpr int kBit32 = 32;
static constexpr int kBit64 = 64;
// Class for data conversion.
class DataConverter {
public:
// Constructor for DataConverter, taking an instance conversion function.
explicit DataConverter(InstanceConvertFunc convert_func) : convert_func_(std::move(convert_func)) {}
virtual ~DataConverter() = default;
// Check if the given Python object matches the conversion type.
virtual bool Matched(const py::object &obj) = 0;
// Convert a Python object to a MindSpore value.
virtual ValuePtr ConvertPyObject(const py::object &obj, bool use_sig, const TypePtr &dtype) {
if (convert_func_ == nullptr) {
MS_LOG(EXCEPTION) << "convert func is null";
@ -82,29 +98,34 @@ using ArgsObjConvertFunc = std::function<ValuePtr(const py::object &)>;
using ArgsObjSigConvertFunc = std::function<ValuePtr(const py::object &, bool)>;
using ArgsOjbTypeConvertFunc = std::function<ValuePtr(const py::object &, const TypePtr &)>;
// Convert the data according instance type
// Template class for data conversion based on instance type.
template <typename T>
class ByTypeDataConverter : public DataConverter {
public:
// Constructor for ByTypeDataConverter, taking an instance conversion function.
explicit ByTypeDataConverter(const InstanceConvertFunc &convert_func)
: DataConverter(convert_func), check_func_(py::isinstance<T>) {}
// Constructor for ByTypeDataConverter, taking a converted type.
explicit ByTypeDataConverter(const ValuePtr &converted_type)
: DataConverter(
[converted_type](const py::object &, bool, const TypePtr &) -> ValuePtr { return converted_type; }),
check_func_(py::isinstance<T>) {}
// Constructor for ByTypeDataConverter, taking an argument object conversion function.
explicit ByTypeDataConverter(const ArgsObjConvertFunc &convert_func)
: DataConverter(
[convert_func](const py::object &obj, bool, const TypePtr &) -> ValuePtr { return convert_func(obj); }),
check_func_(py::isinstance<T>) {}
// Constructor for ByTypeDataConverter, taking an argument object with signature conversion function.
explicit ByTypeDataConverter(const ArgsObjSigConvertFunc &convert_func)
: DataConverter([convert_func](const py::object &obj, bool use_sig, const TypePtr &) -> ValuePtr {
return convert_func(obj, use_sig);
}),
check_func_(py::isinstance<T>) {}
// Constructor for ByTypeDataConverter, taking an argument object with type conversion function.
explicit ByTypeDataConverter(const ArgsOjbTypeConvertFunc &convert_func)
: DataConverter([convert_func](const py::object &obj, bool, const TypePtr &dtype) -> ValuePtr {
return convert_func(obj, dtype);
@ -113,13 +134,14 @@ class ByTypeDataConverter : public DataConverter {
~ByTypeDataConverter() override = default;
// Check if the given Python object matches the instance type.
bool Matched(const py::object &obj) override { return check_func_ != nullptr ? check_func_(obj) : false; }
private:
InstanceCheckFunc check_func_ = nullptr;
};
// Convert the data according object attribute.
// Data converter class for converting Python objects based on object attributes.
class ByAttrDataConverter : public DataConverter {
public:
ByAttrDataConverter(const std::string &attr_name, const ArgsObjConvertFunc &convert_func)
@ -135,28 +157,35 @@ class ByAttrDataConverter : public DataConverter {
~ByAttrDataConverter() override = default;
// Check if the given Python object has the specified attribute.
bool Matched(const py::object &obj) override { return py::hasattr(obj, attr_name_.c_str()); }
private:
std::string attr_name_;
};
// Convert the given Python object to a FuncGraphPtr.
FuncGraphPtr ConvertToBpropCut(const py::object &obj) {
// Extract the object key.
std::vector<std::string> results = data_converter::GetObjKey(obj);
std::string obj_key = results[0];
// Get the custom bprop function.
py::function bprop_func = py::getattr(obj, CUSTOM_BPROP_NAME);
// Create a new FuncGraph.
auto bprop_graph = std::make_shared<FuncGraph>();
std::vector<AnfNodePtr> outputs;
// Create a fake bprop Primitive with a backward hook.
auto fake_bprop = std::make_shared<PrimitivePy>("bprop_cut");
fake_bprop->AddBackwardHookFn(0, bprop_func);
(void)fake_bprop->AddAttr(CUSTOM_BPROP_NAME, MakeValue(true));
outputs.push_back(NewValueNode(fake_bprop));
// Extract parameters from the bprop function.
py::object code_obj = py::getattr(bprop_func, "__code__");
// Three parameters self, out and dout need to be excluded
constexpr auto kBpropExcludeParamNum = 3;
constexpr auto kBpropExcludeParamNum = 3; // Exclude self, out, and dout parameters
size_t inputs_num = py::cast<int64_t>(py::getattr(code_obj, "co_argcount")) - kBpropExcludeParamNum;
for (size_t i = 0; i < inputs_num; ++i) {
auto param = bprop_graph->add_parameter();
@ -167,12 +196,17 @@ FuncGraphPtr ConvertToBpropCut(const py::object &obj) {
outputs.push_back(p1);
outputs.push_back(p2);
// Set the output of the FuncGraph.
bprop_graph->set_output(bprop_graph->NewCNode(std::move(outputs)));
// Store the FuncGraph in the object graph value.
data_converter::SetObjGraphValue(obj_key, bprop_graph);
return bprop_graph;
}
namespace {
// Convert a Python tuple object to a ValuePtr.
ValuePtr ConvertTuple(const py::object &obj, bool use_signature) {
MS_LOG(DEBUG) << "Converting python tuple";
auto tuple = obj.cast<py::tuple>();
@ -188,6 +222,7 @@ ValuePtr ConvertTuple(const py::object &obj, bool use_signature) {
return std::make_shared<ValueTuple>(value_list);
}
// Convert a Python list object to a ValuePtr.
ValuePtr ConvertList(const py::object &obj, bool use_signature) {
MS_LOG(DEBUG) << "Converting python list";
@ -204,6 +239,7 @@ ValuePtr ConvertList(const py::object &obj, bool use_signature) {
return std::make_shared<ValueList>(value_list);
}
// Convert a Python cell list object to a ValuePtr.
ValuePtr ConvertCellList(const py::object &obj, bool use_signature) {
MS_LOG(DEBUG) << "Converting cell list";
py::sequence list = obj;
@ -219,6 +255,7 @@ ValuePtr ConvertCellList(const py::object &obj, bool use_signature) {
return std::make_shared<ValueTuple>(value_list);
}
// Convert a Python dict object to a ValuePtr.
ValuePtr ConvertDict(const py::object &obj, bool use_signature) {
MS_LOG(DEBUG) << "Converting python dict";
@ -240,6 +277,7 @@ ValuePtr ConvertDict(const py::object &obj, bool use_signature) {
return std::make_shared<ValueDictionary>(key_values);
}
// Convert a Python module namespace object to a ValuePtr.
ValuePtr ConvertModuleNameSpace(const py::object &obj) {
MS_LOG(DEBUG) << "Converting python module";
py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE);
@ -250,6 +288,7 @@ ValuePtr ConvertModuleNameSpace(const py::object &obj) {
return converted;
}
// Convert a Python data class object to a ValuePtr.
ValuePtr ConvertDataClass(const py::object &obj) {
MS_LOG(DEBUG) << "Converting dataclass";
// Maybe the obj is dataclass define
@ -258,7 +297,13 @@ ValuePtr ConvertDataClass(const py::object &obj) {
auto converted = std::make_shared<ClassObject>(obj, std::string(desc.begin() + 1, desc.end() - 1));
return converted;
}
// ConvertMsClass function
// This function converts a class instance decorated with ms_class to a ValuePtr.
// It takes a Python object 'obj' as input and returns a ValuePtr representing the class instance.
// Parameters:
// - obj: The Python object to be converted.
// Returns:
// - A ValuePtr representing the class instance.
ValuePtr ConvertMsClass(const py::object &obj) {
MS_LOG(DEBUG) << "Converting ms class";
// Convert class instance decorated with ms_class.
@ -268,14 +313,22 @@ ValuePtr ConvertMsClass(const py::object &obj) {
return std::make_shared<MsClassObject>(obj, cls_name);
}
// ConvertPrimitive function
// This function converts a primitive object to a ValuePtr.
// It takes a Python object 'obj' as input and returns a ValuePtr representing the primitive.
// Parameters:
// - obj: The Python object to be converted.
// - use_signature: A flag indicating whether to use signature for the primitive (default is false).
// Returns:
// - A ValuePtr representing the primitive.
ValuePtr ConvertPrimitive(const py::object &obj, bool use_signature = false) {
MS_LOG(DEBUG) << "Converting primitive object" << use_signature;
// need check the primitive is class type or instance
// Check if the primitive is a class type or instance
auto obj_type = data_converter::GetObjType(obj);
if (obj_type == RESOLVE_TYPE_CLASS_TYPE) {
auto desc = py::cast<std::string>(python_adapter::CallPyObjMethod(obj, PYTHON_GET_OBJ_DESC, obj));
// desc has format "<class xxxx>", strip the '<' and '>' by offset 1.
// 'desc' has format "<class xxxx>", strip the '<' and '>' by offset 1.
return std::make_shared<ClassType>(obj, std::string(desc.begin() + 1, desc.end() - 1));
}
py::object adapter_obj = obj;
@ -299,6 +352,14 @@ ValuePtr ConvertPrimitive(const py::object &obj, bool use_signature = false) {
return primitive;
}
// ConvertMetaFuncGraph function
// This function converts a MetaFuncGraph object to a ValuePtr.
// It takes a Python object 'obj' as input and returns a ValuePtr representing the MetaFuncGraph.
// Parameters:
// - obj: The Python object to be converted.
// - use_signature: A flag indicating whether to use signature for the MetaFuncGraph (default is false).
// Returns:
// - A ValuePtr representing the MetaFuncGraph.
ValuePtr ConvertMetaFuncGraph(const py::object &obj, bool use_signature = false) {
MS_LOG(DEBUG) << "Converting MetaFuncGraph object";
auto meta = obj.cast<MetaFuncGraphPtr>();
@ -312,6 +373,13 @@ ValuePtr ConvertMetaFuncGraph(const py::object &obj, bool use_signature = false)
return meta;
}
// ConvertFuncGraph function
// This function converts a FuncGraph object to a ValuePtr.
// It takes a Python object 'obj' as input and returns a ValuePtr representing the FuncGraph.
// Parameters:
// - obj: The Python object to be converted.
// Returns:
// - A ValuePtr representing the FuncGraph.
ValuePtr ConvertFuncGraph(const py::object &obj) {
MS_LOG(DEBUG) << "Converting FuncGraph object";
auto func_graph = obj.cast<FuncGraphPtr>();
@ -323,6 +391,13 @@ ValuePtr ConvertFuncGraph(const py::object &obj) {
return func_graph;
}
// ConvertSlice function
// This function converts a Python slice object to a ValuePtr.
// It takes a Python object 'obj' as input and returns a ValuePtr representing the slice.
// Parameters:
// - obj: The Python slice object to be converted.
// Returns:
// - A ValuePtr representing the slice.
ValuePtr ConvertSlice(const py::object &obj) {
MS_LOG(DEBUG) << "Converting slice object";
@ -347,13 +422,20 @@ ValuePtr ConvertSlice(const py::object &obj) {
return std::make_shared<ValueSlice>(start, stop, step);
}
// ConvertCellObjToFuncGraph function
// This function converts a Cell object to a FuncGraph and returns it as a ValuePtr.
// It takes a Python object 'obj' as input and returns a ValuePtr representing the FuncGraph.
// Parameters:
// - obj: The Python Cell object to be converted.
// Returns:
// - A ValuePtr representing the FuncGraph.
ValuePtr ConvertCellObjToFuncGraph(const py::object &obj) {
FuncGraphPtr func_graph = ConvertToFuncGraph(obj);
if (func_graph == nullptr) {
MS_LOG(ERROR) << "Parse resolve function error.";
return nullptr;
}
// if the cell object has specified bprop, it has user-defined bprop function parse and record it
// If the cell object has specified bprop, it has a user-defined bprop function parsed and recorded.
if (py::hasattr(obj, CUSTOM_BPROP_NAME)) {
bool enable_bprop_debug = py::cast<bool>(py::getattr(obj, "bprop_debug"));
FuncGraphPtr bprop_graph =
@ -371,13 +453,20 @@ ValuePtr ConvertCellObjToFuncGraph(const py::object &obj) {
return func_graph;
}
// ConvertOtherObj function
// This function converts other Python objects to ValuePtr.
// It takes a Python object 'obj' as input and returns a ValuePtr representing the object.
// Parameters:
// - obj: The Python object to be converted.
// Returns:
// - A ValuePtr representing the object.
ValuePtr ConvertOtherObj(const py::object &obj) {
auto obj_type = data_converter::GetObjType(obj);
MS_LOG(DEBUG) << "Converting the object(" << ((std::string)py::str(obj)) << ") detail type: " << obj_type << " ";
if (obj_type == RESOLVE_TYPE_CLASS_TYPE) {
MS_LOG(DEBUG) << "Resolve the class type, need create class instance.";
MS_LOG(DEBUG) << "Resolve the class type, need to create a class instance.";
std::string desc = py::str(obj);
// desc has format "<class xxxx>", strip the '<' and '>' by offset 1.
// 'desc' has format "<class xxxx>", strip the '<' and '>' by offset 1.
return std::make_shared<ClassType>(obj, std::string(desc.begin() + 1, desc.end() - 1));
}
if (obj_type == RESOLVE_TYPE_FUNCTION || obj_type == RESOLVE_TYPE_METHOD) {
@ -390,8 +479,8 @@ ValuePtr ConvertOtherObj(const py::object &obj) {
return func_graph;
}
if (obj_type == RESOLVE_TYPE_CLASS_INSTANCE) {
// Create the namespace for common class instance
// When the obj is Cell, default parse the 'construct'
// Create the namespace for common class instance.
// When the obj is Cell, default parse the 'construct'.
py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE);
py::object namespace_var = python_adapter::CallPyModFn(mod, PYTHON_MOD_GET_MEMBER_NAMESPACE_SYMBOL, obj);
auto res = std::make_shared<NameSpace>(RESOLVE_NAMESPACE_NAME_CLASS_MEMBER, namespace_var);
@ -399,8 +488,8 @@ ValuePtr ConvertOtherObj(const py::object &obj) {
return res;
}
// Start RESOLVE_TYPE_INVALID...
// The fallback feature is enabled in default.
// Not support change the flag during the process is alive.
// The fallback feature is enabled by default.
// Not supporting changing the flag while the process is alive.
static const auto support_fallback = common::GetEnv("MS_DEV_ENABLE_FALLBACK");
static const auto use_fallback = (support_fallback != "0");
if (use_fallback) {
@ -411,11 +500,23 @@ ValuePtr ConvertOtherObj(const py::object &obj) {
MS_LOG(ERROR) << "Resolve type is invalid, obj: " << py::str(obj);
return nullptr;
}
/**
* @brief Converts a number-like object of type T to a MindSpore Value with the specified dtype.
*
* This function takes an object of type T and a MindSpore TypePtr dtype as input and returns a MindSpore ValuePtr.
* It converts the object to the specified data type and wraps it as a ValuePtr.
*
* @tparam T The type of the input object (e.g., int64_t, float).
* @param obj The input object to be converted.
* @param dtype The target data type to convert the object to.
* @return A ValuePtr containing the converted value.
*/
template <typename T>
ValuePtr ConvertNumberWithType(const T &obj, const TypePtr &dtype) {
ValuePtr data = nullptr;
auto int_dypte = dyn_cast<Int>(dtype);
// Check if dtype is an integer type
if (int_dypte != nullptr) {
switch (int_dypte->nbits()) {
case kBit8:
@ -437,6 +538,8 @@ ValuePtr ConvertNumberWithType(const T &obj, const TypePtr &dtype) {
}
auto uint_dypte = dyn_cast<UInt>(dtype);
// Check if dtype is an unsigned integer type
if (uint_dypte != nullptr) {
switch (uint_dypte->nbits()) {
case kBit8:
@ -458,6 +561,8 @@ ValuePtr ConvertNumberWithType(const T &obj, const TypePtr &dtype) {
}
auto float_dypte = dyn_cast<Float>(dtype);
// Check if dtype is a floating-point type
if (float_dypte != nullptr) {
switch (float_dypte->nbits()) {
case kBit32:
@ -474,6 +579,17 @@ ValuePtr ConvertNumberWithType(const T &obj, const TypePtr &dtype) {
return nullptr;
}
/**
* @brief Converts a Python integer object to a MindSpore Value with the specified dtype.
*
* This function takes a Python integer object and an optional MindSpore TypePtr dtype as input,
* and returns a MindSpore ValuePtr containing the converted value with the specified dtype.
* If dtype is not provided, it defaults to Int64.
*
* @param obj The Python integer object to be converted.
* @param dtype The target data type to convert the integer to (optional).
* @return A ValuePtr containing the converted integer value.
*/
ValuePtr ConvertIntegerWithType(const py::object &obj, const TypePtr &dtype = nullptr) {
auto obj_int64 = py::cast<int64_t>(obj);
if (dtype == nullptr) {
@ -482,6 +598,17 @@ ValuePtr ConvertIntegerWithType(const py::object &obj, const TypePtr &dtype = nu
return ConvertNumberWithType<int64_t>(obj_int64, dtype);
}
/**
* @brief Converts a Python floating-point number object to a MindSpore Value with the specified dtype.
*
* This function takes a Python floating-point number object and an optional MindSpore TypePtr dtype as input,
* and returns a MindSpore ValuePtr containing the converted value with the specified dtype.
* If dtype is not provided, it defaults to FP32.
*
* @param obj The Python floating-point number object to be converted.
* @param dtype The target data type to convert the floating-point number to (optional).
* @return A ValuePtr containing the converted floating-point value.
*/
ValuePtr ConvertFloatWithType(const py::object &obj, const TypePtr &dtype = nullptr) {
auto obj_float64 = py::cast<float>(obj);
if (dtype == nullptr) {
@ -490,16 +617,41 @@ ValuePtr ConvertFloatWithType(const py::object &obj, const TypePtr &dtype = null
return ConvertNumberWithType<float>(obj_float64, dtype);
}
/**
* @brief Converts a Python object to a MindSpore Value of type T.
*
* This template function takes a Python object and returns a MindSpore ValuePtr containing the converted value of type T.
*
* @tparam T The target type for conversion (e.g., Tensor, MetaTensor, Variable).
* @param obj The Python object to be converted.
* @return A ValuePtr containing the converted value of type T.
*/
template <typename T, typename U>
ValuePtr PyCast(const py::object &obj) {
return std::make_shared<T>(py::cast<U>(obj));
}
/**
* @brief Converts a Python object to a MindSpore Value of type T.
*
* This template function takes a Python object and returns a MindSpore ValuePtr containing the converted value of type T.
*
* @tparam T The target type for conversion (e.g., Tensor, MetaTensor, Variable).
* @param obj The Python object to be converted.
* @return A ValuePtr containing the converted value of type T.
*/
template <typename T>
ValuePtr ObjCast(const py::object &obj) {
return obj.cast<T>();
}
/**
* @brief Gets a vector of DataConverterPtr objects for data conversion.
*
* This function returns a vector of DataConverterPtr objects, each responsible for converting specific Python data types to MindSpore Values.
*
* @return A vector of DataConverterPtr objects.
*/
static const std::vector<DataConverterPtr> &GetDataConverters() {
static const std::vector<DataConverterPtr> data_converters{
// Convert data by python object type.
@ -539,8 +691,19 @@ static const std::vector<DataConverterPtr> &GetDataConverters() {
};
return data_converters;
}
} // namespace
/**
* @brief Converts a Python object to a MindSpore Value.
*
* This function takes a Python object and returns a MindSpore ValuePtr containing the converted value.
* It iterates through a list of DataConverterPtr objects to find the appropriate converter for the given object.
*
* @param obj The Python object to be converted.
* @param data A pointer to a ValuePtr where the converted value will be stored.
* @param use_signature A boolean indicating whether to use a signature for conversion (default is false).
* @param dtype The target data type for conversion (optional).
* @return True if the conversion is successful, false otherwise.
*/
bool ConvertData(const py::object &obj, ValuePtr *data, bool use_signature, const TypePtr &dtype) {
// Check parameter valid
if (data == nullptr) {
@ -564,7 +727,18 @@ bool ConvertData(const py::object &obj, ValuePtr *data, bool use_signature, cons
return converted != nullptr;
}
// Convert data to graph
/**
* @brief Converts a Python object to a MindSpore FuncGraph.
*
* This function takes a Python object and a string indicating the method for parsing Python code.
* It returns a MindSpore FuncGraphPtr that represents the converted object.
* If the object has been previously cached, it is retrieved from the cache.
* If not, the object is parsed and converted to a FuncGraph, and the converted FuncGraph is cached for future use.
*
* @param obj The Python object to be converted to a FuncGraph.
* @param python_mod_get_parse_method The method for parsing Python code (e.g., "__getitem__").
* @return A FuncGraphPtr representing the converted object.
*/
FuncGraphPtr ConvertToFuncGraph(const py::object &obj, const std::string &python_mod_get_parse_method) {
std::vector<std::string> results = data_converter::GetObjKey(obj);
std::string obj_id = results[0] + python_mod_get_parse_method;
@ -599,24 +773,30 @@ FuncGraphPtr ConvertToFuncGraph(const py::object &obj, const std::string &python
return func_graph;
}
namespace data_converter {
// A map to store objects and their corresponding values.
static mindspore::HashMap<std::string, ValuePtr> object_map_;
// A map to store objects and their corresponding FuncGraphPtrs.
static mindspore::HashMap<std::string, std::vector<FuncGraphPtr>> object_graphs_map_;
// Set a FuncGraphPtr for an object key.
void SetObjGraphValue(const std::string &obj_key, const FuncGraphPtr &data) {
object_graphs_map_[obj_key].push_back(data);
MS_LOG(DEBUG) << "Set func graph size: " << object_graphs_map_.size();
}
// Get the map of object keys and their corresponding FuncGraphPtrs.
const mindspore::HashMap<std::string, std::vector<FuncGraphPtr>> &GetObjGraphs() {
MS_LOG(DEBUG) << "Obj graphs size: " << object_graphs_map_.size();
return object_graphs_map_;
}
// Cache a ValuePtr for an object key.
void CacheObjectValue(const std::string &obj_key, const ValuePtr &data) { object_map_[obj_key] = data; }
// Get the ValuePtr for an object key.
bool GetObjectValue(const std::string &obj_key, ValuePtr *data) {
if (object_map_.count(obj_key)) {
*data = object_map_[obj_key];
@ -625,6 +805,7 @@ bool GetObjectValue(const std::string &obj_key, ValuePtr *data) {
return false;
}
// Get object keys for a Python object.
std::vector<std::string> GetObjKey(const py::object &obj) {
py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE);
py::tuple obj_tuple = python_adapter::CallPyModFn(mod, PYTHON_MOD_RESOLVE_GET_OBJ_KEY, obj);
@ -634,7 +815,7 @@ std::vector<std::string> GetObjKey(const py::object &obj) {
return {py::cast<std::string>(obj_tuple[0]), py::cast<std::string>(obj_tuple[1])};
}
// Get obj detail type
// Get the ResolveTypeDef (type identifier) of a Python object.
ResolveTypeDef GetObjType(const py::object &obj) {
try {
py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE);
@ -642,15 +823,15 @@ ResolveTypeDef GetObjType(const py::object &obj) {
ResolveTypeDef(python_adapter::CallPyModFn(mod, PYTHON_MOD_RESOLVE_GET_OBJ_TYPE, obj).cast<int32_t>());
return obj_type;
} catch (const py::error_already_set &ex) {
MS_LOG(ERROR) << "Meet a exception from Python when get the type of \'" << py::str(obj) << "\'.\n" << ex.what();
MS_LOG(ERROR) << "Meet an exception from Python when getting the type of \'" << py::str(obj) << "\'.\n" << ex.what();
std::rethrow_exception(std::current_exception());
} catch (const py::type_error &ex) {
MS_LOG(ERROR) << "Meet a exception when get the type of \'" << py::str(obj) << "\'.\n" << ex.what();
MS_LOG(ERROR) << "Meet an exception when getting the type of \'" << py::str(obj) << "\'.\n" << ex.what();
std::rethrow_exception(std::current_exception());
}
}
// Get class instance detail type.
// Get the ClassInstanceTypeDef (class instance type identifier) of a Python object.
ClassInstanceTypeDef GetClassInstanceType(const py::object &obj) {
py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE);
auto class_type =
@ -658,22 +839,22 @@ ClassInstanceTypeDef GetClassInstanceType(const py::object &obj) {
return class_type;
}
// Check the object is Cell Instance.
// Check if the object is an instance of the Cell class.
bool IsCellInstance(const py::object &obj) {
auto class_type = GetClassInstanceType(obj);
bool is_cell = (class_type == CLASS_INSTANCE_TYPE_CELL);
return is_cell;
}
// Create the python class instance.
// Create a Python class instance.
py::object CreatePythonObject(const py::object &type, const py::tuple &args_kwargs) {
py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE);
// `args_kwargs` maybe a tuple(*args), tuple(**kwargs), or tuple(*args, **kwargs).
// `args_kwargs` may be a tuple(*args), tuple(**kwargs), or tuple(*args, **kwargs).
return args_kwargs.empty() ? python_adapter::CallPyModFn(mod, PYTHON_MOD_CREATE_INSTANCE, type)
: python_adapter::CallPyModFn(mod, PYTHON_MOD_CREATE_INSTANCE, type, args_kwargs);
}
// Call the python script string.
// Call a Python script string.
py::object CallPythonScript(const py::object &script, const py::tuple &args_kwargs) {
py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE);
// `args_kwargs` is a tuple(dict(global), dict(local)).
@ -681,12 +862,12 @@ py::object CallPythonScript(const py::object &script, const py::tuple &args_kwar
: python_adapter::CallPyModFn(mod, PYTHON_MOD_EVAL_PY_SCRIPT, script, args_kwargs);
}
// Generate an appropriate name and set to graph debuginfo,
// character <> can not used in the dot file, so change to another symbol.
// Generate an appropriate name and set it to the FuncGraph's debuginfo.
// Characters '<' and '>' cannot be used in the dot file, so they are replaced with '「' and '」'.
void MakeProperNameToFuncGraph(const FuncGraphPtr &func_graph, std::string name) {
MS_EXCEPTION_IF_NULL(func_graph);
MS_EXCEPTION_IF_NULL(func_graph->debug_info());
// Set detail name info of function
// Set detailed name info of the function
std::ostringstream oss;
for (size_t i = 0; i < name.size(); i++) {
if (name[i] == '<') {
@ -700,6 +881,7 @@ void MakeProperNameToFuncGraph(const FuncGraphPtr &func_graph, std::string name)
func_graph->debug_info()->set_full_name(oss.str());
}
// Convert a Python object to a ValuePtr.
ValuePtr PyDataToValue(const py::object &obj) {
py::object to_convert = obj;
ValuePtr value = nullptr;
@ -707,15 +889,18 @@ ValuePtr PyDataToValue(const py::object &obj) {
return value;
}
// Clear the object cache.
void ClearObjectCache() {
object_map_.clear();
object_graphs_map_.clear();
}
} // namespace data_converter
// A map to store data class names and their corresponding ClassPtrs.
static mindspore::HashMap<std::string, ClassPtr> g_dataClassToClass = {};
// Parse dataclass to mindspore Class type
// Parse a data class to a mindspore Class type.
ClassPtr ParseDataClass(const py::object &cls_obj) {
std::string cls_name = py::cast<std::string>(python_adapter::GetPyObjAttr(cls_obj, "__name__"));
std::string cls_module = py::cast<std::string>(python_adapter::GetPyObjAttr(cls_obj, "__module__"));
@ -745,8 +930,7 @@ ClassPtr ParseDataClass(const py::object &cls_obj) {
}
std::shared_ptr<Class> me_class = std::make_shared<Class>(Named(cls_name), attributes, methods_map);
// static Variable for cache
// cppcheck-suppress unreadVariable
// Static Variable for cache
g_dataClassToClass[cls] = me_class;
return me_class;

View File

@ -30,30 +30,57 @@
namespace mindspore {
namespace parse {
// data convert for parse
namespace data_converter {
// Cache the ValuePtr associated with the given object key.
void CacheObjectValue(const std::string &obj_key, const ValuePtr &data);
// Get the ValuePtr associated with the given object key.
// Returns true if the data is found, and false otherwise.
bool GetObjectValue(const std::string &obj_key, ValuePtr *const data);
// Set the FuncGraphPtr associated with the given object key.
void SetObjGraphValue(const std::string &obj_key, const FuncGraphPtr &data);
// Get a mapping of object keys to lists of FuncGraphPtrs.
const mindspore::HashMap<std::string, std::vector<FuncGraphPtr>> &GetObjGraphs();
// Get a list of object keys for the provided Python object.
std::vector<std::string> GetObjKey(const py::object &obj);
// Determine the type of the provided Python object.
ResolveTypeDef GetObjType(const py::object &obj);
// Determine the type of a class instance Python object.
ClassInstanceTypeDef GetClassInstanceType(const py::object &obj);
// Check if the provided Python object is an instance of a cell.
bool IsCellInstance(const py::object &obj);
// Create a Python object of the specified type with the given arguments and keyword arguments.
py::object CreatePythonObject(const py::object &type, const py::tuple &args_kwargs);
// Call a Python script (function) with the provided arguments and keyword arguments.
py::object CallPythonScript(const py::object &script, const py::tuple &args_kwargs);
// Ensure that the given FuncGraph has a proper name for referencing.
void MakeProperNameToFuncGraph(const FuncGraphPtr &func_graph, std::string name);
// Convert a Python object to a MindSpore ValuePtr.
ValuePtr PyDataToValue(const py::object &obj);
// Clear the object cache, removing all cached objects.
void ClearObjectCache();
} // namespace data_converter
// Parse and return the MindSpore ClassPtr for the given Python class object.
ClassPtr ParseDataClass(const py::object &cls_obj);
// Convert the given Python object to a Bprop cut FuncGraph.
FuncGraphPtr ConvertToBpropCut(const py::object &obj);
// Clear the mapping of data classes to MindSpore classes.
void CleanDataClassToClassMap();
} // namespace parse

View File

@ -244,30 +244,57 @@ AnfNodePtr FunctionBlock::MakeResolveClassMember(const std::string &attr) {
MS_LOG(DEBUG) << "name_space: " << name_space->ToString() << ", symbol: " << symbol->ToString();
return MakeResolve(name_space, symbol);
}
// GetResolveNode function
// Creates a Resolve node for the given Python tuple 'info'.
// Parameters:
// - info: A Python tuple containing namespace and symbol information.
// Returns:
// An AnfNodePtr representing the Resolve node.
AnfNodePtr FunctionBlock::GetResolveNode(const py::tuple &info) {
constexpr size_t namespace_index = 0;
constexpr size_t symbol_index = 1;
// Create a NameSpacePtr from the namespace information.
NameSpacePtr name_space = std::make_shared<NameSpace>(RESOLVE_NAMESPACE_NAME_SYMBOL_STR, info[namespace_index]);
// Create a SymbolPtr from the symbol information.
SymbolPtr symbol = std::make_shared<Symbol>(info[symbol_index].cast<std::string>());
// Create and return the Resolve node.
return MakeResolve(name_space, symbol);
}
// HandleNamespaceInfo function
// Handles namespace information in the given Python tuple 'info'.
// Parameters:
// - info: A Python tuple containing namespace and symbol information.
// Returns:
// An AnfNodePtr representing the Resolve node.
AnfNodePtr FunctionBlock::HandleNamespaceInfo(const py::tuple &info) {
constexpr size_t namespace_index = 0;
constexpr size_t symbol_index = 1;
constexpr size_t namespace_info_size = 2;
// Check if the tuple has the correct size.
if (info.size() != namespace_info_size) {
MS_EXCEPTION(NameError) << "namespace info size should be 2, but got " << info.size();
}
// If namespace is None, the symbol is an undefined name.
// If the namespace is None, it means the symbol is an undefined name.
if (info[namespace_index].is_none()) {
MS_EXCEPTION(NameError) << info[symbol_index].cast<std::string>();
}
// Get and return the Resolve node.
return GetResolveNode(info);
}
// HandleBuiltinNamespaceInfo function
// Handles builtin namespace information in the given Python tuple 'info'.
// Parameters:
// - info: A Python tuple containing namespace, symbol, value, and flag information.
// Returns:
// An AnfNodePtr representing the Resolve node.
AnfNodePtr FunctionBlock::HandleBuiltinNamespaceInfo(const py::tuple &info) {
constexpr size_t closure_info_size = 2;
constexpr size_t namespace_info_size = 4;
@ -275,22 +302,28 @@ AnfNodePtr FunctionBlock::HandleBuiltinNamespaceInfo(const py::tuple &info) {
constexpr size_t symbol_index = 1;
constexpr size_t value_index = 2;
constexpr size_t flag_index = 3;
// Check if the tuple has the correct size.
if (info.size() != closure_info_size && info.size() != namespace_info_size) {
MS_EXCEPTION(NameError) << "namespace info size should be 2 or 4, but got " << info.size();
}
// Handle closure namespace info.
if (info.size() == closure_info_size) {
// If namespace is None, the symbol is an undefined name.
// If the namespace is None, it means the symbol is an undefined name.
if (info[namespace_index].is_none()) {
MS_EXCEPTION(NameError) << info[symbol_index].cast<std::string>();
}
// Get and return the Resolve node.
return GetResolveNode(info);
}
// Handle global namespace info.
auto resolved_node = GetResolveNode(info);
auto syntax_support = info[flag_index].cast<int32_t>();
// Set interpret flags based on syntax support.
if (syntax_support != SYNTAX_SUPPORTED) {
resolved_node->set_interpret(true);
if (syntax_support == SYNTAX_UNSUPPORTED_INTERNAL_TYPE) {
@ -300,18 +333,31 @@ AnfNodePtr FunctionBlock::HandleBuiltinNamespaceInfo(const py::tuple &info) {
resolved_node->set_interpret_special_type(true);
}
}
// Create a Symbol from the symbol information.
SymbolPtr symbol = std::make_shared<Symbol>(info[symbol_index].cast<std::string>());
// Get the Python object and add it as a global Python parameter.
py::object py_obj = info[value_index];
AddGlobalPyParam(symbol->name(), py_obj);
// Log the added global Python symbol.
MS_LOG(INFO) << "[" << func_graph()->ToString() << "] Added global python symbol: {" << symbol->name() << " : "
<< py::str(py_obj) << "}";
return resolved_node;
}
// Make a resolve node for symbol string
// MakeResolveSymbol function
// Creates a Resolve node for the given symbol string 'value'.
// Parameters:
// - value: A symbol string.
// Returns:
// An AnfNodePtr representing the Resolve node.
AnfNodePtr FunctionBlock::MakeResolveSymbol(const std::string &value) {
MS_LOG(DEBUG) << "value: " << value;
// The prefix of value is "self.".
// Check if the symbol string starts with "self.".
if (value.compare(0, strlen("self"), "self") == 0) {
auto start = value.find_first_of('.') + 1;
if (start >= value.size()) {
@ -319,14 +365,18 @@ AnfNodePtr FunctionBlock::MakeResolveSymbol(const std::string &value) {
return nullptr;
}
auto bits_str = value.substr(start);
// Create and return a ResolveClassMember node.
return MakeResolveClassMember(bits_str);
}
auto ast = parser_.ast();
MS_EXCEPTION_IF_NULL(ast);
// The fallback feature is enabled in default.
// Not support change the flag during the process is alive.
// Use fallback feature if enabled.
static const auto use_fallback = (parser_.support_fallback() != "0");
// Choose the appropriate parsing method based on the fallback feature.
if (!use_fallback) {
py::tuple namespace_info = ast->CallParserObjMethod(PYTHON_PARSE_GET_NAMESPACE_SYMBOL, value);
return HandleNamespaceInfo(namespace_info);
@ -335,42 +385,84 @@ AnfNodePtr FunctionBlock::MakeResolveSymbol(const std::string &value) {
return HandleBuiltinNamespaceInfo(namespace_info);
}
}
// MakeResolveOperation function
// This function creates a resolve operation for a given value.
// Parameters:
// - value: A string representing the value to be resolved.
// Returns:
// - An AnfNodePtr representing the resolved operation.
AnfNodePtr FunctionBlock::MakeResolveOperation(const std::string &value) {
auto ast = parser_.ast();
MS_EXCEPTION_IF_NULL(ast);
// Call Python AST parsing function to get the operation namespace symbol.
py::tuple namespace_var = ast->CallParseModFunction(PYTHON_PARSE_GET_OPERATION_NAMESPACE_SYMBOL, value);
const size_t namespace_var_size = 2;
if (namespace_var.size() < namespace_var_size) {
MS_EXCEPTION(NameError) << "namespace_var is less than 2";
}
// Create a NameSpacePtr and a SymbolPtr from the parsed values.
NameSpacePtr name_space = std::make_shared<NameSpace>(RESOLVE_NAMESPACE_NAME_COMMON_OPS, namespace_var[0]);
SymbolPtr symbol = std::make_shared<Symbol>(namespace_var[1].cast<std::string>());
MS_LOG(DEBUG) << "name_space: " << name_space->ToString() << ", symbol: " << symbol->ToString();
// Create and return the resolved operation.
return MakeResolve(name_space, symbol);
}
// MakeResolve function
// This function creates a resolve operation given a NameSpacePtr and a SymbolPtr.
// Parameters:
// - name_space: A NameSpacePtr representing the namespace of the resolved symbol.
// - resolve_symbol: A SymbolPtr representing the symbol to be resolved.
// Returns:
// - An AnfNodePtr representing the resolved operation.
AnfNodePtr FunctionBlock::MakeResolve(const NameSpacePtr &name_space, const SymbolPtr &resolve_symbol) {
MS_LOG(DEBUG) << "MakeResolve for " << (name_space ? (std::string)py::str(name_space->obj()) : "null namespace")
<< " , " << (resolve_symbol ? (std::string)resolve_symbol->symbol() : "null resolve symbol.");
// Create ValueNodePtrs for the name_space and resolve_symbol.
ValueNodePtr module_node = NewValueNode(name_space);
ValueNodePtr symbol_node = NewValueNode(resolve_symbol);
// Create a new CNode representing the resolve operation.
auto node = func_graph_->NewCNodeInOrder({NewValueNode(prim::kPrimResolve), module_node, symbol_node});
return node;
}
// MakeInterpret function
// This function creates an interpret operation given a script text, global_dict_node, local_dict_node, and orig_node.
// Parameters:
// - script_text: A string representing the script text to be interpreted.
// - global_dict_node: An AnfNodePtr representing the global dictionary node.
// - local_dict_node: An AnfNodePtr representing the local dictionary node.
// - orig_node: An AnfNodePtr representing the original node for interpretation.
// Returns:
// - An AnfNodePtr representing the interpret operation.
AnfNodePtr FunctionBlock::MakeInterpret(const std::string &script_text, const AnfNodePtr &global_dict_node,
const AnfNodePtr &local_dict_node, const AnfNodePtr &orig_node) {
MS_LOG(DEBUG) << "MakeInterpret for " << script_text;
// Create a ScriptPtr from the script text.
ScriptPtr script = std::make_shared<Script>(script_text);
auto script_node = NewValueNode(script);
// Create a new CNode representing the interpret operation.
auto node = func_graph_->NewCNodeInOrder(
{NewValueNode(prim::kPrimPyInterpret), script_node, global_dict_node, local_dict_node});
// Set the interpret_internal_type of the node.
node->set_interpret_internal_type(orig_node->interpret_internal_type());
return node;
}
// Add input for the block's phi parameter
// SetPhiArgument function
// This function sets the phi argument for the block.
// Parameters:
// - phi: A ParameterPtr representing the phi parameter.
void FunctionBlock::SetPhiArgument(const ParameterPtr &phi) {
MS_EXCEPTION_IF_NULL(phi);
TraceGuard trace_guard(std::make_shared<TraceResolve>(phi->debug_info()));
@ -378,12 +470,15 @@ void FunctionBlock::SetPhiArgument(const ParameterPtr &phi) {
MS_LOG(DEBUG) << "graph " << (func_graph_ ? func_graph_->ToString() : "FG(Null)") << " set phi " << phi->ToString()
<< " for var `" << var << "`";
auto removable = CollectRemovablePhi(phi);
// If the phi node is not necessary, not need to add to jumps_ of the prev blocks.
if (removable) {
MS_LOG(DEBUG) << "remove the phi when call graph " << (func_graph_ ? func_graph_->ToString() : "FG(Null)")
<< " var `" << var << "`";
return;
}
// Add the phi argument to jumps_ of the previous blocks.
for (auto &pred : prev_blocks_) {
MS_EXCEPTION_IF_NULL(pred);
MS_LOG(DEBUG) << "graph " << (func_graph_ ? func_graph_->ToString() : "FG(Null)") << " pred_blocks_ "
@ -395,9 +490,18 @@ void FunctionBlock::SetPhiArgument(const ParameterPtr &phi) {
}
}
// SearchReplaceNode function
// This function searches and replaces a node with the given var and phi parameter.
// Parameters:
// - var: A string representing the variable to be searched and replaced.
// - phi: A ParameterPtr representing the phi parameter.
// Returns:
// - An AnfNodePtr representing the node that may replace the phi parameter, or nullptr if no replacement is found.
AnfNodePtr FunctionBlock::SearchReplaceNode(const std::string &var, const ParameterPtr &phi) {
AnfNodePtr arg_node = nullptr;
MS_LOG(DEBUG) << "Prev_blocks size: " << prev_blocks_.size();
// Iterate through previous blocks and search for the replacement node.
for (auto &prev : prev_blocks_) {
MS_EXCEPTION_IF_NULL(prev);
AnfNodePtr temp_node = prev->ReadVariable(var);
@ -505,14 +609,24 @@ void FunctionBlock::Mature() {
}
matured_ = true;
}
// FunctionBlock class represents a block of code in a function graph.
// Force the condition node to bool using bool operation
// Force the condition node to bool using a bool operation.
// Parameters:
// - cond: The condition node to be forced to bool.
// Returns:
// - CNodePtr: A new CNode representing the bool operation applied to the condition node.
CNodePtr FunctionBlock::ForceToBoolNode(const AnfNodePtr &cond) {
MS_EXCEPTION_IF_NULL(cond);
CNodePtr op_apply_node = func_graph_->NewCNodeInOrder({MakeResolveOperation(NAMED_PRIMITIVE_BOOL), cond});
return op_apply_node;
}
// Force the condition node to a while condition using "while_cond" operation.
// Parameters:
// - cond: The condition node to be forced to a while condition.
// Returns:
// - CNodePtr: A new CNode representing the "while_cond" operation applied to the condition node.
CNodePtr FunctionBlock::ForceToWhileCond(const AnfNodePtr &cond) {
MS_EXCEPTION_IF_NULL(cond);
TraceGuard trace_guard(std::make_shared<TraceForceWhileCond>(cond->debug_info()));
@ -520,12 +634,15 @@ CNodePtr FunctionBlock::ForceToWhileCond(const AnfNodePtr &cond) {
return op_apply_node;
}
// Perform a jump from this block to target block
// Perform a jump from this block to the target block with optional arguments.
// Parameters:
// - target_block: The target FunctionBlockPtr to jump to.
// - args: Optional arguments to pass to the target block.
void FunctionBlock::Jump(const FunctionBlockPtr &target_block, const std::vector<AnfNodePtr> &args) {
MS_LOG(DEBUG) << "Jump from block: " << ToString() << " to block: " << target_block->ToString();
MS_EXCEPTION_IF_NULL(target_block);
if (is_dead_block_) {
MS_LOG(DEBUG) << "Dead code block should not jump to other block! block: " << ToString();
MS_LOG(DEBUG) << "Dead code block should not jump to another block! block: " << ToString();
return;
}
if (func_graph_->get_return() != nullptr) {
@ -542,8 +659,14 @@ void FunctionBlock::Jump(const FunctionBlockPtr &target_block, const std::vector
func_graph_->set_output(jump);
}
// Perform a conditional jump using switch operation.
// The first CNode select graph with condition, and than execute this graph
// Perform a conditional jump using the "Switch" operation.
// The first CNode selects the graph based on the condition, and then executes the selected graph.
// Parameters:
// - cond_node: The condition node to decide which path to take.
// - true_block_call: The CNode representing the true branch.
// - false_block_call: The CNode representing the false branch.
// Returns:
// - CNodePtr: A new CNode representing the conditional jump using the "Switch" operation.
CNodePtr FunctionBlock::ConditionalJump(const AnfNodePtr &cond_node, const AnfNodePtr &true_block_call,
const AnfNodePtr &false_block_call) {
MS_EXCEPTION_IF_NULL(true_block_call);
@ -559,6 +682,13 @@ CNodePtr FunctionBlock::ConditionalJump(const AnfNodePtr &cond_node, const AnfNo
return switch_app_new;
}
// Overloaded function for ConditionalJump, taking FunctionBlockPtr for true and false branches.
// Parameters:
// - cond_node: The condition node to decide which path to take.
// - true_block: The FunctionBlockPtr representing the true branch.
// - false_block: The FunctionBlockPtr representing the false branch.
// Returns:
// - CNodePtr: A new CNode representing the conditional jump using the "Switch" operation.
CNodePtr FunctionBlock::ConditionalJump(const AnfNodePtr &cond_node, const FunctionBlockPtr &true_block,
const FunctionBlockPtr &false_block) {
MS_EXCEPTION_IF_NULL(true_block);
@ -566,8 +696,11 @@ CNodePtr FunctionBlock::ConditionalJump(const AnfNodePtr &cond_node, const Funct
return ConditionalJump(cond_node, NewValueNode(true_block->func_graph()), NewValueNode(false_block->func_graph()));
}
// Create cnode for the assign statement like 'self.target = source'.
// convert it to 'P.Assign(self.target, source)' and then add the cnode as isolate node.
// Create a CNode for the assign statement like 'self.target = source'.
// Converts it to 'P.Assign(self.target, source)' and then adds the CNode as an isolated node.
// Parameters:
// - target: The target node of the assignment.
// - source: The source node of the assignment.
void FunctionBlock::SetStateAssign(const AnfNodePtr &target, const AnfNodePtr &source) {
const std::string primitive_name("assign");
const std::string module_name("mindspore.ops.functional");
@ -580,6 +713,7 @@ void FunctionBlock::SetStateAssign(const AnfNodePtr &target, const AnfNodePtr &s
AddIsolatedNode(assign_node);
}
void FunctionBlock::FindIsolatedNodes() {
//
// Search isolate nodes from variables, for example,
@ -619,16 +753,27 @@ void FunctionBlock::FindIsolatedNodes() {
}
}
}
// AddIsolatedNode function
// Add an isolated node to the FunctionBlock's isolated_nodes_ container.
// Parameters:
// - target: The AnfNodePtr to add.
void FunctionBlock::AddIsolatedNode(const AnfNodePtr &target) {
isolated_nodes_.add(target);
}
void FunctionBlock::AddIsolatedNode(const AnfNodePtr &target) { isolated_nodes_.add(target); }
// AttachIsolatedNodesBeforeReturn function
// Attaches isolated nodes before the return node in the function graph.
// This function creates a new state node that combines the isolated nodes and attaches it before the return node.
// Parameters: None
void FunctionBlock::AttachIsolatedNodesBeforeReturn() {
if (isolated_nodes_.empty()) {
return;
}
std::vector<AnfNodePtr> states;
states.emplace_back(NewValueNode(prim::kPrimMakeTuple));
constexpr int recursive_level = 2;
for (auto &node : isolated_nodes_) {
MS_EXCEPTION_IF_NULL(node);
MS_LOG(DEBUG) << "Adding dependency, node: " << node->DebugString(recursive_level) << " in "
@ -641,10 +786,11 @@ void FunctionBlock::AttachIsolatedNodesBeforeReturn() {
}
}
isolated_nodes_.clear();
AnfNodePtr state = nullptr;
constexpr size_t no_state_size = 1;
constexpr size_t only_one_state_size = 2;
if (states.size() == no_state_size) {
// Only MakeTuple, no state left.
return;
@ -658,11 +804,13 @@ void FunctionBlock::AttachIsolatedNodesBeforeReturn() {
state->debug_info()->set_location(nullptr);
}
}
AnfNodePtr old_output = nullptr;
auto return_node = func_graph_->get_return();
if (return_node) {
const size_t return_input_size = 2;
if (return_node->inputs().size() < return_input_size) {
MS_LOG(EXCEPTION) << "Length of inputs of output node is less than 2";
}
@ -670,14 +818,18 @@ void FunctionBlock::AttachIsolatedNodesBeforeReturn() {
} else {
old_output = NewValueNode(kNone);
}
AnfNodePtr stop_grad_node = func_graph_->NewCNode({NewValueNode(prim::kPrimStopGradient), state});
CNodePtr depend_node = func_graph_->NewCNode({NewValueNode(prim::kPrimDepend), old_output, stop_grad_node});
if (stop_grad_node->debug_info()) {
stop_grad_node->debug_info()->set_location(nullptr);
}
if (depend_node->debug_info()) {
depend_node->debug_info()->set_location(nullptr);
}
// We add this attribute for @constexpr use scene, since we must infer them before other nodes.
// That means isolated nodes will be evaluated first. It's not complete, but works in most scenes.
depend_node->AddAttr(kAttrTopoSortRhsFirst, MakeValue(true));
@ -685,12 +837,17 @@ void FunctionBlock::AttachIsolatedNodesBeforeReturn() {
MS_LOG(INFO) << "Attached for side-effect nodes, depend_node: " << depend_node->DebugString()
<< ", state: " << state->DebugString(recursive_level);
func_graph_->set_output(depend_node, true);
if (return_node && return_node->debug_info()) {
auto new_return = func_graph_->get_return();
new_return->set_debug_info(return_node->debug_info());
}
}
void FunctionBlock::SetAsDeadBlock() { is_dead_block_ = true; }
// SetAsDeadBlock function
// Mark this FunctionBlock as a dead block, indicating it should not be processed further.
void FunctionBlock::SetAsDeadBlock() {
is_dead_block_ = true;
}
} // namespace parse
} // namespace mindspore

View File

@ -45,112 +45,378 @@ using FunctionBlockPtr = std::shared_ptr<FunctionBlock>;
// A function block is a straight-line code sequence with no branches, every block has one one exit point
// which is return. When parsing function, loop or branch , we use function block to track the structure of
// the original source code.
/**
* @brief Represents a block of code in the parser.
*
* This class is responsible for managing function blocks and their operations.
* It provides methods for handling variables, jumps, conditionals, resolving symbols, and more.
*/
class FunctionBlock : public std::enable_shared_from_this<FunctionBlock> {
public:
/**
* @brief Constructor for FunctionBlock.
*
* @param parser The parser associated with this FunctionBlock.
*/
explicit FunctionBlock(const Parser &parser);
/**
* @brief Destructor for FunctionBlock.
*/
virtual ~FunctionBlock() = default;
/**
* @brief Get the function graph associated with this FunctionBlock.
*
* @return The function graph.
*/
FuncGraphPtr func_graph() { return func_graph_; }
/**
* @brief Convert the FunctionBlock to a string representation.
*
* @return A string representing the FunctionBlock.
*/
std::string ToString() const { return func_graph_->ToString(); }
/**
* @brief Write a variable to the FunctionBlock.
*
* @param var_name The name of the variable.
* @param node The AnfNodePtr representing the variable.
*/
void WriteVariable(const std::string &var_name, const AnfNodePtr &node);
/**
* @brief Read a variable from the FunctionBlock.
*
* @param var_name The name of the variable to read.
* @return The AnfNodePtr representing the variable.
*/
AnfNodePtr ReadVariable(const std::string &var_name);
/**
* @brief Add a previous FunctionBlock as a predecessor.
*
* @param block The FunctionBlock to add as a predecessor.
*/
void AddPrevBlock(const FunctionBlockPtr &block);
/**
* @brief Set the phi argument for a parameter.
*
* @param phi The ParameterPtr to set the phi argument for.
*/
void SetPhiArgument(const ParameterPtr &phi);
/**
* @brief Collect removable phi nodes.
*
* @param phi The ParameterPtr representing the phi node.
* @return True if the phi node is collected, false otherwise.
*/
bool CollectRemovablePhi(const ParameterPtr &phi);
// A block is matured if all its predecessors is generated
/**
* @brief Mark the block as matured when all its predecessors are generated.
*/
void Mature();
/**
* @brief Create a CNode for forcing an AnfNode to a boolean value.
*
* @param cond The AnfNodePtr representing the condition.
* @return The CNodePtr representing the boolean conversion.
*/
CNodePtr ForceToBoolNode(const AnfNodePtr &cond);
/**
* @brief Create a CNode for forcing an AnfNode to be a while loop condition.
*
* @param cond The AnfNodePtr representing the condition.
* @return The CNodePtr representing the while loop condition.
*/
CNodePtr ForceToWhileCond(const AnfNodePtr &cond);
/**
* @brief Jump to another FunctionBlock with given arguments.
*
* @param block The FunctionBlockPtr to jump to.
* @param args The vector of AnfNodePtr representing the arguments for the jump.
*/
void Jump(const FunctionBlockPtr &block, const std::vector<AnfNodePtr> &args);
/**
* @brief Search and replace a node with a phi parameter.
*
* @param var The name of the variable to search for.
* @param phi The ParameterPtr representing the phi node for replacement.
* @return The AnfNodePtr after the search and replace operation.
*/
AnfNodePtr SearchReplaceNode(const std::string &var, const ParameterPtr &phi);
/**
* @brief Create a conditional jump CNode based on the condition node.
*
* @param cond_node The AnfNodePtr representing the condition.
* @param true_block_call The AnfNodePtr representing the true branch of the conditional jump.
* @param false_block_call The AnfNodePtr representing the false branch of the conditional jump.
* @return The CNodePtr representing the conditional jump.
*/
CNodePtr ConditionalJump(const AnfNodePtr &cond_node, const AnfNodePtr &true_block_call,
const AnfNodePtr &false_block_call);
/**
* @brief Create a conditional jump CNode based on the condition node.
*
* @param cond_node The AnfNodePtr representing the condition.
* @param true_block The FunctionBlockPtr representing the true branch of the conditional jump.
* @param false_block The FunctionBlockPtr representing the false branch of the conditional jump.
* @return The CNodePtr representing the conditional jump.
*/
CNodePtr ConditionalJump(const AnfNodePtr &cond_node, const FunctionBlockPtr &true_block,
const FunctionBlockPtr &false_block);
// Create cnode for the assign statement like self.target = source.
/**
* @brief Create a CNode for the assign statement like self.target = source.
*
* @param target The AnfNodePtr representing the target variable.
* @param source The AnfNodePtr representing the source variable.
*/
void SetStateAssign(const AnfNodePtr &target, const AnfNodePtr &source);
/**
* @brief Add a global variable.
*
* @param var_name The name of the global variable to add.
*/
void AddGlobalVar(const std::string &var_name) { (void)global_vars_.insert(var_name); }
/**
* @brief Check if a variable is a global variable.
*
* @param var_name The name of the variable to check.
* @return True if the variable is a global variable, false otherwise.
*/
bool IsGlobalVar(const std::string &var_name) { return global_vars_.find(var_name) != global_vars_.end(); }
/**
* @brief Create a ResolveAst op.
*
* @param op The py::object representing the op to resolve.
* @return The AnfNodePtr representing the ResolveAst op.
*/
AnfNodePtr MakeResolveAstOp(const py::object &op);
/**
* @brief Create a ResolveClassMember op.
*
* @param attr The name of the attribute to resolve.
* @return The AnfNodePtr representing the ResolveClassMember op.
*/
AnfNodePtr MakeResolveClassMember(const std::string &attr);
/**
* @brief Create a ResolveSymbol op.
*
* @param value The name of the symbol to resolve.
* @return The AnfNodePtr representing the ResolveSymbol op.
*/
AnfNodePtr MakeResolveSymbol(const std::string &value);
/**
* @brief Create a ResolveOperation op.
*
* @param value The name of the operation to resolve.
* @return The AnfNodePtr representing the ResolveOperation op.
*/
AnfNodePtr MakeResolveOperation(const std::string &value);
AnfNodePtr MakeResolve(const std::shared_ptr<NameSpace> &name_space, const std::shared_ptr<Symbol> &resolve_symbol);
/**
* @brief Create a Resolve op.
*
* @param name_space The shared pointer to NameSpace.
* @param resolve_symbol The shared pointer to Symbol.
* @return The AnfNodePtr representing the Resolve op.
*/
AnfNodePtr MakeResolve(const std::shared_ptr<NameSpace> &name_space,
const std::shared_ptr<Symbol> &resolve_symbol);
/**
* @brief Get the ResolveNode based on the namespace info.
*
* @param namespace_info The py::tuple representing the namespace info.
* @return The AnfNodePtr representing the ResolveNode.
*/
AnfNodePtr GetResolveNode(const py::tuple &namespace_info);
/**
* @brief Handle the namespace info.
*
* @param namespace_info The py::tuple representing the namespace info.
* @return The AnfNodePtr representing the resolved node.
*/
AnfNodePtr HandleNamespaceInfo(const py::tuple &namespace_info);
/**
* @brief Handle the built-in namespace info.
*
* @param namespace_info The py::tuple representing the namespace info.
* @return The AnfNodePtr representing the resolved node.
*/
AnfNodePtr HandleBuiltinNamespaceInfo(const py::tuple &namespace_info);
/**
* @brief Make an Interpret op.
*
* @param script_text The script text to interpret.
* @param global_dict_node The global dictionary node.
* @param local_dict_node The local dictionary node.
* @param orig_node The original node.
* @return The AnfNodePtr representing the Interpret op.
*/
AnfNodePtr MakeInterpret(const std::string &script_text, const AnfNodePtr &global_dict_node,
const AnfNodePtr &local_dict_node, const AnfNodePtr &orig_node);
/**
* @brief Get the collection of removable phi nodes.
*
* @return The HashMap of ParameterPtr and AnfNodePtr representing removable phi nodes.
*/
const mindspore::HashMap<ParameterPtr, AnfNodePtr> &removable_phis() const { return removable_phis_; }
/**
* @brief Find isolated nodes in the FunctionBlock.
*/
void FindIsolatedNodes();
/**
* @brief Add an isolated node to the FunctionBlock.
*
* @param target The AnfNodePtr representing the isolated node to add.
*/
void AddIsolatedNode(const AnfNodePtr &target);
/**
* @brief Attach isolated nodes before return.
*/
void AttachIsolatedNodesBeforeReturn();
/**
* @brief Get the list of previous FunctionBlocks.
*
* @return The vector of FunctionBlock pointers representing previous blocks.
*/
const std::vector<FunctionBlock *> &prev_blocks() const { return prev_blocks_; }
/**
* @brief Check if the FunctionBlock is a dead block.
*
* @return True if the FunctionBlock is a dead block, false otherwise.
*/
bool is_dead_block() const { return is_dead_block_; }
/**
* @brief Set the FunctionBlock as a dead block.
*/
void SetAsDeadBlock();
const py::dict &global_py_params() const { return global_py_params_; }
void set_global_py_params(const py::dict &symbols) { global_py_params_ = symbols; }
void AddGlobalPyParam(const std::string &name, const py::object &obj) { global_py_params_[py::str(name)] = obj; }
void UpdateGlobalPyParam(const py::dict &symbols) {
for (auto &param : symbols) {
if (!global_py_params_.contains(param.first)) {
global_py_params_[param.first] = param.second;
}
}
}
// global_py_params() - Getter for global Python parameters.
// Returns: A reference to the global Python parameters as a Python dictionary.
const py::dict &global_py_params() const { return global_py_params_; }
std::tuple<std::map<std::string, AnfNodePtr>, std::map<std::string, AnfNodePtr>> local_py_params() {
return {local_py_params_keys_, local_py_params_values_};
}
void AddLocalPyParam(const std::string &name, const AnfNodePtr &node) {
MS_LOG(DEBUG) << "Add '" << name << "', " << node->DebugString();
(void)local_py_params_keys_.insert(std::pair<std::string, AnfNodePtr>(name, NewValueNode(name)));
(void)local_py_params_values_.insert(std::pair<std::string, AnfNodePtr>(name, node));
}
// set_global_py_params() - Setter for global Python parameters.
// Parameters:
// symbols - A Python dictionary containing the global Python parameters.
void set_global_py_params(const py::dict &symbols) { global_py_params_ = symbols; }
// Call this methon only if you need update a variable. Usually variable override.
void UpdateLocalPyParam(const std::string &name, const AnfNodePtr &node) {
auto key_iter = local_py_params_keys_.find(name);
if (key_iter == local_py_params_keys_.end()) {
MS_LOG(EXCEPTION) << "Only for updating. Should not call this method if '" << name << "' not exist.";
}
// Find the same position in 'values', and update the node.
MS_LOG(DEBUG) << "Update '" << name << "', " << local_py_params_values_[name]->DebugString() << " -> "
<< node->DebugString();
local_py_params_values_[name] = node;
}
// AddGlobalPyParam() - Add a Python object to the global Python parameters.
// Parameters:
// name - The name of the Python object.
// obj - The Python object to be added.
void AddGlobalPyParam(const std::string &name, const py::object &obj) { global_py_params_[py::str(name)] = obj; }
void EraseLocalPyParam(const std::string &name) {
auto key_iter = local_py_params_keys_.find(name);
auto value_iter = local_py_params_values_.find(name);
if (key_iter != local_py_params_keys_.end() && value_iter != local_py_params_values_.end()) {
MS_LOG(DEBUG) << "Erase '" << name << "' from local_py_params, the key node:" << key_iter->second->DebugString()
<< ", the value node:" << value_iter->second->DebugString();
local_py_params_keys_.erase(key_iter);
local_py_params_values_.erase(value_iter);
// UpdateGlobalPyParam() - Update the global Python parameters with new values.
// Parameters:
// symbols - A Python dictionary containing the new values to update.
void UpdateGlobalPyParam(const py::dict &symbols) {
for (auto &param : symbols) {
if (!global_py_params_.contains(param.first)) {
global_py_params_[param.first] = param.second;
}
}
}
void UpdateLocalPyParam(const std::map<std::string, AnfNodePtr> &keys, std::map<std::string, AnfNodePtr> values) {
if (keys.size() != values.size()) {
MS_LOG(EXCEPTION) << "keys size should be equal to values size.";
}
for (auto iter = keys.begin(); iter != keys.end(); ++iter) {
const std::string &cur_key_name = iter->first;
if (local_py_params_keys_.find(cur_key_name) == local_py_params_keys_.end()) {
(void)local_py_params_keys_.insert(std::pair<std::string, AnfNodePtr>(cur_key_name, iter->second));
(void)local_py_params_values_.insert(std::pair<std::string, AnfNodePtr>(cur_key_name, values[cur_key_name]));
MS_LOG(DEBUG) << "Add '" << iter->second->DebugString() << "', " << values[cur_key_name]->DebugString();
} else {
MS_LOG(DEBUG) << "Update '" << iter->second->DebugString() << "', " << values[cur_key_name]->DebugString();
local_py_params_values_[cur_key_name] = values[cur_key_name];
}
}
if (local_py_params_keys_.size() != local_py_params_values_.size()) {
MS_LOG(EXCEPTION) << "local_py_params_keys_ size should be equal to local_py_params_values_ size.";
// local_py_params() - Getter for local Python parameters.
// Returns: A tuple of two maps - one containing the keys and the other containing the corresponding values.
std::tuple<std::map<std::string, AnfNodePtr>, std::map<std::string, AnfNodePtr>> local_py_params() {
return {local_py_params_keys_, local_py_params_values_};
}
// AddLocalPyParam() - Add a local Python parameter.
// Parameters:
// name - The name of the Python parameter.
// node - The AnfNodePtr corresponding to the Python parameter.
void AddLocalPyParam(const std::string &name, const AnfNodePtr &node) {
MS_LOG(DEBUG) << "Add '" << name << "', " << node->DebugString();
(void)local_py_params_keys_.insert(std::pair<std::string, AnfNodePtr>(name, NewValueNode(name)));
(void)local_py_params_values_.insert(std::pair<std::string, AnfNodePtr>(name, node));
}
// UpdateLocalPyParam() - Update a local Python parameter.
// Call this method only if you need to update an existing variable.
// Parameters:
// name - The name of the Python parameter to be updated.
// node - The new AnfNodePtr value to update with.
void UpdateLocalPyParam(const std::string &name, const AnfNodePtr &node) {
auto key_iter = local_py_params_keys_.find(name);
if (key_iter == local_py_params_keys_.end()) {
MS_LOG(EXCEPTION) << "Only for updating. Should not call this method if '" << name << "' does not exist.";
}
// Find the same position in 'values' and update the node.
MS_LOG(DEBUG) << "Update '" << name << "', " << local_py_params_values_[name]->DebugString() << " -> "
<< node->DebugString();
local_py_params_values_[name] = node;
}
// EraseLocalPyParam() - Erase a local Python parameter.
// Parameters:
// name - The name of the Python parameter to be erased.
void EraseLocalPyParam(const std::string &name) {
auto key_iter = local_py_params_keys_.find(name);
auto value_iter = local_py_params_values_.find(name);
if (key_iter != local_py_params_keys_.end() && value_iter != local_py_params_values_.end()) {
MS_LOG(DEBUG) << "Erase '" << name << "' from local_py_params, the key node:" << key_iter->second->DebugString()
<< ", the value node:" << value_iter->second->DebugString();
local_py_params_keys_.erase(key_iter);
local_py_params_values_.erase(value_iter);
}
}
// UpdateLocalPyParam() - Update multiple local Python parameters with their corresponding keys and values.
// Parameters:
// keys - A map of Python parameter names to AnfNodePtr keys.
// values - A map of Python parameter names to AnfNodePtr values.
void UpdateLocalPyParam(const std::map<std::string, AnfNodePtr> &keys, std::map<std::string, AnfNodePtr> values) {
if (keys.size() != values.size()) {
MS_LOG(EXCEPTION) << "keys size should be equal to values size.";
}
for (auto iter = keys.begin(); iter != keys.end(); ++iter) {
const std::string &cur_key_name = iter->first;
if (local_py_params_keys_.find(cur_key_name) == local_py_params_keys_.end()) {
(void)local_py_params_keys_.insert(std::pair<std::string, AnfNodePtr>(cur_key_name, iter->second));
(void)local_py_params_values_.insert(std::pair<std::string, AnfNodePtr>(cur_key_name, values[cur_key_name]));
MS_LOG(DEBUG) << "Add '" << iter->second->DebugString() << "', " << values[cur_key_name]->DebugString();
} else {
MS_LOG(DEBUG) << "Update '" << iter->second->DebugString() << "', " << values[cur_key_name]->DebugString();
local_py_params_values_[cur_key_name] = values[cur_key_name];
}
}
if (local_py_params_keys_.size() != local_py_params_values_.size()) {
MS_LOG(EXCEPTION) << "local_py_params_keys_ size should be equal to local_py_params_values_ size.";
}
}
private:
// Block graph

File diff suppressed because it is too large Load Diff

View File

@ -366,63 +366,148 @@ class AstNodeType {
};
using AstNodeTypePtr = std::shared_ptr<AstNodeType>;
// A helper class to parse python function
/// \brief A helper class to parse Python functions and their abstract syntax trees (AST).
///
/// This class provides functionality to parse Python functions and retrieve information about their AST.
/// It can be used to analyze Python code and extract various details about the functions and methods.
///
/// \param obj The Python object representing the function or method to be parsed.
class ParseFunctionAst {
public:
/// \brief Constructor for ParseFunctionAst.
/// \param obj The Python object representing the function or method to be parsed.
explicit ParseFunctionAst(const py::object &obj)
: obj_(obj), target_type_(PARSE_TARGET_UNKNOW), function_line_offset_(-1) {}
: obj_(obj), target_type_(PARSE_TARGET_UNKNOWN), function_line_offset_(-1) {}
/// \brief Destructor for ParseFunctionAst.
~ParseFunctionAst() = default;
/// \brief Initializes the parsing information.
///
/// This function initializes the parsing information by calling Python functions to retrieve the AST.
///
/// \param python_mod_get_parse_method Python module method for obtaining parse information.
/// \return True if the initialization is successful, false otherwise.
bool InitParseAstInfo(const std::string &python_mod_get_parse_method = PYTHON_MOD_GET_PARSE_METHOD);
/// \brief Retrieves the AST node.
/// \return The Python object representing the AST node.
py::object GetAstNode();
/// \brief Retrieves the text representation of an AST node.
///
/// This function takes an AST node and returns its text representation.
///
/// \param node The Python object representing the AST node.
/// \return The text representation of the AST node.
py::str GetAstNodeText(const py::object &node);
/// \brief Retrieves the arguments of a function or method.
///
/// This function takes a function or method node and returns a list of its arguments.
///
/// \param func_node The Python object representing the function or method node.
/// \return A list of argument names.
py::list GetArgs(const py::object &func_node);
/// \brief Retrieves the default values of arguments in a function or method.
///
/// This function takes a function or method node and returns a list of default values for its arguments.
///
/// \param func_node The Python object representing the function or method node.
/// \return A list of default values for arguments.
py::list GetArgsDefaultValues(const py::object &func_node);
/// \brief Retrieves the node type of an AST node.
///
/// This function takes an AST node and returns its node type.
///
/// \param node The Python object representing the AST node.
/// \return A shared pointer to the node type.
AstNodeTypePtr GetNodeType(const py::object &node);
/// \brief Retrieves the operation type of an AST node.
///
/// This function takes an AST node and returns its operation type.
///
/// \param node The Python object representing the AST node.
/// \return The operation type of the AST node.
AstSubType GetOpType(const py::object &node);
/// \brief Calls a Python object method with variable arguments.
///
/// This function calls a Python object's method with variable arguments.
///
/// \param method The name of the method to call.
/// \param args The variable number of arguments to pass to the method.
/// \return The result of the method call.
template <class... T>
py::object CallParserObjMethod(const std::string &method, const T &... args) {
return python_adapter::CallPyObjMethod(parser_, method, args...);
}
/// \brief Calls a Python module function with variable arguments.
///
/// This function calls a Python module's function with variable arguments.
///
/// \param function The name of the function to call.
/// \param args The variable number of arguments to pass to the function.
/// \return The result of the function call.
template <class... T>
py::object CallParseModFunction(const std::string &function, const T &... args) {
return python_adapter::CallPyModFn(module_, function, args...);
}
/// \brief Gets the name of the parsed function.
/// \return The name of the parsed function.
const std::string &function_name() const { return function_name_; }
/// \brief Gets the module of the parsed function.
/// \return The module of the parsed function.
const std::string &function_module() const { return function_module_; }
/// \brief Gets the filename of the parsed function.
/// \return The filename of the parsed function.
const std::string &function_filename() const { return function_filename_; }
/// \brief Gets the line offset of the parsed function.
/// \return The line offset of the parsed function.
int64_t function_line_offset() const { return function_line_offset_; }
/// \brief Gets the Python function object.
/// \return The Python function object.
py::function function() { return function_; }
/// \brief Gets the target type of the parsed function.
/// \return The target type of the parsed function.
ParseTargetTypeDef target_type() const { return target_type_; }
/// \brief Gets the Python object representing the parsed function or method.
/// \return The Python object representing the parsed function or method.
py::object obj() { return obj_; }
/// \brief Gets the Python object representing the parser.
/// \return The Python object representing the parser.
py::object parser() { return parser_; }
/// \brief Gets the Python object representing the module.
/// \return The Python object representing the module.
py::object module() { return module_; }
/// \brief Gets the Python object representing the AST tree.
/// \return The Python object representing the AST tree.
py::object ast_tree() { return ast_tree_; }
/// \brief Checks if a given AST node is a class member.
///
/// This function checks if the given AST node is a member of a class.
///
/// \param node The Python object representing the AST node.
/// \return True if the node is a class member, false otherwise.
bool IsClassMember(const py::object &node);
private:
// Save obj,eg: class instance or function
// Save obj, e.g., class instance or function
py::object obj_;
// Function or class method.

View File

@ -25,17 +25,31 @@
#include "mindspore/core/ir/cell.h"
namespace mindspore::parse {
// A set to store cell input argument names.
static mindspore::HashSet<std::string> cell_input_args_ = {};
// A set of cell types that should be ignored when checking for dynamic behavior.
static const std::set<std::string> ignore_judge_dynamic_cell = {
"Cell mindspore.nn.layer.basic.Dense", "Cell mindspore.nn.probability.distribution.normal.Normal",
"Cell src.transformer.create_attn_mask.CreateAttentionMaskFromInputMask", "Cell mindspore.nn.layer.math.MatMul"};
// A set of named primitives that should not be considered for dynamic behavior.
static const std::set<std::string> unchanged_named_primitive = {
parse::NAMED_PRIMITIVE_ATTRIBUTE, parse::NAMED_PRIMITIVE_NAMECONSTANT, parse::NAMED_PRIMITIVE_CONSTANT,
parse::NAMED_PRIMITIVE_NUM, parse::NAMED_PRIMITIVE_STR};
std::string DynamicParser::ParseNodeName(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &node,
parse::AstMainType type) {
MS_EXCEPTION_IF_NULL(ast);
// ParseNodeName function
// This function extracts the name of a Python node and validates its type.
// Parameters:
// ast: A shared pointer to the ParseFunctionAst.
// node: The Python node to be parsed.
// type: The expected AST main type of the node.
// Returns:
// The name of the node as a string.
std::string DynamicParser::ParseNodeName(const std::shared_ptr<parse::ParseFunctionAst> &ast,
const py::object &node, parse::AstMainType type) {
MS_EXCEPTION_IF_NULL(ast);
if (py::isinstance<py::none>(node)) {
MS_LOG(DEBUG) << "Get none type node!";
return "";
@ -53,6 +67,11 @@ std::string DynamicParser::ParseNodeName(const std::shared_ptr<parse::ParseFunct
return node_name;
}
// ParseInputArgs function
// This function extracts input argument names from a Python function node.
// Parameters:
// ast: A shared pointer to the ParseFunctionAst.
// fn_node: The Python function node.
void DynamicParser::ParseInputArgs(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &fn_node) {
MS_EXCEPTION_IF_NULL(ast);
py::list args = ast->GetArgs(fn_node);
@ -63,21 +82,42 @@ void DynamicParser::ParseInputArgs(const std::shared_ptr<parse::ParseFunctionAst
}
}
// ParseIfWhileExprNode function
// This function parses if/while expression nodes and checks for dynamic behavior.
// Parameters:
// ast: A shared pointer to the ParseFunctionAst.
// node: The Python node to be parsed.
// Returns:
// True if dynamic behavior is detected, false otherwise.
// Parse an if/while expression node
bool DynamicParser::ParseIfWhileExprNode(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &node) {
MS_LOG(DEBUG) << "Parse if/while expr";
// Get the 'test' node from the input node
py::object test_node = python_adapter::GetPyObjAttr(node, parse::NAMED_PRIMITIVE_TEST);
// Parse the node name of the 'test' node
const auto &node_name = ParseNodeName(ast, test_node, parse::AST_MAIN_TYPE_EXPR);
// Check if the 'test' node is a comparison operation
if (node_name == parse::NAMED_PRIMITIVE_COMPARE) {
// Get the left and right nodes of the comparison operation
py::object left_node = python_adapter::GetPyObjAttr(test_node, parse::NAMED_PRIMITIVE_LEFT);
py::list comparators_node = python_adapter::GetPyObjAttr(test_node, parse::NAMED_PRIMITIVE_COMPARATORS);
// Check if comparators_node is empty
if (comparators_node.empty()) {
MS_LOG(DEBUG) << "Get comparators node failed!";
return false;
}
// Parse the node names of the left and right nodes
auto left = ParseNodeName(ast, left_node, parse::AST_MAIN_TYPE_EXPR);
auto right = ParseNodeName(ast, comparators_node[0], parse::AST_MAIN_TYPE_EXPR);
// while self.a > self.b and changed self.a or self.b
// Check if the comparison is between two attributes (e.g., self.a > self.b)
if (left == parse::NAMED_PRIMITIVE_ATTRIBUTE && right == parse::NAMED_PRIMITIVE_ATTRIBUTE) {
// Get the values of the left and right attributes
auto left_value = python_adapter::GetPyObjAttr(left_node, parse::NAMED_PRIMITIVE_VALUE);
std::string left_variable;
if (py::hasattr(left_node, "attr") && py::hasattr(left_value, "id")) {
@ -89,120 +129,248 @@ bool DynamicParser::ParseIfWhileExprNode(const std::shared_ptr<parse::ParseFunct
right_variable =
py::cast<std::string>(right_value.attr("id")) + py::cast<std::string>(comparators_node[0].attr("attr"));
}
// Parse the body context with the left and right variables
return ParseBodyContext(ast, node, {left_variable, right_variable});
}
// if a[0]
// Check if the comparison involves a subscript operation (e.g., if a[0])
if (left == parse::NAMED_PRIMITIVE_SUBSCRIPT) {
py::object value_in_subscript = python_adapter::GetPyObjAttr(left_node, parse::NAMED_PRIMITIVE_VALUE);
left = ParseNodeName(ast, value_in_subscript, parse::AST_MAIN_TYPE_EXPR);
}
MS_LOG(DEBUG) << "Left is " << left << " Right is " << right;
// Check if the comparison involves any unchanged named primitives
if (unchanged_named_primitive.find(left) == unchanged_named_primitive.end() ||
unchanged_named_primitive.find(right) == unchanged_named_primitive.end()) {
return true;
}
}
// if flag:
// Check if the 'test' node is a name (e.g., if flag:)
if (node_name == parse::NAMED_PRIMITIVE_NAME) {
// Get the id of the 'test' node
std::string id = py::cast<std::string>(test_node.attr("id"));
// Check if the id is present in the cell_input_args_ set
if (cell_input_args_.find(id) != cell_input_args_.end()) {
return true;
}
}
// Return false if none of the conditions were met
return false;
}
// ParseAssignExprNode function
// This function parses assign expression nodes and checks for dynamic behavior.
// Parameters:
// ast: A shared pointer to the ParseFunctionAst.
// node: The Python node to be parsed.
// Returns:
// True if dynamic behavior is detected, false otherwise.
// Parse an assignment expression node
bool DynamicParser::ParseAssignExprNode(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &node) {
MS_LOG(DEBUG) << "Parse assign expr";
// Get the 'value' node from the input node
py::object value_node = python_adapter::GetPyObjAttr(node, parse::NAMED_PRIMITIVE_VALUE);
// Parse the node name of the 'value' node to determine its type
const auto &node_name = ParseNodeName(ast, value_node, parse::AST_MAIN_TYPE_EXPR);
// Check if the 'value' node represents a function call
if (node_name == parse::NAMED_PRIMITIVE_CALL) {
// Get the 'func' node from the 'value' node
py::object func_node = python_adapter::GetPyObjAttr(value_node, parse::NAMED_PRIMITIVE_FUNC);
// Parse the node name of the 'func' node to determine its type
const auto &func_name = ParseNodeName(ast, func_node, parse::AST_MAIN_TYPE_EXPR);
// Check if the function call involves a subscript operation (e.g., some_func()[index])
if (func_name == parse::NAMED_PRIMITIVE_SUBSCRIPT) {
// Get the 'slice' node from the 'func' node
py::object slice_node = python_adapter::GetPyObjAttr(func_node, parse::NAMED_PRIMITIVE_SLICE);
// Get the 'value' within the 'slice' node
py::object value_in_slice_node = python_adapter::GetPyObjAttr(slice_node, parse::NAMED_PRIMITIVE_VALUE);
// Check if the 'value' within the 'slice' node is 'None'
if (py::isinstance<py::none>(value_in_slice_node)) {
MS_LOG(DEBUG) << "Parse value node is none!";
return false;
}
// Parse the node name of the 'value' within the 'slice' node
const auto &node_name_in_slice_node = ParseNodeName(ast, value_in_slice_node, parse::AST_MAIN_TYPE_EXPR);
// Initialize an empty string 'id'
std::string id;
// Check if the 'value' within the 'slice' node has an 'id' attribute
if (py::hasattr(value_in_slice_node, "id")) {
// Get the 'id' attribute as a string
id = py::cast<std::string>(value_in_slice_node.attr("id"));
}
// Check if the node name within the 'slice' node or the 'id' (if not empty) are present in 'cell_input_args_'
if (cell_input_args_.find(node_name_in_slice_node) != cell_input_args_.end() ||
(!id.empty() && cell_input_args_.find(id) != cell_input_args_.end())) {
return true;
}
}
}
// Return false if none of the conditions were met
return false;
}
bool DynamicParser::ParseAugAssignExprNode(const std::shared_ptr<parse::ParseFunctionAst> &, const py::object &node,
const std::vector<std::string> &compare_prim) {
// ParseAugAssignExprNode function
// This function parses augassign expression nodes and checks for dynamic behavior.
// Parameters:
// ast: A shared pointer to the ParseFunctionAst.
// node: The Python node to be parsed.
// compare_prim: A vector of named primitives for comparison.
// Returns:
// True if dynamic behavior is detected, false otherwise.
// Parse an augmented assignment expression node
bool DynamicParser::ParseAugAssignExprNode(const std::shared_ptr<parse::ParseFunctionAst> &ast,
const py::object &node, const std::vector<std::string> &compare_prim) {
MS_LOG(DEBUG) << "Parse augassign expr";
// Initialize the return value 'ret' as false
bool ret = false;
// Check if the 'compare_prim' vector is empty, if so, return false
if (compare_prim.empty()) {
return ret;
}
// Get the 'target' node from the input 'node'
py::object target_node = python_adapter::GetPyObjAttr(node, parse::NAMED_PRIMITIVE_TARGET);
// Check if the 'target' node is 'None', if so, return false
if (py::isinstance<py::none>(target_node)) {
MS_LOG(DEBUG) << "Parse target node is none!";
return ret;
}
// Get the 'value' node from the 'target' node
py::object value_node = python_adapter::GetPyObjAttr(target_node, parse::NAMED_PRIMITIVE_VALUE);
// Check if the 'value' node is 'None', if so, return false
if (py::isinstance<py::none>(value_node)) {
MS_LOG(DEBUG) << "Parse value node is none!";
return ret;
}
// Initialize an empty string 'assign_prim'
std::string assign_prim;
// Check if both 'target' and 'value' nodes have 'attr' and 'id' attributes
if (py::hasattr(target_node, "attr") && py::hasattr(value_node, "id")) {
// Concatenate the 'id' attribute of 'value_node' and the 'attr' attribute of 'target_node' to form 'assign_prim'
assign_prim = py::cast<std::string>(value_node.attr("id")) + py::cast<std::string>(target_node.attr("attr"));
}
// Find 'assign_prim' in the 'compare_prim' vector
auto iter = std::find(compare_prim.begin(), compare_prim.end(), assign_prim);
// If 'assign_prim' is found in 'compare_prim', set 'ret' to true
if (iter != compare_prim.end()) {
ret = true;
}
// Return the value of 'ret'
return ret;
}
// ParseForExprNode function
// This function parses for expression nodes and checks for dynamic behavior.
// Parameters:
// ast: A shared pointer to the ParseFunctionAst.
// node: The Python node to be parsed.
// Returns:
// True if dynamic behavior is detected, false otherwise.
// Parse a for expression node
bool DynamicParser::ParseForExprNode(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &node) {
MS_LOG(DEBUG) << "Parse for expr";
// Get the 'body' node from the input 'node'
py::object body_node = python_adapter::GetPyObjAttr(node, parse::NAMED_PRIMITIVE_BODY);
// Check if the 'body' node is 'None', if so, return false
if (py::isinstance<py::none>(body_node)) {
MS_LOG(DEBUG) << "Parse body of for expression is none!";
return false;
}
// Get the length of the 'body' node
py::int_ pcount = python_adapter::CallPyObjMethod(body_node, parse::PYTHON_GET_METHOD_LEN);
size_t count = LongToSize(pcount);
MS_LOG(DEBUG) << "The for nodes count in body is " << count;
// Iterate through the nodes in the 'body' node
for (size_t i = 0; i < count; ++i) {
auto it = py::cast<py::list>(body_node)[i];
// Parse the node name of the current node in the 'body'
const auto &node_name = ParseNodeName(ast, it, parse::AST_MAIN_TYPE_STMT);
// Check if the node represents an assignment expression and if ParseAssignExprNode returns true
if (node_name == parse::NAMED_PRIMITIVE_ASSIGN && ParseAssignExprNode(ast, it)) {
return true;
}
}
// Return false if no assignment expression is found in the 'body' node
return false;
}
bool DynamicParser::ParseBodyContext(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &fn_node,
const std::vector<std::string> &compare_prim) {
// ParseBodyContext function
// This function parses the body of a Python function node and checks for dynamic behavior.
// Parameters:
// ast: A shared pointer to the ParseFunctionAst.
// fn_node: The Python function node.
// compare_prim: A vector of named primitives for comparison.
// Returns:
// True if dynamic behavior is detected, false otherwise.
// Parse the body context of a function and check for dynamic behavior
bool DynamicParser::ParseBodyContext(const std::shared_ptr<parse::ParseFunctionAst> &ast,
const py::object &fn_node, const std::vector<std::string> &compare_prim) {
MS_EXCEPTION_IF_NULL(ast);
// Get the 'body' node from the input 'fn_node'
py::object func_obj = python_adapter::GetPyObjAttr(fn_node, parse::NAMED_PRIMITIVE_BODY);
// Check if the 'body' node is 'None'. If it is, return false.
if (py::isinstance<py::none>(func_obj)) {
MS_LOG(DEBUG) << "Parse body of cell is none!";
return false;
}
// Get the length of the 'body' node
py::int_ pcount = python_adapter::CallPyObjMethod(func_obj, parse::PYTHON_GET_METHOD_LEN);
size_t count = IntToSize(pcount);
MS_LOG(DEBUG) << "The nodes count in body is " << count;
// Initialize the return value 'ret' as false
bool ret = false;
// Iterate through the nodes in the 'body' node
for (size_t i = 0; i < count; ++i) {
auto node = py::cast<py::list>(func_obj)[i];
// Parse the node name of the current node in the 'body'
const auto &node_name = ParseNodeName(ast, node, parse::AST_MAIN_TYPE_STMT);
// Check the type of the current node and call the appropriate parsing function
if (node_name == parse::NAMED_PRIMITIVE_ASSIGN) {
ret = ParseAssignExprNode(ast, node);
} else if (node_name == parse::NAMED_PRIMITIVE_AUGASSIGN) {
@ -212,14 +380,19 @@ bool DynamicParser::ParseBodyContext(const std::shared_ptr<parse::ParseFunctionA
} else if (node_name == parse::NAMED_PRIMITIVE_IF || node_name == parse::NAMED_PRIMITIVE_WHILE) {
ret = ParseIfWhileExprNode(ast, node);
}
// If any of the parsing functions returns true, set 'ret' to true and break out of the loop
if (ret) {
MS_LOG(INFO) << "Current cell is dynamic!";
break;
}
}
// Return the final value of 'ret', indicating whether dynamic behavior was detected
return ret;
}
} // namespace mindspore::parse
std::string DynamicParser::GetCellInfo(const py::object &cell) {
if (py::isinstance<Cell>(cell)) {
auto c_cell = py::cast<CellPtr>(cell);

View File

@ -31,22 +31,82 @@ class DynamicParser {
DynamicParser() = default;
~DynamicParser() = default;
// Check cell struct
// Check if the given Python object represents a dynamic cell.
// Parameters:
// cell: A Python object representing a cell.
// Returns:
// True if the cell is a dynamic cell, False otherwise.
static bool IsDynamicCell(const py::object &cell);
private:
// Get information about the cell object.
// Parameters:
// cell: A Python object representing a cell.
// Returns:
// A string containing information about the cell.
static std::string GetCellInfo(const py::object &cell);
// Parse input arguments of a function.
// Parameters:
// ast: A shared pointer to the parse::ParseFunctionAst object.
// fn_node: A Python object representing a function node.
static void ParseInputArgs(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &fn_node);
// Parse the body context of a function.
// Parameters:
// ast: A shared pointer to the parse::ParseFunctionAst object.
// fn_node: A Python object representing a function node.
// compare_prim: A vector of strings containing primitive names to compare.
// Returns:
// True if parsing is successful, False otherwise.
static bool ParseBodyContext(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &fn_node,
const std::vector<std::string> &compare_prim = {});
// Parse if and while expression nodes.
// Parameters:
// ast: A shared pointer to the parse::ParseFunctionAst object.
// node: A Python object representing an expression node.
// Returns:
// True if parsing is successful, False otherwise.
static bool ParseIfWhileExprNode(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &node);
// Parse assign expression nodes.
// Parameters:
// ast: A shared pointer to the parse::ParseFunctionAst object.
// node: A Python object representing an assign expression node.
// Returns:
// True if parsing is successful, False otherwise.
static bool ParseAssignExprNode(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &node);
// Parse augmented assign expression nodes.
// Parameters:
// ast: A shared pointer to the parse::ParseFunctionAst object.
// node: A Python object representing an augmented assign expression node.
// compare_prim: A vector of strings containing primitive names to compare.
// Returns:
// True if parsing is successful, False otherwise.
static bool ParseAugAssignExprNode(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &node,
const std::vector<std::string> &compare_prim = {});
// Parse for expression nodes.
// Parameters:
// ast: A shared pointer to the parse::ParseFunctionAst object.
// node: A Python object representing a for expression node.
// Returns:
// True if parsing is successful, False otherwise.
static bool ParseForExprNode(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &node);
// Parse the name of a Python node.
// Parameters:
// ast: A shared pointer to the parse::ParseFunctionAst object.
// node: A Python object representing a node.
// type: An enumeration indicating the main type of the node.
// Returns:
// A string containing the name of the node.
static std::string ParseNodeName(const std::shared_ptr<parse::ParseFunctionAst> &ast, const py::object &node,
parse::AstMainType type);
};
} // namespace mindspore::parse
#endif

View File

@ -35,20 +35,24 @@
namespace mindspore {
namespace parse {
namespace {
/// \brief Replaces special characters in the input string with corresponding replacements.
/// \param str The input string.
/// \return The modified string with special characters replaced.
std::string ReplaceSpecialChar(const std::string &str) {
std::ostringstream oss;
for (size_t i = 0; i < str.size(); i++) {
if (str[i] == '<') {
oss << "";
oss << ""; // Replace '<' with '「'
} else if (str[i] == '>') {
oss << "";
oss << ""; // Replace '>' with '」'
} else {
oss << str[i];
oss << str[i]; // Keep other characters as is.
}
}
return oss.str();
}
/// \brief A struct used to register handlers for AnfDumpHandler.
struct AnfDumpHandlerRegister {
AnfDumpHandlerRegister() {
AnfDumpHandler::SetValueNodeStrHandler([](const std::shared_ptr<ValueNode> &node) -> std::string {
@ -62,11 +66,14 @@ struct AnfDumpHandlerRegister {
} else if (IsValueNode<parse::Symbol>(node)) {
return ReplaceSpecialChar(node->value()->cast<parse::SymbolPtr>()->name());
}
return "";
return ""; // Default case, return an empty string.
});
}
} callback_register;
} callback_register; // Register callback for AnfDumpHandler.
} // namespace
/// \brief Converts a ClassObject to an AbstractBase.
/// \return The AbstractBase representation of the ClassObject.
abstract::AbstractBasePtr ClassObject::ToAbstract() {
ClassPtr cls_ptr = ParseDataClass(obj());
auto abs_scalar = std::make_shared<abstract::AbstractScalar>();
@ -78,6 +85,9 @@ abstract::AbstractBasePtr ClassObject::ToAbstract() {
return std::make_shared<abstract::PartialAbstractClosure>(func_ptr, args_spec_list);
}
/// \brief Checks if creating an instance of the given Python object is supported.
/// \param obj The Python object to check.
/// \return True if supported, false otherwise.
static inline bool IsSupportedCreateInstanceType(const py::object &obj) {
py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE);
auto res = python_adapter::CallPyModFn(mod, PYTHON_MOD_IS_SUPPORTED_CREATE_INSTANCE_TYPE, obj);
@ -88,12 +98,14 @@ static inline bool IsSupportedCreateInstanceType(const py::object &obj) {
return res.cast<bool>();
}
/// \brief Converts a ClassType to an AbstractBase.
/// \return The AbstractBase representation of the ClassType.
abstract::AbstractBasePtr ClassType::ToAbstract() {
auto abs_scalar =
std::make_shared<abstract::AbstractScalar>(shared_from_base<ClassType>(), std::make_shared<TypeType>());
// The fallback feature is enabled in default.
// Not support change the flag during the process is alive.
// The fallback feature is enabled by default.
// Do not support changing the flag during the process execution.
static const auto support_fallback = common::GetEnv("MS_DEV_ENABLE_FALLBACK");
static const auto use_fallback = (support_fallback != "0");
if (use_fallback && !IsSupportedCreateInstanceType(obj())) {
@ -108,112 +120,150 @@ abstract::AbstractBasePtr ClassType::ToAbstract() {
}
namespace {
/// \brief Gets the unique identifier for a Python object.
/// \param obj The Python object for which the identifier is retrieved.
/// \return A string containing the unique identifier of the Python object.
std::string GetPyObjId(const py::object &obj) {
// Call a Python function to obtain the object's ID.
py::object out = python_adapter::CallPyFn(parse::PYTHON_MOD_PARSE_MODULE, parse::PYTHON_MOD_GET_OBJ_ID, obj);
// Check if the result is a None object, indicating a failure.
if (py::isinstance<py::none>(out)) {
MS_LOG(EXCEPTION) << "Get pyobj failed";
}
// Cast the result to a string and return it as the object's unique identifier.
return out.cast<std::string>();
}
// If any mixed precision flag add a cast node after the parameter node.
// argument obj should be python Parameter object
// it will be converted to Parameter node here
/// \brief Resolves a Python Parameter object to an AnfNode representing a parameter in a FuncGraph.
/// \param func_graph The FuncGraph in which the parameter should be resolved.
/// \param obj The Python Parameter object to be resolved.
/// \return A pointer to the resolved AnfNode representing the parameter.
AnfNodePtr ResolveParameterObj(const FuncGraphPtr &func_graph, const py::object &obj) {
MS_EXCEPTION_IF_NULL(func_graph);
// Parameter object should not be none
// Ensure that the Python object is not None.
if (py::isinstance<py::none>(obj)) {
MS_LOG(EXCEPTION) << "Resolve class Parameter error because obj is null.";
}
// Check if the Python object has a 'name' attribute.
if (!py::hasattr(obj, "name")) {
MS_LOG(EXCEPTION) << "Resolve class Parameter error: cannot find name attr for obj";
}
// Get the parameter name from parameter object
// Retrieve the 'name' attribute from the Python object.
auto name_attr = python_adapter::GetPyObjAttr(obj, "name");
// Ensure that the 'name' attribute is not None.
if (py::isinstance<py::none>(name_attr)) {
MS_LOG(EXCEPTION) << "Parameter object should have name attribute";
MS_LOG(EXCEPTION) << "Parameter object should have a name attribute";
}
// Obtain the unique identifier of the Python object.
auto obj_id = GetPyObjId(obj);
static std::vector<std::string> param_obj_ids;
auto param_name = py::cast<std::string>(name_attr);
// Retrieve the top-level FuncGraph.
auto top_func_graph = Parser::GetTopFuncGraph();
// If the parameter node has been created , return it.
// Check if a parameter node with the same name already exists.
AnfNodePtr para_node = nullptr;
for (auto const &param : top_func_graph->parameters()) {
auto param_node = dyn_cast<Parameter>(param);
if (param_node != nullptr && param_node->name() == param_name) {
if (param_node->is_top_graph_param()) {
// If the name of the input of construct is same as the parameters,
// add suffix to the name of the input of construct.
// If the name of the input of construct is the same as the parameters,
// add a suffix to the name of the input of construct.
string suffix_name = param_node->name() + "_$";
param_node->set_name(suffix_name);
param_node->debug_info()->set_name(suffix_name);
MS_LOG(DEBUG) << "Add suffix to the name of the input of construct " << func_graph->ToString()
<< ", input: " << param_node->DebugString();
} else {
// Exist two parameter object which name is the same.
// Exist two parameter objects with the same name.
if (std::find(param_obj_ids.begin(), param_obj_ids.end(), obj_id) == param_obj_ids.end()) {
MS_LOG(EXCEPTION) << "The parameter " << param_node->DebugString() << " , its name '" << param_name
<< "' already exists. Please set a unique name for the parameter.";
}
para_node = param;
MS_LOG(DEBUG) << "Found existing parameter for " << func_graph->ToString()
MS_LOG(DEBUG) << "Found an existing parameter for " << func_graph->ToString()
<< ", param: " << para_node->DebugString() << ", top_func_graph: " << top_func_graph->ToString();
break;
}
}
}
// If the parameter node does not exist, create a new one.
if (para_node == nullptr) {
auto node = top_func_graph->AddWeightParameter(param_name);
auto value = py::cast<tensor::MetaTensorPtr>(obj);
param_obj_ids.emplace_back(obj_id);
node->set_default_param(value);
// Set abstract for parameter
// Set abstract for the parameter.
auto abs = value->ToAbstract();
node->set_abstract(abs);
para_node = node;
MS_LOG(DEBUG) << "Created a new weight parameter for " << func_graph->ToString()
<< ", param: " << para_node->DebugString() << ", top_func_graph: " << top_func_graph->ToString();
}
// Add the parameter node to the FuncGraph's parameter object nodes.
func_graph->add_parameter_obj_node(para_node);
// Return the resolved parameter node.
return para_node;
}
/// \brief Broadens the abstract values of CNodes in a FuncGraph.
/// \param func_graph The FuncGraph in which CNode abstract values should be broadened.
void BroadenCNodeAbstract(const FuncGraphPtr &func_graph) {
// Topologically sort the nodes in the FuncGraph.
std::vector<AnfNodePtr> nodes = TopoSort(func_graph->get_return(), SuccIncoming, AlwaysInclude);
// Iterate through the nodes and broaden the abstract values of CNodes.
for (const AnfNodePtr &node : nodes) {
if (!node->isa<CNode>()) {
continue;
continue; // Skip non-CNode nodes.
}
auto abstract = node->abstract();
if (abstract != nullptr) {
// Broaden the abstract value of the CNode.
node->set_abstract(abstract->Broaden());
}
}
}
/// \brief Converts a loaded graph represented as a Value to the corresponding FuncGraph.
/// \param func_graph The target FuncGraph in which the loaded graph should be converted.
/// \param value The Value object representing the loaded graph.
void ConvertLoadedGraph(const FuncGraphPtr &func_graph, const ValuePtr &value) {
// Check if the Value represents a FuncGraph.
if (!value->isa<FuncGraph>()) {
return;
return; // Not a FuncGraph, nothing to convert.
}
// Cast the Value to a FuncGraph.
auto resolved_graph = value->cast<FuncGraphPtr>();
MS_EXCEPTION_IF_NULL(resolved_graph);
// Check if the resolved graph has an 'is_load' attribute.
if (!resolved_graph->has_attr("is_load")) {
return;
return; // Not a loaded graph, nothing to convert.
}
// Retrieve the top-level FuncGraph.
auto top_graph = Parser::GetTopFuncGraph();
std::vector<AnfNodePtr> input_params;
// Iterate through the parameters of the resolved graph.
for (auto const &param : resolved_graph->parameters()) {
auto param_ptr = dyn_cast<Parameter>(param);
MS_EXCEPTION_IF_NULL(param_ptr);
// Check if the parameter has a default value.
if (param_ptr->has_default()) {
param_ptr->set_func_graph(top_graph);
func_graph->add_parameter_obj_node(param_ptr);
// Update top_graph
// Update the top-level graph with the parameter.
top_graph->add_parameter(param_ptr);
size_t hyper_param_count = top_graph->hyper_param_count();
top_graph->set_hyper_param_count(hyper_param_count + 1);
@ -221,12 +271,22 @@ void ConvertLoadedGraph(const FuncGraphPtr &func_graph, const ValuePtr &value) {
input_params.push_back(param_ptr);
}
}
// Set the parameters of the resolved graph to the input_params vector.
resolved_graph->set_parameters(input_params);
// Broaden the abstract values of CNodes in the resolved graph.
BroadenCNodeAbstract(resolved_graph);
}
/// \brief Resolves a Python object to an AnfNode in a FuncGraph.
/// \param func_graph The FuncGraph in which the object should be resolved.
/// \param obj The Python object to be resolved.
/// \param node A pointer to store the resolved AnfNode.
/// \return True if the resolution is successful, false otherwise.
bool ResolveObjectToNode(const FuncGraphPtr &func_graph, const py::object &obj, AnfNodePtr *const node) {
AnfNodePtr output = nullptr;
// Check if the Python object is a parameter object and a MetaTensor.
if (py::hasattr(obj, "__parameter__") && py::isinstance<tensor::MetaTensor>(obj)) {
auto param = ResolveParameterObj(func_graph, obj);
if (param == nullptr) {
@ -257,12 +317,19 @@ bool ResolveObjectToNode(const FuncGraphPtr &func_graph, const py::object &obj,
return false;
}
MS_EXCEPTION_IF_NULL(convert_result);
// Convert the loaded graph if the Value represents a FuncGraph.
ConvertLoadedGraph(func_graph, convert_result);
output = NewValueNode(convert_result);
// If the converted result is a tensor, perform mixed-precision casting.
if (convert_result->isa<tensor::Tensor>()) {
output = GetMixedPrecisionCastHelp(func_graph, output);
}
}
// Store the resolved AnfNode in the 'node' pointer.
*node = output;
return true;
}