Eigen LSCG求解器性能问题

时间:2017-09-02 14:20:00

标签: c++ sparse-matrix linear-algebra eigen

我正在使用Eigen的LSCG来迭代地解决我使用稀疏矩阵表达的过度确定的系统,我相信它也是slow。通过迭代,我的意思是:

    //The following is pseudo code
    main() {
    //compute A
    A=..
    //compute B
    B=..
    while(someCondition)
    {   
        x=solve(A,B)
        //based on x alter A and B  
        A=..
        B=..
    }       
}

例如,当A有36k行和18k cols,有145k nnz元素而B有 36k行3列和110k nnz元素(注意B实际上是密集的)我的桌面74s执行x = solve(A,B)。

  1. 这些时间是正常的吗?我想答案取决于机器 代码正在运行。我有AMD FX 6300,所以我想 硬件不是主要问题。
  2. 如果它确实很慢可能是什么问题? B矩阵实际上不是密集的事实会减缓解决步骤吗?
  3. 为了测试你机器上的时间,我写了一些简单的测试代码:

    #include <Eigen/Sparse> //system solving and Eigen::SparseMatrix
    #include <ctime> //measure time to execute
    #include <unsupported/Eigen/SparseExtra> //loadMarket
    
    using SpMatrix = Eigen::SparseMatrix<double>;
    using Matrix = Eigen::MatrixXd;
    int main() {
    
      //load A and B
      SpMatrix A, B;
      Eigen::loadMarket(A, "/AMatrixDirectory/A.mtx");
      Eigen::loadMarket(B, "/BMatrixDirectory/B.mtx");
    
      std::clock_t start;
    
      start = std::clock();
    
      Eigen::LeastSquaresConjugateGradient<SpMatrix> solver;
      solver.compute(A);
      Matrix x = solver.solve(B);
    
      std::cout << "Time: " << (std::clock() - start) / (double)(CLOCKS_PER_SEC)
                << " s" << std::endl;
    
      return 0;
    }
    

    以上是上面伪代码中while循环的一次迭代。 要运行上述代码,您需要:

    1. 本征
    2. C ++ 11(如果没有使用typedef替换)
    3. 我上传的{A / B}矩阵A和B
    4. 修改

      ggael建议使用here声称在我的问题中它与LSCG相比具有更好的性能。

      为了将Eigen的求解器性能与特定问题进行比较,Eigen提供了SimplicialLDLT。不幸的是我无法使用它,因为只有方形矩阵可以使用它。

      我编辑了比较LSCG和SimplicialLDLT的代码(请不要因代码长度而感到沮丧,因为它由4个相似的块组成,只有一些小的改动):

      #include <Eigen/Sparse> //system solving and Eigen::SparseMatrix
      #include <ctime>        //measure time to execute
      #include <unsupported/Eigen/SparseExtra> //loadMarket
      
      // Use typedefs instead of using if c++11 is not supported by your compiler
      using SpMatrix = Eigen::SparseMatrix<double>;
      using Matrix = Eigen::MatrixXd;
      
      int main() {
        // Use multi-threading. If you don't have OpenMP on your system
        // it will simply use 1 thread and it will not crash. So do not worry about
        // that.
        Eigen::initParallel();
        Eigen::setNbThreads(6);
      
        // Load system matrices
        SpMatrix A, B;
        Eigen::loadMarket(A, "/home/iason/Desktop/Thesis/build/A.mtx");
        Eigen::loadMarket(B, "/home/iason/Desktop/Thesis/build/B.mtx");
      
        // Initialize the clock which will measure the solvers
        std::clock_t start;
      
        {
          // Reset clock
          start = std::clock();
          // Solve system using LSCG
          Eigen::LeastSquaresConjugateGradient<SpMatrix> LSCG_solver;
          LSCG_solver.setTolerance(pow(10, -10));
          LSCG_solver.compute(A);
          // Use auto specifier to hold the return value of solve. Eigen::Solve object
          // is being returned
          auto LSCG_solution = LSCG_solver.solve(B);
          std::cout << "LSCG Time Using auto: "
                    << (std::clock() - start) / (double)(CLOCKS_PER_SEC) << " s"
                    << std::endl;
        }
        {
          // Reset clock
          start = std::clock();
          // Solve system using LSCG
          Eigen::LeastSquaresConjugateGradient<SpMatrix> LSCG_solver;
          LSCG_solver.setTolerance(pow(10, -10));
          LSCG_solver.compute(A);
          // Use Matrix specifier instead of auto. Implicit conversion taking place(?)
          Matrix LSCG_solution = LSCG_solver.solve(B);
          std::cout << "LSCG Time Using Matrix: "
                    << (std::clock() - start) / (double)(CLOCKS_PER_SEC) << " s"
                    << std::endl;
        }
        {
          // Reset clock
          start = std::clock();
          // Solve system using SimplicialLDLT
          Eigen::SimplicialLDLT<SpMatrix> SLDLT_solver;
          SLDLT_solver.compute(A.transpose() * A);
          auto SLDLT_solution = SLDLT_solver.solve(A.transpose() * B);
      
          std::cout << "SimplicialLDLT Time Using auto: "
                    << (std::clock() - start) / (double)(CLOCKS_PER_SEC) << " s"
                    << std::endl;
        }
        {
          // Reset clock
          start = std::clock();
          // Solve system using SimplicialLDLT
          Eigen::SimplicialLDLT<SpMatrix> SLDLT_solver;
          SLDLT_solver.compute(A.transpose() * A);
          // Use Matrix specifier instead of auto. Implicit conversion taking place(?)
          Matrix SLDLT_solution = SLDLT_solver.solve(A.transpose() * B);
      
          std::cout << "SimplicialLDLT Time Using Matrix: "
                    << (std::clock() - start) / (double)(CLOCKS_PER_SEC) << " s"
                    << std::endl;
        }
        return 0;
      

      以上代码会产生以下结果

      LSCG时间使用auto:0.000415 s

      使用矩阵的LSCG时间:52.7668 s

      SimplicialLDLT时间使用auto:0.216593 s

      SimplicialLDLT时间使用矩阵:0.239976 s

      当我使用Eigen :: MatrixXd而不是auto时,结果证明我得到了他的一条评论中描述的情况ggael,即LSCG没有设置更高的容差而SimplicialLDLT更快的情况下没有实现解决方案。

      1. 当我使用auto时,解算器是否真正解决了系统问题?
      2. 是否将Solver对象隐式转换为Matrix对象 当我使用Matrix说明符?因为当使用LSCG时唯一 前两种情况的变化是使用auto和Matrix 分别,这个隐式转换到Matrix 52.7668-0.000415秒?
      3. 是否有更快的方法从中提取解决方案矩阵 解决对象?

1 个答案:

答案 0 :(得分:0)

鉴于矩阵A的稀疏模式,明确形成正规方程并使用SimplicialLDLT之类的直接求解器将会快得多。也更好地使用B的密集类型,因为它无论如何都是密集的,并且在内部,所有求解器都将稀疏的右侧转换为密集的列:

Eigen::MatrixXd dB = B; // or directly fill dB
Eigen::SimplicialLDLT<SpMatrix> solver;
solver.compute(A.transpose()*A);
MatrixXd x = solver.solve(A.transpose()*dB);

将LSCG的容差设置为1E-14后,这需要0.15s,而LSCG为6s。