Sample Usage Guide

1. Functional Description

This sample uses control edges for graph construction, aiming to help graph construction developers quickly understand the concept of control edges and use control edges for graph construction

2. Directory Structure

python/
├── src/
|   └── make_control_edge_graph.py // sample file
├── CMakeLists.txt                 // Compilation script
├── README.md                      // README file
├── run_sample.sh                  // Execution script

3. Usage

3.1. Prepare CANN Package

  • Correctly install toolkit and ops packages through installation guide Environment Preparation
  • Set environment variables (assuming packages are installed in /usr/local/Ascend/)
source /usr/local/Ascend/cann/set_env.sh

3.2. Compilation and Execution

  • Note: Compared with C/C++ graph construction, Python graph construction requires additional LD_LIBRARY_PATH and PYTHONPATH (refer to sample configuration)
bash run_sample.sh -t sample_and_run_python

This command will:

  1. Automatically generate ES interfaces
  2. Compile sample program
  3. Generate dump graph and run the graph

After successful execution you will see:

[Success] sample executed successfully, pbtxt dump generated in current directory. This file starts with ge_onnx_ and can be opened in netron for display

Output File Description

After successful execution, the following files will be generated in the current directory:

  • ge_onnx_*.pbtxt - Graph structure protobuf text format, can be viewed with netron

3.3. Log Printing

If you need log printing to assist debugging during executable program execution, you can set the following environment variables before bash run_sample.sh -t sample_and_run_python to print logs to screen

export ASCEND_SLOG_PRINT_TO_STDOUT=1 #Print logs to screen
export ASCEND_GLOBAL_LOG_LEVEL=0 #Log level is debug level

3.4. DUMP Graph During Graph Compilation Process

During executable program execution, if you need to DUMP graph to assist debugging graph compilation process, you can set the following environment variable before bash run_sample.sh -t sample_and_run_python to DUMP graph to execution path

export DUMP_GE_GRAPH=2

4. Core Concept Introduction

4.1. Graph Construction Steps

  • Create graph builder (used to provide context, workspace and construction-related methods needed for graph construction)
  • Add start nodes (start nodes refer to nodes without input dependencies, usually including graph inputs (like Data nodes) and weight constants (like Const nodes))
  • Add intermediate nodes (intermediate nodes are computation nodes with input dependencies, usually generated by user graph construction logic, and connected through existing nodes as inputs)
  • Set graph output (explicitly specify graph output nodes as endpoints of computation results)

4.2. Control Edge

Concept Description: Control edges are used to specify execution order of nodes in computation graphs, even if there are no data dependencies between these nodes. Control edges do not transmit data, only transmit control signals, ensuring source nodes execute before target nodes.

Graph Construction API Features:

  • ES API provides add_control_dependency() method, supports usage in Python
  • Can add control dependencies from multiple source nodes for one target node

5. Control Dependency Relationship Example

5.1. Overview

Control dependencies are used to specify execution order of nodes in computation graphs, even if there are no data dependencies between these nodes. This document demonstrates how to express control dependency relationships at the Python level.

Python API Example

Method 1: Directly Call add_control_dependency()

from ge.es import GraphBuilder

# 1. Create graph builder
builder = GraphBuilder("control_dep_example")

# 2. Create nodes
tensor_a = builder.create_scalar_float(1.0)
tensor_b = builder.create_scalar_float(2.0)

# 3. Create dependency target node (assuming generated Add operation exists)
from ge.es.all import Add

tensor_c = Add(tensor_a, tensor_b)

# 4. Add control dependency: tensor_c depends on tensor_a and tensor_b
builder.add_control_dependency(
    dst_tensor=tensor_c,
    src_tensors=[tensor_a, tensor_b]
)

# 5. Set output and build
builder.set_graph_output(tensor_c, 0)
graph = builder.build_and_reset()

from ge.es import GraphBuilder
from ge.es.graph_builder import control_dependency_scope
from ge.es.all import Add

# 1. Create graph builder
builder = GraphBuilder("control_dep_scope_example")

# 2. Create dependency source nodes
tensor_a = builder.create_scalar_float(1.0)
tensor_b = builder.create_scalar_float(2.0)

# 3. Use scope: all nodes created within scope automatically depend on tensor_a and tensor_b
with control_dependency_scope([tensor_a, tensor_b]):
    # Nodes created in this scope will automatically add control dependencies
    tensor_c = builder.create_scalar_float(3.0)
    tensor_d = Add(tensor_a, tensor_c)

    # Producer nodes of tensor_c and tensor_d both automatically depend on tensor_a and tensor_b

# 4. Set output and build
builder.set_graph_output(tensor_d, 0)
graph = builder.build_and_reset()

Nested Scope Example

from ge.es import GraphBuilder
from ge.es.graph_builder import control_dependency_scope
from ge.es.all import Add


def build_graph_with_nested_scopes():
    """Demonstrate nested control dependency scopes"""
    builder = GraphBuilder("nested_scopes")

    # Global initialization node
    global_init = builder.create_scalar_float(0.0)

    # First layer scope
    with control_dependency_scope([global_init]):
        # Module A initialization
        module_a_init = builder.create_scalar_float(1.0)

        # Second layer nested scope
        with control_dependency_scope([module_a_init]):
            # Module A computation (depends on global_init and module_a_init)
            module_a_output = Add(module_a_init, global_init)

        # Return to first layer scope
        # Module B initialization (only depends on global_init)
        module_b_init = builder.create_scalar_float(2.0)

    # Final output (not in any scope, no additional control dependencies)
    final_output = Add(module_a_output, module_b_init)

    builder.set_graph_output(final_output, 0)
    return builder.build_and_reset()


# Usage
graph = build_graph_with_nested_scopes()