切片矩阵:未解决的访问

Slicing a matrix: unresolved access of

我正在深入研究 Chapel,但一直坚持在函数内对矩阵进行切片。接收矩阵的函数如下:

proc outer_function(x: [?DX]) {

    var number_rows: int = x.shape(1);
    var number_columns: int = DX.rank;
    var result: [1..number_columns] real;

    if number_columns == 1 then {
        result[1] = inner_function(x);
    } else {
        var column_domain: domain(1) = {1..number_columns};
        for i in column_domain do {
            result[i] = inner_function(x[1..number_rows, i]);
        }

    }

    return result;
}

outer_function可以接收向量或矩阵。如果它接收到一个向量,它会使用相同的输入参数调用 inner_function。如果 outer_function 接收到一个矩阵,那么我想按列对输入矩阵进行切片并调用 inner_function.

问题是使用 x[1..number_rows, i]x[{1..number_rows}, i] 对输入矩阵进行切片会在编译时引发错误:

我需要帮助找出出现此错误的原因,以及是否有更类似于教堂的方法来实现我正在尝试做的事情。

你得到的错误是由于你的函数的 else 分支试图切片 x 就好像它是一个二维数组(因为它使用两个索引表达式:1..number_rowsi),即使某些调用站点传入一维数组/向量(或者我根据您的描述和错误消息假设)。默认情况下,Chapel 仅支持使用单个索引表达式切片一维数组和使用两个索引表达式切片二维数组;否则会导致无法解决的访问错误,就像您遇到的那样。

我相信解决方法相当简单:您希望编译器折叠函数的条件,以便在 x 为 1D 时仅考虑 then 分支,而 else 分支当 x 是二维的。那么,二维切片表达式只有在合法的情况下才会被求值。

Chapel 折叠其测试为 param 表达式的条件语句(这意味着表达式可以在编译时求值)。数组的等级是一个 param 值,因此您的条件非常接近折叠,只是您将等级存储到变量 (var number_columns) 中并在条件中使用它。因为编译器通常无法知道变量的值,所以测试表达式 (number_columns == 1) 中 var 的存在禁用了它折叠条件的能力(即使你和我可以看到变量用 param 初始化,此后不会更改)。

一个解决方法是将 number_columns 声明为 param:

param number_columns: int = DX.rank;
...
if number_columns == 1 then {

这将导致条件测试成为 param 表达式,因为 (a) number_columns 现在是 param,(b) 1param,并且 (c) Chapel 支持 == 的实现,该实现将 param ints 和 returns 与 param bool 进行比较。因此,现在可以在编译时评估表达式,并且条件将被折叠,以便对于给定的 1D 或 2D 形式 x.

只有 1D 或 2D 版本将持续存在

也就是说,一个更简单的修复方法是直接将 DX 的排名作为测试原因:

if DX.rank == 1 then {

这将导致条件被折叠,原因与之前的重写相同。

(请注意,这实际上可能是您想要的,因为据推测 number_columns 应该更像 x.shape(2) 而不是 x 的排名,这将导致它总是 1 或 2?)。

为此,这里建议重写您的代码,其中包含说明性调用和接受一维数组的建议 inner_function()

config var n = 3;

var v: [1..n] real = -1;
writeln(v);
writeln(outer_function(v));

var D2 = {1..n, 1..n};
var A: [D2] real = [(i,j) in D2] i + j / 10.0;
writeln(A);
writeln(outer_function(A));

proc outer_function(x: [?DX]) {
  var number_rows: int = x.shape(1);
  var number_columns: int = if x.rank == 1 then 1 else x.shape(2);
  var result: [1..number_columns] real;

  if x.rank == 1 then {
    result[1] = inner_function(x);
  } else {
    var column_domain: domain(1) = {1..number_columns};
    for i in column_domain do {
      result[i] = inner_function(x[1..number_rows, i]);
    }
  }

  return result;
}

proc inner_function(x: [?DX]) {
  if x.rank != 1 then
    compilerError("inner_function only accepts 1D arrays");
  return + reduce x;
}

请注意,我的 inner_function() 同样依赖条件折叠,仅在向 inner_function() 传递秩不为 1 的数组时才生成编译器错误(尝试调用 inner_function(A) 触发此编译器错误)。

假设我在你想要的轨道上,这里是 outer_function():

的更清晰(更灵活)的实现
proc outer_function(x: [?DX]) {
  if x.rank == 1 {
    return inner_function(x);
  } else {
    var result: [DX.dim(2)] real;
    for i in result.domain do {
      result[i] = inner_function(x[DX.dim(1), i]);
    }
    return result;
  }
}

在这里,我做了两件主要的事情和一件小事:

  • 利用条件将被折叠以使每个分支 return 不同类型这一事实——一维情况将 return 一个标量,而二维情况将 return 一维数组
  • 使用 DX.dim(i) 来引用定义 DX 维度的范围,因此无论 DX 具有基于 1 的索引还是基于 0 的索引(或 b基于索引)并将这些索引保存在它创建的 result 向量中,并且 returns
  • 删除了 then 关键字,它在使用大括号/复合语句定义条件语句时不是必需的。