GNU Octave 7.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-2022 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
44namespace octave
45{
46 namespace math
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
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
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,
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>
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;
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
564
567
570
571 }
572}
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:411
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 * xridx(void)
Definition: Sparse.h:589
octave_idx_type * xcidx(void)
Definition: Sparse.h:602
void drop_zeros(const cholmod_sparse *S)
Definition: sparse-chol.cc:148
cholmod_sparse * L(void) const
Definition: sparse-chol.cc:96
octave_idx_type init(const chol_type &a, bool natural, bool force)
Definition: sparse-chol.cc:209
sparse_chol_rep(const chol_type &a, bool natural, bool force)
Definition: sparse-chol.cc:60
sparse_chol_rep(const sparse_chol_rep &)=delete
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:462
chol_type inverse(void) const
Definition: sparse-chol.cc:483
std::shared_ptr< sparse_chol_rep > m_rep
Definition: sparse-chol.h:95
sparse_chol< chol_type > & operator=(const sparse_chol< chol_type > &a)=default
chol_type::element_type chol_elt
Definition: sparse-chol.h:89
double rcond(void) const
Definition: sparse-chol.cc:476
RowVector perm(void) const
Definition: sparse-chol.cc:455
chol_type L(void) const
Definition: sparse-chol.cc:417
octave_idx_type P(void) const
Definition: sparse-chol.cc:448
bool is_positive_definite(void) const
Definition: sparse-chol.cc:469
static double get_key(const std::string &key)
Definition: oct-spparms.cc:87
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 chol2inv(const T &r)
Definition: chol.cc:242
template OCTAVE_API SparseComplexMatrix chol2inv< SparseComplexMatrix >(const SparseComplexMatrix &r)
int get_xtype< Complex >(void)
Definition: sparse-chol.cc:200
template OCTAVE_API SparseMatrix chol2inv< SparseMatrix >(const SparseMatrix &r)
int get_xtype< double >(void)
Definition: sparse-chol.cc:193
int get_xtype(void)
#define CHOLMOD_NAME(name)
Definition: oct-sparse.h:129
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