From 55932f74f507a47fcceb478513863d9a7f75c01f Mon Sep 17 00:00:00 2001
From: jcarpent <jcarpent@laas.fr>
Date: Wed, 18 Oct 2017 10:25:18 +0200
Subject: [PATCH] [Solvers] Expose LSCG with a fix in the preconditionner

---
 CMakeLists.txt                                |   1 +
 src/solvers/LeastSquaresConjugateGradient.hpp | 121 ++++++++++++++++++
 src/solvers/solvers.cpp                       |   2 +
 3 files changed, 124 insertions(+)
 create mode 100644 src/solvers/LeastSquaresConjugateGradient.hpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index adff098c..cdb06c7b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -92,6 +92,7 @@ SET(HEADERS
   quaternion.hpp
   solvers/solvers.hpp
   solvers/IterativeSolverBase.hpp
+  solvers/LeastSquaresConjugateGradient.hpp
   solvers/ConjugateGradient.hpp
   solvers/SparseSolverBase.hpp
 )
diff --git a/src/solvers/LeastSquaresConjugateGradient.hpp b/src/solvers/LeastSquaresConjugateGradient.hpp
new file mode 100644
index 00000000..011ff49e
--- /dev/null
+++ b/src/solvers/LeastSquaresConjugateGradient.hpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2017, Justin Carpentier, LAAS-CNRS
+ *
+ * This file is part of eigenpy.
+ * eigenpy is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ * eigenpy is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.  You should
+ * have received a copy of the GNU Lesser General Public License along
+ * with eigenpy.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __eigenpy_least_square_conjugate_gradient_hpp__
+#define __eigenpy_least_square_conjugate_gradient_hpp__
+
+#include <boost/python.hpp>
+#include <Eigen/IterativeLinearSolvers>
+
+#include "eigenpy/solvers/IterativeSolverBase.hpp"
+
+namespace Eigen
+{
+  template <typename _Scalar>
+  class LeastSquareDiagonalPreconditionerFix : public LeastSquareDiagonalPreconditioner<_Scalar>
+  {
+    typedef _Scalar Scalar;
+    typedef typename NumTraits<Scalar>::Real RealScalar;
+    typedef LeastSquareDiagonalPreconditioner<_Scalar> Base;
+    using DiagonalPreconditioner<_Scalar>::m_invdiag;
+  public:
+    
+    LeastSquareDiagonalPreconditionerFix() : Base() {}
+    
+    template<typename MatType>
+    explicit LeastSquareDiagonalPreconditionerFix(const MatType& mat) : Base()
+    {
+      compute(mat);
+    }
+    
+    template<typename MatType>
+    LeastSquareDiagonalPreconditionerFix& analyzePattern(const MatType& )
+    {
+      return *this;
+    }
+    
+    template<typename MatType>
+    LeastSquareDiagonalPreconditionerFix& factorize(const MatType& mat)
+    {
+      // Compute the inverse squared-norm of each column of mat
+      m_invdiag.resize(mat.cols());
+      if(MatType::IsRowMajor)
+      {
+        m_invdiag.setZero();
+        for(Index j=0; j<mat.outerSize(); ++j)
+        {
+          for(typename MatType::InnerIterator it(mat,j); it; ++it)
+            m_invdiag(it.index()) += numext::abs2(it.value());
+        }
+        for(Index j=0; j<mat.cols(); ++j)
+          if(numext::real(m_invdiag(j))>RealScalar(0))
+            m_invdiag(j) = RealScalar(1)/numext::real(m_invdiag(j));
+      }
+      else
+      {
+        for(Index j=0; j<mat.outerSize(); ++j)
+        {
+          RealScalar sum = mat.col(j).squaredNorm();
+          if(sum>RealScalar(0))
+            m_invdiag(j) = RealScalar(1)/sum;
+          else
+            m_invdiag(j) = RealScalar(1);
+        }
+      }
+      Base::m_isInitialized = true;
+      return *this;
+    }
+    
+  };
+}
+
+namespace eigenpy
+{
+  
+  namespace bp = boost::python;
+  
+  template<typename LeastSquaresConjugateGradient>
+  struct LeastSquaresConjugateGradientVisitor
+  :  public boost::python::def_visitor< LeastSquaresConjugateGradientVisitor<LeastSquaresConjugateGradient> >
+  {
+    typedef Eigen::MatrixXd MatrixType;
+    
+    template<class PyClass>
+    void visit(PyClass& cl) const
+    {
+      cl
+      .def(bp::init<>("Default constructor"))
+      .def(bp::init<MatrixType>(bp::arg("A"),"Initialize the solver with matrix A for further || Ax - b || solving.\n"
+                                "This constructor is a shortcut for the default constructor followed by a call to compute()."))
+      ;
+      
+    }
+    
+    static void expose()
+    {
+      bp::class_<LeastSquaresConjugateGradient,boost::noncopyable>("LeastSquaresConjugateGradient",
+                                                       bp::no_init)
+      .def(IterativeSolverVisitor<LeastSquaresConjugateGradient>())
+      .def(LeastSquaresConjugateGradientVisitor<LeastSquaresConjugateGradient>())
+      ;
+      
+    }
+    
+  };
+  
+} // namespace eigenpy
+
+#endif // ifndef __eigenpy_least_square_conjugate_gradient_hpp__
diff --git a/src/solvers/solvers.cpp b/src/solvers/solvers.cpp
index ba594d0e..62e267fe 100644
--- a/src/solvers/solvers.cpp
+++ b/src/solvers/solvers.cpp
@@ -16,6 +16,7 @@
 
 #include "eigenpy/solvers/solvers.hpp"
 #include "eigenpy/solvers/ConjugateGradient.hpp"
+#include "eigenpy/solvers/LeastSquaresConjugateGradient.hpp"
 
 namespace eigenpy
 {
@@ -23,6 +24,7 @@ namespace eigenpy
   {
     using namespace Eigen;
     ConjugateGradientVisitor< ConjugateGradient<MatrixXd,Lower|Upper> >::expose();
+    LeastSquaresConjugateGradientVisitor< LeastSquaresConjugateGradient<MatrixXd, LeastSquareDiagonalPreconditionerFix<MatrixXd::Scalar> > >::expose();
     
     boost::python::enum_<Eigen::ComputationInfo>("ComputationInfo")
     .value("Success",Eigen::Success)
-- 
GitLab