当 A 是下三角矩阵时实现 ATA 的更好方法

Better way to implement ATA when A is lower triangular matrix

在 Eigen 库中实现 A^T*A 可以这样写:

X.template triangularView<Lower>().setZero(); 
X.template selfadjointView<Lower>().rankUpdate(A.transpose());

如果A是下三角矩阵,有没有更好(更有效)的写法? 我试过以下,但它给出了编译错误:

X.template selfadjointView<Lower>().rankUpdate(A.template triangularView<Lower>().transpose());

它给出错误:

 error: no matching member function for call to 'rankUpdate'

Eigen 通常只包括那些在 BLAS 中具有相应功能的矩阵例程。这种特殊情况没有 BLAS 例程。另一个问题是,Eigen 的 rankUpdate 实现和三角矩阵乘法不会并行化,除非您针对执行此操作的 BLAS 库定义 -DEIGEN_USE_BLAS 和 link,例如OpenBLAS.

我对各种实现进行了一些测试。

排名更新

  void trtr(Eigen::MatrixXf& out, const Eigen::MatrixXf& in)
  {
    out = Eigen::MatrixXf::Zero(in.rows(), in.cols());
    out.selfadjointView<Eigen::Lower>().rankUpdate(in.transpose());
    out.triangularView<Eigen::StrictlyUpper>() = out.transpose();
  }

仅与 OpenBLAS 并行化。然后就是挺快的,不然就有点慢了。

三角矩阵乘积

  void trtr(Eigen::MatrixXf& out, const Eigen::MatrixXf& in)
  { out.noalias() = in.transpose() * in.triangularView<Eigen::Lower>(); }

仅与 OpenBLAS 并行化。即使这样还是有点慢。

一般矩阵乘积

  void trtr(Eigen::MatrixXf& out, const Eigen::MatrixXf& in)
  { out.noalias() = in.transpose() * in; }

始终并行化。始终如一的快速。

自定义块乘法

我尝试实现我自己的版本,将矩阵分成块以删除冗余操作。这对于大型矩阵来说要快一些,尤其是在使用 OpenBLAS 时。

它不适用于小矩阵。

  void trtr_recursive
  (Eigen::Ref<Eigen::MatrixXf> out,
   const Eigen::Ref<const Eigen::MatrixXf>& in) noexcept
  {
    Eigen::Index size = out.rows();
    if(size <= 16) {
      /*
       * Two notes:
       * 1. The cutoff point is a possible tuning parameter
       * 2. This is the only place where we assume that the upper triangle
       *    is initialized to zero. We can remove this assumption by making
       *    a copy of the input into a fixed size matrix. Use Eigen::Matrix
       *    with the MaxRows and MaxCols template argument for this
       */
      out.selfadjointView<Eigen::Lower>().rankUpdate(in.transpose());
      return;
    }
    Eigen::Index full = (size + 1) >> 1;
    Eigen::Index part = size >> 1;
    const auto& fullin = in.bottomLeftCorner(full, full);
    const auto& bottomright = in.bottomRightCorner(part, part);
    const auto& bottomleft = in.bottomLeftCorner(part, full);
    out.topLeftCorner(full, full).selfadjointView<Eigen::Lower>()
      .rankUpdate(fullin.transpose());
    out.bottomLeftCorner(part, full).noalias() +=
      bottomright.transpose().triangularView<Eigen::Upper>() *
      bottomleft;
    trtr_recursive(out.topLeftCorner(part, part),
                   in.topLeftCorner(part, part));
    trtr_recursive(out.bottomRightCorner(part, part),
                   bottomright);
  }
  void trtr(Eigen::MatrixXf& out, const Eigen::MatrixXf& in)
  {
    out = Eigen::MatrixXf::Zero(in.rows(), in.cols());
    trtr4_recursive(out, in);
    out.triangularView<Eigen::StrictlyUpper>() = out.transpose();
  }