{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "b68347cc",
   "metadata": {},
   "source": [
    "# AscendIR — 用户构图的基础概念\n",
    "\n",
    "本节开始深入学习 GE 编译流程使用的核心 IR —— AscendIR,掌握构图的基础概念。这些知识将为你后续使用 ATC 编译模型或通过 GeSession 在线构图奠定基础。\n",
    "\n",
    "本节学习大纲如下:\n",
    "\n",
    "- AscendIR 概述\n",
    "- 图的对象模型:ComputeGraph → Node → OpDesc → GeTensorDesc(含属性表)\n",
    "- Tensor 描述:shape、dtype、format\n",
    "- 数据边与控制边、Anchor\n",
    "- 静态 Shape 与动态 Shape\n",
    "- 算子生态\n",
    "- 构图方式预览"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e07130b2",
   "metadata": {},
   "source": [
    "## 1. AscendIR 概述\n",
    "\n",
    "AscendIR 是 AI 处理器专用的抽象数据结构,用于表达计算流程。PyTorch、TensorFlow、MindSpore、PaddlePaddle 等主流框架的模型可统一转换为 AscendIR 表示的计算图,用户也可以通过 GE 接口直接构图。后续的编译、优化和执行调度都围绕该计算图展开。\n",
    "\n",
    "AscendIR(Ascend Intermediate Representation,昇腾中间表示)是 GE 编译流程使用的核心 IR,用图结构表达模型的计算逻辑与数据依赖关系。AscendIR 也被简称为 \"AIR\",TorchAir 中的 \"Air\" 取自于此。\n",
    "\n",
    "在抽象层级上,AscendIR 与 ONNX、AtenIR、StableHLO 等同属高层图表示(HLO 级别),以算子和张量为基本构成单元,用于描述模型的运算语义与图结构。\n",
    "\n",
    "下图是一个使用 AscendIR 表示的计算图示例:两个 `Data` 输入经 `Matmul` 做矩阵乘,再经 `ReLU` 激活,最终由 `NetOutput` 节点汇总输出。\n",
    "\n",
    "<p align=\"left\"><img src=\"./images/ascendir_graph.svg\" alt=\"使用 AscendIR 表示的计算图\" width=\"75%\"></p>\n",
    "\n",
    "图中的 `Graph`、`Node`、Tensor 数据边和输出节点之间的对应关系可用下面的结构图进一步核对。它把一张图拆解为可用于构图和编译的基本元素:\n",
    "\n",
    "<p align=\"left\"><img src=\"./images/ascendir_elements.svg\" alt=\"AscendIR 图的组成要素\" width=\"85%\"></p>\n",
    "\n",
    "### AscendIR 的定位\n",
    "\n",
    "- **统一编译入口**:无论来自前端框架(通过 Adapter),还是来自模型文件(通过 ATC / Parser),所有输入都会先转换为 AscendIR,再进入 GE Compiler。\n",
    "- **统一优化底座**:转换为 AscendIR 后,GE 能以整图视角进行常量折叠、算子融合、内存复用等优化。\n",
    "\n",
    "<div align=\"left\">\n",
    "<blockquote style=\"margin-left: 0; margin-right: auto;\">\n",
    "<p><strong>AscendIR 既能表达静态 Shape 图,也能表达动态 Shape 图,GE 均支持二者的编译与执行:</strong></p>\n",
    "<ul><li><strong>静态 Shape 图</strong>:多次执行中所有 Tensor 的 shape 均固定。</li><li><strong>动态 Shape 图</strong>:不同执行中,部分 Tensor 的 shape 可能随实际输入变化。</li></ul>\n",
    "</blockquote>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "83ecdfcf",
   "metadata": {},
   "source": [
    "## 2. 图的对象模型\n",
    "\n",
    "AscendIR 图是一张有向无环图(DAG)。它的核心对象模型由四个层次**层层持有**构成:\n",
    "\n",
    "- **ComputeGraph(图)**:图的容器,管理节点集合、图的输入/输出、子图等。\n",
    "- **Node(节点 / 算子)**:图中的算子节点,持有一个 **OpDesc** 和一组 **Anchor(锚点)**。\n",
    "- **OpDesc(算子描述符)**:定义算子的名称(name)、类型(type)、属性、推导函数,以及**输入/输出 Tensor 描述**。\n",
    "- **GeTensorDesc(张量描述)**:描述一个张量的 shape、dtype、format 等信息,作为节点的 input_desc / output_desc 由 OpDesc 持有。\n",
    "\n",
    "一个关键设计特征是:**图中不存在独立的 Edge(边)对象**,节点之间的连边由成对的 **Anchor** 维护(详见第 4 节)。因此 Tensor 描述并不漂浮在数据边上,而是分别存放在生产者节点的 output_desc 与消费者节点的 input_desc 中。\n",
    "\n",
    "下面分别展开用户构图时最常接触的 Tensor、Node、Graph 的属性(OpDesc 作为 Node 内部的算子描述符,其字段在 Node 一节一并说明)。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9a4e8de",
   "metadata": {},
   "source": [
    "### 2.1 Tensor\n",
    "\n",
    "Tensor 是算子的输入输出数据实体,**包括 Tensor 描述与数据两部分**。Tensor 描述包含该 Tensor 的 name、shape、dtype、format 信息。\n",
    "\n",
    "<div align=\"left\">\n",
    "<table style=\"margin-left: 0; margin-right: auto;\">\n",
    "<thead>\n",
    "<tr><th>属性</th><th>定义</th></tr>\n",
    "</thead>\n",
    "<tbody>\n",
    "<tr><td>名称(name)</td><td>用于对 Tensor 进行索引,不同 Tensor 的 name 需要保持唯一。</td></tr>\n",
    "<tr><td>形状(shape)</td><td>Tensor 的形状,例如 <code>(10,)</code>、<code>(1024, 1024)</code> 或 <code>(2, 3, 4)</code> 等。如形状 <code>(3, 4)</code> 表示第一维有 3 个元素、第二维有 4 个元素,即一个 3 行 4 列的矩阵数组。形式为 <code>(i1, i2, …, in)</code>,静态 shape 下 i1 到 in 均为正整数;动态 shape 下还可取 <code>-1</code> / <code>-2</code> 作为占位(见下方说明)。</td></tr>\n",
    "<tr><td>数据类型(dtype)</td><td>Tensor 对象中每个元素的数据类型。取值范围:float16, float32, int8, int16, int32, uint8, uint16, bfloat16, bool 等。</td></tr>\n",
    "<tr><td>数据排布格式(format)</td><td>数据的物理排布格式,如 ND / NCHW / NHWC / NC1HWC0 等。</td></tr>\n",
    "</tbody>\n",
    "</table>\n",
    "</div>\n",
    "\n",
    "<div align=\"left\">\n",
    "<blockquote style=\"margin-left: 0; margin-right: auto;\">\n",
    "<p>说明:在 GE 编译期,Tensor 描述(name/shape/dtype/format)用于推导与优化;真实数据通常在执行期才填入,构图阶段一般只描述元信息。</p>\n",
    "<p><strong>动态 shape 的特殊取值</strong>:当某些维度在编译期无法确定时,shape 用负数占位:</p>\n",
    "<ul><li><strong><code>-1</code>(UNKNOWN_DIM,未知维度)</strong>:表示<strong>某一维</strong>的大小编译期未知、运行时才确定,但维度个数(rank)已知。例如 <code>(-1, 3, 224, 224)</code> 表示 batch 维动态、其余维固定。</li><li><strong><code>-2</code>(UNKNOWN_RANK,未知秩)</strong>:作为<strong>整个 shape</strong>(即 <code>[-2]</code>)时,表示 shape 完全未知——连维度个数(rank)都不确定,是动态程度最高的情形。</li></ul>\n",
    "<p>这两种取值的具体编译与执行处理,会在后续「静态 Shape 与动态 Shape」一节及进阶章节展开。</p>\n",
    "</blockquote>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "210d9604",
   "metadata": {},
   "source": [
    "### 2.2 Node\n",
    "\n",
    "Node 表示算子级计算单元,是图中执行计算的基本实体。它持有一个 **OpDesc(算子描述符)**,下表中算子的 name、type、输入、输出、属性等信息都记录在该 OpDesc 中。\n",
    "\n",
    "<div align=\"left\">\n",
    "<table style=\"margin-left: 0; margin-right: auto;\">\n",
    "<thead>\n",
    "<tr><th>属性</th><th>定义</th></tr>\n",
    "</thead>\n",
    "<tbody>\n",
    "<tr><td>名称(name)</td><td>算子的名称,用于标识图中的某个算子,<strong>同一图中算子的名称需保持唯一</strong>。如下图中的 Conv1、Pool1、Conv2 都是算子名称。</td></tr>\n",
    "<tr><td>类型(type)</td><td>算子根据 type 进行实现匹配,<strong>相同 type 的算子实现逻辑相同</strong>。同一图中同 type 算子可能有多个,例如下图中 Conv1 与 Conv2 的 type 都是 <code>Convolution</code>,表示各做一次卷积。</td></tr>\n",
    "<tr><td>输入(input)</td><td>算子的输入 Tensor 数据。</td></tr>\n",
    "<tr><td>输出(output)</td><td>算子的输出 Tensor 数据。</td></tr>\n",
    "<tr><td>属性(Attributes)</td><td>定义算子的行为和功能,常见属性如轴(Axis)、权重(Weight)、偏差(Bias)。属性在构图时确定。</td></tr>\n",
    "</tbody>\n",
    "</table>\n",
    "</div>\n",
    "\n",
    "<p align=\"left\"><img src=\"./images/node_example.svg\" alt=\"算子命名示例:Conv1、Pool1、Conv2\" width=\"78%\"></p>\n",
    "\n",
    "<div align=\"left\">\n",
    "<blockquote style=\"margin-left: 0; margin-right: auto;\">\n",
    "<p>注意区分 <strong>name</strong> 与 <strong>type</strong>:name 是图中唯一的\"身份标识\",type 是决定计算逻辑的\"算子种类\"。上图中 Conv1 与 Conv2 是两个不同的 name,但共享同一个 type <code>Convolution</code>。</p>\n",
    "</blockquote>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87ff499a",
   "metadata": {},
   "source": [
    "### 2.3 Graph\n",
    "\n",
    "Graph 是图的容器,承载节点、连边、输入输出描述等,是 GE 编译的基本处理单元。\n",
    "\n",
    "<div align=\"left\">\n",
    "<table style=\"margin-left: 0; margin-right: auto;\">\n",
    "<thead>\n",
    "<tr><th>属性</th><th>定义</th></tr>\n",
    "</thead>\n",
    "<tbody>\n",
    "<tr><td>名称(name)</td><td>图的名称,用于标识网络中的某个 Graph,同一网络中 Graph 的名称需保持唯一。</td></tr>\n",
    "<tr><td>算子列表</td><td>图中所有节点(Node)的列表。</td></tr>\n",
    "<tr><td>输入算子(input)</td><td>图的输入算子(通常是 Data / 输入占位节点)。</td></tr>\n",
    "<tr><td>输出算子(output)</td><td>图的输出算子。</td></tr>\n",
    "</tbody>\n",
    "</table>\n",
    "</div>\n",
    "\n",
    "- 一个 Graph 包含若干 Node 以及它们之间的连接关系。\n",
    "- ComputeGraph 可包含子图(如 If / While 等控制流算子产生的子图)。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "66816244",
   "metadata": {},
   "source": [
    "## 3. Tensor 描述\n",
    "\n",
    "承接上一节的 Tensor 属性表,这里把构图时最常关注的 shape、dtype、format 三项展开说明。\n",
    "\n",
    "### 3.1 Shape\n",
    "\n",
    "描述 Tensor 各维度的大小,例如:\n",
    "\n",
    "- 一张 224×224 的 RGB 图片(NCHW):`[1, 3, 224, 224]`\n",
    "- 一个 batch size 为 32 的向量序列:`[32, 128, 768]`\n",
    "- 一个 3 行 4 列的矩阵:`(3, 4)`\n",
    "\n",
    "### 3.2 DataType\n",
    "\n",
    "描述 Tensor 中每个元素的数据类型,GE 中常见取值如下:\n",
    "\n",
    "<div align=\"left\">\n",
    "<table style=\"margin-left: 0; margin-right: auto;\">\n",
    "<thead>\n",
    "<tr><th>数据类型(dtype)</th><th>说明</th></tr>\n",
    "</thead>\n",
    "<tbody>\n",
    "<tr><td>float32</td><td>32 位浮点数</td></tr>\n",
    "<tr><td>float16</td><td>16 位浮点数</td></tr>\n",
    "<tr><td>bfloat16</td><td>16 位脑浮点数(指数位更宽,常用于训练/混精)</td></tr>\n",
    "<tr><td>int8 / int16 / int32</td><td>有符号整数</td></tr>\n",
    "<tr><td>uint8 / uint16</td><td>无符号整数</td></tr>\n",
    "<tr><td>bool</td><td>布尔值</td></tr>\n",
    "</tbody>\n",
    "</table>\n",
    "</div>\n",
    "\n",
    "<div align=\"left\">\n",
    "<blockquote style=\"margin-left: 0; margin-right: auto;\">\n",
    "<p>完整取值范围:float16, float32, int8, int16, int32, uint8, uint16, bfloat16, bool 等。</p>\n",
    "</blockquote>\n",
    "</div>\n",
    "\n",
    "### 3.3 Format\n",
    "\n",
    "描述 Tensor 在内存中的物理排列方式,常见的有:\n",
    "\n",
    "<div align=\"left\">\n",
    "<table style=\"margin-left: 0; margin-right: auto;\">\n",
    "<thead>\n",
    "<tr><th>格式</th><th>说明</th></tr>\n",
    "</thead>\n",
    "<tbody>\n",
    "<tr><td>ND</td><td>通用多维数组格式(N-Dimension),不绑定语义维度</td></tr>\n",
    "<tr><td>NCHW</td><td>按 Batch-Channel-Height-Width 排列</td></tr>\n",
    "<tr><td>NHWC</td><td>按 Batch-Height-Width-Channel 排列</td></tr>\n",
    "<tr><td>NC1HWC0</td><td>昇腾特有的 5HD 格式,适配 AI Core 计算单元</td></tr>\n",
    "</tbody>\n",
    "</table>\n",
    "</div>\n",
    "\n",
    "<div align=\"left\">\n",
    "<blockquote style=\"margin-left: 0; margin-right: auto;\">\n",
    "<p>GE 编译过程中会根据算子要求和硬件特性自动进行 format 推导与转换(<strong>InferFormat</strong>),用户构图时通常只需关注 ND 等通用格式,无需手工指定昇腾私有格式。</p>\n",
    "</blockquote>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86021c4e",
   "metadata": {},
   "source": [
    "## 4. 数据边、控制边与 Anchor\n",
    "\n",
    "AscendIR 图中的节点通过两种“边”连接:数据边与控制边。\n",
    "\n",
    "<p align=\"left\"><img src=\"./images/data_control_edge.svg\" alt=\"数据边与控制边示意\" width=\"72%\"></p>\n",
    "\n",
    "### 4.1 数据边\n",
    "\n",
    "数据边表示 Tensor 的生产者与消费者关系,方向由源节点(src)指向目标节点(dst)。Node A 的输出 Tensor 作为 Node B 的输入 Tensor,承载实际的数据依赖关系。\n",
    "\n",
    "### 4.2 控制边\n",
    "控制边表示纯依赖关系,无数据传递;用于显式约束执行顺序,保证 src 节点先于 dst 节点执行,常用于变量更新、资源管理等场景。\n",
    "\n",
    "### 4.3 Anchor\n",
    "\n",
    "在 GE 的实际实现中,**图里并不存在独立的 Edge 对象**,而是通过\"锚点(Anchor)\"来表达节点间的连边关系:\n",
    "\n",
    "- **DataAnchor**:表示数据边(数据流向)。一个节点的每个输入 / 输出端口对应一个 DataAnchor。\n",
    "- **CtrlAnchor**:表示控制边(仅约束执行顺序)。\n",
    "\n",
    "每个锚点维护其对端锚点(peer anchor),通过 src 锚点与 dst 锚点的配对来描述一条连边。换言之,GE 用\"成对的 Anchor\"代替了独立的\"Edge\"对象。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dfe9a21d",
   "metadata": {},
   "source": [
    "## 5. 静态 Shape 与动态 Shape\n",
    "\n",
    "根据 Tensor shape 在编译期是否完全确定,以及执行时是否可能随输入变化,常见场景可以分为静态 Shape、动态分档和纯动态 Shape。\n",
    "\n",
    "### 5.1 静态 Shape 图\n",
    "\n",
    "- 所有 Tensor 的 shape 在编译期完全确定。\n",
    "- 编译产物可直接下沉执行,运行时开销最低。\n",
    "- 适合输入规格固定的推理场景(如固定分辨率的图片分类)。\n",
    "\n",
    "### 5.2 动态分档与纯动态 Shape\n",
    "\n",
    "动态 Shape 图中需要区分两类处理方式:\n",
    "\n",
    "- **动态分档**:提前设置多个 shape 档位,例如 `[1, 128]`、`[1, 256]`、`[1, 512]`。每个档位内的 shape 相对确定,编译和执行方式更接近静态 Shape;运行时根据实际输入选择匹配的档位。\n",
    "- **纯动态 Shape**:不依赖预设档位覆盖所有输入规格,执行时根据实际输入计算 shape、tiling 和资源使用。它更灵活,但通常需要更多运行时推导与调度开销。\n",
    "\n",
    "<div align=\"left\">\n",
    "<table style=\"margin-left: 0; margin-right: auto;\">\n",
    "<thead>\n",
    "<tr><th>维度</th><th>静态 Shape 图</th><th>动态分档</th><th>纯动态 Shape 图</th></tr>\n",
    "</thead>\n",
    "<tbody>\n",
    "<tr><td>Shape 确定性</td><td>编译期完全确定</td><td>运行时从预设档位中选择,档位内相对确定</td><td>执行时根据实际输入确定</td></tr>\n",
    "<tr><td>编译方式</td><td>一次编译固定规格</td><td>为多个档位分别生成或选择相对确定的编译产物</td><td>编译产物保留动态维度,运行时计算 shape / tiling / 资源</td></tr>\n",
    "<tr><td>运行时开销</td><td>低</td><td>中等,主要来自档位匹配和必要的地址刷新</td><td>较高,主要来自运行时 shape 推导、tiling 和资源决策</td></tr>\n",
    "<tr><td>典型场景</td><td>固定输入推理</td><td>常见变长序列、有限分辨率集合</td><td>输入规格变化范围大或无法提前枚举档位的场景</td></tr>\n",
    "</tbody>\n",
    "</table>\n",
    "</div>\n",
    "\n",
    "<div align=\"left\">\n",
    "<blockquote style=\"margin-left: 0; margin-right: auto;\">\n",
    "<p>后续章节讲解动态 Shape 编译与执行时,需要先确认讨论的是动态分档还是纯动态 Shape,避免把二者混为一种机制。</p>\n",
    "</blockquote>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1564cc8",
   "metadata": {},
   "source": [
    "## 6. 算子生态\n",
    "\n",
    "在 GE 的整体架构中,AscendIR 的基础结构(包括 Graph、Node、Tensor、Attribute 等)由 GE 仓维护;然而,**具体的算子定义并不位于 GE 仓,而由独立的算子仓进行维护**。\n",
    "\n",
    "这种设计的原因是:\n",
    "\n",
    "- 算子实现需要在\"入图(Graph)\"和\"aclnn(原生 API 调用)\"两类场景中保持统一。\n",
    "- 算子仓独立维护,使算子实现不受 GE 编译流程的约束。\n",
    "- GE 通过算子注册机制识别和调用算子。\n",
    "\n",
    "### 自定义算子\n",
    "\n",
    "当业务特有算子不在 GE 内置算子库中时,用户可以通过 AscendC 开发自定义算子并注册入 GE 图编译流程:\n",
    "\n",
    "<p align=\"left\"><img src=\"./images/custom_op_flow.svg\" alt=\"自定义算子注册入图流程\" width=\"78%\"></p>\n",
    "\n",
    "<div align=\"left\">\n",
    "<blockquote style=\"margin-left: 0; margin-right: auto;\">\n",
    "<p>自定义算子入图的详细开发流程将在第三章\"图编译\"中深入讲解。</p>\n",
    "</blockquote>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3dd9c094",
   "metadata": {},
   "source": [
    "## 7. 构图方式预览\n",
    "\n",
    "了解了 AscendIR 的元素与连边后,自然的问题是:**用户如何构建一张 AscendIR 图?** GE 提供了三种主流方式,本节先做概览,后续章节再深入。\n",
    "\n",
    "### 7.1 GE 图引擎 C++ 接口(全新构建)\n",
    "\n",
    "用算子原型(`op::` 风格)逐个实例化算子、连接输入输出,从零构建一张 Graph。代码直观、控制力最强:\n",
    "\n",
    "```cpp\n",
    "auto data0 = op::Data(\"data0\");\n",
    "auto data1 = op::Data(\"data1\");\n",
    "auto add   = op::Add(\"add\").set_input_x1(data0).set_input_x2(data1);\n",
    "\n",
    "Graph graph(\"demo_graph\");\n",
    "graph.SetInputs({data0, data1}).SetOutputs({add});\n",
    "```\n",
    "\n",
    "<div align=\"left\">\n",
    "<blockquote style=\"margin-left: 0; margin-right: auto;\">\n",
    "<p>上述为示意写法,旨在说明\"算子原型 + set_input + 组图\"的范式,实际接口名以版本文档为准。</p>\n",
    "</blockquote>\n",
    "</div>\n",
    "\n",
    "### 7.2 Parser 接口(从已有模型解析)\n",
    "\n",
    "将已有的 onnx / pb 等模型文件解析为 AscendIR Graph,无需手工逐算子搭建。典型接口如 `aclgrphParseONNX`(解析 ONNX)、`aclgrphParseTensorFlow`(解析 TF pb)等:\n",
    "\n",
    "```\n",
    "model.onnx ──▶ aclgrphParseONNX ──▶ AscendIR Graph\n",
    "```\n",
    "\n",
    "### 7.3 ES 极简构图(函数风格)\n",
    "\n",
    "采用函数风格的极简接口构图,相比 C++ 原型风格代码量更少,适合快速搭建与实验。GE 仓提供了可执行的 ES examples,例如 [examples/es/normal_input](https://gitcode.com/cann/ge/tree/master/examples/es/normal_input) 中的 C++ 示例会构建 `input -> Relu -> Relu -> Add -> output` 图并支持 dump / run。\n",
    "\n",
    "下面的代码片段摘取并简化自该示例,保留 ES 构图的核心步骤:创建 `EsGraphBuilder`、创建输入、用函数式 API 连接算子、设置输出并构建 Graph。\n",
    "\n",
    "```cpp\n",
    "#include \"es_Add.h\"\n",
    "#include \"es_Relu.h\"\n",
    "#include \"ge/ge_api.h\"\n",
    "\n",
    "using namespace ge;\n",
    "using namespace ge::es;\n",
    "\n",
    "std::unique_ptr<ge::Graph> MakeReluAddGraphByEs() {\n",
    "  auto graph_builder = std::make_unique<EsGraphBuilder>(\"MakeReluAddGraph\");\n",
    "  auto input = graph_builder->CreateInput(0, \"input\", ge::DT_FLOAT, ge::FORMAT_ND, {2, 3});\n",
    "\n",
    "  auto relu1 = Relu(input);\n",
    "  auto relu2 = Relu(relu1);\n",
    "  auto result = Add(input, relu2);\n",
    "\n",
    "  (void)graph_builder->SetOutput(result, 0);\n",
    "  return graph_builder->BuildAndReset();\n",
    "}\n",
    "```\n",
    "\n",
    "完整工程的构建脚本、运行方式,维测说明可参考 GE 仓 examples 目录:\n",
    "\n",
    "- [examples/es](https://gitcode.com/cann/ge/tree/master/examples/es)\n",
    "\n",
    "\n",
    "### 三种方式与后续章节的关系\n",
    "\n",
    "<div align=\"left\">\n",
    "<table style=\"margin-left: 0; margin-right: auto;\">\n",
    "<thead>\n",
    "<tr><th>构图方式</th><th>风格</th><th>适用场景</th></tr>\n",
    "</thead>\n",
    "<tbody>\n",
    "<tr><td>GE C++ 接口(op:: 原型)</td><td>显式、可控</td><td>全新构图、精细控制</td></tr>\n",
    "<tr><td>Parser 接口</td><td>复用已有模型</td><td>已有 onnx / pb 模型迁移</td></tr>\n",
    "<tr><td>ES 极简构图</td><td>函数式、简洁</td><td>快速实验、代码量敏感</td></tr>\n",
    "</tbody>\n",
    "</table>\n",
    "</div>\n",
    "\n",
    "<div align=\"left\">\n",
    "<blockquote style=\"margin-left: 0; margin-right: auto;\">\n",
    "<p><strong>三种方式产出的都是同一种 AscendIR 图</strong>,差别只在\"怎么写\"。无论哪种方式构图,后续都会进入统一的 GE 编译与执行流程:</p>\n",
    "<ul><li><strong><a href=\"../02_inference_workflow/02.02_offline_inference.ipynb\">2.2 离线推理流程:ATC 编译与 ACL 推理</a></strong>:走<strong>离线</strong>路径(ATC 编译为 OM + ACL 加载执行)。</li><li><strong><a href=\"../02_inference_workflow/02.03_online_execution.ipynb\">2.3 在线执行流程:GeSession 构图与执行</a></strong>:走<strong>在线</strong>路径(GeSession 在线构图、编译并执行)。</li></ul>\n",
    "</blockquote>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b1d841a7",
   "metadata": {},
   "source": [
    "## 课后练习\n",
    "\n",
    "本节介绍了 AscendIR 的核心概念和构图基础,请根据学习内容完成以下题目进行自测。\n",
    "\n",
    "1. (判断题)AscendIR 是 GE 编译流程使用的核心 IR,用图结构表达模型的计算逻辑与数据依赖关系。\n",
    "\n",
    "2. (判断题)在 GE 的实际实现中,图中存在独立的 Edge 对象来描述节点间的连接关系。\n",
    "\n",
    "3. (单选题)AscendIR 的核心对象模型是一条自顶向下、层层持有的链路,下列哪一项正确?\n",
    "    A. ComputeGraph → Node → Edge → Tensor\n",
    "    B. ComputeGraph → Node → OpDesc → GeTensorDesc\n",
    "    C. ComputeGraph → OpDesc → Node → Anchor\n",
    "    D. Graph → Tensor → Node → OpDesc\n",
    "\n",
    "4. (单选题)关于 AscendIR 中 Tensor 描述的存放位置,下列说法正确的是?\n",
    "    A. Tensor 是独立漂浮在数据边上的对象,不被任何节点持有\n",
    "    B. 每个 Node 持有一个算子描述符 OpDesc,算子的输入/输出 Tensor 描述就保存在 OpDesc 中(input_desc / output_desc),即 Tensor 描述由节点持有\n",
    "    C. Graph 直接持有所有 Tensor,Node 只负责把它们连接起来\n",
    "    D. Tensor 描述独立于 Node,单独存放在图的全局张量表中\n",
    "\n",
    "5. (单选题)以下关于控制边的描述,哪个是正确的?\n",
    "    A. 控制边用于传递 Tensor 数据\n",
    "    B. 控制边表示纯依赖关系,无数据传递,仅约束执行顺序\n",
    "    C. 控制边和数据边的功能完全相同\n",
    "    D. 控制边只能用于输入节点和输出节点之间\n",
    "\n",
    "6. (多选题)以下关于静态 Shape、动态分档和纯动态 Shape 的描述,哪些是正确的?\n",
    "    A. 静态 Shape 图的所有 Tensor shape 在编译期完全确定\n",
    "    B. 动态分档会设置多个 shape 档位,每个档位按相对确定的静态 shape 方式编译 / 执行\n",
    "    C. 静态 Shape 图的运行时开销通常低于动态 Shape 图\n",
    "    D. GE 不支持动态 Shape 图的编译和执行\n",
    "\n",
    "7. (单选题)某图中有两个算子 Conv1 和 Conv2,它们的 type 都是 Convolution。以下说法正确的是?\n",
    "    A. name 相同、type 不同\n",
    "    B. name 不同、type 相同,二者实现逻辑相同\n",
    "    C. 这是非法的,同一图中不允许出现两个卷积算子\n",
    "    D. 二者的 name 必须相同才能共享 type\n",
    "\n",
    "8. (多选题)以下关于 Anchor 与构图方式的描述,哪些是正确的?\n",
    "    A. GE 实现中没有独立的 Edge 对象,用 DataAnchor 表数据边、CtrlAnchor 表控制边\n",
    "    B. Parser 接口(如 aclgrphParseONNX)可将已有 onnx 模型解析为 AscendIR Graph\n",
    "    C. GE C++ 接口、Parser、ES 极简构图三种方式产出的都是同一种 AscendIR 图\n",
    "    D. CtrlAnchor 用于传递 Tensor 数据\n",
    "\n",
    "**执行以下代码获取答案。**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!cat answer/01.03_answer.txt"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.8.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}