Main MRPT website > C++ reference
MRPT logo
CSparseMatrix.h
Go to the documentation of this file.
1 /* +---------------------------------------------------------------------------+
2  | The Mobile Robot Programming Toolkit (MRPT) C++ library |
3  | |
4  | http://www.mrpt.org/ |
5  | |
6  | Copyright (C) 2005-2012 University of Malaga |
7  | |
8  | This software was written by the Machine Perception and Intelligent |
9  | Robotics Lab, University of Malaga (Spain). |
10  | Contact: Jose-Luis Blanco <jlblanco@ctima.uma.es> |
11  | |
12  | This file is part of the MRPT project. |
13  | |
14  | MRPT is free software: you can redistribute it and/or modify |
15  | it under the terms of the GNU General Public License as published by |
16  | the Free Software Foundation, either version 3 of the License, or |
17  | (at your option) any later version. |
18  | |
19  | MRPT is distributed in the hope that it will be useful, |
20  | but WITHOUT ANY WARRANTY; without even the implied warranty of |
21  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
22  | GNU General Public License for more details. |
23  | |
24  | You should have received a copy of the GNU General Public License |
25  | along with MRPT. If not, see <http://www.gnu.org/licenses/>. |
26  | |
27  +---------------------------------------------------------------------------+ */
28 #ifndef CSparseMatrix_H
29 #define CSparseMatrix_H
30 
31 #include <mrpt/utils/utils_defs.h>
32 #include <mrpt/utils/exceptions.h>
33 #include <mrpt/utils/CUncopiable.h>
34 
35 #include <mrpt/math/math_frwds.h>
39 
40 // I don't like this solution, since can cause problems if CSparse structs
41 // change in the future (unlikely): even if we are linking against the CSparse
42 // embedded lib or a system lib, use the embedded headers. The reason: not to
43 // force MRPT users to install CSparse headers.
44 extern "C"{
45 #include <suitesparse/cs.h>
46 }
47 
48 namespace mrpt
49 {
50  namespace math
51  {
52  /** Used in mrpt::math::CSparseMatrix */
54  {
55  public:
56  CExceptionNotDefPos(const std::string &s) : CMRPTException(s) { }
57  };
58 
59 
60  /** A sparse matrix structure, wrapping T. Davis' CSparse library (part of suitesparse)
61  * The type of the matrix entries is fixed to "double".
62  *
63  * There are two formats for the non-zero entries in this matrix:
64  * - A "triplet" matrix: a set of (r,c)=val triplet entries.
65  * - A column-compressed sparse (CCS) matrix.
66  *
67  * The latter is the "normal" format, which is expected by all mathematical operations defined
68  * in this class. There're two three ways of initializing and populating a sparse matrix:
69  *
70  * <ol>
71  * <li> <b>As a triplet (empty), then add entries, then compress:</b>
72  * \code
73  * CSparseMatrix SM(100,100);
74  * SM.insert_entry(i,j, val); // or
75  * SM.insert_submatrix(i,j, MAT); // ...
76  * SM.compressFromTriplet();
77  * \endcode
78  * </li>
79  * <li> <b>As a triplet from a CSparseMatrixTemplate<double>:</b>
80  * \code
81  * CSparseMatrixTemplate<double> data;
82  * data(row,col) = val;
83  * ...
84  * CSparseMatrix SM(data);
85  * \endcode
86  * </li>
87  * <li> <b>From an existing dense matrix:</b>
88  * \code
89  * CMatrixDouble data(100,100); // or
90  * CMatrixFloat data(100,100); // or
91  * CMatrixFixedNumeric<double,4,6> data; // etc...
92  * CSparseMatrix SM(data);
93  * \endcode
94  * </li>
95  *
96  * </ol>
97  *
98  * Due to its practical utility, there is a special inner class CSparseMatrix::CholeskyDecomp to handle Cholesky-related methods and data.
99  *
100  * \note This class was initially adapted from "robotvision", by Hauke Strasdat, Steven Lovegrove and Andrew J. Davison. See http://www.openslam.org/robotvision.html
101  * \note CSparse is maintained by Timothy Davis: http://people.sc.fsu.edu/~jburkardt/c_src/csparse/csparse.html .
102  * \note See also his book "Direct methods for sparse linear systems". http://books.google.es/books?id=TvwiyF8vy3EC&pg=PA12&lpg=PA12&dq=cs_compress&source=bl&ots=od9uGJ793j&sig=Wa-fBk4sZkZv3Y0Op8FNH8PvCUs&hl=es&ei=UjA0TJf-EoSmsQay3aXPAw&sa=X&oi=book_result&ct=result&resnum=8&ved=0CEQQ6AEwBw#v=onepage&q&f=false
103  *
104  * \sa mrpt::math::CMatrixFixedNumeric, mrpt::math::CMatrixTemplateNumeric, etc.
105  * \ingroup mrpt_base_grp
106  */
108  {
109  private:
111 
112  /** Initialization from a dense matrix of any kind existing in MRPT. */
113  template <class MATRIX>
114  void construct_from_mrpt_mat(const MATRIX & C)
115  {
116  std::vector<int> row_list, col_list; // Use "int" for convenience so we can do a memcpy below...
117  std::vector<double> content_list;
118  const int nCol = C.getColCount();
119  const int nRow = C.getRowCount();
120  for (int c=0; c<nCol; ++c)
121  {
122  col_list.push_back(row_list.size());
123  for (int r=0; r<nRow; ++r)
124  if (C.get_unsafe(r,c)!=0)
125  {
126  row_list.push_back(r);
127  content_list.push_back(C(r,c));
128  }
129  }
130  col_list.push_back(row_list.size());
131 
132  sparse_matrix.m = nRow;
133  sparse_matrix.n = nCol;
134  sparse_matrix.nzmax = content_list.size();
135  sparse_matrix.i = (int*)malloc(sizeof(int)*row_list.size());
136  sparse_matrix.p = (int*)malloc(sizeof(int)*col_list.size());
137  sparse_matrix.x = (double*)malloc(sizeof(double)*content_list.size());
138 
139  ::memcpy(sparse_matrix.i, &row_list[0], sizeof(row_list[0])*row_list.size() );
140  ::memcpy(sparse_matrix.p, &col_list[0], sizeof(col_list[0])*col_list.size() );
141  ::memcpy(sparse_matrix.x, &content_list[0], sizeof(content_list[0])*content_list.size() );
142 
143  sparse_matrix.nz = -1; // <0 means "column compressed", ">=0" means triplet.
144  }
145 
146  /** Initialization from a triplet "cs", which is first compressed */
147  void construct_from_triplet(const cs & triplet);
148 
149  /** To be called by constructors only, assume previous pointers are trash and overwrite them */
150  void construct_from_existing_cs(const cs &sm);
151 
152  /** free buffers (deallocate the memory of the i,p,x buffers) */
153  void internal_free_mem();
154 
155  /** Copy the data from an existing "cs" CSparse data structure */
156  void copy(const cs * const sm);
157 
158  /** Fast copy the data from an existing "cs" CSparse data structure, copying the pointers and leaving NULLs in the source structure. */
159  void copy_fast(cs * const sm);
160 
161  public:
162 
163  /** @name Constructors, destructor & copy operations
164  @{ */
165 
166  /** Create an initially empty sparse matrix, in the "triplet" form.
167  * Notice that you must call "compressFromTriplet" after populating the matrix and before using the math operatons on this matrix.
168  * The initial size can be later on extended with insert_entry() or setRowCount() & setColCount().
169  * \sa insert_entry, setRowCount, setColCount
170  */
171  CSparseMatrix(const size_t nRows=0, const size_t nCols=0);
172 
173  /** A good way to initialize a sparse matrix from a list of non NULL elements.
174  * This constructor takes all the non-zero entries in "data" and builds a column-compressed sparse representation.
175  */
176  template <typename T>
178  {
179  ASSERTMSG_(!data.empty(), "Input data must contain at least one non-zero element.")
180  sparse_matrix.i = NULL; // This is to know they shouldn't be tried to free()
181  sparse_matrix.p = NULL;
182  sparse_matrix.x = NULL;
183 
184  // 1) Create triplet matrix
185  CSparseMatrix triplet(data.getRowCount(),data.getColCount());
186  // 2) Put data in:
187  for (typename CSparseMatrixTemplate<T>::const_iterator it=data.begin();it!=data.end();++it)
188  triplet.insert_entry_fast(it->first.first,it->first.second, it->second);
189 
190  // 3) Compress:
191  construct_from_triplet(triplet.sparse_matrix);
192  }
193 
194 
195  // We can't do a simple "template <class ANYMATRIX>" since it would be tried to match against "cs*"...
196 
197  /** Constructor from a dense matrix of any kind existing in MRPT, creating a "column-compressed" sparse matrix. */
198  template <typename T,size_t N,size_t M> inline explicit CSparseMatrix(const CMatrixFixedNumeric<T,N,M> &MAT) { construct_from_mrpt_mat(MAT); }
199 
200  /** Constructor from a dense matrix of any kind existing in MRPT, creating a "column-compressed" sparse matrix. */
201  template <typename T> inline explicit CSparseMatrix(const CMatrixTemplateNumeric<T> &MAT) { construct_from_mrpt_mat(MAT); }
202 
203  /** Copy constructor */
204  CSparseMatrix(const CSparseMatrix & other);
205 
206  /** Copy constructor from an existing "cs" CSparse data structure */
207  explicit CSparseMatrix(const cs * const sm);
208 
209  /** Destructor */
210  virtual ~CSparseMatrix();
211 
212  /** Copy operator from another existing object */
213  void operator = (const CSparseMatrix & other);
214 
215  /** Erase all previous contents and leave the matrix as a "triplet" 1x1 matrix without any data. */
216  void clear();
217 
218  /** @} */
219 
220  /** @name Math operations (the interesting stuff...)
221  @{ */
222 
223  void add_AB(const CSparseMatrix & A,const CSparseMatrix & B); //!< this = A+B
224  void multiply_AB(const CSparseMatrix & A,const CSparseMatrix & B); //!< this = A*B
225  void multiply_Ab(const mrpt::vector_double &b, mrpt::vector_double &out_res) const; //!< out_res = this * b
226 
227  inline CSparseMatrix operator + (const CSparseMatrix & other) const
228  {
229  CSparseMatrix RES;
230  RES.add_AB(*this,other);
231  return RES;
232  }
233  inline CSparseMatrix operator * (const CSparseMatrix & other) const
234  {
235  CSparseMatrix RES;
236  RES.multiply_AB(*this,other);
237  return RES;
238  }
241  multiply_Ab(other,res);
242  return res;
243  }
244  inline void operator += (const CSparseMatrix & other) {
245  this->add_AB(*this,other);
246  }
247  inline void operator *= (const CSparseMatrix & other) {
248  this->multiply_AB(*this,other);
249  }
250  CSparseMatrix transpose() const;
251 
252  /** @} */
253 
254 
255  /** @ Access the matrix, get, set elements, etc.
256  @{ */
257 
258  /** ONLY for TRIPLET matrices: insert a new non-zero entry in the matrix.
259  * This method cannot be used once the matrix is in column-compressed form.
260  * The size of the matrix will be automatically extended if the indices are out of the current limits.
261  * \sa isTriplet, compressFromTriplet
262  */
263  void insert_entry(const size_t row, const size_t col, const double val );
264 
265  /** This was an optimized version, but is now equivalent to insert_entry() due to the need to be compatible with unmodified CSparse system libraries. */
266  inline void insert_entry_fast(const size_t row, const size_t col, const double val ) { insert_entry(row,col,val); }
267 
268  /** ONLY for TRIPLET matrices: insert a given matrix (in any of the MRPT formats) at a given location of the sparse matrix.
269  * This method cannot be used once the matrix is in column-compressed form.
270  * The size of the matrix will be automatically extended if the indices are out of the current limits.
271  * Since this is inline, it can be very efficient for fixed-size MRPT matrices.
272  * \sa isTriplet, compressFromTriplet, insert_entry
273  */
274  template <class MATRIX>
275  inline void insert_submatrix(const size_t row, const size_t col, const MATRIX &M )
276  {
277  if (!isTriplet()) THROW_EXCEPTION("insert_entry() is only available for sparse matrix in 'triplet' format.")
278  const size_t nR = M.getRowCount();
279  const size_t nC = M.getColCount();
280  for (size_t r=0;r<nR;r++)
281  for (size_t c=0;c<nC;c++)
282  insert_entry_fast(row+r,col+c, M.get_unsafe(r,c));
283  // If needed, extend the size of the matrix:
284  sparse_matrix.m = std::max(sparse_matrix.m, int(row+nR));
285  sparse_matrix.n = std::max(sparse_matrix.n, int(col+nC));
286  }
287 
288 
289  /** ONLY for TRIPLET matrices: convert the matrix in a column-compressed form.
290  * \sa insert_entry
291  */
292  void compressFromTriplet();
293 
294  /** Return a dense representation of the sparse matrix.
295  * \sa saveToTextFile_dense
296  */
297  void get_dense(CMatrixDouble &outMat) const;
298 
299  /** Static method to convert a "cs" structure into a dense representation of the sparse matrix.
300  */
301  static void cs2dense(const cs& SM, CMatrixDouble &outMat);
302 
303  /** save as a dense matrix to a text file \return False on any error.
304  */
305  bool saveToTextFile_dense(const std::string &filName);
306 
307  /** Save sparse structure to a text file loadable from MATLAB (can be called on triplet or CCS matrices).
308  *
309  * The format of the text file is:
310  * \code
311  * NUM_ROWS NUM_COLS NUM_NON_ZERO_MAX
312  * row_1 col_1 value_1
313  * row_2 col_2 value_2
314  * ...
315  * \endcode
316  *
317  * Instructions for loading from MATLAB in triplet form will be automatically writen to the
318  * output file as comments in th first lines:
319  *
320  * \code
321  * D=load('file.txt');
322  * SM=spconvert(D(2:end,:));
323  * or, to always preserve the actual matrix size m x n:
324  * m=D(1,1); n=D(1,2); nzmax=D(1,3);
325  * Di=D(2:end,1); Dj=D(2:end,2); Ds=D(2:end,3);
326  * M=sparse(Di,Dj,Ds, m,n, nzmax);
327  * \endcode
328  * \return False on any error.
329  */
330  bool saveToTextFile_sparse(const std::string &filName);
331 
332  // Very basic, standard methods that MRPT methods expect for any matrix:
333  inline size_t getRowCount() const { return sparse_matrix.m; }
334  inline size_t getColCount() const { return sparse_matrix.n; }
335 
336  /** Change the number of rows in the matrix (can't be lower than current size) */
337  inline void setRowCount(const size_t nRows) { ASSERT_(nRows>=(size_t)sparse_matrix.m); sparse_matrix.m = nRows; }
338  inline void setColCount(const size_t nCols) { ASSERT_(nCols>=(size_t)sparse_matrix.n); sparse_matrix.n = nCols; }
339 
340  /** Returns true if this sparse matrix is in "triplet" form. \sa isColumnCompressed */
341  inline bool isTriplet() const { return sparse_matrix.nz>=0; } // <0 means "column compressed", ">=0" means triplet.
342 
343  /** Returns true if this sparse matrix is in "column compressed" form. \sa isTriplet */
344  inline bool isColumnCompressed() const { return sparse_matrix.nz<0; } // <0 means "column compressed", ">=0" means triplet.
345 
346  /** @} */
347 
348 
349  /** @name Cholesky factorization
350  @{ */
351 
352  /** Auxiliary class to hold the results of a Cholesky factorization of a sparse matrix.
353  * This implementation does not allow updating/downdating.
354  *
355  * Usage example:
356  * \code
357  * CSparseMatrix SM(100,100);
358  * SM.insert_entry(i,j, val); ...
359  * SM.compressFromTriplet();
360  *
361  * // Do Cholesky decomposition:
362  * CSparseMatrix::CholeskyDecomp CD(SM);
363  * CD.get_inverse();
364  * ...
365  * \endcode
366  *
367  * \note Only the upper triangular part of the input matrix is accessed.
368  * \note This class was initially adapted from "robotvision", by Hauke Strasdat, Steven Lovegrove and Andrew J. Davison. See http://www.openslam.org/robotvision.html
369  * \note This class designed to be "uncopiable".
370  * \sa The main class: CSparseMatrix
371  */
373  {
374  private:
377  const CSparseMatrix *m_originalSM; //!< A const reference to the original matrix used to build this decomposition.
378 
379  public:
380  /** Constructor from a square definite-positive sparse matrix A, which can be use to solve Ax=b
381  * The actual Cholesky decomposition takes places in this constructor.
382  * \note Only the upper triangular part of the matrix is accessed.
383  * \exception std::runtime_error On non-square input matrix.
384  * \exception mrpt::math::CExceptionNotDefPos On non-definite-positive matrix as input.
385  */
386  CholeskyDecomp(const CSparseMatrix &A);
387 
388  /** Destructor */
389  virtual ~CholeskyDecomp();
390 
391  /** Return the L matrix (L*L' = M), as a dense matrix. */
392  inline CMatrixDouble get_L() const { CMatrixDouble L; get_L(L); return L; }
393 
394  /** Return the L matrix (L*L' = M), as a dense matrix. */
395  void get_L(CMatrixDouble &out_L) const;
396 
397  /** Return the vector from a back-substitution step that solves: Ux=b */
398  inline mrpt::vector_double backsub(const mrpt::vector_double &b) const { mrpt::vector_double res; backsub(b,res); return res; }
399 
400  /** Return the vector from a back-substitution step that solves: Ux=b */
401  void backsub(const mrpt::vector_double &b, mrpt::vector_double &result_x) const;
402 
403  /** \overload for double pointers which assume the user has reserved the output memory for \a result */
404  void backsub(const double *b, double *result, const size_t N) const;
405 
406  /** Update the Cholesky factorization from an updated vesion of the original input, square definite-positive sparse matrix.
407  * NOTE: This new matrix MUST HAVE exactly the same sparse structure than the original one.
408  */
409  void update(const CSparseMatrix &new_SM);
410  };
411 
412 
413  /** @} */
414 
415  }; // end class CSparseMatrix
416 
417  }
418 }
419 #endif



Page generated by Doxygen 1.8.3 for MRPT 0.9.6 SVN: at Fri Feb 15 22:05:02 EST 2013