切片矩阵:未解析的访问权限

时间:2017-06-18 14:59:08

标签: chapel

我正在挖掘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]:错误:未解析的'[domain(1,int(64),false)] int(64)'by'[range(int(64),bounded,false),int( 64)]'
  • x[{1..number_rows}, i]:错误:未解析'[domain(1,int(64),false)] int(64)'by'[domain(1,int(64),false),int( 64)]'

我需要帮助才能找出我收到此错误的原因,以及是否有更多像Chapel一样的方法来实现我想要做的事情。

1 个答案:

答案 0 :(得分:1)

您获得的错误是由于您的函数else分支正在尝试将x切片,就好像它是一个2D数组(因为它使用了两个索引表达式:1..number_rowsi)即使一些callite传入一维数组/向量(或者我假设基于你的描述和错误消息)。默认情况下,Chapel仅支持使用单个索引表达式和使用两个索引表达式的2D数组切片1D数组;否则会导致未解决的访问错误,例如您正在获取的错误。

我相信修复非常简单:您希望编译器折叠您的函数的条件,以便在then为1D且x时仅考虑else分支} x为2D时的分支。然后,只有在合法的情况下才会评估2D切片表达式。

Chapel折叠条件,其测试为param表达式(意味着可以在编译时计算表达式)。数组的等级是param值,因此您的条件非常接近折叠,除非您将等级存储到变量(var number_columns)中并在条件中使用它。因为编译器通常不知道变量的值,所以测试表达式中var的存在(number_columns == 1)会禁用它折叠条件的能力(即使你和我能看到)变量用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 int并返回param bool。因此,现在可以在编译时评估表达式,并且条件将被折叠,使得对于给定的1D或2D正式x,仅1D或2D版本将持续存在。

也就是说,一个更简单的解决办法就是直接测试DX等级的测试原因:

if DX.rank == 1 then {

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

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

为此,我们建议使用说明性调用重写代码,并建议接受1D数组的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;
  }
}

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

  • 利用条件将被折叠以使每个分支返回不同类型的事实 - 1D情况将返回标量而2D情况将返回1D数组
  • 使用DX.dim(i)来引用定义DX维度的范围,以便无论DX具有基于1的索引还是基于0的索引(或基于b),此函数都将起作用index)并保留它创建并返回的result向量中的那些索引
  • 删除了使用花括号/复合语句定义条件主体时不必要的then关键字。