GNU Octave  8.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
sparse-chol.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1998-2023 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <cstddef>
31 
32 #include "CSparse.h"
33 #include "MatrixType.h"
34 #include "dRowVector.h"
35 #include "dSparse.h"
36 #include "lo-error.h"
37 #include "oct-cmplx.h"
38 #include "oct-sparse.h"
39 #include "oct-spparms.h"
40 #include "quit.h"
41 #include "sparse-chol.h"
42 #include "sparse-util.h"
43 
45 
47 
48 template <typename chol_type>
49 class sparse_chol<chol_type>::sparse_chol_rep
50 {
51 public:
52 
54  : m_is_pd (false), m_minor_p (0), m_perm (), m_rcond (0)
55 #if defined (HAVE_CHOLMOD)
56  , m_L (nullptr), m_common ()
57 #endif
58  { }
59 
60  sparse_chol_rep (const chol_type& a, bool natural, bool force)
61  : m_is_pd (false), m_minor_p (0), m_perm (), m_rcond (0)
62 #if defined (HAVE_CHOLMOD)
63  , m_L (nullptr), m_common ()
64 #endif
65  {
66  init (a, natural, force);
67  }
68 
69  sparse_chol_rep (const chol_type& a, octave_idx_type& info,
70  bool natural, bool force)
71  : m_is_pd (false), m_minor_p (0), m_perm (), m_rcond (0)
72 #if defined (HAVE_CHOLMOD)
73  , m_L (nullptr), m_common ()
74 #endif
75  {
76  info = init (a, natural, force);
77  }
78 
79  // No copying!
80 
81  sparse_chol_rep (const sparse_chol_rep&) = delete;
82 
84 
86  {
87 #if defined (HAVE_CHOLMOD)
88  if (m_L)
89  CHOLMOD_NAME (free_sparse) (&m_L, &m_common);
90 
91  CHOLMOD_NAME(finish) (&m_common);
92 #endif
93  }
94 
95 #if defined (HAVE_CHOLMOD)
96  cholmod_sparse * L (void) const
97  {
98  return m_L;
99  }
100 #endif
101 
102  octave_idx_type P (void) const
103  {
104 #if defined (HAVE_CHOLMOD)
105  return (m_minor_p == static_cast<octave_idx_type> (m_L->ncol) ?
106  0 : m_minor_p + 1);
107 #else
108  return 0;
109 #endif
110  }
111 
112  RowVector perm (void) const { return m_perm + 1; }
113 
114  SparseMatrix Q (void) const;
115 
116  bool is_positive_definite (void) const { return m_is_pd; }
117 
118  double rcond (void) const { return m_rcond; }
119 
120 private:
121 
122  bool m_is_pd;
123 
125 
127 
128  double m_rcond;
129 
130 #if defined (HAVE_CHOLMOD)
131  cholmod_sparse *m_L;
132 
133  cholmod_common m_common;
134 
135  void drop_zeros (const cholmod_sparse *S);
136 #endif
137 
138  octave_idx_type init (const chol_type& a, bool natural, bool force);
139 };
140 
141 #if defined (HAVE_CHOLMOD)
142 
143 // Can't use CHOLMOD_NAME(drop)(0.0, S, cm) because it doesn't treat
144 // complex matrices.
145 
146 template <typename chol_type>
147 void
149 {
150  if (! S)
151  return;
152 
153  octave_idx_type *Sp = static_cast<octave_idx_type *>(S->p);
154  octave_idx_type *Si = static_cast<octave_idx_type *>(S->i);
155  chol_elt *Sx = static_cast<chol_elt *>(S->x);
156 
157  octave_idx_type pdest = 0;
158  octave_idx_type ncol = S->ncol;
159 
160  for (octave_idx_type k = 0; k < ncol; k++)
161  {
162  octave_idx_type p = Sp[k];
163  octave_idx_type pend = Sp[k+1];
164  Sp[k] = pdest;
165 
166  for (; p < pend; p++)
167  {
168  chol_elt sik = Sx[p];
169 
170  if (CHOLMOD_IS_NONZERO (sik))
171  {
172  if (p != pdest)
173  {
174  Si[pdest] = Si[p];
175  Sx[pdest] = sik;
176  }
177 
178  pdest++;
179  }
180  }
181  }
182 
183  Sp[ncol] = pdest;
184 }
185 
186 // Must provide a specialization for this function.
187 template <typename T>
188 int
189 get_xtype (void);
190 
191 template <>
192 inline int
193 get_xtype<double> (void)
194 {
195  return CHOLMOD_REAL;
196 }
197 
198 template <>
199 inline int
200 get_xtype<Complex> (void)
201 {
202  return CHOLMOD_COMPLEX;
203 }
204 
205 #endif
206 
207 template <typename chol_type>
210  bool natural, bool force)
211 {
212  volatile octave_idx_type info = 0;
213 
214 #if defined (HAVE_CHOLMOD)
215 
216  octave_idx_type a_nr = a.rows ();
217  octave_idx_type a_nc = a.cols ();
218 
219  if (a_nr != a_nc)
220  (*current_liboctave_error_handler)
221  ("sparse_chol requires square matrix");
222 
223  cholmod_common *cm = &m_common;
224 
225  // Setup initial parameters
226 
227  CHOLMOD_NAME(start) (cm);
228  cm->prefer_zomplex = false;
229 
230  double spu = sparse_params::get_key ("spumoni");
231 
232  if (spu == 0.)
233  {
234  cm->print = -1;
235  SUITESPARSE_ASSIGN_FPTR (printf_func, cm->print_function, nullptr);
236  }
237  else
238  {
239  cm->print = static_cast<int> (spu) + 2;
240  SUITESPARSE_ASSIGN_FPTR (printf_func, cm->print_function,
241  &SparseCholPrint);
242  }
243 
244  cm->error_handler = &SparseCholError;
245 
246  SUITESPARSE_ASSIGN_FPTR2 (divcomplex_func, cm->complex_divide,
247  divcomplex);
248 
249  SUITESPARSE_ASSIGN_FPTR2 (hypot_func, cm->hypotenuse, hypot);
250 
251  cm->final_asis = false;
252  cm->final_super = false;
253  cm->final_ll = true;
254  cm->final_pack = true;
255  cm->final_monotonic = true;
256  cm->final_resymbol = false;
257 
258  cholmod_sparse A;
259  cholmod_sparse *ac = &A;
260  double dummy;
261 
262  ac->nrow = a_nr;
263  ac->ncol = a_nc;
264 
265  ac->p = a.cidx ();
266  ac->i = a.ridx ();
267  ac->nzmax = a.nnz ();
268  ac->packed = true;
269  ac->sorted = true;
270  ac->nz = nullptr;
271 #if defined (OCTAVE_ENABLE_64)
272  ac->itype = CHOLMOD_LONG;
273 #else
274  ac->itype = CHOLMOD_INT;
275 #endif
276  ac->dtype = CHOLMOD_DOUBLE;
277  ac->stype = 1;
278  ac->xtype = get_xtype<chol_elt> ();
279 
280  if (a_nr < 1)
281  ac->x = &dummy;
282  else
283  ac->x = a.data ();
284 
285  // use natural ordering if no q output parameter
286  if (natural)
287  {
288  cm->nmethods = 1;
289  cm->method[0].ordering = CHOLMOD_NATURAL;
290  cm->postorder = false;
291  }
292 
293  cholmod_factor *Lfactor = CHOLMOD_NAME(analyze) (ac, cm);
294  CHOLMOD_NAME(factorize) (ac, Lfactor, cm);
295 
296  m_is_pd = cm->status == CHOLMOD_OK;
297  info = (m_is_pd ? 0 : cm->status);
298 
299  if (m_is_pd || force)
300  {
301  m_rcond = CHOLMOD_NAME(rcond) (Lfactor, cm);
302 
303  m_minor_p = Lfactor->minor;
304 
305  m_L = CHOLMOD_NAME(factor_to_sparse) (Lfactor, cm);
306 
307  if (m_minor_p > 0 && m_minor_p < a_nr)
308  {
309  std::size_t n1 = a_nr + 1;
310  m_L->p = CHOLMOD_NAME(realloc) (m_minor_p+1,
311  sizeof(octave_idx_type),
312  m_L->p, &n1, cm);
313 
314  CHOLMOD_NAME(reallocate_sparse)
315  (static_cast<octave_idx_type *>(m_L->p)[m_minor_p],
316  m_L, cm);
317 
318  m_L->ncol = m_minor_p;
319  }
320 
321  drop_zeros (m_L);
322 
323  if (! natural)
324  {
325  m_perm.resize (a_nr);
326  for (octave_idx_type i = 0; i < a_nr; i++)
327  m_perm(i) = static_cast<octave_idx_type *>(Lfactor->Perm)[i];
328  }
329  }
330 
331  // NAME used to prefix statistics report from print_common
332  static char blank_name[] = " ";
333 
334  CHOLMOD_NAME(print_common) (blank_name, cm);
335  CHOLMOD_NAME(free_factor) (&Lfactor, cm);
336 
337  return info;
338 
339 #else
340 
341  octave_unused_parameter (a);
342  octave_unused_parameter (natural);
343  octave_unused_parameter (force);
344 
345  (*current_liboctave_error_handler)
346  ("support for CHOLMOD was unavailable or disabled when liboctave was built");
347 
348  return info;
349 
350 #endif
351 }
352 
353 template <typename chol_type>
356 {
357 #if defined (HAVE_CHOLMOD)
358 
359  octave_idx_type n = m_L->nrow;
360  SparseMatrix p (n, n, n);
361 
362  for (octave_idx_type i = 0; i < n; i++)
363  {
364  p.xcidx (i) = i;
365  p.xridx (i) = static_cast<octave_idx_type> (m_perm (i));
366  p.xdata (i) = 1;
367  }
368 
369  p.xcidx (n) = n;
370 
371  return p;
372 
373 #else
374 
375  return SparseMatrix ();
376 
377 #endif
378 }
379 
380 template <typename chol_type>
382  : m_rep (new typename sparse_chol<chol_type>::sparse_chol_rep ())
383 { }
384 
385 template <typename chol_type>
386 sparse_chol<chol_type>::sparse_chol (const chol_type& a, bool natural,
387  bool force)
388  : m_rep (new typename
389  sparse_chol<chol_type>::sparse_chol_rep (a, natural, force))
390 { }
391 
392 template <typename chol_type>
394  octave_idx_type& info,
395  bool natural, bool force)
396  : m_rep (new typename
397  sparse_chol<chol_type>::sparse_chol_rep (a, info, natural, force))
398 { }
399 
400 template <typename chol_type>
402  octave_idx_type& info,
403  bool natural)
404  : m_rep (new typename
405  sparse_chol<chol_type>::sparse_chol_rep (a, info, natural, false))
406 { }
407 
408 template <typename chol_type>
410  octave_idx_type& info)
411  : m_rep (new typename
412  sparse_chol<chol_type>::sparse_chol_rep (a, info, false, false))
413 { }
414 
415 template <typename chol_type>
416 chol_type
418 {
419 #if defined (HAVE_CHOLMOD)
420 
421  cholmod_sparse *m = m_rep->L ();
422 
423  octave_idx_type nc = m->ncol;
424  octave_idx_type nnz = m->nzmax;
425 
426  chol_type ret (m->nrow, nc, nnz);
427 
428  for (octave_idx_type j = 0; j < nc+1; j++)
429  ret.xcidx (j) = static_cast<octave_idx_type *>(m->p)[j];
430 
431  for (octave_idx_type i = 0; i < nnz; i++)
432  {
433  ret.xridx (i) = static_cast<octave_idx_type *>(m->i)[i];
434  ret.xdata (i) = static_cast<chol_elt *>(m->x)[i];
435  }
436 
437  return ret;
438 
439 #else
440 
441  return chol_type ();
442 
443 #endif
444 }
445 
446 template <typename chol_type>
449 {
450  return m_rep->P ();
451 }
452 
453 template <typename chol_type>
454 RowVector
456 {
457  return m_rep->perm ();
458 }
459 
460 template <typename chol_type>
463 {
464  return m_rep->Q ();
465 }
466 
467 template <typename chol_type>
468 bool
470 {
471  return m_rep->is_positive_definite ();
472 }
473 
474 template <typename chol_type>
475 double
477 {
478  return m_rep->rcond ();
479 }
480 
481 template <typename chol_type>
482 chol_type
484 {
485  chol_type retval;
486 
487 #if defined (HAVE_CHOLMOD)
488 
489  cholmod_sparse *m = m_rep->L ();
490  octave_idx_type n = m->ncol;
491  RowVector m_perm = m_rep->perm ();
492  double rcond2;
493  octave_idx_type info;
494  MatrixType mattype (MatrixType::Upper);
495  chol_type linv = L ().hermitian ().inverse (mattype, info, rcond2, 1, 0);
496 
497  if (m_perm.numel () == n)
498  {
499  SparseMatrix Qc = Q ();
500 
501  retval = Qc * linv * linv.hermitian () * Qc.transpose ();
502  }
503  else
504  retval = linv * linv.hermitian ();
505 
506 #endif
507 
508  return retval;
509 }
510 
511 template <typename chol_type>
512 chol_type
513 chol2inv (const chol_type& r)
514 {
515  octave_idx_type r_nr = r.rows ();
516  octave_idx_type r_nc = r.cols ();
517  chol_type retval;
518 
519  if (r_nr != r_nc)
520  (*current_liboctave_error_handler) ("U must be a square matrix");
521 
522  MatrixType mattype (r);
523  int typ = mattype.type (false);
524  double rcond;
525  octave_idx_type info;
526  chol_type rtra, multip;
527 
528  if (typ == MatrixType::Upper)
529  {
530  rtra = r.transpose ();
531  multip = (rtra*r);
532  }
533  else if (typ == MatrixType::Lower)
534  {
535  rtra = r.transpose ();
536  multip = (r*rtra);
537  }
538  else
539  (*current_liboctave_error_handler) ("U must be a triangular matrix");
540 
541  MatrixType mattypenew (multip);
542  retval = multip.inverse (mattypenew, info, rcond, true, false);
543  return retval;
544 }
545 
546 // SparseComplexMatrix specialization (the value for the NATURAL
547 // parameter in the sparse_chol<T>::sparse_chol_rep constructor is
548 // different from the default).
549 
550 template <>
553  octave_idx_type& info)
554  : m_rep (new sparse_chol<SparseComplexMatrix>::sparse_chol_rep (a, info,
555  true,
556  false))
557 { }
558 
559 // Instantiations we need.
560 
562 
563 template class sparse_chol<SparseComplexMatrix>;
564 
567 
570 
OCTAVE_END_NAMESPACE(octave)
OCTARRAY_OVERRIDABLE_FUNC_API octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:414
SparseMatrix transpose(void) const
Definition: dSparse.h:124
SparseMatrix hermitian(void) const
Definition: dSparse.h:128
T * xdata(void)
Definition: Sparse.h:576
octave_idx_type * xcidx(void)
Definition: Sparse.h:602
octave_idx_type * xridx(void)
Definition: Sparse.h:589
octave_idx_type init(const chol_type &a, bool natural, bool force)
Definition: sparse-chol.cc:209
sparse_chol_rep(const chol_type &a, octave_idx_type &info, bool natural, bool force)
Definition: sparse-chol.cc:69
SparseMatrix Q(void) const
Definition: sparse-chol.cc:355
void drop_zeros(const cholmod_sparse *S)
Definition: sparse-chol.cc:148
octave_idx_type P(void) const
Definition: sparse-chol.cc:102
bool is_positive_definite(void) const
Definition: sparse-chol.cc:116
cholmod_sparse * L(void) const
Definition: sparse-chol.cc:96
sparse_chol_rep(const sparse_chol_rep &)=delete
double rcond(void) const
Definition: sparse-chol.cc:118
RowVector perm(void) const
Definition: sparse-chol.cc:112
sparse_chol_rep(const chol_type &a, bool natural, bool force)
Definition: sparse-chol.cc:60
RowVector perm(void) const
Definition: sparse-chol.cc:455
sparse_chol< chol_type > & operator=(const sparse_chol< chol_type > &a)=default
SparseMatrix Q(void) const
Definition: sparse-chol.cc:462
chol_type L(void) const
Definition: sparse-chol.cc:417
std::shared_ptr< sparse_chol_rep > m_rep
Definition: sparse-chol.h:91
double rcond(void) const
Definition: sparse-chol.cc:476
bool is_positive_definite(void) const
Definition: sparse-chol.cc:469
chol_type inverse(void) const
Definition: sparse-chol.cc:483
chol_type::element_type chol_elt
Definition: sparse-chol.h:89
sparse_chol(void)
Definition: sparse-chol.cc:381
octave_idx_type P(void) const
Definition: sparse-chol.cc:448
static double get_key(const std::string &key)
Definition: oct-spparms.cc:87
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
OCTAVE_NORETURN liboctave_error_handler current_liboctave_error_handler
Definition: lo-error.c:41
F77_RET_T const F77_INT const F77_INT const F77_INT F77_DBLE const F77_INT F77_DBLE const F77_INT F77_DBLE * Q
F77_RET_T const F77_INT F77_CMPLX * A
#define OCTAVE_API
Definition: main.in.cc:55
class OCTAVE_API SparseMatrix
Definition: mx-fwd.h:55
T octave_idx_type m
Definition: mx-inlines.cc:773
return ac
Definition: mx-inlines.cc:753
octave_idx_type n
Definition: mx-inlines.cc:753
T * r
Definition: mx-inlines.cc:773
#define CHOLMOD_NAME(name)
Definition: oct-sparse.h:140
template OCTAVE_API SparseMatrix chol2inv< SparseMatrix >(const SparseMatrix &r)
int get_xtype(void)
int get_xtype< Complex >(void)
Definition: sparse-chol.cc:200
int get_xtype< double >(void)
Definition: sparse-chol.cc:193
chol_type chol2inv(const chol_type &r)
Definition: sparse-chol.cc:513
template OCTAVE_API SparseComplexMatrix chol2inv< SparseComplexMatrix >(const SparseComplexMatrix &r)
void SparseCholError(int status, char *file, int line, char *message)
Definition: sparse-util.cc:64
int SparseCholPrint(const char *fmt,...)
Definition: sparse-util.cc:76