GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
symbfact.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1998-2024 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 <cmath>
31 
32 #include <algorithm>
33 #include <string>
34 
35 #include "CSparse.h"
36 #include "boolSparse.h"
37 #include "dColVector.h"
38 #include "dSparse.h"
39 #include "oct-locbuf.h"
40 #include "oct-sparse.h"
41 #include "oct-spparms.h"
42 #include "sparse-util.h"
43 
44 #include "defun.h"
45 #include "error.h"
46 #include "errwarn.h"
47 #include "ovl.h"
48 #include "parse.h"
49 #include "utils.h"
50 
52 
53 DEFUN (symbfact, args, nargout,
54  doc: /* -*- texinfo -*-
55 @deftypefn {} {[@var{count}, @var{h}, @var{parent}, @var{post}, @var{R}] =} symbfact (@var{S})
56 @deftypefnx {} {[@dots{}] =} symbfact (@var{S}, @var{typ})
57 @deftypefnx {} {[@dots{}] =} symbfact (@var{S}, @var{typ}, @var{mode})
58 
59 Perform a symbolic factorization analysis of the sparse matrix @var{S}.
60 
61 The input variables are
62 
63 @table @var
64 @item S
65 @var{S} is a real or complex sparse matrix.
66 
67 @item typ
68 Is the type of the factorization and can be one of
69 
70 @table @asis
71 @item @qcode{"sym"} (default)
72 Factorize @var{S}. Assumes @var{S} is symmetric and uses the upper
73 triangular portion of the matrix.
74 
75 @item @qcode{"col"}
76 Factorize @tcode{@var{S}' * @var{S}}.
77 
78 @item @qcode{"row"}
79 Factorize @tcode{@var{S} * @var{S}'}.
80 
81 @item @qcode{"lo"}
82 Factorize @tcode{@var{S}'}. Assumes @var{S} is symmetric and uses the lower
83 triangular portion of the matrix.
84 @end table
85 
86 @item mode
87 When @var{mode} is unspecified return the Cholesky@tie{}factorization for
88 @var{R}. If @var{mode} is @qcode{"lower"} or @qcode{"L"} then return
89 the conjugate transpose @tcode{@var{R}'} which is a lower triangular factor.
90 The conjugate transpose version is faster and uses less memory, but still
91 returns the same values for all other outputs: @var{count}, @var{h},
92 @var{parent}, and @var{post}.
93 @end table
94 
95 The output variables are:
96 
97 @table @var
98 @item count
99 The row counts of the Cholesky@tie{}factorization as determined by
100 @var{typ}. The computational difficulty of performing the true
101 factorization using @code{chol} is @code{sum (@var{count} .^ 2)}.
102 
103 @item h
104 The height of the elimination tree.
105 
106 @item parent
107 The elimination tree itself.
108 
109 @item post
110 A sparse boolean matrix whose structure is that of the
111 Cholesky@tie{}factorization as determined by @var{typ}.
112 @end table
113 @seealso{chol, etree, treelayout}
114 @end deftypefn */)
115 {
116 #if defined (HAVE_CHOLMOD)
117 
118  int nargin = args.length ();
119 
120  if (nargin < 1 || nargin > 3)
121  print_usage ();
122 
123  octave_value_list retval;
124 
125  double dummy;
126  cholmod_sparse Astore;
127  cholmod_sparse *A = &Astore;
128  A->packed = true;
129  A->sorted = true;
130  A->nz = nullptr;
131 #if defined (OCTAVE_ENABLE_64)
132  A->itype = CHOLMOD_LONG;
133 #else
134  A->itype = CHOLMOD_INT;
135 #endif
136  A->dtype = CHOLMOD_DOUBLE;
137  A->stype = 1;
138  A->x = &dummy;
139 
140  SparseMatrix sm;
142 
143  if (args(0).isreal ())
144  {
145  sm = args(0).sparse_matrix_value ();
146  A->nrow = sm.rows ();
147  A->ncol = sm.cols ();
148  A->p = sm.cidx ();
149  A->i = sm.ridx ();
150  A->nzmax = sm.nnz ();
151  A->xtype = CHOLMOD_REAL;
152 
153  if (A->nrow > 0 && A->ncol > 0)
154  A->x = sm.data ();
155  }
156  else if (args(0).iscomplex ())
157  {
158  scm = args(0).sparse_complex_matrix_value ();
159  A->nrow = scm.rows ();
160  A->ncol = scm.cols ();
161  A->p = scm.cidx ();
162  A->i = scm.ridx ();
163  A->nzmax = scm.nnz ();
164  A->xtype = CHOLMOD_COMPLEX;
165 
166  if (A->nrow > 0 && A->ncol > 0)
167  A->x = scm.data ();
168  }
169  else
170  err_wrong_type_arg ("symbfact", args(0));
171 
172  bool coletree = false;
173  octave_idx_type n = A->nrow;
174 
175  if (nargin > 1)
176  {
177  std::string str = args(1).xstring_value ("TYP must be a string");
178  // FIXME: The input validation could be improved to use strncmp
179  char ch;
180  ch = tolower (str[0]);
181  if (ch == 'r') // 'row'
182  A->stype = 0;
183  else if (ch == 'c') // 'col'
184  {
185  n = A->ncol;
186  coletree = true;
187  A->stype = 0;
188  }
189  else if (ch == 's') // 'sym' (default)
190  A->stype = 1;
191  else if (ch == 'l') // 'lo'
192  A->stype = -1;
193  else
194  error (R"(symbfact: unrecognized TYP "%s")", str.c_str ());
195  }
196 
197  if (nargin == 3)
198  {
199  std::string str = args(2).xstring_value ("MODE must be a string");
200  // FIXME: The input validation could be improved to use strncmp
201  char ch;
202  ch = toupper (str[0]);
203  if (ch != 'L')
204  error (R"(symbfact: unrecognized MODE "%s")", str.c_str ());
205  }
206 
207  if (A->stype && A->nrow != A->ncol)
208  err_square_matrix_required ("symbfact", "S");
209 
215 
216  cholmod_common Common;
217  cholmod_common *cm = &Common;
218  CHOLMOD_NAME(start) (cm);
219 
220  double spu = sparse_params::get_key ("spumoni");
221  if (spu == 0.0)
222  {
223  cm->print = -1;
224  SUITESPARSE_ASSIGN_FPTR (printf_func, cm->print_function, nullptr);
225  }
226  else
227  {
228  cm->print = static_cast<int> (spu) + 2;
229  SUITESPARSE_ASSIGN_FPTR (printf_func, cm->print_function, &SparseCholPrint);
230  }
231 
232  cm->error_handler = &SparseCholError;
233  SUITESPARSE_ASSIGN_FPTR2 (divcomplex_func, cm->complex_divide, divcomplex);
234  SUITESPARSE_ASSIGN_FPTR2 (hypot_func, cm->hypotenuse, hypot);
235 
236  cholmod_sparse *F = CHOLMOD_NAME(transpose) (A, 0, cm);
237  cholmod_sparse *Aup, *Alo;
238 
239  if (A->stype == 1 || coletree)
240  {
241  Aup = A;
242  Alo = F;
243  }
244  else
245  {
246  Aup = F;
247  Alo = A;
248  }
249 
250  CHOLMOD_NAME(etree) (Aup, Parent, cm);
251 
252  ColumnVector tmp (n); // Declaration must precede any goto cleanup.
253  std::string err_msg;
254 
255  if (cm->status < CHOLMOD_OK)
256  {
257  err_msg = "symbfact: matrix corrupted";
258  goto cleanup;
259  }
260 
261  if (CHOLMOD_NAME(postorder) (Parent, n, nullptr, Post, cm) != n)
262  {
263  err_msg = "symbfact: postorder failed";
264  goto cleanup;
265  }
266 
267  CHOLMOD_NAME(rowcolcounts) (Alo, nullptr, 0, Parent, Post, nullptr, ColCount,
268  First, to_suitesparse_intptr (Level), cm);
269 
270  if (cm->status < CHOLMOD_OK)
271  {
272  err_msg = "symbfact: matrix corrupted";
273  goto cleanup;
274  }
275 
276  if (nargout > 4)
277  {
278  cholmod_sparse *A1, *A2;
279 
280  if (A->stype == 1)
281  {
282  A1 = A;
283  A2 = nullptr;
284  }
285  else if (A->stype == -1)
286  {
287  A1 = F;
288  A2 = nullptr;
289  }
290  else if (coletree)
291  {
292  A1 = F;
293  A2 = A;
294  }
295  else
296  {
297  A1 = A;
298  A2 = F;
299  }
300 
301  // count the total number of entries in L
302  octave_idx_type lnz = 0;
303  for (octave_idx_type j = 0 ; j < n ; j++)
304  lnz += ColCount[j];
305 
306  // allocate the output matrix L (pattern-only)
307  SparseBoolMatrix L (dim_vector (n, n), lnz);
308 
309  // initialize column pointers
310  lnz = 0;
311  for (octave_idx_type j = 0 ; j < n ; j++)
312  {
313  L.xcidx(j) = lnz;
314  lnz += ColCount[j];
315  }
316  L.xcidx(n) = lnz;
317 
318  // create a copy of the column pointers
319  suitesparse_integer *W = First;
320  for (octave_idx_type j = 0 ; j < n ; j++)
321  W[j] = L.xcidx (j);
322 
323  // get workspace for computing one row of L
324  cholmod_sparse *R
325  = CHOLMOD_NAME(allocate_sparse) (n, 1, n, false, true,
326  0, CHOLMOD_PATTERN, cm);
327  octave_idx_type *Rp = static_cast<octave_idx_type *> (R->p);
328  octave_idx_type *Ri = static_cast<octave_idx_type *> (R->i);
329 
330  // compute L one row at a time
331  for (octave_idx_type k = 0 ; k < n ; k++)
332  {
333  // get the kth row of L and store in the columns of L
334  CHOLMOD_NAME(row_subtree) (A1, A2, k, Parent, R, cm);
335  for (octave_idx_type p = 0 ; p < Rp[1] ; p++)
336  L.xridx (W[Ri[p]]++) = k;
337 
338  // add the diagonal entry
339  L.xridx (W[k]++) = k;
340  }
341 
342  // free workspace
343  CHOLMOD_NAME(free_sparse) (&R, cm);
344 
345  // fill L with one's
346  std::fill_n (L.xdata (), lnz, true);
347 
348  // transpose L to get R, or leave as is
349  if (nargin < 3)
350  L = L.transpose ();
351 
352  retval(4) = L;
353  }
354 
355  if (nargout > 3)
356  {
357  for (octave_idx_type i = 0; i < n; i++)
358  tmp(i) = Post[i] + 1;
359  retval(3) = tmp;
360  }
361 
362  if (nargout > 2)
363  {
364  for (octave_idx_type i = 0; i < n; i++)
365  tmp(i) = Parent[i] + 1;
366  retval(2) = tmp;
367  }
368 
369  if (nargout > 1)
370  {
371  // compute the elimination tree height
372  octave_idx_type height = 0;
373  for (int i = 0 ; i < n ; i++)
374  height = std::max (height, Level[i]);
375  height++;
376  retval(1) = static_cast<double> (height);
377  }
378 
379  for (octave_idx_type i = 0; i < n; i++)
380  tmp(i) = ColCount[i];
381  retval(0) = tmp;
382 
383 cleanup:
384  CHOLMOD_NAME(free_sparse) (&F, cm);
385  CHOLMOD_NAME(finish) (cm);
386 
387  if (! err_msg.empty ())
388  error ("%s", err_msg.c_str ());
389 
390  return retval;
391 
392 #else
393 
394  octave_unused_parameter (args);
395  octave_unused_parameter (nargout);
396 
397  err_disabled_feature ("symbfact", "CHOLMOD");
398 
399 #endif
400 }
401 
402 /*
403 %!testif HAVE_CHOLMOD
404 %! A = sparse (magic (3));
405 %! [count, h, parent, post, r] = symbfact (A);
406 %! assert (count, [3; 2; 1]);
407 %! assert (h, 3);
408 %! assert (parent, [2; 3; 0]);
409 %! assert (r, sparse (triu (true (3))));
410 
411 %!testif HAVE_CHOLMOD
412 %! ## Test MODE "lower"
413 %! A = sparse (magic (3));
414 %! [~, ~, ~, ~, l] = symbfact (A, "sym", "lower");
415 %! assert (l, sparse (tril (true (3))));
416 
417 %!testif HAVE_CHOLMOD <*42587>
418 %! ## singular matrix
419 %! A = sparse ([1 0 8;0 1 8;8 8 1]);
420 %! [count, h, parent, post, r] = symbfact (A);
421 
422 ## Test input validation
423 %!testif HAVE_CHOLMOD
424 %! fail ("symbfact ()");
425 %! fail ("symbfact (1,2,3,4)");
426 %! fail ("symbfact ({1})", "wrong type argument 'cell'");
427 %! fail ("symbfact (sparse (1), {1})", "TYP must be a string");
428 %! fail ("symbfact (sparse (1), 'foobar')", 'unrecognized TYP "foobar"');
429 %! fail ("symbfact (sparse (1), 'sym', {'L'})", "MODE must be a string");
430 %! fail ('symbfact (sparse (1), "sym", "foobar")',
431 %! 'unrecognized MODE "foobar"');
432 %! fail ("symbfact (sparse ([1, 2; 3, 4; 5, 6]))", "S must be a square matrix");
433 
434 */
435 
436 OCTAVE_END_NAMESPACE(octave)
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
SparseBoolMatrix transpose() const
Definition: boolSparse.h:91
octave_idx_type cols() const
Definition: Sparse.h:352
octave_idx_type * xcidx()
Definition: Sparse.h:602
T * xdata()
Definition: Sparse.h:576
octave_idx_type * cidx()
Definition: Sparse.h:596
T * data()
Definition: Sparse.h:574
octave_idx_type * ridx()
Definition: Sparse.h:583
octave_idx_type nnz() const
Actual number of nonzero terms.
Definition: Sparse.h:339
octave_idx_type rows() const
Definition: Sparse.h:351
octave_idx_type * xridx()
Definition: Sparse.h:589
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
static double get_key(const std::string &key)
Definition: oct-spparms.cc:95
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage(void)
Definition: defun-int.h:72
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void() error(const char *fmt,...)
Definition: error.cc:988
void err_square_matrix_required(const char *fcn, const char *name)
Definition: errwarn.cc:122
void err_disabled_feature(const std::string &fcn, const std::string &feature, const std::string &pkg)
Definition: errwarn.cc:53
void err_wrong_type_arg(const char *name, const char *s)
Definition: errwarn.cc:166
F77_RET_T const F77_INT F77_CMPLX * A
octave_idx_type n
Definition: mx-inlines.cc:761
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition: oct-locbuf.h:44
suitesparse_integer * to_suitesparse_intptr(octave_idx_type *i)
Definition: oct-sparse.cc:51
#define CHOLMOD_NAME(name)
Definition: oct-sparse.h:140
int suitesparse_integer
Definition: oct-sparse.h:184
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