Search code examples
c++cudaeigen

Convert Eigen::SparseMatrix to cuSparse and vice versa


I am having trouble figuring out how to convert Eigen::SparseMatrix to cuSparse due to how little documentation and examples are online. For dense matrices, converting from Eigen to CUDA for cublas is fairly straight forward

Eigen::MatrixXd A = Eigen::MatrixXd::Identity(3,3);

double *d_A;

cudaMalloc(reinterpret_cast<void **>(&d_A), 3 * 3 * sizeof(double));

cudaMemcpy(d_A, A.data(), sizeof(double) * 3 * 3, cudaMemcpyHostToDevice);

// do cublas operations on d_A

How to do the equivalent for the sparse matrices?

std::vector<Eigen::Triplet<double>> trip;
trip.emplace_back(0, 0, 1);
trip.emplace_back(1, 1, 1);
trip.emplace_back(2, 2, 1);

Eigen::SparseMatrix<double> A(3, 3);

A.setFromTriplets(trip.begin(), trip.end());

double *d_A;

// cudaMalloc?

// cudaMemcpy? some conversion?

// do cusparse operations

Solution

  • Just in case people are interested, I figured it out. The tricky part is Eigen's sparse matrix is in CSC format, whereas cuSparse is in CSR format. Fortunately, the conversion can be done by simply transpose CSC into CSR.

    void EigenSparseToCuSparseTranspose(
        const Eigen::SparseMatrix<double> &mat, int *row, int *col, double *val)
    {
      const int num_non0  = mat.nonZeros();
      const int num_outer = mat.cols() + 1;
    
      cudaMemcpy(row,
                 mat.outerIndexPtr(),
                 sizeof(int) * num_outer,
                 cudaMemcpyHostToDevice);
    
      cudaMemcpy(
          col, mat.innerIndexPtr(), sizeof(int) * num_non0, cudaMemcpyHostToDevice);
    
      cudaMemcpy(
          val, mat.valuePtr(), sizeof(double) * num_non0, cudaMemcpyHostToDevice);
    }
    
    void CuSparseTransposeToEigenSparse(
        const int *row,
        const int *col,
        const double *val,
        const int num_non0,
        const int mat_row,
        const int mat_col,
        Eigen::SparseMatrix<double> &mat)
    {
      std::vector<int> outer(mat_col + 1);
      std::vector<int> inner(num_non0);
      std::vector<double> value(num_non0);
    
      cudaMemcpy(
          outer.data(), row, sizeof(int) * (mat_col + 1), cudaMemcpyDeviceToHost);
    
      cudaMemcpy(inner.data(), col, sizeof(int) * num_non0, cudaMemcpyDeviceToHost);
    
      cudaMemcpy(
          value.data(), val, sizeof(double) * num_non0, cudaMemcpyDeviceToHost);
    
      Eigen::Map<Eigen::SparseMatrix<double>> mat_map(
          mat_row, mat_col, num_non0, outer.data(), inner.data(), value.data());
    
      mat = mat_map.eval();
    }