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