EIGEN C++ 中的 Matlab Spdiags 等价物

Matlab Spdiags equivalent in EIGEN C++

我正在搜索 C++ 中 A=Spdiags(B,d,N,N) 的等效项。此函数提取矩阵 B 的对角线元素,方法是获取 B 的列并将它们放置在向量 d 指定的对角线上。 N N 是输出矩阵A的大小。

我在Eigen里找过,好像没有。

有什么想法吗?

据我所知,没有内置方法,但通过索引构建新矩阵并不难。请注意第 k 对角线从索引 (max(1, 1-k), max(1, 1-k)+k) 到 (min(m, n-k), min(m, n-k)+k)

template <typename Scalar> 
Eigen::SparseMatrix<Scalar> spdiags(const Matrix<Scalar, -1, -1>& B, const Eigen::Matrix<int, -1, 1>& d, size_t m, size_t n) {
  Eigen::SparseMatrix<Scalar> A(m,n);

  typedef Eigen::Triplet<Scalar> T;
  std::vector<T> triplets;
  triplets.reserve(std::min(m,n)*d.size());

  for (int k = 0; k < d.size(); k++) {
    int i_min = std::max(0, -d(k));
    int i_max = std::min(m - 1, n - d(k) - 1);
    int B_idx_start = m >= n ? d(k) : 0;

    for (int i = i_min; i <= i_max; i++) {
      triplets.push_back( T(i, i+k, B(B_idx_start + i, k)) );
    }
  }

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

  return A;
}

请注意,我还没有对此进行测试,但你明白了。 B 的第一个索引有点奇怪,但我认为它是正确的。

其他版本,spdiags(A):

Eigen::MatrixXd spdiags(const Eigen::SparseMatrix<double>& A) {
  // find nonzero diagonals by iterating over all nonzero elements
  // d(i) = 1 if the ith diagonal of A contains a nonzero, 0 else
  Eigen::VectorXi d = Eigen::VectorXi::Zero(A.rows() + A.cols() - 1);

  for (int k=0; k < A.outerSize(); ++k) {
    for (SparseMatrix<double>::InnerIterator it(A,k); it; ++it) {
      d(it.col() - it.row() + A.rows() - 1) = 1;
    }
  }

  int num_diags = d.sum();
  Eigen::MatrixXd B(std::min(A.cols(), A.rows()), num_diags);

  // fill B with diagonals
  int B_col_idx = 0;
  int B_row_sign = A.rows() >= A.cols() ? 1 : -1;

  for (int i = 1 - A.rows(); i <= A.cols() - 1; i++) {
    if (d(i + A.rows() - 1)) {
      const auto& diag = A.diagonal(i);

      int B_row_start = std::max(0, B_row_sign * i);

      B.block(B_row_start, B_col_idx, diag.size(), 1) = diag;
      B_col_idx++;
    }
  }

  return B;
}

相同的免责声明:尚未测试,但应该可以。如果需要

,请像以前一样将 double 替换为 template <typename Scalar>

这是我制定的解决方案。我已经实现了对角线(i),因为我的本征版本没有考虑这个功能(我怎么知道我使用的是哪个版本?)。我用这个获得了很好的结果,但我不知道是否可以进一步优化它:

void spdiags(Eigen::SparseMatrix<double> A)
{

    //Extraction of the diagnols before the main diagonal
   vector<double> vec1; int flag=0;int l=0;
   int i=0; int j=0; vector<vector<double> > diagD;
   vector<vector<double> > diagG;  int z=0; int z1=0;


   for(int i=0;i<A.rows();i++)
   {l=i;
       do
       {
           if(A.coeff(l,j)!=0)
           flag=1;

           vec1.push_back(A.coeff(l,j));
           l++;j++;

       }while(l<A.rows() && j<A.cols());

       if(flag==1) {diagG.resize(diagG.size()+1);diagG[z]=vec1; z++; }
       vec1.clear(); l=0;j=0; flag=0; cout<<endl;
   }


   flag=0;z=0; vec1.clear();
  
    // Extraction of the diagonals after the main diagonal                                        
   for(int i=1;i<A.cols();i++)
   {l=i;
       do
       {
           if(A.coeff(j,l)!=0)
           flag=1; 

           vec1.push_back(A.coeff(j,l));
           l++;j++;

       }while(l<A.cols() && j<A.rows());

       if(flag==1) {diagD.resize(diagD.size()+1);diagD[z]=vec1; z++;  }
       vec1.clear(); l=0;j=0; flag=0; cout<<endl;
   }
// End extraction of the diagonals

Eigen::VectorXi d = Eigen::VectorXi::Zero(A.rows() + A.cols() - 1);

for (int k=0; k < A.outerSize(); ++k) 
{
  for (SparseMatrix<double>::InnerIterator it(A,k); it; ++it)
   {
    d(it.col() - it.row() + A.rows() - 1) = 1;

   }
}


int num_diags = d.sum();
Eigen::MatrixXd B(std::min(A.cols(), A.rows()), num_diags);

// fill B with diagonals
Eigen::ArrayXd v;
int B_col_idx = 0;
int B_row_sign = A.rows() >= A.cols() ? 1 : -1;
int indG=diagG.size()-1; int indD=0;

for (int i = 1 - A.rows(); i <=A.cols() - 1; i++)
{
  if (d(i + A.rows() - 1))
  {
      if(i<1)
      {   v.resize(diagG[indG].size());
          for(int i=0;i<diagG[indG].size();i++)
          {
                  v(i)=diagG[indG][i];
          }

          int B_row_start = std::max(0, B_row_sign * i);
            B.block(B_row_start, B_col_idx, diagG[indG].size(), 1) = v;

          B_col_idx++;
          indG--;
      }
      else
      {
         v.resize(diagD[indD].size());

         for(int i=0;i<diagD[indD].size();i++)
         {
            v(i)=diagD[indD][i] ;
         }

          int B_row_start = std::max(0, B_row_sign * i);
          B.block(B_row_start, B_col_idx, diagD[indD].size(), 1) = v;

          B_col_idx++;
          indD++;
      }
  }

}
   cout<<B<<endl; //the result of the function
}//end of the function