【问题标题】:Eigen efficient type for dense symmetric matrix密集对称矩阵的特征有效类型
【发布时间】:2012-11-04 10:25:16
【问题描述】:

Eigen 是否具有存储密集、固定大小、对称矩阵的有效类型? (嘿,它们无处不在!)

即对于 N=9,它应该只存储 (1+9)*9/2==45 个元素并且它有适当的操作。例如,应该有两个对称矩阵的有效相加,返回相似的对称矩阵。

如果没有这样的事情,我应该采取哪些行动(看起来像this)来将这种类型引入Eigen?它有“视图”的概念吗?我可以为我自己的类型编写类似“矩阵视图”的东西,这将使它成为 Eigen-friednly 吗?

附:可能我可以使用map 将普通数组视为 1xN 矩阵,并对其进行操作。但这不是最干净的解决方案。

【问题讨论】:

  • N=9 几乎没有优势,因为您的代码中的分歧来自于解析矩阵值。你有你的记忆,但你真的没有内存,还是你期望一些相关的计算优势?你能用一些使用场景来激发你的问题吗?
  • "你能用一些使用场景来激发你的问题吗?" - 我有数百万个这样的矩阵。我需要将它们存储在数组中,并对它们进行一些操作。
  • @Mikhail,两个三角矩阵相加,编码为 1x((1+N)*N/2) 矩阵 - 不会破坏任何矢量化。
  • @Mikhail:内存可能很便宜,但带宽却不是。根据 OP 计划对这些矩阵执行的操作,缓存未命中的减少可能会比使用压缩存储形式(如果存在,取决于操作)的开销降低更多。
  • @Grizzly,好点,我忘了提。紧凑的数据结构将改善整体时间,而不仅仅是内存使用量。

标签: c++ matrix linear-algebra eigen


【解决方案1】:

对称矩阵的高效类型

您只需将值分配给矩阵的下/上三角部分,并使用本征三角和自伴随视图。但是,我已经在小型固定大小的矩阵上进行了测试。我注意到在性能方面,使用视图并不总是最好的选择。考虑以下代码:

Eigen::Matrix2d m;
m(0,0) = 2.0;
m(1,0) = 1.0;
// m(0,1) = 1.0;
m(1,1) = 2.0;
Eigen::Vector2d v;
v << 1.0,1.0;
auto result = m.selfadjointView<Eigen::Lower>()*v;

与下面介绍的替代解决方案相比,最后一行中的产品非常慢(在我的例子中,double 2x2 矩阵慢了大约 20%)。 (通过取消注释m(0,1) = 1.0; 并使用auto result = m*v,使用完整矩阵的乘积对于double 2x2 矩阵更快)。

一些替代方案。

1) 将对称矩阵存储在向量中

您可以将矩阵存储在大小为 45 的向量中。以向量格式对 2 个矩阵求和很简单(只需对向量求和)。但是您必须为产品编写自己的实现。

这里是这样一个matrix * vector 乘积(密集,固定大小)的实现,其中矩阵的下部按列存储在向量中:

template <typename T, size_t S>
Eigen::Matrix<T,S,1> matrixVectorTimesVector(const Eigen::Matrix<T,S*(S+1)/2,1>& m, const Eigen::Matrix<T,S,1>& v)
{
    Eigen::Matrix<T,S,1> ret(Eigen::Matrix<T,S,1>::Zero());
    int counter(0);
    for (int i=0; i<S; ++i)
    {
        ret[i] += m(counter++)*v(i);
        for (int j=i+1; j<S; ++j)
        {
            ret[i] += m(counter)*v(j);
            ret[j] += m(counter++)*v(i);
        }
    }
    return ret;
}

2) 只存储三角形部分并实现自己的操作

您当然也可以实现自己的产品matrix * vector,其中矩阵仅存储 45 个元素(假设我们存储下三角形部分)。这可能是最优雅的解决方案,因为它保持矩阵的格式(而不是使用表示矩阵的向量)。然后,您还可以使用本征函数,如下例所示:

template <typename T, size_t S>
Eigen::Matrix<T,S,S> symmMatrixPlusSymmMatrix( Eigen::Matrix<T,S,S>& m1, const Eigen::Matrix<T,S,S>& m2)
{
    Eigen::Matrix<T,S,S> ret;
    ret.template triangularView<Eigen::Lower>() = m1 + m2; // no performance gap here!
    return ret;
}

在上面的函数中(2个对称矩阵的和),只访问了m1和m2的下三角部分。请注意,triangularView 在这种情况下不会出现性能差距(我根据我的基准确认了这一点)。

matrix * vector 产品见下例(与备选方案 1 中的产品性能相同)。该算法只访问矩阵的下三角部分。

template <typename T, size_t S>
Eigen::Matrix<T,S,1> symmMatrixTimesVector(const Eigen::Matrix<T,S,S>& m, const Eigen::Matrix<T,S,1>& v)
{
    Eigen::Matrix<T,S,1> ret(Eigen::Matrix<T,S,1>::Zero());
    int counter(0);

    for (int c=0; c<S; ++c)
    {
        ret(c) += m(c,c)*v(c);
        for (int r=c+1; r<S; ++r)
        {
            ret(c) += m(r,c)*v(r);
            ret(r) += m(r,c)*v(c);
        }
    }
    return ret;
}

在我的例子中,与使用完整矩阵(2x2 = 4 个元素)的产品相比,产品 Matrix2d*Vector2d 的性能增益为 10%。

【讨论】:

    【解决方案2】:

    Packed storage 的对称矩阵是矢量化代码的 敌人,即速度。 标准做法是将相关的 N*(N+1)/2 系数存储在全密集 NxN 矩阵的上三角或下三角部分中,而剩余的 (N-1)*N/2 不被引用。然后通过考虑这种特殊的存储来定义对称矩阵上的所有操作。在 eigen 中,你有 triangular and self-adjoint views 的概念来获得这个。

    来自eigen 参考:(对于实矩阵selfadjoint==对称)。

    就像三角矩阵一样,你可以引用任何三角部分 的方阵将其视为自伴随矩阵并执行 特殊和优化的操作。再次相反的三角形部分 永远不会被引用,可用于存储其他信息。

    除非内存是个大问题,否则我建议将矩阵中未引用的部分留空。 (更易读的代码,没有性能问题。)

    【讨论】:

    • “对称矩阵的打包存储是矢量化代码的一大敌人,即速度” - 就我而言,我只需要添加这样的矩阵,我看不出它如何影响矢量化这种情况。 “除非内存是一个大问题” - 内存是一个真正的问题 - 我有数百万个这样的矩阵..
    • 如果您不执行任何“真实”矩阵运算,如 rank1 或 rank2 更新、LLT 分解等,我只会选择一个 single BIG 2D 数组,其中您将矩阵上三角部分存储为 N*(N+1)/2 列(当然要确保您具有列主顺序)。然后您可以访问此矩阵的列并有效地执行求和和缩放操作。访问i,j 矩阵元素只是一个#define 问题...
    • 好吧,在一些非“真实”操作(如加法)之后,我确实执行了 LLT 分解。但是分解矩阵使用单独的方阵是没有问题的。顺便说一句,Eigen 不会就地 LLT,它无论如何都会执行复制。
    • 好的,我刚才注意到这只是之前question 的后续。对不起,如果我再次打扰你:忘记 eigen 并自己实现它,像 answer 一样展开循环,但实现线性存储方案
    • 我认为使用 Eigen 来执行此类矩阵的加法实际上更好。即使没有“真正的”支持,仍然可以使用 Eigen。例如,正如我在原始问题中所说 - 只需使用一维矩阵。仅在需要时转换为真正的二维矩阵..
    【解决方案3】:

    是的,eigen3 有views 的概念。但是它对存储没有任何作用。不过,作为一个想法,您也许可以为两个相同类型的对称矩阵共享一个更大的块:

    Matrix<float,4,4> A1, A2; // assume A1 and A2 to be symmetric
    Matrix<float,5,4> A;
    A.topRightCorner<4,4>().triangularView<Upper>() = A1;
    A.bottomLeftCorner<4,4>().triangularView<Lower>() = A2;
    

    虽然很麻烦,而且我只会在你的记忆真的很宝贵的情况下使用它。

    【讨论】:

    • 不应该是A.topRightCorner&lt;N,N&gt;().triangularView&lt;Upper&gt;()吗?
    • "矩阵 A;"我认为使用 Matrix A; 会更有效。或矩阵 A;用于存储两个 4x4 矩阵。这很好,但不幸的是我的矩阵是独立的实体,不能存储在一起。
    猜你喜欢
    • 2016-11-29
    • 1970-01-01
    • 1970-01-01
    • 2014-12-23
    • 1970-01-01
    • 1970-01-01
    • 2019-12-16
    • 1970-01-01
    • 2013-11-30
    相关资源
    最近更新 更多