c++ - 使用 C++ 在 R 中乘以复杂矩阵

标签 c++ r matrix eigen complex-numbers

假设A是复数矩阵。我对计算产品感兴趣 A%*%Conj(t(A))R有效率的。据我所知,使用 C++ 会显着加快速度,所以这就是我想要做的。

我有以下实矩阵代码,可以在 R 中使用.

library(Rcpp); 
library(inline); 
library(RcppEigen);

crossprodCpp <- '
using Eigen::Map;
using Eigen::MatrixXd;
using Eigen::Lower;

const Map<MatrixXd> A(as<Map<MatrixXd> >(AA));
const int   m(A.rows());
MatrixXd    AAt(MatrixXd(m, m).setZero().selfadjointView<Lower>().rankUpdate(A));
return wrap(AAt);
'

fcprd <- cxxfunction(signature(AA = "matrix"), crossprodCpp, "RcppEigen")
A<-matrix(rnorm(100^2),100)
all.equal(fcprd(A),tcrossprod(A))

fcprd(A)在我的笔记本电脑上运行速度比 tcrossprod(A) 快得多.这就是我得到的 A<-matrix(rnorm(1000^2),1000) :

 microbenchmark::microbenchmark('tcrossprod(A)'=tcrossprod(A),'A%*%t(A)'=A%*%t(A),fcprd=fcprd(A))
 Unit: milliseconds
          expr       min       lq     mean   median       uq      max neval
 tcrossprod(A) 428.06452 435.9700 468.9323 448.8168 504.2628 618.7681   100
      A%*%t(A) 722.24053 736.6197 775.4814 767.7668 809.8356 903.8592   100
         fcprd  95.04678 100.0733 111.5021 103.6616 107.2551 197.4479   100

但是,此代码仅适用于具有 double 条目的矩阵。我如何修改此代码以使其适用于复杂矩阵?

我的编程知识非常有限,但我正在努力学习。 非常感谢任何帮助!

最佳答案

Eigen 库还通过 Eigen::MatrixXcd 支持复杂的条目。所以原则上,如果将 MatrixXd 替换为 MatrixXcd,它应该可以工作。但是,这可能无法编译,因为对于使用 Map 的复杂矩阵,没有 as 函数(c.f. https://github.com/RcppCore/RcppEigen/blob/master/inst/unitTests/runit.RcppEigen.R#L205)。 as 函数需要在 R 数据类型和 C++/Eigen 数据类型之间进行转换(参见 http://dirk.eddelbuettel.com/code/rcpp/Rcpp-extending.pdf)。如果你不使用 Map,那么你可以使用这个:

crossprodCpp <- '
using Eigen::MatrixXcd;
using Eigen::Lower;

const MatrixXcd A(as<MatrixXcd>(AA));
const int   m(A.rows());
MatrixXcd    AAt(MatrixXcd(m, m).setZero().selfadjointView<Lower>().rankUpdate(A));
return wrap(AAt);
'

fcprd <- inline::cxxfunction(signature(AA = "matrix"), crossprodCpp, "RcppEigen")
N <- 100
A <- matrix(complex(real = rnorm(N), imaginary = rnorm(N)), N)
all.equal(fcprd(A), A %*% Conj(t(A)))

但是,在我的测试中,这比基本 R 版本慢:

N <- 1000
A <- matrix(complex(real = rnorm(N * N), imaginary = rnorm(N * N)), N)
all.equal(fcprd(A), A %*% Conj(t(A)))
#> [1] TRUE
microbenchmark::microbenchmark(base = A %*% Conj(t(A)), eigen = fcprd(A))
#> Unit: milliseconds
#>   expr      min       lq     mean   median       uq      max neval
#>   base 111.6512 124.4490 145.7583 140.9199 160.3420 241.8986   100
#>  eigen 453.6702 501.5419 535.0192 537.2925 564.8746 628.4999   100

请注意,R 中的矩阵乘法是通过 BLAS 完成的。然而,R 使用的默认 BLAS 实现并不是很快。提高 R 性能的一种方法是使用优化的 BLAS 库,c.f. https://csgillespie.github.io/efficientR/set-up.html#blas-and-alternative-r-interpreters .

或者,您可以使用 BLAS 函数 zherk如果您有完整的 BLAS 可用。 非常粗糙:

dyn.load("/usr/lib/libblas.so")

zherk <- function(a, uplo = 'u', trans = 'n') {
    n <- nrow(a)
    k <- ncol(a)
    c <- matrix(complex(real = 0, imaginary = 0), nrow = n, ncol = n)
    z <- .Fortran("zherk",
             uplo = as.character(uplo),
             trans = as.character(trans),
             n = as.integer(n),
             k = as.integer(k),
             alpha = as.double(1),
             a = as.complex(a),
             lda = as.integer(n),
             beta = as.double(0),
             c = as.complex(c),
             ldc = as.integer(n))
    matrix(z$c, nrow = n, ncol = n)
}

N <- 2
A <- matrix(complex(real = rnorm(N * N), imaginary = rnorm(N * N)), N)
zherk(A, uplo = "l") - A %*% Conj(t(A))

请注意,这只填充了上(或下)三角形部分,但速度非常快:

microbenchmark::microbenchmark(base = A %*% Conj(t(A)), blas = zherk(A))
#> Unit: milliseconds
#>  expr      min        lq      mean    median       uq      max neval
#>  base 112.5588 117.12531 146.10026 138.37565 167.6811 282.3564   100
#>  blas  66.9541  70.12438  91.44617  82.74522 108.4979 188.3728   100

关于c++ - 使用 C++ 在 R 中乘以复杂矩阵,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48620529/

相关文章:

c++ - 压缩嵌套循环

r - 根据列中的值为每个组选择前 N 行

r - grid.arrange 将所有表格对齐到 Rmarkdown 的顶部

r - 从单个矩阵创建多个单独的热图

c++ - Linux 在使用 SDL_Mixer 的自动启动过程中无法在系统启动开始时播放声音

c++ - 不同类型的C/C++头文件有什么区别

r - R中的行交错添加

java - ojalgo 中的 QR/SVD 分解是否需要与列一样多的行?

c - 如何使用递归函数计算 C 中矩阵的行列式

c++ - 即使在使用命名空间指令后也没有歧义引用错误