简体   繁体   中英

How to multiply a sparse matrix and a dense vector?

I am trying the following:

Eigen::SparseMatrix<double> bijection(2 * face_count, 2 * vert_count);
/* initialization */
Eigen::VectorXd toggles(2 * vert_count);
toggles.setOnes();
Eigen::SparseMatrix<double> deformed;
deformed = bijection * toggles;

Eigen is returning an error claiming:

 error: static assertion failed: THE_EVAL_EVALTO_FUNCTION_SHOULD_NEVER_BE_CALLED_FOR_DENSE_OBJECTS
  586 |       EIGEN_STATIC_ASSERT((internal::is_same<Dest,void>::value),THE_EVAL_EVALTO_FUNCTION_SHOULD_NEVER_BE_CALLED_FOR_DENSE_OBJECTS);

According to the eigen documentaion

Sparse matrix and vector products are allowed. What am I doing wrong?

The problem is you have the wrong output type for that product.

The Eigen documentations says the following type of multiplication is legal:

dv2 = sm1 * dv1;

Sparse matrix times dense vector equals dense vector. I think you can just convert the dense vector with the sparseView member function though. eg

Eigen::SparseMatrix<double> bijection(2 * face_count, 2 * vert_count);
/* initialization */
Eigen::VectorXd toggles(2 * vert_count);
toggles.setOnes();
Eigen::VectorXd deformedDense = bijection * toggles;
Eigen::SparseMatrix<double> deformedSparse = deformedDense.sparseView();

This can be faster than outputting to a dense vector if it is very sparse. Otherwise, 99/100 times the conventional product is faster.

void sparsem_densev_sparsev(const SparseMatrix<double>& A, const VectorX<double>& x, SparseVector<double>& Ax)
{
    Ax.resize(x.size());

    for (int j = 0; j < A.outerSize(); ++j)
    {
        if (A.outerIndexPtr()[j + 1] - A.outerIndexPtr()[j] > 0)
        {
            Ax.insertBack(j) = 0;
        }
    }

    for (int j_idx = 0; j_idx < Ax.nonZeros(); j_idx++)
    {
        int j = Ax.innerIndexPtr()[j_idx];

        for (int k = A.outerIndexPtr()[j]; k < A.outerIndexPtr()[j + 1]; ++k)
        {
            int i = A.innerIndexPtr()[k];
            Ax.valuePtr()[j_idx] += A.valuePtr()[k] * x.coeff(i);
        }
    }
}

For a (probably not optimal) self-adjoint version (lower triangle), change the j_idx loop to:


for (int j_idx = 0; j_idx < Ax.nonZeros(); j_idx++)
    {
        int j = Ax.innerIndexPtr()[j_idx];
        int i_idx = j_idx;//i>= j, trick to improve binary search

        for (int k = A.outerIndexPtr()[j]; k < A.outerIndexPtr()[j + 1]; ++k)
        {
            int i = A.innerIndexPtr()[k];
            Ax.valuePtr()[j_idx] += A.valuePtr()[k] * x.coeff(i);
            if (i != j)
            {
                i_idx = std::distance(Ax.innerIndexPtr(), std::lower_bound(Ax.innerIndexPtr() + i_idx, Ax.innerIndexPtr() + Ax.nonZeros(), i));
                Ax.valuePtr()[i_idx] += A.valuePtr()[k] * x.coeff(j);
            }
        }
    }

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