GammaLib  2.1.0.dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GSparseNumeric.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GSparseNumeric.cpp - Sparse matrix numeric analysis class *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2006-2022 by Juergen Knoedlseder *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22  * @file GSparseNumeric.cpp
23  * @brief Sparse matrix numeric analysis class implementation
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include "GException.hpp"
32 #include "GTools.hpp"
33 #include "GMatrixSparse.hpp"
34 #include "GSparseNumeric.hpp"
35 #include "GSparseSymbolic.hpp"
36 
37 /* __ Method name definitions ____________________________________________ */
38 #define G_CHOLESKY "GSparseNumeric::cholesky_numeric_analysis("\
39  "GMatrixSparse&, GSparseSymbolic&)"
40 
41 /* __ Macros _____________________________________________________________ */
42 #define CS_FLIP(i) (-(i)-2)
43 #define CS_MARK(w,j) { w [j] = CS_FLIP (w [j]) ; }
44 #define CS_MARKED(w,j) (w [j] < 0)
45 
46 
47 /*==========================================================================
48  = =
49  = Constructors/destructors =
50  = =
51  ==========================================================================*/
52 
53 /***************************************************************************
54  * GSparseNumeric constructor *
55  ***************************************************************************/
57 {
58  // Initialise private members for clean destruction
59  m_L = NULL;
60  m_U = NULL;
61  m_pinv = NULL;
62  m_B = NULL;
63  m_n_pinv = 0;
64  m_n_B = 0;
65 
66  // Return
67  return;
68 }
69 
70 
71 /***************************************************************************
72  * GSparseNumeric destructor *
73  ***************************************************************************/
75 {
76  // De-allocate only if memory has indeed been allocated
77  if (m_L != NULL) delete m_L;
78  if (m_U != NULL) delete m_U;
79  if (m_pinv != NULL) delete [] m_pinv;
80  if (m_B != NULL) delete [] m_B;
81 
82  // Return
83  return;
84 }
85 
86 
87 /*==========================================================================
88  = =
89  = GSparseNumeric operators =
90  = =
91  ==========================================================================*/
92 
93 /***************************************************************************
94  * GSparseNumeric assignment operator *
95  ***************************************************************************/
97 {
98  // Execute only if object is not identical
99  if (this != &n) {
100 
101  // De-allocate only if memory has indeed been allocated
102  if (m_L != NULL) delete m_L;
103  if (m_U != NULL) delete m_U;
104  if (m_pinv != NULL) delete [] m_pinv;
105  if (m_B != NULL) delete [] m_B;
106 
107  // Initialise private members for clean destruction
108  m_L = NULL;
109  m_U = NULL;
110  m_pinv = NULL;
111  m_B = NULL;
112  m_n_pinv = 0;
113  m_n_B = 0;
114 
115  // Copy m_L if it exists
116  if (n.m_L != NULL) {
117  m_L = new GMatrixSparse(*n.m_L);
118  }
119 
120  // Copy m_U if it exists
121  if (n.m_U != NULL) {
122  m_U = new GMatrixSparse(*n.m_U);
123  }
124 
125  // Copy m_pinv array if it exists
126  if (n.m_pinv != NULL && n.m_n_pinv > 0) {
127  m_pinv = new int[n.m_n_pinv];
128  for (int i = 0; i < n.m_n_pinv; ++i) {
129  m_pinv[i] = n.m_pinv[i];
130  }
131  m_n_pinv = n.m_n_pinv;
132  }
133 
134  // Copy m_B array if it exists
135  if (n.m_B != NULL && n.m_n_B > 0) {
136  m_B = new double[n.m_n_B];
137  for (int i = 0; i < n.m_n_B; ++i) {
138  m_B[i] = n.m_B[i];
139  }
140  m_n_B = n.m_n_B;
141  }
142 
143  } // endif: object was not identical
144 
145  // Return
146  return *this;
147 }
148 
149 
150 /*==========================================================================
151  = =
152  = GSparseNumeric member functions =
153  = =
154  ==========================================================================*/
155 
156 /***************************************************************************
157  * @brief Numerical Cholesky analysis
158  *
159  * @param[in] A Spare matrix.
160  * @param[in] S Symbolic analysis of sparse matrix.
161  ***************************************************************************/
163  const GSparseSymbolic& S)
164 {
165  // De-allocate memory that has indeed been previously allocated
166  if (m_L != NULL) delete m_L;
167  if (m_U != NULL) delete m_U;
168  if (m_pinv != NULL) delete [] m_pinv;
169  if (m_B != NULL) delete [] m_B;
170 
171  // Initialise members
172  m_L = NULL;
173  m_U = NULL;
174  m_pinv = NULL;
175  m_B = NULL;
176  m_n_pinv = 0;
177  m_n_B = 0;
178 
179  // Return if arrays in the symbolic analysis have not been allocated
180  if (!S.m_cp || !S.m_parent) {
181  return;
182  }
183 
184  // Declare
185  double lki;
186  int i, p, k;
187 
188  // Assign input matrix attributes
189  int n = A.m_cols;
190 
191  // Allocate int workspace
192  int wrk_size = 2*n;
193  int* wrk_int = new int[wrk_size];
194 
195  // Allocate double workspace
196  wrk_size = n;
197  double* wrk_double = new double[wrk_size];
198 
199  // Assign pointers
200  int* cp = S.m_cp;
201  int* pinv = S.m_pinv;
202  int* parent = S.m_parent;
203 
204  // Assign C = A(p,p) where A and C are symmetric and the upper part stored
205  GMatrixSparse C = (pinv) ? cs_symperm(A, pinv) : (A);
206 
207  // Assign workspace pointer
208  int* c = wrk_int;
209  int* s = wrk_int + n;
210  double* x = wrk_double;
211 
212  // Assign C matrix pointers
213  int* Cp = C.m_colstart;
214  int* Ci = C.m_rowinx;
215  double* Cx = C.m_data;
216 
217  // Allocate L matrix
218  m_L = new GMatrixSparse(n, n, cp[n]);
219 
220  // Assign L matrix pointers
221  int* Lp = m_L->m_colstart;
222  int* Li = m_L->m_rowinx;
223  double* Lx = m_L->m_data;
224 
225  // Initialise column pointers of L and c
226  for (k = 0; k < n; ++k) {
227  Lp[k] = c[k] = cp[k];
228  }
229 
230  // Compute L(:,k) for L*L' = C
231  for (k = 0; k < n; k++) {
232 
233  // Nonzero pattern of L(k,:).
234  // Returns -1 if parent = NULL, s = NULL or c = NULL
235  int top = cs_ereach(&C, k, parent, s, c); // find pattern of L(k,:)
236  x[k] = 0; // x (0:k) is now zero
237 
238  // x = full(triu(C(:,k)))
239  for (p = Cp[k]; p < Cp[k+1]; p++) {
240  if (Ci[p] <= k) {
241  x[Ci[p]] = Cx[p];
242  }
243  }
244 
245  // d = C(k,k)
246  double d = x[k];
247 
248  // Clear x for k+1st iteration
249  x[k] = 0;
250 
251  // Triangular solve: Solve L(0:k-1,0:k-1) * x = C(:,k)
252  for ( ; top < n; top++) {
253  i = s[top]; // s [top..n-1] is pattern of L(k,:)
254  lki = x[i]/Lx[Lp[i]]; // L(k,i) = x (i) / L(i,i)
255  x[i] = 0; // clear x for k+1st iteration
256  for (p = Lp[i]+1; p < c[i]; p++) {
257  x[Li[p]] -= Lx[p] * lki;
258  }
259  d -= lki * lki; // d = d - L(k,i)*L(k,i)
260  p = c[i]++;
261  Li[p] = k; // store L(k,i) in column i
262  Lx[p] = lki;
263  }
264 
265  // Compute L(k,k)
266  if (d <= 0) {
267  std::string msg = "Matrix is not positive definite, the sum "+
268  gammalib::str(d)+" occured in row and column "+
269  gammalib::str(k)+".";
271  }
272 
273  // Store L(k,k) = sqrt (d) in column k
274  p = c[k]++;
275  Li[p] = k;
276  Lx[p] = sqrt(d);
277 
278  }
279 
280  // Finalize L
281  Lp[n] = cp[n];
282 
283  // Free workspace
284  delete [] wrk_int;
285  delete [] wrk_double;
286 
287  // Return void
288  return;
289 }
290 
291 
292 /*==========================================================================
293  = =
294  = GSparseNumeric private functions =
295  = =
296  ==========================================================================*/
297 
298 /***************************************************************************
299  * @brief cs_ereach
300  *
301  * @param[in] A Sparse matrix.
302  * @param[in] k Node.
303  * @param[in] parent Argument.
304  * @param[in] s Argument.
305  * @param[in] w Argument.
306  * @return Index, where s[top..n-1] contains pattern of L(k,:); -1 if
307  * arrays have not been allocated
308  *
309  * Find nonzero pattern of Cholesky L(k,1:k-1) using etree and triu(A(:,k))
310  ***************************************************************************/
312  int k,
313  const int* parent,
314  int* s,
315  int* w)
316 {
317  // Return -1 if arrays have not been allocated
318  if (!parent || !s || !w) {
319  return (-1);
320  }
321 
322  // Assign A matrix attributes and pointers
323  int n = A->m_cols;
324  int top = n;
325  int* Ap = A->m_colstart;
326  int* Ai = A->m_rowinx;
327 
328  // Mark node k as visited
329  CS_MARK(w, k);
330 
331  // Loop over elements of node
332  for (int p = Ap[k]; p < Ap[k+1]; ++p) {
333  int i = Ai[p]; // A(i,k) is nonzero
334  if (i > k) {
335  continue; // only use upper triangular part of A
336  }
337 
338  // Traverse up etree
339  int len;
340  for (len = 0; !CS_MARKED(w,i); i = parent[i]) {
341  s[len++] = i; // L(k,i) is nonzero
342  CS_MARK(w, i); // mark i as visited
343  }
344 
345  // Push path onto stack
346  while (len > 0) {
347  s[--top] = s[--len];
348  }
349 
350  } // endfor: looped over elements of node
351 
352  // Unmark all nodes
353  for (int p = top; p < n; ++p) {
354  CS_MARK(w, s[p]);
355  }
356 
357  // Unmark node k
358  CS_MARK(w, k);
359 
360  // Return index top, where s[top..n-1] contains pattern of L(k,:)
361  return top;
362 }
363 
364 
365 /*==========================================================================
366  = =
367  = GSparseNumeric static member functions =
368  = =
369  ==========================================================================*/
370 
371 /*==========================================================================
372  = =
373  = GSparseNumeric friends =
374  = =
375  ==========================================================================*/
376 
377 /***************************************************************************
378  * Output operator *
379  ***************************************************************************/
380 std::ostream& operator<< (std::ostream& os, const GSparseNumeric& n)
381 {
382  // Put header is stream
383  os << "=== GSparseNumeric ===";
384 
385  // Show L or V matrix
386  if (n.m_L != NULL) {
387  os << std::endl << " === L or V ";
388  os << *n.m_L << std::endl;
389  }
390 
391  // Show U or R matrix
392  if (n.m_U != NULL) {
393  os << std::endl << " === U or R matrix ";
394  os << *n.m_U << std::endl;
395  }
396 
397  // Show inverse permutation if it exists
398  if (n.m_pinv != NULL) {
399  os << std::endl << " Inverse permutation (of ";
400  os << n.m_n_pinv << " elements)" << std::endl;
401  for (int i = 0; i < n.m_n_pinv; ++i) {
402  os << " " << n.m_pinv[i];
403  }
404  }
405 
406  // Show beta array if it exists
407  if (n.m_B != NULL) {
408  os << std::endl << " Beta[0.." << n.m_n_B << "]" << std::endl;
409  for (int i = 0; i < n.m_n_B; ++i) {
410  os << " " << n.m_B[i];
411  }
412  }
413 
414  // Return output stream
415  return os;
416 }
GMatrixSparse cs_symperm(const GMatrixSparse &matrix, const int *pinv)
cs_symperm
GMatrixSparse * m_L
Sparse matrix numeric analysis class definition.
int * m_parent
elimination tree for Cholesky and QR
int * m_cp
column pointers for Cholesky, row counts for QR
Sparse matrix class interface definition.
#define CS_MARKED(w, j)
Sparse matrix symbolic analysis class definition.
GMatrixSparse * m_U
Gammalib tools definition.
#define G_CHOLESKY
#define CS_MARK(w, j)
Sparse matrix symbolic analysis class.
GVector sqrt(const GVector &vector)
Computes square root of vector elements.
Definition: GVector.cpp:1358
double * m_data
Matrix data.
int * m_pinv
Inverse row permutation for QR, fill reduce permutation for Cholesky.
int m_cols
Number of columns.
std::ostream & operator<<(std::ostream &os, const GBase &base)
Output operator.
Definition: GBase.cpp:57
int * m_rowinx
Row-indices of all elements.
Sparse matrix numeric analysis class.
GSparseNumeric & operator=(const GSparseNumeric &n)
int cs_ereach(const GMatrixSparse *A, int k, const int *parent, int *s, int *w)
Sparse matrix class definition.
Exception handler interface definition.
virtual ~GSparseNumeric(void)
friend class GMatrixSparse
void cholesky_numeric_analysis(const GMatrixSparse &m, const GSparseSymbolic &s)
int * m_colstart
Column start indices (m_cols+1)
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489