Armadillo:arma :: subview的非连续视图

时间:2016-12-13 14:31:08

标签: c++ armadillo

有没有办法使用矩阵的非连续视图

即。喜欢在

arma::mat A = arma::zeros(3,3);
arma::uvec idx = {0,2};
A(idx,idx) += 2;

但使用矩阵A的子视图?

arma::subview<double> swA = A.submat(0,0,2,2);
swA(idx,idx) += 2.5;

这最后一位没有编译为

 error: no match for call to ‘(arma::subview<double>) (arma::uvec&, arma::uvec&)’    
swA(idx,idx) += 2.5;

只是给出一些可以帮助我使用arma::subviewc s作为函数参数的上下文。由于A.submat(0,0,2,2)rtype,我无法将其作为非const参数传递给函数,而在函数内部,我需要能够以非非连续的方式访问元素。

最小(非工作)示例也可以看出我的意思可能是以下

#include <iostream>
#include <armadillo>

void f(arma::subview<double> x)
{ 
  arma::uvec i = {0,2};
  arma::uvec j = {1,2};
  x(i,j)  += 2.5;
}

int main ()
{

  arma::mat A = arma::zeros(5,5);
  std::cout << A << std::endl;

  f(A.submat(0,0,2,2));
  std::cout << A << std::endl;

  return 0;
}

gcc再次返回

 error: no match for call to ‘(arma::subview<double>) (arma::uvec&, arma::uvec&)’

解决这个问题的愚蠢之处在于复制矩阵,将其作为参考传递,然后复制回A:

#include <iostream>
#include <armadillo>

void f(arma::mat& x)
{ 
  arma::uvec i = {0,2};
  arma::uvec j = {1,2};
  x(i,j)  += 2.5;
}

int main ()
{

  arma::mat A = arma::zeros(5,5);
  std::cout << A << std::endl;

  arma::mat B = A.submat(0,0,2,2);
  f(B);
  A.submat(0,0,2,2) = B;
  std::cout << A << std::endl;

  return 0;
}

编译和运行完美,但我需要不惜一切代价避免复制矩阵(A远大于5x5!)

再次明确,我不能做以下

[...]
void f(arma::mat& x)
{ 
  arma::uvec i = {0,2};
  arma::uvec j = {1,2};
  x(i,j)  += 2.5;
}
[...]
f(A.submat(0,0,2,2));

因为子视图是rtype而我是从gcc获得的

invalid initialization of non-const reference of type ‘arma::mat& {aka arma::Mat<double>&}’ from an rvalue of type ‘arma::mat {aka arma::Mat<double>}

我遇到了麻烦(只是一个实施问题而且不在开发人员的TODO列表中),还是有一个比我聪明的人有优雅的解决方案?

谢谢!

侧注

  • 我愿意将线性代数库更改为其他人(例如Eigen或其他),如果做这样的事情在那里是微不足道的,但我不愿意,因为我一直在使用犰狳几年了,我一直很满意......
  • 我知道使用循环和更简单的子视图可以在我已经展示的更简单的代码中获得相同的结果,但真实的代码更复杂,这个子视图将用于矩阵操作,所以我必须在一个临时对象中循环并复制子矩阵,我想避免

1 个答案:

答案 0 :(得分:-1)

当你在矩阵上调用方法subview时,他/她/它使用了不同的类(arma::matrix<T >operator())......当你找到时(和我来这里回答)这是一个Rvalue所以它不能被引用...但是类arma::matrix<T>的元素可以由类子视图的对象初始化(那么为什么它们不创建使用子视图作为左值的访问函数?为什么他们甚至创建了类子视图?)...我认为他们可以通过一些模板和Move()来解决这个问题,但是从底层继承到像arma::mat这样的上层calsses。 (你要求的问题不仅发生在稀疏矩阵上,即使在密集矩阵上,它只有在你放f(const arma:mat& )但你不能修改它时才有效)

解决方案:没有,我尝试使用std :: move()将子视图的rvalue欺骗为一个空矩阵,标记时间来执行这些3的循环100000次;

/ ******** 1 ************** ******** /

void f(arma::mat &B)
{
}

// [[Rcpp::export]]
int func()
{
  arma::mat A = arma::zeros(50,50);
  //std::cout <<"A:\n"<< A << std::endl;

  arma::uvec i = arma::regspace<arma::uvec>( 0, 1, 10);
  arma::uvec j = arma::regspace<arma::uvec>( 0, 1, 10);
  unsigned p=1;
  do{
    arma::mat B=A(i,j);
    f(B);
    A(i,j)= B;
    p++;
  }while(p<100000);
  return 0;
}

/ ************************** 2 ******************* **** /

void f(arma::mat &B)
{
}

// [[Rcpp::export]]
int func2()
{
  arma::mat A = arma::zeros(50,50);
  arma::uvec i = arma::regspace<arma::uvec>( 0, 1, 10);
  arma::uvec j = arma::regspace<arma::uvec>( 0, 1, 10);
  unsigned p=1;
  arma::mat B;
  do{
    B=A(i,j);
    f(B);
    A(i,j) = B;
    p++;
  }while(p<100000);
  return 0;
}

/ ************************ 3 ********************* * /

void f(arma::mat &B)
{
}

// [[Rcpp::export]]
int func3()
{
  arma::mat A = arma::zeros(50,50);
  arma::uvec i = arma::regspace<arma::uvec>( 0, 1, 10);
  arma::uvec j = arma::regspace<arma::uvec>( 0, 1, 10);
  unsigned p=1;
  arma::mat B;
  do{
    B=std::move(A(i,j));
    f(B);
    A(i,j)= std::move(B);
    p++;
  }while(p<100000);
  return 0;
}

从R编译和启动显示与构造函数副本或std :: move()没有区别。尝试arma::mat B(std::move(A(i,j))没有区别,时间与func()相同(由于变量声明而略高于func2()func3()

  Unit: milliseconds
    expr      min       lq     mean   median       uq      max neval cld
  func() 20.56159 20.68200 21.22679 20.98329 21.55746 27.62831  1000   b
 func2() 19.21450 19.37398 19.88782 19.66397 20.14731 27.86020  1000  a 
 func3() 19.19892 19.37547 19.89098 19.67835 20.17973 27.19525  1000  a 

这似乎是最快的(如先前A(i,j)= std :: move(B)将B放到0大小并且在B = A(i,j)时需要重新分配):

arma::mat B;
  do{
    B=std::move(A(i,j));
    f(B);
    A(i,j)= B;
    p++;
  }while(p<100000);
  return 0;

相同的基准

Unit: milliseconds
    expr      min       lq    mean   median       uq      max neval
 func4() 19.22223 19.30687 19.3907 19.33198 19.36293 23.20771  1000

std::move(A(i,j))似乎不会影响(不会松散)矩阵A的内容,因为A(i,j)是子视图的r值,而不是矩阵本身。