GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
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