2

我正在研究 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]会在编译时引发错误:

  • x[1..number_rows, i]: 错误: '[range(int(64),bounded,false), int(64)]' 无法解决对 '[domain(1,int(64),false)] int(64)' 的访问
  • x[{1..number_rows}, i]: 错误: '[domain(1,int(64),false), int(64)]' 无法解决对 '[domain(1,int(64),false)] int(64)' 的访问

我需要帮助来找出我收到此错误的原因,以及是否有一种更像教堂的方式来实现我想要做的事情。

4

1 回答 1

1

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

我相信修复相当简单:您希望编译器折叠函数的条件,以便它只考虑1Dthen时的分支和2D时的分支。然后,2D 切片表达式只有在合法时才会被评估。xelsex

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

一种解决方法是声明number_columnsparam

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

这将导致条件的测试成为一个param表达式,因为 (a)number_columns现在是 a param, (b)1是 a param,并且 (c) Chapel 支持==比较param ints 并返回 a 的实现param bool。结果,现在可以在编译时评估表达式,并且条件将被折叠,使得对于给定的 1D 或 2D 形式,只有 1D 或 2D 版本将持续存在x

也就是说,一个更简单的解决方法是简单地使测试原因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;
}

请注意,myinner_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;
  }
}

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

  • 利用条件将被折叠以使每个分支返回不同类型的事实——一维案例将返回一个标量,二维案例将返回一个一维数组
  • 用于DX.dim(i)引用定义维度的范围,DX以便此函数无论DX具有基于 1 的索引还是基于 0 的索引(或基于 b 的索引)都可以工作,并将这些索引保留在result它创建和返回的向量中
  • 删除了then使用大括号/复合语句定义条件主体时不需要的关键字。
于 2017-06-20T00:03:44.893 回答