linear_solver_cholmod.h
Go to the documentation of this file.
00001 // g2o - General Graph Optimization
00002 // Copyright (C) 2011 R. Kuemmerle, G. Grisetti, W. Burgard
00003 // 
00004 // g2o is free software: you can redistribute it and/or modify
00005 // it under the terms of the GNU Lesser General Public License as published
00006 // by the Free Software Foundation, either version 3 of the License, or
00007 // (at your option) any later version.
00008 // 
00009 // g2o is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU Lesser General Public License for more details.
00013 // 
00014 // You should have received a copy of the GNU Lesser General Public License
00015 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016 
00017 #ifndef LINEAR_SOLVER_CHOLMOD
00018 #define LINEAR_SOLVER_CHOLMOD
00019 
00020 #include "g2o/core/linear_solver.h"
00021 #include "g2o/core/marginal_covariance_cholesky.h"
00022 #include "g2o/core/batch_stats.h"
00023 #include "g2o/stuff/timeutil.h"
00024 #include "g2o/stuff/sparse_helper.h"
00025 
00026 #include <cholmod.h>
00027 
00028 namespace g2o {
00029 
00033 struct CholmodExt : public cholmod_sparse
00034 {
00035   CholmodExt()
00036   {
00037     nzmax = 0;
00038     nrow  = 0;
00039     ncol  = 0;
00040     p     = 0;
00041     i     = 0;
00042     nz    = 0;
00043     x     = 0;
00044     z     = 0;
00045     stype = 1; // upper triangular block only
00046     itype = CHOLMOD_INT;
00047     xtype = CHOLMOD_REAL;
00048     dtype = CHOLMOD_DOUBLE;
00049     sorted = 1;
00050     packed = 1;
00051     columnsAllocated = 0;
00052   }
00053   ~CholmodExt()
00054   {
00055     delete[] (int*)p; p = 0;
00056     delete[] (double*)x; x = 0;
00057     delete[] (int*)i; i = 0;
00058   }
00059   size_t columnsAllocated;
00060 };
00061 
00062 
00067 template <typename MatrixType>
00068 class LinearSolverCholmod : public LinearSolver<MatrixType>
00069 {
00070   public:
00071     LinearSolverCholmod() :
00072       LinearSolver<MatrixType>()
00073     {
00074       _blockOrdering = false;
00075       _cholmodSparse = new CholmodExt();
00076       _cholmodFactor = 0;
00077       cholmod_start(&_cholmodCommon);
00078 
00079       // setup ordering strategy
00080       _cholmodCommon.nmethods = 1 ;
00081       _cholmodCommon.method[0].ordering = CHOLMOD_AMD; //CHOLMOD_COLAMD
00082       //_cholmodCommon.postorder = 0;
00083 
00084       _cholmodCommon.supernodal = CHOLMOD_AUTO; 
00085                                 //CHOLMOD_SUPERNODAL; //CHOLMOD_SIMPLICIAL;
00086     }
00087 
00088    /***************************************************************************/
00089 
00090     virtual ~LinearSolverCholmod()
00091     {
00092       delete _cholmodSparse;
00093       if (_cholmodFactor)
00094       {
00095         cholmod_free_factor(&_cholmodFactor, &_cholmodCommon);
00096         _cholmodFactor = 0;
00097       }
00098       cholmod_finish(&_cholmodCommon);
00099     }
00100 
00101    /***************************************************************************/
00102 
00103     virtual bool init()
00104     {
00105       if (_cholmodFactor)
00106       {
00107         cholmod_free_factor(&_cholmodFactor, &_cholmodCommon);
00108         _cholmodFactor = 0;
00109       }
00110       return true;
00111     }
00112 
00113    /***************************************************************************/
00114 
00115     bool solve(const SparseBlockMatrix<MatrixType>& A, double* x, double* b)
00116     {
00117       //cerr << __PRETTY_FUNCTION__ << " using cholmod" << endl;
00118 
00119       // _cholmodFactor used as bool, if not existing will copy the whole
00120       // structure, otherwise only the values
00121       fillCholmodExt(A, _cholmodFactor); 
00122 
00123       if (! _cholmodFactor) {
00124         computeSymbolicDecomposition(A);
00125         assert(_cholmodFactor && "Symbolic cholesky failed");
00126       }
00127       double t=get_time();
00128 
00129       // setting up b for calling cholmod
00130       cholmod_dense bcholmod;
00131       bcholmod.nrow  = bcholmod.d = _cholmodSparse->nrow;
00132       bcholmod.ncol  = 1;
00133       bcholmod.x     = b;
00134       bcholmod.xtype = CHOLMOD_REAL;
00135       bcholmod.dtype = CHOLMOD_DOUBLE;
00136 
00137       cholmod_factorize(_cholmodSparse, _cholmodFactor, &_cholmodCommon);
00138       if (_cholmodCommon.status == CHOLMOD_NOT_POSDEF) {
00139         std::cerr << "Cholesky failure, writing debug.txt "
00140                      "(Hessian loadable by Octave)" << std::endl;
00141         writeCCSMatrix
00142         (
00143            "debug.txt", _cholmodSparse->nrow, _cholmodSparse->ncol,
00144            (int*)_cholmodSparse->p, (int*)_cholmodSparse->i,
00145            (double*)_cholmodSparse->x, true
00146         );
00147         return false;
00148       }
00149 
00150       cholmod_dense* xcholmod = 
00151          cholmod_solve(CHOLMOD_A, _cholmodFactor, &bcholmod, &_cholmodCommon);
00152 
00153       // copy back to our array
00154       memcpy(x, xcholmod->x, sizeof(double) * bcholmod.nrow);
00155       cholmod_free_dense(&xcholmod, &_cholmodCommon);
00156 
00157       if (globalStats)
00158       {
00159         globalStats->timeNumericDecomposition = get_time() - t;
00160         globalStats->choleskyNNZ = _cholmodCommon.method[0].lnz;
00161       }
00162 
00163       return true;
00164     }
00165 
00166    /***************************************************************************/
00167 
00168     bool solveBlocks(double**& blocks, const SparseBlockMatrix<MatrixType>& A)
00169     {
00170       //cerr << __PRETTY_FUNCTION__ << " using cholmod" << endl;
00171 
00172       // _cholmodFactor used as bool, if not existing will copy the whole 
00173       // structure, otherwise only the values
00174       fillCholmodExt(A, _cholmodFactor); 
00175 
00176       if (! _cholmodFactor)
00177       {
00178         computeSymbolicDecomposition(A);
00179         assert(_cholmodFactor && "Symbolic cholesky failed");
00180       }
00181 
00182       if (! blocks)
00183       {
00184         blocks = new double*[A.rows()];
00185         double **block = blocks;
00186         for (size_t i = 0; i < A.rowBlockIndices().size(); ++i)
00187         {
00188           int dim = A.rowsOfBlock(i)*A.colsOfBlock(i);
00189           *block = new double [dim];
00190           block++;
00191         }
00192       }
00193 
00194       cholmod_factorize(_cholmodSparse, _cholmodFactor, &_cholmodCommon);
00195       if (_cholmodCommon.status == CHOLMOD_NOT_POSDEF)   return false;
00196 
00197       // convert the factorization to LL, simplical, packed, monotonic
00198       int change_status = 
00199          cholmod_change_factor
00200             (CHOLMOD_REAL, 1, 0, 1, 1, _cholmodFactor, &_cholmodCommon);
00201 
00202       if (! change_status)   return false;
00203 
00204       assert(  _cholmodFactor->is_ll && !_cholmodFactor->is_super 
00205             && _cholmodFactor->is_monotonic 
00206             && "Cholesky factor has wrong format");
00207 
00208       // invert the permutation
00209       int* p = (int*)_cholmodFactor->Perm;
00210       VectorXi pinv; pinv.resize(_cholmodSparse->ncol);
00211 
00212       for (size_t i = 0; i < _cholmodSparse->ncol; ++i)   pinv(p[i]) = i;
00213 
00214       // compute the marginal covariance
00215       MarginalCovarianceCholesky mcc;
00216       mcc.setCholeskyFactor
00217       (
00218          _cholmodSparse->ncol, (int*)_cholmodFactor->p,
00219          (int*)_cholmodFactor->i, (double*)_cholmodFactor->x, pinv.data()
00220       );
00221       mcc.computeCovariance(blocks, A.rowBlockIndices());
00222 
00223       if (globalStats) {
00224         globalStats->choleskyNNZ = 
00225            _cholmodCommon.method[_cholmodCommon.selected].lnz;
00226       }
00227 
00228       return true;
00229     }
00230 
00231    /***************************************************************************/
00232 
00233     virtual bool solvePattern
00234     (
00235        SparseBlockMatrix<MatrixXd>& spinv,
00236        const std::vector<std::pair<int, int> >& blockIndices,
00237        const SparseBlockMatrix<MatrixType>& A
00238     )
00239     {
00240       //cerr << __PRETTY_FUNCTION__ << " using cholmod" << endl;
00241 
00242       // _cholmodFactor used as bool, if not existing will copy the whole 
00243       // structure, otherwise only the values
00244       fillCholmodExt(A, _cholmodFactor);
00245 
00246       if (! _cholmodFactor)
00247       {
00248         computeSymbolicDecomposition(A);
00249         assert(_cholmodFactor && "Symbolic cholesky failed");
00250       }
00251 
00252       cholmod_factorize(_cholmodSparse, _cholmodFactor, &_cholmodCommon);
00253       if (_cholmodCommon.status == CHOLMOD_NOT_POSDEF)   return false;
00254 
00255       // convert the factorization to LL, simplical, packed, monotonic
00256       int change_status =
00257          cholmod_change_factor
00258             (CHOLMOD_REAL, 1, 0, 1, 1, _cholmodFactor, &_cholmodCommon);
00259 
00260       if (! change_status)   return false;
00261 
00262       assert(  _cholmodFactor->is_ll && !_cholmodFactor->is_super 
00263             && _cholmodFactor->is_monotonic 
00264             && "Cholesky factor has wrong format");
00265 
00266       // invert the permutation
00267       int* p = (int*)_cholmodFactor->Perm;
00268       VectorXi pinv; pinv.resize(_cholmodSparse->ncol);
00269       for (size_t i = 0; i < _cholmodSparse->ncol; ++i)   pinv(p[i]) = i;
00270 
00271       // compute the marginal covariance
00272       MarginalCovarianceCholesky mcc;
00273       mcc.setCholeskyFactor
00274       (
00275          _cholmodSparse->ncol, (int*)_cholmodFactor->p, (int*)_cholmodFactor->i,
00276          (double*)_cholmodFactor->x, pinv.data()
00277       );
00278 
00279       mcc.computeCovariance(spinv, A.rowBlockIndices(), blockIndices);
00280 
00281       if (globalStats) {
00282         globalStats->choleskyNNZ = 
00283            _cholmodCommon.method[_cholmodCommon.selected].lnz;
00284       }
00285 
00286       return true;
00287     }
00288 
00289    /***************************************************************************/
00290 
00292     bool blockOrdering() const { return _blockOrdering;}
00293     void setBlockOrdering(bool blockOrdering) { _blockOrdering = blockOrdering;}
00294 
00295   protected:
00296     // temp used for cholesky with cholmod
00297     cholmod_common _cholmodCommon;
00298     CholmodExt* _cholmodSparse;
00299     cholmod_factor* _cholmodFactor;
00300     bool _blockOrdering;
00301     MatrixStructure _matrixStructure;
00302     VectorXi _scalarPermutation, _blockPermutation;
00303 
00304    /***************************************************************************/
00305 
00306     void computeSymbolicDecomposition(const SparseBlockMatrix<MatrixType>& A)
00307     {
00308       double t = get_time();
00309       if (! _blockOrdering) {
00310         // setup ordering strategy
00311         _cholmodCommon.nmethods = 1;
00312         _cholmodCommon.method[0].ordering = CHOLMOD_AMD; //CHOLMOD_COLAMD
00313 
00314         // symbolic factorization
00315         _cholmodFactor = cholmod_analyze(_cholmodSparse, &_cholmodCommon);
00316       }else 
00317       {
00318 
00319         A.fillBlockStructure(_matrixStructure);
00320 
00321         // get the ordering for the block matrix
00322         if (_blockPermutation.size() == 0)
00323            _blockPermutation.resize(_matrixStructure.n);
00324 
00325         // double space if resizing
00326         if (_blockPermutation.size() < _matrixStructure.n)
00327           _blockPermutation.resize(2*_matrixStructure.n);
00328  
00329         // prepare AMD call via CHOLMOD
00330         cholmod_sparse auxCholmodSparse;
00331         auxCholmodSparse.nzmax = _matrixStructure.nzMax();
00332         auxCholmodSparse.nrow = auxCholmodSparse.ncol = _matrixStructure.n;
00333         auxCholmodSparse.p = _matrixStructure.Ap;
00334         auxCholmodSparse.i = _matrixStructure.Aii;
00335         auxCholmodSparse.nz = 0;
00336         auxCholmodSparse.x = 0;
00337         auxCholmodSparse.z = 0;
00338         auxCholmodSparse.stype = 1;
00339         auxCholmodSparse.xtype = CHOLMOD_PATTERN;
00340         auxCholmodSparse.itype = CHOLMOD_INT;
00341         auxCholmodSparse.dtype = CHOLMOD_DOUBLE;
00342         auxCholmodSparse.sorted = 1;
00343         auxCholmodSparse.packed = 1;
00344         int amdStatus = 
00345            cholmod_amd
00346            (
00347               &auxCholmodSparse, NULL, 0, _blockPermutation.data(), 
00348               &_cholmodCommon
00349            );
00350 
00351         if (! amdStatus)   return;
00352 
00353         // blow up the permutation to the scalar matrix
00354         if (_scalarPermutation.size() == 0)
00355           _scalarPermutation.resize(_cholmodSparse->ncol);
00356 
00357         if (_scalarPermutation.size() < (int)_cholmodSparse->ncol)
00358           _scalarPermutation.resize(2*_cholmodSparse->ncol);
00359 
00360         size_t scalarIdx = 0;
00361         for (int i = 0; i < _matrixStructure.n; ++i)
00362         {
00363           const int& p = _blockPermutation(i);
00364           int base  = A.colBaseOfBlock(p);
00365           int nCols = A.colsOfBlock(p);
00366           for (int j = 0; j < nCols; ++j)
00367              _scalarPermutation(scalarIdx++) = base++;
00368         }
00369         assert(scalarIdx == _cholmodSparse->ncol);
00370 
00371         // apply the ordering
00372         _cholmodCommon.nmethods = 1 ;
00373         _cholmodCommon.method[0].ordering = CHOLMOD_GIVEN;
00374         _cholmodFactor = 
00375            cholmod_analyze_p
00376            (
00377               _cholmodSparse, _scalarPermutation.data(), NULL,
00378               0, &_cholmodCommon
00379            );
00380       }
00381       if (globalStats)  globalStats->timeSymbolicDecomposition = get_time() - t;
00382     }
00383 
00384    /***************************************************************************/
00385 
00386     void fillCholmodExt(const SparseBlockMatrix<MatrixType>& A, bool onlyValues)
00387     {
00388       size_t m = A.rows();
00389       size_t n = A.cols();
00390 
00391       if (_cholmodSparse->columnsAllocated < n)
00392       {
00393         //std::cerr << __PRETTY_FUNCTION__ 
00394         //          << ": reallocating columns" << std::endl;
00395 
00396         // pre-allocate more space if re-allocating
00397         _cholmodSparse->columnsAllocated =
00398            _cholmodSparse->columnsAllocated == 0 ? n : 2 * n;
00399         delete[] (int*)_cholmodSparse->p;
00400         _cholmodSparse->p = new int[_cholmodSparse->columnsAllocated+1];
00401       }
00402 
00403       if (! onlyValues)
00404       {
00405         size_t nzmax = A.nonZeros();
00406         if (_cholmodSparse->nzmax < nzmax)
00407         {
00408           //std::cerr << __PRETTY_FUNCTION__ 
00409           //          << ": reallocating row + values" << std::endl;
00410 
00411            // pre-allocate more space if re-allocating
00412           _cholmodSparse->nzmax = 
00413              _cholmodSparse->nzmax == 0 ? nzmax : 2 * nzmax;
00414 
00415           delete[] (double*)_cholmodSparse->x;
00416           delete[] (int*)_cholmodSparse->i;
00417           _cholmodSparse->i = new int[_cholmodSparse->nzmax];
00418           _cholmodSparse->x = new double[_cholmodSparse->nzmax];
00419         }
00420       }
00421       _cholmodSparse->ncol = n;
00422       _cholmodSparse->nrow = m;
00423 
00424       if (onlyValues)
00425         A.fillCCS((double*)_cholmodSparse->x, true);
00426       else
00427         A.fillCCS
00428         (
00429            (int*)_cholmodSparse->p, (int*)_cholmodSparse->i, 
00430            (double*)_cholmodSparse->x, true
00431         );
00432     }
00433 
00434 };
00435 
00436 } // end namespace
00437 
00438 #endif


re_vision
Author(s): Dorian Galvez-Lopez
autogenerated on Sun Jan 5 2014 11:31:38