I have a problem when calculating the product of two sparse matrices.
Here ist the program:
void RandomWalk::calculateAx(const SpMat &x, const SpMat &adj_mat1, const SpMat &adj_mat2, const double &lambda, SpMat &result)
{
SpMat Y(adj_mat1.cols(), adj_mat2.rows());
for (int k=0; k<x.outerSize(); ++k)
{
for (SpMat::InnerIterator it(x,k); it; ++it)
{
div_t divresult;
divresult = div (it.row(),adj_mat1.rows());
Y.insert(divresult.quot, divresult.rem) = it.value();
}
}
SpMat tmp;
tmp = adj_mat1 * Y; // <-- error in this line
tmp = tmp * SpMat(adj_mat2.transpose());
result.resize(adj_mat1.rows()*adj_mat2.rows(), 1);
result.setZero();
for (int k=0; k<tmp.outerSize(); ++k)
{
for (SpMat::InnerIterator it(tmp,k); it; ++it)
{
result.insert(it.col()*adj_mat1.rows()+it.row(), 0) = it.value();
}
}
result = lambda * result;
result = x - result;
}
x is a Matrix of size (k,1). adj_mat1 is a matrix of size nxn and adj_mat2 of size mxm. They both are symmetric. First I have to rescale x to a matrix Y of size (nxm) (by using the first n elements as the first column, the second n as the second column ans so on. After that the matrix adj_mat1*Y*adj_mat2^T has to be calculated. This result then has again to be vectorized by writing all the columns below each other into a vector.
I get a Segmentation fault at the multiplication of adj_mat1 with Y.
The problem only occurs if adj_mat1 and adj_mat 2 are of different sizes.
If you need any more information just ask.
Thank you in advance.
Alex
Solution:
The problem was the insertion of the values. I had to change the quot and the rem at the inset statement. Now it works
Y.insert(divresult.rem, divresult.quot) = it.value();
Related
I would like to know if there is a function or an optimized way to reshape sparse matrices in Eigen.
In the documentation there is no reshape method for such matrices, so I implemented a function myself, but I don't know if it is optimized (i need it to be as fast as possible). Here is my approach:
Eigen::SparseMatrix<double> reshape_sp(const Eigen::SparseMatrix<double>& x,
lint a, lint b) {
Eigen::SparseMatrix<double> y(a, b);
for (int k=0; k<x.outerSize(); ++k) {
for (Eigen::SparseMatrix<double>::InnerIterator it(x,k); it; ++it) {
int pos = it.col()*x.rows()+it.row();
int col = int(pos/a);
int row = pos%a;
y.insert(row, col) = it.value();
}
}
y.makeCompressed();
return y;
}
For performance, it is absolutely crucial that you call reserve on your matrix. I've tested with a 100,000 x 100,000 matrix population 1%. Your version (after fixing the 32 bit overflow in pos computation), took 3 minutes. This fixed version a few seconds:
Eigen::SparseMatrix<double>
reshape(const Eigen::SparseMatrix<double>& orig,
int rows, int cols)
{
Eigen::SparseMatrix<double> rtrn(rows, cols);
rtrn.reserve(orig.nonZeros());
using InnerIterator = Eigen::SparseMatrix<double>::InnerIterator;
for(int k = 0; k < orig.outerSize(); ++k) {
for(InnerIterator it(orig, k); it; ++it) {
std::int64_t pos = std::int64_t(it.col()) * orig.rows() + it.row();
int col = int(pos / rows);
int row = int(pos % rows);
rtrn.insert(row, col) = it.value();
}
}
rtrn.makeCompressed();
return rtrn;
}
An alternative is to work with triplets again. This is a bit slower but less likely to explode in your face the same way insert does. This is particularly helpful for more complex operations like transposing where you cannot guarantee that the insert appends at the end.
Eigen::SparseMatrix<double>
reshape(const Eigen::SparseMatrix<double>& orig,
int rows, int cols)
{
using InnerIterator = Eigen::SparseMatrix<double>::InnerIterator;
using Triplet = Eigen::Triplet<double>;
std::vector<Triplet> triplets;
triplets.reserve(std::size_t(orig.nonZeros()));
for(int k = 0; k < orig.outerSize(); ++k) {
for(InnerIterator it(orig, k); it; ++it) {
std::int64_t pos = std::int64_t(it.col()) * orig.rows() + it.row();
int col = int(pos / rows);
int row = int(pos % rows);
triplets.emplace_back(row, col, it.value());
}
}
Eigen::SparseMatrix<double> rtrn(rows, cols);
rtrn.setFromTriplets(triplets.begin(), triplets.end());
return rtrn;
}
Things I tested that did not work:
Using FXDiv to replace the division with a cheaper operation
Computing maximum distance from one index to the next within a single column to skip dividing if both values are in the same output column (may still be worth it for sparse matrices with suitable inner structure)
Parallelizing the loop with OpenMP, using a final std::sort(std::execution::par, ...) for the triplets.
I am looking for an efficient way to calculate the following matrix product using AVX2 and FMA3:
C=B' * A * B
The matrices are quite small with just a few entries. Matrix A is square whereas matrix B is rectangular.
I am looking for a solution in C++.
So far I have tried to call two times a matrix-matrix product function and store the transposed matrix B in some temporary space, but everything so far was slower compared to the non vectorized and serial code.
For matrix-matrix product I use the following code:
void matrix_matrix(int mat1[N][N], int mat2[N][N], int result[N][N])
{
__m256i vec_multi_res = _mm256_setzero_si256(); //Initialize vector to zero
__m256i vec_mat1 = _mm256_setzero_si256(); //Initialize vector to zero
__m256i vec_mat2 = _mm256_setzero_si256(); //Initialize vector to zero
int i, j, k;
for (i = 0; i < N; i++)
{
for (j = 0; j < N; ++j)
{
//Stores one element in mat1 and use it in all computations needed before proceeding
//Stores as vector to increase computations per cycle
vec_mat1 = _mm256_set1_epi32(mat1[i][j]);
for (k = 0; k < N; k += 8)
{
vec_mat2 = _mm256_loadu_si256((__m256i*)&mat2[j][k]); //Stores row of second matrix (eight in each iteration)
vec_multi_res = _mm256_loadu_si256((__m256i*)&result[i][k]); //Loads the result matrix row as a vector
vec_multi_res = _mm256_add_epi32(vec_multi_res ,_mm256_mullo_epi32(vec_mat1, vec_mat2));//Multiplies the vectors and adds to th the result vector
_mm256_storeu_si256((__m256i*)&result[i][k], vec_multi_res); //Stores the result vector into the result array
}
}
}
}
The code is according to this post.
Eigen / C++ newbie here.
I do have massive (sparse) matrix to initialize and fill and I do have the vector of indices (row,col) and vector of values that correspond to those. How do I quickly (efficiently) build a matrix out of the two?
At this time, I prepare the vector of Triplets, and then I use setFromTriplets, but making that vector of Triplets in the loop is far too inefficient.
I feel that there has to be a better way than a loop. Please help.
void ImagingObjects::InitSparseSigmaPriorTriplets(Eigen::VectorXd& signals_vec)
{
Eigen::MatrixXd values = signals_vec.sum() * ( *this->GetSigmaPValues() );
for (size_t i=0; i < sparse_sigma_prior_triplets_vector.size(); ++i)
{
int index_row = (int) (*this->GetSigmaPIndices())(i, 0);
int index_col = (int) (*this->GetSigmaPIndices())(i, 1);
sparse_sigma_prior_triplets_vector[i] = (Eigen::Triplet <double> ){ index_row, index_col, values(i) } ;
}
}
So basically I prepare these triplets, every time, for every data sample, by cranking the nested loop.
Then, inside the algorithm solver iterative loop, I have this code:
size_t array_it = 0;
for (size_t i=0; i < sigma_prior_retain_bool_array.size(); ++i)
{
if( sigma_prior_retain_bool_array[i] )
{
triplets_sigma_prior[array_it] = *ImagingObjsPtr->GetSparseSigmaPriorTriplet(i);
array_it++;
}
}
auto end_it = triplets_sigma_prior.begin() + array_it;
Sigma_prior_sparse.setFromTriplets(triplets_sigma_prior.begin(),end_it );
I have two sparse matrices in Eigen, and I would like to join them vertically into one. As an example the target of the code would be:
SparseMatrix<double> matrix1;
matrix1.resize(10, 10);
SparseMatrix<double> matrix2;
matrix2.resize(5, 10);
SparseMatrix<double> MATRIX_JOIN;
MATRIX_JOIN.resize(15, 10);
MATRIX_JOIN << matrix1, matrix2;
I found some solutions on a forum however, I wasn't able to implement it.
What's the proper way to join the matrices vertically?
Edit
My implementation:
SparseMatrix<double> L;
SparseMatrix<double> C;
// ... (Operations with the matrices)
SparseMatrix<double> EMATRIX;
EMATRIX.resize(L.rows() + C.rows(), L.cols());
EMATRIX.middleRows(0, L.rows()) = L;
EMATRIX.middleRows(L.rows(), C.rows()) = C;
I get an error of types, acording to the compiler the right hand side is an Eigen::Block and the left side is Eigen::SparseMatrix
As far as I know, there is currently no built-in solution. You can be way more efficient than your solution by using the internal insertBack function:
SparseMatrix<double> M(L.rows() + C.rows(), L.cols());
M.reserve(L.nonZeros() + C.nonZeros());
for(Index c=0; c<L.cols(); ++c)
{
M.startVec(c); // Important: Must be called once for each column before inserting!
for(SparseMatrix<double>::InnerIterator itL(L, c); itL; ++itL)
M.insertBack(itL.row(), c) = itL.value();
for(SparseMatrix<double>::InnerIterator itC(C, c); itC; ++itC)
M.insertBack(itC.row()+L.rows(), c) = itC.value();
}
M.finalize();
Based on #Javier's answer.
Fixed the number of columns in the output matrix (just cols instead of cols+cols)
Fixed the lower matrice's triplet indices (upper.rows() + it.row() instead of just it.row())
using sparse_matrix_type = Eigen::SparseMatrix<T>;
using triplet_type = Eigen::Triplet<T, size_t>;
static sparse_matrix_type sparse_vstack(sparse_matrix_type const& upper, sparse_matrix_type const& lower) {
assert(upper.cols() == lower.cols() && "vstack with mismatching number of columns");
std::vector<triplet_type> triplets;
triplets.reserve(upper.nonZeros() + lower.nonZeros());
for (int k = 0; k < upper.outerSize(); ++k) {
for (sparse_matrix_type::InnerIterator it(upper, k); it; ++it) {
triplets.emplace_back(it.row(), it.col(), it.value());
}
}
for (int k = 0; k < lower.outerSize(); ++k) {
for (sparse_matrix_type::InnerIterator it(lower, k); it; ++it) {
triplets.emplace_back(upper.rows() + it.row(), it.col(), it.value());
}
}
sparse_matrix_type result(lower.rows() + upper.rows(), upper.cols());
result.setFromTriplets(triplets.begin(), triplets.end());
return result;
}
Unfortunately I coulnd't get #chtz's example to work with Eigen 3.3.4 due to a static assertion error THIS_SPARSE_BLOCK_SUBEXPRESSION_IS_READ_ONLY. It seems to be explicitly forbidden by Eigen (see https://eigen.tuxfamily.org/dox/SparseBlock_8h_source.html).
I ended up doing the following:
MATRIX_JOIN.resize(matrix1.rows() + matrix2.rows(), matrix1.cols() + matrix2.cols());
MATRIX_JOIN.setZero();
// Fill MATRIX_JOIN with triples from the other matrices
std::vector<Triplet<double> > tripletList;
for (int k = 0; k < matrix1.outerSize(); ++k)
{
for (SparseMatrix<double>::InnerIterator it(matrix1, k); it; ++it)
{
tripletList.push_back(Triplet<double>(it.row(), it.col(), it.value()));
}
}
for (int k = 0; k < matrix2.outerSize(); ++k)
{
for (SparseMatrix<double>::InnerIterator it(matrix2, k); it; ++it)
{
tripletList.push_back(Triplet<double>(it.row(), it.col(), it.value()));
}
}
FINALMATRIX.setFromTriplets(tripletList.begin(), tripletList.end());
There can be a speedup by calling tripleList.reserve(X) with X being the expected amount of triplets to insert.
I'm trying to multiply two upper triangular matrices together. The matrices are stored in single dimensional arrays instead of the usual 2-D arrays by omitting the zeros that would be under the diagonal (to conserve space). I've figured out how to map elements given a pair of indices to the index for the single array. but I'm having trouble with the actual calculation (The calculation works for smaller n x n square matrices, but for some reason gives incorrect results for larger n x n matrices). I believe that I might be passing in incorrect parameters to the getValue() function, but I think they should be right considering the general formula for matrix multiplication. Any help will be appreciated!
Here's my relevant code:
// mat is an array containing the upper triangle data for a square matrix of size n
// returns element at (i,j), or 0 for the lower triangle
int val(int *mat, int n, int i, int j)
{
if (i > j) {
return 0; // lower triangle
} else {
return mat[j + (i*n) - i*(i+1)/2];
}
}
user101263,
You might not want to map your 2D array into a 1D array for simplification, beacuse in reality, it just complicates the simple matrix-multiplication algorithm.
Here is an implementation of the MM algorithm:
int main()
{
int[5][5] result;
/* omitted: create your 2 2D arrays a & b */
matrixMulitplcation(a,b, result)
}
int** matrixMultiplcation(int a[5][5], int b[5][5], result[5][5])
{
for(int R=0;R<5;R++)
{
for(int C=0;C<5;C++)
{
result[R][C]=0;
for(int T=0;T<5;T++)
result[R][C]+=a[R][T]*b[T][C];
}
}
return result;
}
Please let me know if you have any questions!
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
for(int k = 0;k < n; k++)
{
C[ij_to_k(i,j,n)] += A[ij_to_k(i,k,n)] * B[ij_to_k(k,j,n)];
}
}
}
Just create the ij_to_k() function and you are good to go.