Loop

Loop - 23

版本

  • 名称: Loop (GitHub)

  • : main

  • 起始版本: 23

  • 函数: False

  • 支持级别: SupportType.COMMON

  • 形状推断: True

该版本的运算符自版本 23 起可用。

摘要

通用循环结构。此循环有多个终止条件

  1. 迭代次数。运行时指定的迭代计数。通过指定输入 M 设置。可选。设为空字符串表示省略。请注意,可以通过为输入 M 传递一个常量节点来指定静态迭代次数(在图构建时指定)。

  2. 循环终止条件。这是运算符的一个输入,用于确定是否运行第一次迭代,同时也是主体图的循环携带依赖项。无论是否提供此输入,主体图都必须为条件变量生成一个值。

此表总结了该运算符的操作模式及其等效的 C 风格代码

运算符输入定义为 (最大迭代次数, 条件变量)。

  • 输入 (“”, “”): for (int i=0; ; ++i) { cond = … // 请注意,此值被忽略,但在主体中是必需的 }

  • 输入 (“”, cond) // 请注意,这类似于 while 循环 bool cond = …; for (int i=0; cond; ++i) { cond = …; }

  • 输入 (“”, 1) // 请注意,这类似于 do-while 循环 bool cond = true for (int i=0; cond; ++i) { cond = …; }

  • 输入 (trip_count, “”) // 请注意,这类似于 for 循环 int trip_count = … for (int i=0; i < trip_count; ++i) { cond = …; // 被忽略 }

  • 输入 (trip_count, cond) int trip_count = …; bool cond = …; for (int i=0; i < trip_count && cond; ++i) { cond = …; }

示例用法 - 条件和迭代次数

graph predict-net {
  %a = Constant[value = <Scalar Tensor [3]>]()
  %b = Constant[value = <Scalar Tensor [6]>]()
  %keepgoing = Constant[value = <Scalar Tensor [1]>]()
  %max_trip_count = Constant[value = <Scalar Tensor [10]>]()
  %keepgoing_out, %b_out, %user_defined_vals = Loop[body = <graph body-net>](%max_trip_count, %keepgoing, %b)
  return
}

graph body-net (
  %i[INT32, scalar]           // iteration number
  %keepgoing_in[BOOL, scalar] // incoming loop-termination-condition; not used
  %b_in[INT32, scalar]        // incoming value of loop-carried-dependency b
) {
  %my_local = Add(%a, %b_in)
  %b_out = Sub(%a, %b_in) // outgoing value of loop-carried-dependency b
  %keepgoing_out = Greater(%my_local, %b_out) // outgoing loop-termination-condition
  %user_defined_val = Add(%b_in, %b_in) // scan-output value to be accumulated
  return %keepgoing_out, %b_out, %user_defined_val
}

示例等效 C 代码

{
  /* User-defined code (enclosing scope) */
  int a = 3, b = 6;
  bool keepgoing = true; // Analogous to input cond
  /* End user-defined code */

  /* Implicitly-defined code */
  const int max_trip_count = 10; // Analogous to input M
  int user_defined_vals[]; // Imagine this is resizable
  /* End implicitly-defined code */
  /* initialize loop-carried variables and scan-output variables */
  bool keepgoing_out = keepgoing
  int b_out = b

  for (int i=0; i < max_trip_count && keepgoing_out; ++i) {
    /* Implicitly-defined code: bind actual parameter values
       to formal parameter variables of loop-body */
    bool keepgoing_in = keepgoing_out;
    bool b_in = b_out;

    /* User-defined code (loop body) */
    int my_local = a + b_in; // Reading value "a" from the enclosing scope is fine
    b_out = a - b_in;
    keepgoing_out = my_local > b_out;
    user_defined_val = b_in + b_in; // b_in and b_out are different variables
    /* End user-defined code */

    /* Implicitly defined-code */
    user_defined_vals[i] = user_defined_val // accumulate scan-output values
  }
  // int t = my_local; // Can't do this. my_local is not accessible here.

  // The values below are bound to the output variables of the loop and therefore accessible
  // b_out; user_defined_vals; keepgoing_out;
}

此代码片段中有几点需要注意

  1. 封闭作用域中的值(即此处的变量“a”)在作用域内,可以在循环输入中引用。

  2. 在循环体中计算的、需要在后续迭代或循环结束后使用的任何值都通过循环体中的一对变量来建模,包括一个输入变量(例如 b_in)和一个输出变量(例如 b_out)。这些被称为循环携带依赖项。循环操作节点为第一次迭代提供输入变量的输入值,并返回最终迭代产生的输出变量的输出值。

  3. Scan_output 变量用于隐式地连接所有迭代中计算的值。在上面的示例中,在所有迭代中计算的 user_defined_val 的值被连接起来,并在循环后作为 user_defined_vals 的值返回。

  4. 在主体中创建的值不能在封闭作用域中访问,除非使用上述机制。

请注意,此运算符的语义支持“对角线”或“波前”执行。(此处步骤 3 提供了一个示例:https://devblogs.nvidia.com/optimizing-recurrent-neural-networks-cudnn-5/)。前端应该将多层 RNN 发射为一系列 While 运算符(时间是内部循环维度),其中每个连续层消耗前一层的 scan_outputs,可能经过几个点对点运算符(例如 dropout、残差连接、线性层)。

子图(由循环节点产生)的输入/输出匹配基于顺序而不是名称。实现将根据此顺序确定名称。

属性

  • body - GRAPH (必需)

    每次迭代运行的图。它有 2+N 个输入:(iteration_num,条件,循环携带依赖项…)。它有 1+N+K 个输出:(条件,循环携带依赖项…,scan_outputs…)。每个 scan_output 通过连接循环每次迭代结束时指定输出值来创建。如果这些 scan_outputs 的维度或数据类型在循环迭代中发生变化,则会出错。

输入

输入数量在 2 到 2147483647 之间。

  • M (可选,异构) - I

    运行时指定的循环最大迭代次数。可选。传递空字符串表示跳过。

  • cond (可选,异构) - B

    一个布尔终止条件。可选。传递空字符串表示跳过。

  • v_initial (可变参数) - V

    任何循环携带依赖项(在循环迭代中变化的值)的初始值

输出

输出数量在 1 到 2147483647 之间。

  • v_final_and_scan_outputs (可变参数) - V

    最终 N 个循环携带依赖项值,然后是 K 个 scan_outputs。Scan outputs 必须是张量。

类型约束

  • V 属于 ( optional(seq(tensor(bfloat16))), optional(seq(tensor(bool))), optional(seq(tensor(complex128))), optional(seq(tensor(complex64))), optional(seq(tensor(double))), optional(seq(tensor(float))), optional(seq(tensor(float16))), optional(seq(tensor(int16))), optional(seq(tensor(int32))), optional(seq(tensor(int64))), optional(seq(tensor(int8))), optional(seq(tensor(string))), optional(seq(tensor(uint16))), optional(seq(tensor(uint32))), optional(seq(tensor(uint64))), optional(seq(tensor(uint8))), optional(tensor(bfloat16)), optional(tensor(bool)), optional(tensor(complex128)), optional(tensor(complex64)), optional(tensor(double)), optional(tensor(float)), optional(tensor(float16)), optional(tensor(float4e2m1)), optional(tensor(float8e4m3fn)), optional(tensor(float8e4m3fnuz)), optional(tensor(float8e5m2)), optional(tensor(float8e5m2fnuz)), optional(tensor(int16)), optional(tensor(int32)), optional(tensor(int4)), optional(tensor(int64)), optional(tensor(int8)), optional(tensor(string)), optional(tensor(uint16)), optional(tensor(uint32)), optional(tensor(uint4)), optional(tensor(uint64)), optional(tensor(uint8)), seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(complex128)), seq(tensor(complex64)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float4e2m1)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int4)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint4)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(complex128), tensor(complex64), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8) )

    IRv11 及之前的所有 Tensor, Sequence(Tensor), Optional(Tensor) 和 Optional(Sequence(Tensor)) 类型。

  • I 属于 ( tensor(int64) )

    int64 张量,应为标量。

  • B 属于 ( tensor(bool) )

    bool 张量,应为标量。

Loop - 21

版本

  • 名称: Loop (GitHub)

  • : main

  • 起始版本: 21

  • 函数: False

  • 支持级别: SupportType.COMMON

  • 形状推断: True

该版本的运算符自版本 21 起可用。

摘要

通用循环结构。此循环有多个终止条件

  1. 迭代次数。运行时指定的迭代计数。通过指定输入 M 设置。可选。设为空字符串表示省略。请注意,可以通过为输入 M 传递一个常量节点来指定静态迭代次数(在图构建时指定)。

  2. 循环终止条件。这是运算符的一个输入,用于确定是否运行第一次迭代,同时也是主体图的循环携带依赖项。无论是否提供此输入,主体图都必须为条件变量生成一个值。

此表总结了该运算符的操作模式及其等效的 C 风格代码

运算符输入定义为 (最大迭代次数, 条件变量)。

  • 输入 (“”, “”): for (int i=0; ; ++i) { cond = … // 请注意,此值被忽略,但在主体中是必需的 }

  • 输入 (“”, cond) // 请注意,这类似于 while 循环 bool cond = …; for (int i=0; cond; ++i) { cond = …; }

  • 输入 (“”, 1) // 请注意,这类似于 do-while 循环 bool cond = true for (int i=0; cond; ++i) { cond = …; }

  • 输入 (trip_count, “”) // 请注意,这类似于 for 循环 int trip_count = … for (int i=0; i < trip_count; ++i) { cond = …; // 被忽略 }

  • 输入 (trip_count, cond) int trip_count = …; bool cond = …; for (int i=0; i < trip_count && cond; ++i) { cond = …; }

示例用法 - 条件和迭代次数

graph predict-net {
  %a = Constant[value = <Scalar Tensor [3]>]()
  %b = Constant[value = <Scalar Tensor [6]>]()
  %keepgoing = Constant[value = <Scalar Tensor [1]>]()
  %max_trip_count = Constant[value = <Scalar Tensor [10]>]()
  %keepgoing_out, %b_out, %user_defined_vals = Loop[body = <graph body-net>](%max_trip_count, %keepgoing, %b)
  return
}

graph body-net (
  %i[INT32, scalar]           // iteration number
  %keepgoing_in[BOOL, scalar] // incoming loop-termination-condition; not used
  %b_in[INT32, scalar]        // incoming value of loop-carried-dependency b
) {
  %my_local = Add(%a, %b_in)
  %b_out = Sub(%a, %b_in) // outgoing value of loop-carried-dependency b
  %keepgoing_out = Greater(%my_local, %b_out) // outgoing loop-termination-condition
  %user_defined_val = Add(%b_in, %b_in) // scan-output value to be accumulated
  return %keepgoing_out, %b_out, %user_defined_val
}

示例等效 C 代码

{
  /* User-defined code (enclosing scope) */
  int a = 3, b = 6;
  bool keepgoing = true; // Analogous to input cond
  /* End user-defined code */

  /* Implicitly-defined code */
  const int max_trip_count = 10; // Analogous to input M
  int user_defined_vals[]; // Imagine this is resizable
  /* End implicitly-defined code */
  /* initialize loop-carried variables and scan-output variables */
  bool keepgoing_out = keepgoing
  int b_out = b

  for (int i=0; i < max_trip_count && keepgoing_out; ++i) {
    /* Implicitly-defined code: bind actual parameter values
       to formal parameter variables of loop-body */
    bool keepgoing_in = keepgoing_out;
    bool b_in = b_out;

    /* User-defined code (loop body) */
    int my_local = a + b_in; // Reading value "a" from the enclosing scope is fine
    b_out = a - b_in;
    keepgoing_out = my_local > b_out;
    user_defined_val = b_in + b_in; // b_in and b_out are different variables
    /* End user-defined code */

    /* Implicitly defined-code */
    user_defined_vals[i] = user_defined_val // accumulate scan-output values
  }
  // int t = my_local; // Can't do this. my_local is not accessible here.

  // The values below are bound to the output variables of the loop and therefore accessible
  // b_out; user_defined_vals; keepgoing_out;
}

此代码片段中有几点需要注意

  1. 封闭作用域中的值(即此处的变量“a”)在作用域内,可以在循环输入中引用。

  2. 在循环体中计算的、需要在后续迭代或循环结束后使用的任何值都通过循环体中的一对变量来建模,包括一个输入变量(例如 b_in)和一个输出变量(例如 b_out)。这些被称为循环携带依赖项。循环操作节点为第一次迭代提供输入变量的输入值,并返回最终迭代产生的输出变量的输出值。

  3. Scan_output 变量用于隐式地连接所有迭代中计算的值。在上面的示例中,在所有迭代中计算的 user_defined_val 的值被连接起来,并在循环后作为 user_defined_vals 的值返回。

  4. 在主体中创建的值不能在封闭作用域中访问,除非使用上述机制。

请注意,此运算符的语义支持“对角线”或“波前”执行。(此处步骤 3 提供了一个示例:https://devblogs.nvidia.com/optimizing-recurrent-neural-networks-cudnn-5/)。前端应该将多层 RNN 发射为一系列 While 运算符(时间是内部循环维度),其中每个连续层消耗前一层的 scan_outputs,可能经过几个点对点运算符(例如 dropout、残差连接、线性层)。

子图(由循环节点产生)的输入/输出匹配基于顺序而不是名称。实现将根据此顺序确定名称。

属性

  • body - GRAPH (必需)

    每次迭代运行的图。它有 2+N 个输入:(iteration_num,条件,循环携带依赖项…)。它有 1+N+K 个输出:(条件,循环携带依赖项…,scan_outputs…)。每个 scan_output 通过连接循环每次迭代结束时指定输出值来创建。如果这些 scan_outputs 的维度或数据类型在循环迭代中发生变化,则会出错。

输入

输入数量在 2 到 2147483647 之间。

  • M (可选,异构) - I

    运行时指定的循环最大迭代次数。可选。传递空字符串表示跳过。

  • cond (可选,异构) - B

    一个布尔终止条件。可选。传递空字符串表示跳过。

  • v_initial (可变参数) - V

    任何循环携带依赖项(在循环迭代中变化的值)的初始值

输出

输出数量在 1 到 2147483647 之间。

  • v_final_and_scan_outputs (可变参数) - V

    最终 N 个循环携带依赖项值,然后是 K 个 scan_outputs。Scan outputs 必须是张量。

类型约束

  • V 属于 ( optional(seq(tensor(bfloat16))), optional(seq(tensor(bool))), optional(seq(tensor(complex128))), optional(seq(tensor(complex64))), optional(seq(tensor(double))), optional(seq(tensor(float))), optional(seq(tensor(float16))), optional(seq(tensor(int16))), optional(seq(tensor(int32))), optional(seq(tensor(int64))), optional(seq(tensor(int8))), optional(seq(tensor(string))), optional(seq(tensor(uint16))), optional(seq(tensor(uint32))), optional(seq(tensor(uint64))), optional(seq(tensor(uint8))), optional(tensor(bfloat16)), optional(tensor(bool)), optional(tensor(complex128)), optional(tensor(complex64)), optional(tensor(double)), optional(tensor(float)), optional(tensor(float16)), optional(tensor(float8e4m3fn)), optional(tensor(float8e4m3fnuz)), optional(tensor(float8e5m2)), optional(tensor(float8e5m2fnuz)), optional(tensor(int16)), optional(tensor(int32)), optional(tensor(int4)), optional(tensor(int64)), optional(tensor(int8)), optional(tensor(string)), optional(tensor(uint16)), optional(tensor(uint32)), optional(tensor(uint4)), optional(tensor(uint64)), optional(tensor(uint8)), seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(complex128)), seq(tensor(complex64)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int4)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint4)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(complex128), tensor(complex64), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8) )

    IRv10 及之前的所有 Tensor, Sequence(Tensor), Optional(Tensor) 和 Optional(Sequence(Tensor)) 类型。

  • I 属于 ( tensor(int64) )

    int64 张量,应为标量。

  • B 属于 ( tensor(bool) )

    bool 张量,应为标量。

Loop - 19

版本

  • 名称: Loop (GitHub)

  • : main

  • 起始版本: 19

  • 函数: False

  • 支持级别: SupportType.COMMON

  • 形状推断: True

该版本的运算符自版本 19 起可用。

摘要

通用循环结构。此循环有多个终止条件

  1. 迭代次数。运行时指定的迭代计数。通过指定输入 M 设置。可选。设为空字符串表示省略。请注意,可以通过为输入 M 传递一个常量节点来指定静态迭代次数(在图构建时指定)。

  2. 循环终止条件。这是运算符的一个输入,用于确定是否运行第一次迭代,同时也是主体图的循环携带依赖项。无论是否提供此输入,主体图都必须为条件变量生成一个值。

此表总结了该运算符的操作模式及其等效的 C 风格代码

运算符输入定义为 (最大迭代次数, 条件变量)。

  • 输入 (“”, “”): for (int i=0; ; ++i) { cond = … // 请注意,此值被忽略,但在主体中是必需的 }

  • 输入 (“”, cond) // 请注意,这类似于 while 循环 bool cond = …; for (int i=0; cond; ++i) { cond = …; }

  • 输入 (“”, 1) // 请注意,这类似于 do-while 循环 bool cond = true for (int i=0; cond; ++i) { cond = …; }

  • 输入 (trip_count, “”) // 请注意,这类似于 for 循环 int trip_count = … for (int i=0; i < trip_count; ++i) { cond = …; // 被忽略 }

  • 输入 (trip_count, cond) int trip_count = …; bool cond = …; for (int i=0; i < trip_count && cond; ++i) { cond = …; }

示例用法 - 条件和迭代次数

graph predict-net {
  %a = Constant[value = <Scalar Tensor [3]>]()
  %b = Constant[value = <Scalar Tensor [6]>]()
  %keepgoing = Constant[value = <Scalar Tensor [1]>]()
  %max_trip_count = Constant[value = <Scalar Tensor [10]>]()
  %keepgoing_out, %b_out, %user_defined_vals = Loop[body = <graph body-net>](%max_trip_count, %keepgoing, %b)
  return
}

graph body-net (
  %i[INT32, scalar]           // iteration number
  %keepgoing_in[BOOL, scalar] // incoming loop-termination-condition; not used
  %b_in[INT32, scalar]        // incoming value of loop-carried-dependency b
) {
  %my_local = Add(%a, %b_in)
  %b_out = Sub(%a, %b_in) // outgoing value of loop-carried-dependency b
  %keepgoing_out = Greater(%my_local, %b_out) // outgoing loop-termination-condition
  %user_defined_val = Add(%b_in, %b_in) // scan-output value to be accumulated
  return %keepgoing_out, %b_out, %user_defined_val
}

示例等效 C 代码

{
  /* User-defined code (enclosing scope) */
  int a = 3, b = 6;
  bool keepgoing = true; // Analogous to input cond
  /* End user-defined code */

  /* Implicitly-defined code */
  const int max_trip_count = 10; // Analogous to input M
  int user_defined_vals[]; // Imagine this is resizable
  /* End implicitly-defined code */
  /* initialize loop-carried variables and scan-output variables */
  bool keepgoing_out = keepgoing
  int b_out = b

  for (int i=0; i < max_trip_count && keepgoing_out; ++i) {
    /* Implicitly-defined code: bind actual parameter values
       to formal parameter variables of loop-body */
    bool keepgoing_in = keepgoing_out;
    bool b_in = b_out;

    /* User-defined code (loop body) */
    int my_local = a + b_in; // Reading value "a" from the enclosing scope is fine
    b_out = a - b_in;
    keepgoing_out = my_local > b_out;
    user_defined_val = b_in + b_in; // b_in and b_out are different variables
    /* End user-defined code */

    /* Implicitly defined-code */
    user_defined_vals[i] = user_defined_val // accumulate scan-output values
  }
  // int t = my_local; // Can't do this. my_local is not accessible here.

  // The values below are bound to the output variables of the loop and therefore accessible
  // b_out; user_defined_vals; keepgoing_out;
}

此代码片段中有几点需要注意

  1. 封闭作用域中的值(即此处的变量“a”)在作用域内,可以在循环输入中引用。

  2. 在循环体中计算的、需要在后续迭代或循环结束后使用的任何值都通过循环体中的一对变量来建模,包括一个输入变量(例如 b_in)和一个输出变量(例如 b_out)。这些被称为循环携带依赖项。循环操作节点为第一次迭代提供输入变量的输入值,并返回最终迭代产生的输出变量的输出值。

  3. Scan_output 变量用于隐式地连接所有迭代中计算的值。在上面的示例中,在所有迭代中计算的 user_defined_val 的值被连接起来,并在循环后作为 user_defined_vals 的值返回。

  4. 在主体中创建的值不能在封闭作用域中访问,除非使用上述机制。

请注意,此运算符的语义支持“对角线”或“波前”执行。(此处步骤 3 提供了一个示例:https://devblogs.nvidia.com/optimizing-recurrent-neural-networks-cudnn-5/)。前端应该将多层 RNN 发射为一系列 While 运算符(时间是内部循环维度),其中每个连续层消耗前一层的 scan_outputs,可能经过几个点对点运算符(例如 dropout、残差连接、线性层)。

子图(由循环节点产生)的输入/输出匹配基于顺序而不是名称。实现将根据此顺序确定名称。

属性

  • body - GRAPH (必需)

    每次迭代运行的图。它有 2+N 个输入:(iteration_num,条件,循环携带依赖项…)。它有 1+N+K 个输出:(条件,循环携带依赖项…,scan_outputs…)。每个 scan_output 通过连接循环每次迭代结束时指定输出值来创建。如果这些 scan_outputs 的维度或数据类型在循环迭代中发生变化,则会出错。

输入

输入数量在 2 到 2147483647 之间。

  • M (可选,异构) - I

    运行时指定的循环最大迭代次数。可选。传递空字符串表示跳过。

  • cond (可选,异构) - B

    一个布尔终止条件。可选。传递空字符串表示跳过。

  • v_initial (可变参数) - V

    任何循环携带依赖项(在循环迭代中变化的值)的初始值

输出

输出数量在 1 到 2147483647 之间。

  • v_final_and_scan_outputs (可变参数) - V

    最终 N 个循环携带依赖项值,然后是 K 个 scan_outputs。Scan outputs 必须是张量。

类型约束

  • V 属于 ( optional(seq(tensor(bfloat16))), optional(seq(tensor(bool))), optional(seq(tensor(complex128))), optional(seq(tensor(complex64))), optional(seq(tensor(double))), optional(seq(tensor(float))), optional(seq(tensor(float16))), optional(seq(tensor(int16))), optional(seq(tensor(int32))), optional(seq(tensor(int64))), optional(seq(tensor(int8))), optional(seq(tensor(string))), optional(seq(tensor(uint16))), optional(seq(tensor(uint32))), optional(seq(tensor(uint64))), optional(seq(tensor(uint8))), optional(tensor(bfloat16)), optional(tensor(bool)), optional(tensor(complex128)), optional(tensor(complex64)), optional(tensor(double)), optional(tensor(float)), optional(tensor(float16)), optional(tensor(float8e4m3fn)), optional(tensor(float8e4m3fnuz)), optional(tensor(float8e5m2)), optional(tensor(float8e5m2fnuz)), optional(tensor(int16)), optional(tensor(int32)), optional(tensor(int4)), optional(tensor(int64)), optional(tensor(int8)), optional(tensor(string)), optional(tensor(uint16)), optional(tensor(uint32)), optional(tensor(uint4)), optional(tensor(uint64)), optional(tensor(uint8)), seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(complex128)), seq(tensor(complex64)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int4)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint4)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(complex128), tensor(complex64), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8) )

    IRv9 及之前的所有 Tensor, Sequence(Tensor), Optional(Tensor) 和 Optional(Sequence(Tensor)) 类型。

  • I 属于 ( tensor(int64) )

    int64 张量,应为标量。

  • B 属于 ( tensor(bool) )

    bool 张量,应为标量。

Loop - 16

版本

  • 名称: Loop (GitHub)

  • : main

  • 起始版本: 16

  • 函数: False

  • 支持级别: SupportType.COMMON

  • 形状推断: True

该版本的运算符自版本 16 起可用。

摘要

通用循环结构。此循环有多个终止条件

  1. 迭代次数。运行时指定的迭代计数。通过指定输入 M 设置。可选。设为空字符串表示省略。请注意,可以通过为输入 M 传递一个常量节点来指定静态迭代次数(在图构建时指定)。

  2. 循环终止条件。这是运算符的一个输入,用于确定是否运行第一次迭代,同时也是主体图的循环携带依赖项。无论是否提供此输入,主体图都必须为条件变量生成一个值。

此表总结了该运算符的操作模式及其等效的 C 风格代码

运算符输入定义为 (最大迭代次数, 条件变量)。

  • 输入 (“”, “”): for (int i=0; ; ++i) { cond = … // 请注意,此值被忽略,但在主体中是必需的 }

  • 输入 (“”, cond) // 请注意,这类似于 while 循环 bool cond = …; for (int i=0; cond; ++i) { cond = …; }

  • 输入 (“”, 1) // 请注意,这类似于 do-while 循环 bool cond = true for (int i=0; cond; ++i) { cond = …; }

  • 输入 (trip_count, “”) // 请注意,这类似于 for 循环 int trip_count = … for (int i=0; i < trip_count; ++i) { cond = …; // 被忽略 }

  • 输入 (trip_count, cond) int trip_count = …; bool cond = …; for (int i=0; i < trip_count && cond; ++i) { cond = …; }

示例用法 - 条件和迭代次数

graph predict-net {
  %a = Constant[value = <Scalar Tensor [3]>]()
  %b = Constant[value = <Scalar Tensor [6]>]()
  %keepgoing = Constant[value = <Scalar Tensor [1]>]()
  %max_trip_count = Constant[value = <Scalar Tensor [10]>]()
  %keepgoing_out, %b_out, %user_defined_vals = Loop[body = <graph body-net>](%max_trip_count, %keepgoing, %b)
  return
}

graph body-net (
  %i[INT32, scalar]           // iteration number
  %keepgoing_in[BOOL, scalar] // incoming loop-termination-condition; not used
  %b_in[INT32, scalar]        // incoming value of loop-carried-dependency b
) {
  %my_local = Add(%a, %b_in)
  %b_out = Sub(%a, %b_in) // outgoing value of loop-carried-dependency b
  %keepgoing_out = Greater(%my_local, %b_out) // outgoing loop-termination-condition
  %user_defined_val = Add(%b_in, %b_in) // scan-output value to be accumulated
  return %keepgoing_out, %b_out, %user_defined_val
}

示例等效 C 代码

{
  /* User-defined code (enclosing scope) */
  int a = 3, b = 6;
  bool keepgoing = true; // Analogous to input cond
  /* End user-defined code */

  /* Implicitly-defined code */
  const int max_trip_count = 10; // Analogous to input M
  int user_defined_vals[]; // Imagine this is resizable
  /* End implicitly-defined code */
  /* initialize loop-carried variables and scan-output variables */
  bool keepgoing_out = keepgoing
  int b_out = b

  for (int i=0; i < max_trip_count && keepgoing_out; ++i) {
    /* Implicitly-defined code: bind actual parameter values
       to formal parameter variables of loop-body */
    bool keepgoing_in = keepgoing_out;
    bool b_in = b_out;

    /* User-defined code (loop body) */
    int my_local = a + b_in; // Reading value "a" from the enclosing scope is fine
    b_out = a - b_in;
    keepgoing_out = my_local > b_out;
    user_defined_val = b_in + b_in; // b_in and b_out are different variables
    /* End user-defined code */

    /* Implicitly defined-code */
    user_defined_vals[i] = user_defined_val // accumulate scan-output values
  }
  // int t = my_local; // Can't do this. my_local is not accessible here.

  // The values below are bound to the output variables of the loop and therefore accessible
  // b_out; user_defined_vals; keepgoing_out;
}

此代码片段中有几点需要注意

  1. 封闭作用域中的值(即此处的变量“a”)在作用域内,可以在循环输入中引用。

  2. 在循环体中计算的、需要在后续迭代或循环结束后使用的任何值都通过循环体中的一对变量来建模,包括一个输入变量(例如 b_in)和一个输出变量(例如 b_out)。这些被称为循环携带依赖项。循环操作节点为第一次迭代提供输入变量的输入值,并返回最终迭代产生的输出变量的输出值。

  3. Scan_output 变量用于隐式地连接所有迭代中计算的值。在上面的示例中,在所有迭代中计算的 user_defined_val 的值被连接起来,并在循环后作为 user_defined_vals 的值返回。

  4. 在主体中创建的值不能在封闭作用域中访问,除非使用上述机制。

请注意,此运算符的语义支持“对角线”或“波前”执行。(此处步骤 3 提供了一个示例:https://devblogs.nvidia.com/optimizing-recurrent-neural-networks-cudnn-5/)。前端应该将多层 RNN 发射为一系列 While 运算符(时间是内部循环维度),其中每个连续层消耗前一层的 scan_outputs,可能经过几个点对点运算符(例如 dropout、残差连接、线性层)。

子图(由循环节点产生)的输入/输出匹配基于顺序而不是名称。实现将根据此顺序确定名称。

属性

  • body - GRAPH (必需)

    每次迭代运行的图。它有 2+N 个输入:(iteration_num,条件,循环携带依赖项…)。它有 1+N+K 个输出:(条件,循环携带依赖项…,scan_outputs…)。每个 scan_output 通过连接循环每次迭代结束时指定输出值来创建。如果这些 scan_outputs 的维度或数据类型在循环迭代中发生变化,则会出错。

输入

输入数量在 2 到 2147483647 之间。

  • M (可选,异构) - I

    运行时指定的循环最大迭代次数。可选。传递空字符串表示跳过。

  • cond (可选,异构) - B

    一个布尔终止条件。可选。传递空字符串表示跳过。

  • v_initial (可变参数) - V

    任何循环携带依赖项(在循环迭代中变化的值)的初始值

输出

输出数量在 1 到 2147483647 之间。

  • v_final_and_scan_outputs (可变参数) - V

    最终 N 个循环携带依赖项值,然后是 K 个 scan_outputs。Scan outputs 必须是张量。

类型约束

  • V 可以是 ( optional(seq(tensor(bfloat16))), optional(seq(tensor(bool))), optional(seq(tensor(complex128))), optional(seq(tensor(complex64))), optional(seq(tensor(double))), optional(seq(tensor(float))), optional(seq(tensor(float16))), optional(seq(tensor(int16))), optional(seq(tensor(int32))), optional(seq(tensor(int64))), optional(seq(tensor(int8))), optional(seq(tensor(string))), optional(seq(tensor(uint16))), optional(seq(tensor(uint32))), optional(seq(tensor(uint64))), optional(seq(tensor(uint8))), optional(tensor(bfloat16)), optional(tensor(bool)), optional(tensor(complex128)), optional(tensor(complex64)), optional(tensor(double)), optional(tensor(float)), optional(tensor(float16)), optional(tensor(int16)), optional(tensor(int32)), optional(tensor(int64)), optional(tensor(int8)), optional(tensor(string)), optional(tensor(uint16)), optional(tensor(uint32)), optional(tensor(uint64)), optional(tensor(uint8)), seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(complex128)), seq(tensor(complex64)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(complex128), tensor(complex64), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8) )

    所有张量、张量序列、可选张量和可选张量序列类型(最高支持 IRv4)。

  • I 属于 ( tensor(int64) )

    int64 张量,应为标量。

  • B 属于 ( tensor(bool) )

    bool 张量,应为标量。

循环 - 13

版本

  • 名称: Loop (GitHub)

  • : main

  • since_version: 13

  • 函数: False

  • 支持级别: SupportType.COMMON

  • 形状推断: True

该版本的算子自版本 13 起可用。

摘要

通用循环结构。此循环有多个终止条件

  1. 迭代次数。运行时指定的迭代计数。通过指定输入 M 设置。可选。设为空字符串表示省略。请注意,可以通过为输入 M 传递一个常量节点来指定静态迭代次数(在图构建时指定)。

  2. 循环终止条件。这是运算符的一个输入,用于确定是否运行第一次迭代,同时也是主体图的循环携带依赖项。无论是否提供此输入,主体图都必须为条件变量生成一个值。

此表总结了该运算符的操作模式及其等效的 C 风格代码

Operator inputs defined as (max_trip_count, condition_var).

input ("", ""):
    for (int i=0; ; ++i) {
      cond = ... // Note this value is ignored, but is required in the body
    }

input ("", cond) // Note this is analogous to a while loop
    bool cond = ...;
    for (int i=0; cond; ++i) {
      cond = ...;
    }

input ("", 1) // Note this is analogous to a do-while loop
    bool cond = true
    for (int i=0; cond; ++i) {
      cond = ...;
    }

input (trip_count, "") // Note this is analogous to a for loop
    int trip_count = ...
    for (int i=0; i < trip_count; ++i) {
      cond = ...; // ignored
    }

input (trip_count, cond)
    int trip_count = ...;
    bool cond = ...;
    for (int i=0; i < trip_count && cond; ++i) {
      cond = ...;
    }

示例用法 - 条件和迭代次数

graph predict-net {
  %a = Constant[value = <Scalar Tensor [3]>]()
  %b = Constant[value = <Scalar Tensor [6]>]()
  %keepgoing = Constant[value = <Scalar Tensor [1]>]()
  %max_trip_count = Constant[value = <Scalar Tensor [10]>]()
  %keepgoing_out, %b_out, %user_defined_vals = Loop[body = <graph body-net>](%max_trip_count, %keepgoing, %b)
  return
}

graph body-net (
  %i[INT32, scalar]           // iteration number
  %keepgoing_in[BOOL, scalar] // incoming loop-termination-condition; not used
  %b_in[INT32, scalar]        // incoming value of loop-carried-dependency b
) {
  %my_local = Add(%a, %b_in)
  %b_out = Sub(%a, %b_in) // outgoing value of loop-carried-dependency b
  %keepgoing_out = Greater(%my_local, %b_out) // outgoing loop-termination-condition
  %user_defined_val = Add(%b_in, %b_in) // scan-output value to be accumulated
  return %keepgoing_out, %b_out, %user_defined_val
}

示例等效 C 代码

{
  /* User-defined code (enclosing scope) */
  int a = 3, b = 6;
  bool keepgoing = true; // Analogous to input cond
  /* End user-defined code */

  /* Implicitly-defined code */
  const int max_trip_count = 10; // Analogous to input M
  int user_defined_vals[]; // Imagine this is resizable
  /* End implicitly-defined code */
  /* initialize loop-carried variables and scan-output variables */
  bool keepgoing_out = keepgoing
  int b_out = b

  for (int i=0; i < max_trip_count && keepgoing_out; ++i) {
    /* Implicitly-defined code: bind actual parameter values
       to formal parameter variables of loop-body */
    bool keepgoing_in = keepgoing_out;
    bool b_in = b_out;

    /* User-defined code (loop body) */
    int my_local = a + b_in; // Reading value "a" from the enclosing scope is fine
    b_out = a - b_in;
    keepgoing_out = my_local > b_out;
    user_defined_val = b_in + b_in; // b_in and b_out are different variables
    /* End user-defined code */

    /* Implicitly defined-code */
    user_defined_vals[i] = user_defined_val // accumulate scan-output values
  }
  // int t = my_local; // Can't do this. my_local is not accessible here.

  // The values below are bound to the output variables of the loop and therefore accessible
  // b_out; user_defined_vals; keepgoing_out;
}

此代码片段中有几点需要注意

  1. 封闭作用域中的值(即此处的变量“a”)在作用域内,可以在循环输入中引用。

  2. 在循环体中计算的、需要在后续迭代或循环结束后使用的任何值都通过循环体中的一对变量来建模,包括一个输入变量(例如 b_in)和一个输出变量(例如 b_out)。这些被称为循环携带依赖项。循环操作节点为第一次迭代提供输入变量的输入值,并返回最终迭代产生的输出变量的输出值。

  3. Scan_output 变量用于隐式地连接所有迭代中计算的值。在上面的示例中,在所有迭代中计算的 user_defined_val 的值被连接起来,并在循环后作为 user_defined_vals 的值返回。

  4. 在主体中创建的值不能在封闭作用域中访问,除非使用上述机制。

请注意,此运算符的语义支持“对角线”或“波前”执行。(此处步骤 3 提供了一个示例:https://devblogs.nvidia.com/optimizing-recurrent-neural-networks-cudnn-5/)。前端应该将多层 RNN 发射为一系列 While 运算符(时间是内部循环维度),其中每个连续层消耗前一层的 scan_outputs,可能经过几个点对点运算符(例如 dropout、残差连接、线性层)。

子图(由循环节点产生)的输入/输出匹配基于顺序而不是名称。实现将根据此顺序确定名称。

属性

  • body - GRAPH (必需)

    每次迭代运行的图。它有 2+N 个输入:(iteration_num,条件,循环携带依赖项…)。它有 1+N+K 个输出:(条件,循环携带依赖项…,scan_outputs…)。每个 scan_output 通过连接循环每次迭代结束时指定输出值来创建。如果这些 scan_outputs 的维度或数据类型在循环迭代中发生变化,则会出错。

输入

输入数量在 2 到 2147483647 之间。

  • M (可选,异构) - I

    运行时指定的循环最大迭代次数。可选。传递空字符串表示跳过。

  • cond (可选,异构) - B

    一个布尔终止条件。可选。传递空字符串表示跳过。

  • v_initial (可变参数) - V

    任何循环携带依赖项(在循环迭代中变化的值)的初始值

输出

输出数量在 1 到 2147483647 之间。

  • v_final_and_scan_outputs (可变参数) - V

    最终 N 个循环携带依赖项值,然后是 K 个 scan_outputs。Scan outputs 必须是张量。

类型约束

  • V 可以是 ( seq(tensor(bool)), seq(tensor(complex128)), seq(tensor(complex64)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(complex128), tensor(complex64), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8) )

    所有张量和序列类型

  • I 属于 ( tensor(int64) )

    int64 张量,应为标量。

  • B 属于 ( tensor(bool) )

    bool 张量,应为标量。

循环 - 11

版本

  • 名称: Loop (GitHub)

  • : main

  • since_version: 11

  • 函数: False

  • 支持级别: SupportType.COMMON

  • 形状推断: True

该版本的算子自版本 11 起可用。

摘要

通用循环结构。此循环有多个终止条件

  1. 迭代次数。运行时指定的迭代计数。通过指定输入 M 设置。可选。设为空字符串表示省略。请注意,可以通过为输入 M 传递一个常量节点来指定静态迭代次数(在图构建时指定)。

  2. 循环终止条件。这是运算符的一个输入,用于确定是否运行第一次迭代,同时也是主体图的循环携带依赖项。无论是否提供此输入,主体图都必须为条件变量生成一个值。

此表总结了该运算符的操作模式及其等效的 C 风格代码

Operator inputs defined as (max_trip_count, condition_var).

input ("", ""):
    for (int i=0; ; ++i) {
      cond = ... // Note this value is ignored, but is required in the body
    }

input ("", cond) // Note this is analogous to a while loop
    bool cond = ...;
    for (int i=0; cond; ++i) {
      cond = ...;
    }

input ("", 1) // Note this is analogous to a do-while loop
    bool cond = true
    for (int i=0; cond; ++i) {
      cond = ...;
    }

input (trip_count, "") // Note this is analogous to a for loop
    int trip_count = ...
    for (int i=0; i < trip_count; ++i) {
      cond = ...; // ignored
    }

input (trip_count, cond)
    int trip_count = ...;
    bool cond = ...;
    for (int i=0; i < trip_count && cond; ++i) {
      cond = ...;
    }

示例用法 - 条件和迭代次数

graph predict-net {
  %a = Constant[value = <Scalar Tensor [3]>]()
  %b = Constant[value = <Scalar Tensor [6]>]()
  %keepgoing = Constant[value = <Scalar Tensor [1]>]()
  %max_trip_count = Constant[value = <Scalar Tensor [10]>]()
  %keepgoing_out, %b_out, %user_defined_vals = Loop[body = <graph body-net>](%max_trip_count, %keepgoing, %b)
  return
}

graph body-net (
  %i[INT32, scalar]           // iteration number
  %keepgoing_in[BOOL, scalar] // incoming loop-termination-condition; not used
  %b_in[INT32, scalar]        // incoming value of loop-carried-dependency b
) {
  %my_local = Add(%a, %b_in)
  %b_out = Sub(%a, %b_in) // outgoing value of loop-carried-dependency b
  %keepgoing_out = Greater(%my_local, %b_out) // outgoing loop-termination-condition
  %user_defined_val = Add(%b_in, %b_in) // scan-output value to be accumulated
  return %keepgoing_out, %b_out, %user_defined_val
}

示例等效 C 代码

{
  /* User-defined code (enclosing scope) */
  int a = 3, b = 6;
  bool keepgoing = true; // Analogous to input cond
  /* End user-defined code */

  /* Implicitly-defined code */
  const int max_trip_count = 10; // Analogous to input M
  int user_defined_vals[]; // Imagine this is resizable
  /* End implicitly-defined code */
  /* initialize loop-carried variables and scan-output variables */
  bool keepgoing_out = keepgoing
  int b_out = b

  for (int i=0; i < max_trip_count && keepgoing_out; ++i) {
    /* Implicitly-defined code: bind actual parameter values
       to formal parameter variables of loop-body */
    bool keepgoing_in = keepgoing_out;
    bool b_in = b_out;

    /* User-defined code (loop body) */
    int my_local = a + b_in; // Reading value "a" from the enclosing scope is fine
    b_out = a - b_in;
    keepgoing_out = my_local > b_out;
    user_defined_val = b_in + b_in; // b_in and b_out are different variables
    /* End user-defined code */

    /* Implicitly defined-code */
    user_defined_vals[i] = user_defined_val // accumulate scan-output values
  }
  // int t = my_local; // Can't do this. my_local is not accessible here.

  // The values below are bound to the output variables of the loop and therefore accessible
  // b_out; user_defined_vals; keepgoing_out;
}

此代码片段中有几点需要注意

  1. 封闭作用域中的值(即此处的变量“a”)在作用域内,可以在循环输入中引用。

  2. 在循环体中计算的、需要在后续迭代或循环结束后使用的任何值都通过循环体中的一对变量来建模,包括一个输入变量(例如 b_in)和一个输出变量(例如 b_out)。这些被称为循环携带依赖项。循环操作节点为第一次迭代提供输入变量的输入值,并返回最终迭代产生的输出变量的输出值。

  3. Scan_output 变量用于隐式地连接所有迭代中计算的值。在上面的示例中,在所有迭代中计算的 user_defined_val 的值被连接起来,并在循环后作为 user_defined_vals 的值返回。

  4. 在主体中创建的值不能在封闭作用域中访问,除非使用上述机制。

请注意,此运算符的语义支持“对角线”或“波前”执行。(此处步骤 3 提供了一个示例:https://devblogs.nvidia.com/optimizing-recurrent-neural-networks-cudnn-5/)。前端应该将多层 RNN 发射为一系列 While 运算符(时间是内部循环维度),其中每个连续层消耗前一层的 scan_outputs,可能经过几个点对点运算符(例如 dropout、残差连接、线性层)。

属性

  • body - GRAPH (必需)

    每次迭代运行的图。它有 2+N 个输入:(iteration_num,条件,循环携带依赖项…)。它有 1+N+K 个输出:(条件,循环携带依赖项…,scan_outputs…)。每个 scan_output 通过连接循环每次迭代结束时指定输出值来创建。如果这些 scan_outputs 的维度或数据类型在循环迭代中发生变化,则会出错。

输入

输入数量在 2 到 2147483647 之间。

  • M (可选,异构) - I

    运行时指定的循环最大迭代次数。可选。传递空字符串表示跳过。

  • cond (可选,异构) - B

    一个布尔终止条件。可选。传递空字符串表示跳过。

  • v_initial (可变参数) - V

    任何循环携带依赖项(在循环迭代中变化的值)的初始值

输出

输出数量在 1 到 2147483647 之间。

  • v_final_and_scan_outputs (可变参数) - V

    最终的 N 个循环携带依赖值,然后是 K 个扫描输出

类型约束

  • V 可以是 ( tensor(bool), tensor(complex128), tensor(complex64), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8) )

    所有张量类型

  • I 属于 ( tensor(int64) )

    int64 张量,应为标量。

  • B 属于 ( tensor(bool) )

    bool 张量,应为标量。

循环 - 1

版本

  • 名称: Loop (GitHub)

  • : main

  • since_version: 1

  • 函数: False

  • 支持级别: SupportType.COMMON

  • 形状推断: True

该版本的算子自版本 1 起可用。

摘要

通用循环结构。此循环有多个终止条件

  1. 迭代次数。运行时指定的迭代计数。通过指定输入 M 设置。可选。设为空字符串表示省略。请注意,可以通过为输入 M 传递一个常量节点来指定静态迭代次数(在图构建时指定)。

  2. 循环终止条件。这是运算符的一个输入,用于确定是否运行第一次迭代,同时也是主体图的循环携带依赖项。无论是否提供此输入,主体图都必须为条件变量生成一个值。

此表总结了该运算符的操作模式及其等效的 C 风格代码

Operator inputs defined as (max_trip_count, condition_var).

input ("", ""):
    for (int i=0; ; ++i) {
      cond = ... // Note this value is ignored, but is required in the body
    }

input ("", cond) // Note this is analogous to a while loop
    bool cond = ...;
    for (int i=0; cond; ++i) {
      cond = ...;
    }

input ("", 1) // Note this is analogous to a do-while loop
    bool cond = true
    for (int i=0; cond; ++i) {
      cond = ...;
    }

input (trip_count, "") // Note this is analogous to a for loop
    int trip_count = ...
    for (int i=0; i < trip_count; ++i) {
      cond = ...; // ignored
    }

input (trip_count, cond)
    int trip_count = ...;
    bool cond = ...;
    for (int i=0; i < trip_count && cond; ++i) {
      cond = ...;
    }

示例用法 - 条件和迭代次数

graph predict-net {
  %a = Constant[value = <Scalar Tensor [3]>]()
  %b = Constant[value = <Scalar Tensor [6]>]()
  %keepgoing = Constant[value = <Scalar Tensor [1]>]()
  %max_trip_count = Constant[value = <Scalar Tensor [10]>]()
  %keepgoing_out, %b_out, %user_defined_vals = Loop[body = <graph body-net>](%max_trip_count, %keepgoing, %b)
  return
}

graph body-net (
  %i[INT32, scalar]
  %keepgoing[BOOL, scalar]
  %b[INT32, scalar]
) {
  %my_local = Add(%a, %b)
  %b_out = Sub(%a, %b)
  %keepgoing_out = Greater(%my_local, %b_out)
  %user_defined_vals = Add(%b, %b)
  return %keepgoing_out, %b_out, %user_defined_vals
}

示例等效 C 代码

{
  /* User-defined code (enclosing scope) */
  int a = 3, b = 6;
  bool keepgoing = true; // Analogous to input cond
  /* End user-defined code */

  /* Implicitly-defined code */
  const int max_trip_count = 10; // Analogous to input M
  int user_defined_vals[]; // Imagine this is resizable
  /* End implicitly-defined code */
  for (int i=0; i < max_trip_count && keepgoing; ++i) {
    /* User-defined code (loop body) */
    int my_local = a + b; // Reading values in the enclosing scope is fine
    b = a - b; // writes fine if we specify b as a loop-carried dependency
    keepgoing = my_local > b; // keepgoing is a loop-carried dependency
    user_defined_vals[i] = b + b;
    /* End user-defined code */
  }
  // my_local = 123; // Can't do this. my_local was defined in the body

  // These below values are live-out from the loop and therefore accessible
  b_out; user_defined_vals; keepgoing_out;
}

此代码片段中有几点需要注意

  1. 封闭作用域中的值(例如这里的变量 a)在作用域内,可以在循环的输入中引用。

  2. 任何你想在封闭作用域中可用的变量(例如变量 b 和 keepgoing)必须声明为循环携带依赖(在算子输入和输出以及主体网络输入和输出处)或扫描输出。

  3. 在主体中创建的值不能在封闭作用域中访问。

请注意,此运算符的语义支持“对角线”或“波前”执行。(此处步骤 3 提供了一个示例:https://devblogs.nvidia.com/optimizing-recurrent-neural-networks-cudnn-5/)。前端应该将多层 RNN 发射为一系列 While 运算符(时间是内部循环维度),其中每个连续层消耗前一层的 scan_outputs,可能经过几个点对点运算符(例如 dropout、残差连接、线性层)。

属性

  • body - GRAPH (必需)

    每次迭代运行的图。它有 2+N 个输入:(iteration_num,条件,循环携带依赖项…)。它有 1+N+K 个输出:(条件,循环携带依赖项…,scan_outputs…)。每个 scan_output 通过连接循环每次迭代结束时指定输出值来创建。如果这些 scan_outputs 的维度或数据类型在循环迭代中发生变化,则会出错。

输入

输入数量在 3 到 2147483647 之间。

  • M (可选,异构) - I

    运行时指定的循环最大迭代次数。可选。传递空字符串表示跳过。

  • cond (可选,异构) - B

    一个布尔终止条件。可选。传递空字符串表示跳过。

  • v_initial (可变参数) - V

    任何循环携带依赖项(在循环迭代中变化的值)的初始值

输出

输出数量在 1 到 2147483647 之间。

  • v_final_and_scan_outputs (可变参数) - V

    最终的 N 个循环携带依赖值,然后是 K 个扫描输出

类型约束

  • V 可以是 ( tensor(bool), tensor(complex128), tensor(complex64), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8) )

    所有张量类型

  • I 属于 ( tensor(int64) )

    int64 张量,应为标量。

  • B 属于 ( tensor(bool) )

    bool 张量,应为标量。