简体   繁体   中英

sum triangular views of sparse matrices in eigen

Using Visual Studio 2010, I'm trying to express the following matlab code in eigen (from the repositories 3.3 branch ) using sparse matrices:

m = [1 2 3 ;
4 5 6;
7 8 9];
r = triu(m,1) + tril(m)';

that is, calculating the sum of the upper triangle of a square matrix not including the diagonal and the transposed lower triangle of the same square matrix including the diagonal.

For this easy example, the result is

1 6 10
0 5 14
0 0 9

Trying to do this in eigen using dense matrices, i came up with the following mostly (the only not so intuitive thing is to use the member method addTo instead of a + operator) straight forward approach:

Eigen::Matrix3d dm;
dm << 1,2,3, 4,5,6, 7,8,9;
std::cout << "dm" << std::endl << dm << std::endl;

auto dsut = dm.triangularView<Eigen::StrictlyUpper>();
auto dltt = dm.triangularView<Eigen::Lower>().transpose();
std::cout << "dsut" << std::endl << dsut.toDenseMatrix() << std::endl;
std::cout << "dltt" << std::endl << dltt.toDenseMatrix() << std::endl;

// doesn't compile --> Eigen::Matrix3d dj = dsut + dltt;
// (last) error:
// error C2676: binary '+' : 'Eigen::TriangularView<_MatrixType,_Mode>' does not define this operator or a conversion to a type acceptable to the predefined operator
// with
// [
//     _MatrixType=Eigen::Matrix<double,3,3>,
//     _Mode=10
// ]

Eigen::Matrix3d dj = dsut;
dltt.addTo(dj);
std::cout << "dj" << std::endl << dj << std::endl;

The output is as expected:

dm
1 2 3
4 5 6
7 8 9
dsut
0 2 3
0 0 6
0 0 0
dltt
1 4 7
0 5 8
0 0 9
dj
1  6 10
0  5 14
0  0  9

but i couldn't find a way to do the same with sparse matrices. Here's what i tried:

std::vector<Eigen::Triplet<double> > triplets;
triplets.push_back(Eigen::Triplet<double>(0,0,1));
triplets.push_back(Eigen::Triplet<double>(0,1,2));
triplets.push_back(Eigen::Triplet<double>(0,2,3));
triplets.push_back(Eigen::Triplet<double>(1,0,4));
triplets.push_back(Eigen::Triplet<double>(1,1,5));
triplets.push_back(Eigen::Triplet<double>(1,2,6));
triplets.push_back(Eigen::Triplet<double>(2,0,7));
triplets.push_back(Eigen::Triplet<double>(2,1,8));
triplets.push_back(Eigen::Triplet<double>(2,2,9));

Eigen::SparseMatrix<double> sm(3, 3);
sm.setFromTriplets(triplets.begin(), triplets.end());
std::cout << "sm" << std::endl << sm << std::endl;

auto ssut = sm.triangularView<Eigen::StrictlyUpper>();
auto sltt = sm.triangularView<Eigen::Lower>().transpose();
std::cout << "ssut" << std::endl << ssut << std::endl;
std::cout << "sltt" << std::endl << sltt << std::endl;

// doesn't compile --> Eigen::SparseMatrix<double> j = ssut + sltt;
// (last) error:
// eigen\eigen\src/Core/CwiseBinaryOp.h(49): error C2752: 'Eigen::internal::cwise_promote_storage_order<LhsKind,RhsKind,LhsOrder,RhsOrder>' : more than one partial specialization matches the template argument list
// with
// [
//     LhsKind=Eigen::internal::traits<Eigen::SparseMatrix<double>>::StorageKind,
//     RhsKind=Eigen::internal::traits<Eigen::SparseMatrix<double>>::StorageKind,
//     LhsOrder=0,
//     RhsOrder=1
// ]
// eigen\src/Core/util/XprHelper.h(540): could be 'Eigen::internal::cwise_promote_storage_order<Eigen::Sparse,RhsKind,LhsOrder,RhsOrder>'
// eigen\src/Core/util/XprHelper.h(539): or       'Eigen::internal::cwise_promote_storage_order<LhsKind,Eigen::Sparse,LhsOrder,RhsOrder>'
// eigen\src/Core/EigenBase.h(41) : see reference to class template instantiation 'Eigen::internal::traits<T>' being compiled

Eigen::SparseMatrix<double> j = ssut;
// doesn't compile --> sltt.addTo(j);
// (last) error:
// eigen\src/Core/EigenBase.h(72): error C2248: 'Eigen::SparseMatrixBase<Derived>::evalTo' : cannot access private member declared in class 'Eigen::SparseMatrixBase<Derived>'
// with
// [
//     Derived=Eigen::TriangularView<const Eigen::Transpose<const Eigen::SparseMatrix<double>>,2>
// ]

std::cout << "j" << std::endl << j.toDense() << std::endl;

I remarked the non-compiling code, so here's the output for the compilable portion:

sm
Nonzero entries:
(1,0) (4,1) (7,2) (2,0) (5,1) (8,2) (3,0) (6,1) (9,2)

Outer pointers:
0 3 6  $

1 2 3
4 5 6
7 8 9

ssut
0 2 3
0 0 6
0 0 0

sltt
1 4 7
0 5 8
0 0 9

j
0 2 3
0 0 6
0 0 0

It seems like neither the + operator nor the addTo member method can work with the two triangular views when one is transposed.

When none is transposed, the + operator works but not the addTo method. Replacing transpose with adjoint leads to the same compile errors.

Is there something obvious i'm missing? Or is there a way to reformulate this? My goal is to use existing function from eigen and to avoid converting the sparse to dense matrices.

The main problem is that you are adding a column-major sparse matrix to a row-major one, and that's forbidden because there is no way to perform such an operation directly and efficiently. Basically the following simpler code snippet will fail to compile too:

SparseMatrix<double> A,B,C;
C = A + B.transpose();

You should get an explicit static assertion telling your mistake.

The solution is to explicitly copy (evaluate) one operand to an explicit sparse matrix with appropriate storage order:

C = A + SparseMatrix<double>(B.transpose());

So in your case, replace auto sltt by SparseMatrix<double> sltt , and you should be fine.

Finally, if you are trying to symmetrize some matrices, selfadjointView might be a better alternative, eg:

C = A.selfadjointView<Lower>();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM