循环

循环 - 23

版本

  • 名称: Loop (GitHub)

  • 域名: main

  • since_version: 23

  • 函数: False

  • support_level: SupportType.COMMON

  • 形状推断: True

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

摘要

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

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

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

此表总结了此操作符的操作模式,并提供了等效的C风格代码:

操作符输入定义为 (max_trip_count, condition_var)。

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

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

  • input (“”, 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个输入:(迭代次数,条件,循环携带的依赖项…)。它有1+N+K个输出:(条件,循环携带的依赖项…,扫描输出…)。每个扫描输出是通过在每次循环迭代结束时连接指定输出值的值来创建的。如果这些扫描输出的维度或数据类型在循环迭代中发生变化,则会出现错误。

输入

输入数量在2到2147483647之间。

  • M (可选, 异构) - I:

    在运行时指定的循环的最大行程计数。可选。传递空字符串以跳过。

  • cond (可选, 异构) - B:

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

  • v_initial (可变参数) - V:

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

输出

输出在1到2147483647之间。

  • v_final_and_scan_outputs (可变参数) - V:

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

类型约束

  • 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) ):

    所有Tensor、Sequence(Tensor)、Optional(Tensor)和Optional(Sequence(Tensor))类型,直到IRv11。

  • I 在 ( tensor(int64) ) 中:

    int64的张量,应该是一个标量。

  • B 在 ( tensor(bool) ) 中:

    布尔张量,应该是一个标量。

循环 - 21

版本

  • 名称: Loop (GitHub)

  • 域名: main

  • since_version: 21

  • 函数: False

  • support_level: SupportType.COMMON

  • 形状推断: True

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

摘要

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

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

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

此表总结了此操作符的操作模式,并提供了等效的C风格代码:

操作符输入定义为 (max_trip_count, condition_var)。

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

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

  • input (“”, 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, condition, loop carried dependencies…)。它有1+N+K个输出:(condition, loop carried dependencies…, 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 在 ( 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) ):

    所有Tensor、Sequence(Tensor)、Optional(Tensor)和Optional(Sequence(Tensor))类型,直到IRv10。

  • I 在 ( tensor(int64) ) 中:

    int64的张量,应该是一个标量。

  • B 在 ( tensor(bool) ) 中:

    布尔张量,应该是一个标量。

循环 - 19

版本

  • 名称: Loop (GitHub)

  • 域名: main

  • since_version: 19

  • 函数: False

  • support_level: SupportType.COMMON

  • 形状推断: True

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

总结

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

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

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

此表总结了此操作符的操作模式,并提供了等效的C风格代码:

操作符输入定义为 (max_trip_count, condition_var)。

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

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

  • input (“”, 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个输入:(迭代次数,条件,循环携带的依赖项…)。它有1+N+K个输出:(条件,循环携带的依赖项…,扫描输出…)。每个扫描输出是通过在每次循环迭代结束时连接指定输出值的值来创建的。如果这些扫描输出的维度或数据类型在循环迭代中发生变化,则会出现错误。

输入

输入数量在2到2147483647之间。

  • M (可选, 异构) - I:

    在运行时指定的循环的最大行程计数。可选。传递空字符串以跳过。

  • cond (可选, 异构) - B:

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

  • v_initial (可变参数) - V:

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

输出

输出在1到2147483647之间。

  • v_final_and_scan_outputs (可变参数) - V:

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

类型约束

  • 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(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(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), 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(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8) ):

    所有Tensor、Sequence(Tensor)、Optional(Tensor)和Optional(Sequence(Tensor))类型,直到IRv9。

  • I 在 ( tensor(int64) ) 中:

    int64的张量,应该是一个标量。

  • B 在 ( tensor(bool) ) 中:

    布尔张量,应该是一个标量。

循环 - 16

版本

  • 名称: Loop (GitHub)

  • 域名: main

  • since_version: 16

  • 函数: False

  • support_level: SupportType.COMMON

  • 形状推断: True

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

摘要

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

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

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

此表总结了此操作符的操作模式,并提供了等效的C风格代码:

操作符输入定义为 (max_trip_count, condition_var)。

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

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

  • input (“”, 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个输入:(迭代次数,条件,循环携带的依赖项…)。它有1+N+K个输出:(条件,循环携带的依赖项…,扫描输出…)。每个扫描输出是通过在每次循环迭代结束时连接指定输出值的值来创建的。如果这些扫描输出的维度或数据类型在循环迭代中发生变化,则会出现错误。

输入

输入数量在2到2147483647之间。

  • M (可选, 异构) - I:

    在运行时指定的循环的最大行程计数。可选。传递空字符串以跳过。

  • cond (可选, 异构) - B:

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

  • v_initial (可变参数) - V:

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

输出

输出在1到2147483647之间。

  • v_final_and_scan_outputs (可变参数) - V:

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

类型约束

  • 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) ):

    所有Tensor、Sequence(Tensor)、Optional(Tensor)和Optional(Sequence(Tensor))类型,直到IRv4。

  • I 在 ( tensor(int64) ) 中:

    int64的张量,应该是一个标量。

  • B 在 ( tensor(bool) ) 中:

    布尔张量,应该是一个标量。

循环 - 13

版本

  • 名称: Loop (GitHub)

  • 域名: main

  • since_version: 13

  • 函数: False

  • support_level: 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, condition, loop carried dependencies…)。它有1+N+K个输出:(condition, loop carried dependencies…, 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 在 ( 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) ) 中:

    布尔张量,应该是一个标量。

循环 - 11

版本

  • 名称: Loop (GitHub)

  • 域名: main

  • since_version: 11

  • 函数: False

  • support_level: 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, condition, loop carried dependencies…)。它有1+N+K个输出:(condition, loop carried dependencies…, 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) ) 中:

    布尔张量,应该是一个标量。

循环 - 1

版本

  • 名称: Loop (GitHub)

  • 域名: main

  • since_version: 1

  • 函数: False

  • support_level: 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, condition, loop carried dependencies…)。它有1+N+K个输出:(condition, loop carried dependencies…, 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) ) 中:

    布尔张量,应该是一个标量。