GNU Octave  4.4.1
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) 2005-2018 David Bateman
4 Copyright (C) 1998-2005 Andy Adler
5 
6 This file is part of Octave.
7 
8 Octave is free software: you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12 
13 Octave is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with Octave; see the file COPYING. If not, see
20 <https://www.gnu.org/licenses/>.
21 
22 */
23 
24 #if defined (HAVE_CONFIG_H)
25 # include "config.h"
26 #endif
27 
28 #include <cmath>
29 
30 #include <algorithm>
31 #include <string>
32 
33 #include "CSparse.h"
34 #include "boolSparse.h"
35 #include "dColVector.h"
36 #include "dSparse.h"
37 #include "oct-locbuf.h"
38 #include "oct-sparse.h"
39 #include "oct-spparms.h"
40 #include "sparse-util.h"
41 
42 #include "defun-dld.h"
43 #include "error.h"
44 #include "errwarn.h"
45 #include "ovl.h"
46 #include "utils.h"
47 
48 DEFUN_DLD (symbfact, args, nargout,
49  doc: /* -*- texinfo -*-
50 @deftypefn {} {[@var{count}, @var{h}, @var{parent}, @var{post}, @var{R}] =} symbfact (@var{S})
51 @deftypefnx {} {[@dots{}] =} symbfact (@var{S}, @var{typ})
52 @deftypefnx {} {[@dots{}] =} symbfact (@var{S}, @var{typ}, @var{mode})
53 
54 Perform a symbolic factorization analysis of the sparse matrix @var{S}.
55 
56 The input variables are
57 
58 @table @var
59 @item S
60 @var{S} is a real or complex sparse matrix.
61 
62 @item typ
63 Is the type of the factorization and can be one of
64 
65 @table @asis
66 @item @qcode{"sym"} (default)
67 Factorize @var{S}. Assumes @var{S} is symmetric and uses the upper
68 triangular portion of the matrix.
69 
70 @item @qcode{"col"}
71 Factorize @tcode{@var{S}' * @var{S}}.
72 
73 @item @qcode{"row"}
74 Factorize @tcode{@var{S} * @var{S}'}.
75 
76 @item @qcode{"lo"}
77 Factorize @tcode{@var{S}'}. Assumes @var{S} is symmetric and uses the lower
78 triangular portion of the matrix.
79 @end table
80 
81 @item mode
82 When @var{mode} is unspecified return the Cholesky@tie{}factorization for
83 @var{R}. If @var{mode} is @qcode{"lower"} or @qcode{"L"} then return
84 the conjugate transpose @tcode{@var{R}'} which is a lower triangular factor.
85 The conjugate transpose version is faster and uses less memory, but still
86 returns the same values for all other outputs: @var{count}, @var{h},
87 @var{parent}, and @var{post}.
88 @end table
89 
90 The output variables are:
91 
92 @table @var
93 @item count
94 The row counts of the Cholesky@tie{}factorization as determined by
95 @var{typ}. The computational difficulty of performing the true
96 factorization using @code{chol} is @code{sum (@var{count} .^ 2)}.
97 
98 @item h
99 The height of the elimination tree.
100 
101 @item parent
102 The elimination tree itself.
103 
104 @item post
105 A sparse boolean matrix whose structure is that of the
106 Cholesky@tie{}factorization as determined by @var{typ}.
107 @end table
108 @seealso{chol, etree, treelayout}
109 @end deftypefn */)
110 {
111 #if defined (HAVE_CHOLMOD)
112 
113  int nargin = args.length ();
114 
116  print_usage ();
117 
119 
120  double dummy;
121  cholmod_sparse Astore;
122  cholmod_sparse *A = &Astore;
123  A->packed = true;
124  A->sorted = true;
125  A->nz = nullptr;
126 #if defined (OCTAVE_ENABLE_64)
127  A->itype = CHOLMOD_LONG;
128 #else
129  A->itype = CHOLMOD_INT;
130 #endif
131  A->dtype = CHOLMOD_DOUBLE;
132  A->stype = 1;
133  A->x = &dummy;
134 
135  if (args(0).isreal ())
136  {
137  const SparseMatrix a = args(0).sparse_matrix_value ();
138  A->nrow = a.rows ();
139  A->ncol = a.cols ();
140  A->p = a.cidx ();
141  A->i = a.ridx ();
142  A->nzmax = a.nnz ();
143  A->xtype = CHOLMOD_REAL;
144 
145  if (a.rows () > 0 && a.cols () > 0)
146  A->x = a.data ();
147  }
148  else if (args(0).iscomplex ())
149  {
150  const SparseComplexMatrix a = args(0).sparse_complex_matrix_value ();
151  A->nrow = a.rows ();
152  A->ncol = a.cols ();
153  A->p = a.cidx ();
154  A->i = a.ridx ();
155  A->nzmax = a.nnz ();
156  A->xtype = CHOLMOD_COMPLEX;
157 
158  if (a.rows () > 0 && a.cols () > 0)
159  A->x = a.data ();
160  }
161  else
162  err_wrong_type_arg ("symbfact", args(0));
163 
164  octave_idx_type coletree = false;
165  octave_idx_type n = A->nrow;
166 
167  if (nargin > 1)
168  {
169  std::string str = args(1).xstring_value ("TYP must be a string");
170  // FIXME: The input validation could be improved to use strncmp
171  char ch;
172  ch = tolower (str[0]);
173  if (ch == 'r') // 'row'
174  A->stype = 0;
175  else if (ch == 'c') // 'col'
176  {
177  n = A->ncol;
178  coletree = true;
179  A->stype = 0;
180  }
181  else if (ch == 's') // 'sym' (default)
182  A->stype = 1;
183  else if (ch == 'l') // 'lo'
184  A->stype = -1;
185  else
186  error (R"(symbfact: unrecognized TYP "%s")", str.c_str ());
187  }
188 
189  if (nargin == 3)
190  {
191  std::string str = args(2).xstring_value ("MODE must be a string");
192  // FIXME: The input validation could be improved to use strncmp
193  char ch;
194  ch = toupper (str[0]);
195  if (ch != 'L')
196  error (R"(symbfact: unrecognized MODE "%s")", str.c_str ());
197  }
198 
199  if (A->stype && A->nrow != A->ncol)
200  err_square_matrix_required ("symbfact", "S");
201 
207 
208  cholmod_common Common;
209  cholmod_common *cm = &Common;
210  CHOLMOD_NAME(start) (cm);
211 
212  double spu = octave_sparse_params::get_key ("spumoni");
213  if (spu == 0.)
214  {
215  cm->print = -1;
216  SUITESPARSE_ASSIGN_FPTR (printf_func, cm->print_function, nullptr);
217  }
218  else
219  {
220  cm->print = static_cast<int> (spu) + 2;
221  SUITESPARSE_ASSIGN_FPTR (printf_func, cm->print_function, &SparseCholPrint);
222  }
223 
224  cm->error_handler = &SparseCholError;
225  SUITESPARSE_ASSIGN_FPTR2 (divcomplex_func, cm->complex_divide, divcomplex);
226  SUITESPARSE_ASSIGN_FPTR2 (hypot_func, cm->hypotenuse, hypot);
227 
228  cholmod_sparse *F = CHOLMOD_NAME(transpose) (A, 0, cm);
229  cholmod_sparse *Aup, *Alo;
230 
231  if (A->stype == 1 || coletree)
232  {
233  Aup = A;
234  Alo = F;
235  }
236  else
237  {
238  Aup = F;
239  Alo = A;
240  }
241 
242  CHOLMOD_NAME(etree) (Aup, Parent, cm);
243 
244  ColumnVector tmp (n); // Declaration must precede any goto cleanup.
245  std::string err_msg;
246 
247  if (cm->status < CHOLMOD_OK)
248  {
249  err_msg = "symbfact: matrix corrupted";
250  goto cleanup;
251  }
252 
253  if (CHOLMOD_NAME(postorder) (Parent, n, nullptr, Post, cm) != n)
254  {
255  err_msg = "symbfact: postorder failed";
256  goto cleanup;
257  }
258 
259  CHOLMOD_NAME(rowcolcounts) (Alo, nullptr, 0, Parent, Post, nullptr, ColCount,
260  First, octave::to_suitesparse_intptr (Level), cm);
261 
262  if (cm->status < CHOLMOD_OK)
263  {
264  err_msg = "symbfact: matrix corrupted";
265  goto cleanup;
266  }
267 
268  if (nargout > 4)
269  {
270  cholmod_sparse *A1, *A2;
271 
272  if (A->stype == 1)
273  {
274  A1 = A;
275  A2 = nullptr;
276  }
277  else if (A->stype == -1)
278  {
279  A1 = F;
280  A2 = nullptr;
281  }
282  else if (coletree)
283  {
284  A1 = F;
285  A2 = A;
286  }
287  else
288  {
289  A1 = A;
290  A2 = F;
291  }
292 
293  // count the total number of entries in L
294  octave_idx_type lnz = 0;
295  for (octave_idx_type j = 0 ; j < n ; j++)
296  lnz += ColCount[j];
297 
298  // allocate the output matrix L (pattern-only)
299  SparseBoolMatrix L (dim_vector (n, n), lnz);
300 
301  // initialize column pointers
302  lnz = 0;
303  for (octave_idx_type j = 0 ; j < n ; j++)
304  {
305  L.xcidx(j) = lnz;
306  lnz += ColCount[j];
307  }
308  L.xcidx(n) = lnz;
309 
310  // create a copy of the column pointers
311  octave::suitesparse_integer *W = First;
312  for (octave_idx_type j = 0 ; j < n ; j++)
313  W[j] = L.xcidx (j);
314 
315  // get workspace for computing one row of L
316  cholmod_sparse *R
317  = CHOLMOD_NAME(allocate_sparse) (n, 1, n, false, true,
318  0, CHOLMOD_PATTERN, cm);
319  octave_idx_type *Rp = static_cast<octave_idx_type *> (R->p);
320  octave_idx_type *Ri = static_cast<octave_idx_type *> (R->i);
321 
322  // compute L one row at a time
323  for (octave_idx_type k = 0 ; k < n ; k++)
324  {
325  // get the kth row of L and store in the columns of L
326  CHOLMOD_NAME(row_subtree) (A1, A2, k, Parent, R, cm);
327  for (octave_idx_type p = 0 ; p < Rp[1] ; p++)
328  L.xridx (W[Ri[p]]++) = k;
329 
330  // add the diagonal entry
331  L.xridx (W[k]++) = k;
332  }
333 
334  // free workspace
335  CHOLMOD_NAME(free_sparse) (&R, cm);
336 
337  // fill L with one's
338  std::fill_n (L.xdata (), lnz, true);
339 
340  // transpose L to get R, or leave as is
341  if (nargin < 3)
342  L = L.transpose ();
343 
344  retval(4) = L;
345  }
346 
347  if (nargout > 3)
348  {
349  for (octave_idx_type i = 0; i < n; i++)
350  tmp(i) = Post[i] + 1;
351  retval(3) = tmp;
352  }
353 
354  if (nargout > 2)
355  {
356  for (octave_idx_type i = 0; i < n; i++)
357  tmp(i) = Parent[i] + 1;
358  retval(2) = tmp;
359  }
360 
361  if (nargout > 1)
362  {
363  // compute the elimination tree height
364  octave_idx_type height = 0;
365  for (int i = 0 ; i < n ; i++)
366  height = std::max (height, Level[i]);
367  height++;
368  retval(1) = static_cast<double> (height);
369  }
370 
371  for (octave_idx_type i = 0; i < n; i++)
372  tmp(i) = ColCount[i];
373  retval(0) = tmp;
374 
375 cleanup:
376  CHOLMOD_NAME(free_sparse) (&F, cm);
377  CHOLMOD_NAME(finish) (cm);
378 
379  if (! err_msg.empty ())
380  error (err_msg.c_str ());
381 
382  return retval;
383 
384 #else
385 
386  octave_unused_parameter (args);
387  octave_unused_parameter (nargout);
388 
389  err_disabled_feature ("symbfact", "CHOLMOD");
390 
391 #endif
392 }
393 
394 /*
395 %!testif HAVE_CHOLMOD
396 %! A = sparse (magic (3));
397 %! [count, h, parent, post, r] = symbfact (A);
398 %! assert (count, [3; 2; 1]);
399 %! assert (h, 3);
400 %! assert (parent, [2; 3; 0]);
401 %! assert (r, sparse (triu (true (3))));
402 
403 %!testif HAVE_CHOLMOD
404 %! ## Test MODE "lower"
405 %! A = sparse (magic (3));
406 %! [~, ~, ~, ~, l] = symbfact (A, "sym", "lower");
407 %! assert (l, sparse (tril (true (3))));
408 
409 %!testif HAVE_CHOLMOD <*42587>
410 %! ## singular matrix
411 %! A = sparse ([1 0 8;0 1 8;8 8 1]);
412 %! [count, h, parent, post, r] = symbfact (A);
413 
414 ## Test input validation
415 %!testif HAVE_CHOLMOD
416 %! fail ("symbfact ()");
417 %! fail ("symbfact (1,2,3,4)");
418 %! fail ("symbfact ({1})", "wrong type argument 'cell'");
419 %! fail ("symbfact (sparse (1), {1})", "TYP must be a string");
420 %! fail ("symbfact (sparse (1), 'foobar')", 'unrecognized TYP "foobar"');
421 %! fail ("symbfact (sparse (1), 'sym', {'L'})", "MODE must be a string");
422 %! fail ('symbfact (sparse (1), "sym", "foobar")', 'unrecognized MODE "foobar"');
423 %! fail ("symbfact (sparse ([1, 2; 3, 4; 5, 6]))", "S must be a square matrix");
424 
425 */
OCTINTERP_API void print_usage(void)
Definition: defun.cc:54
void SparseCholError(int status, char *file, int line, char *message)
Definition: sparse-util.cc:61
for large enough k
Definition: lu.cc:617
static void transpose(octave_idx_type N, const octave_idx_type *ridx, const octave_idx_type *cidx, octave_idx_type *ridx2, octave_idx_type *cidx2)
Definition: symrcm.cc:386
void error(const char *fmt,...)
Definition: error.cc:578
void err_square_matrix_required(const char *fcn, const char *name)
Definition: errwarn.cc:118
static void coletree(const octave_idx_type *ridx, const octave_idx_type *colbeg, octave_idx_type *colend, octave_idx_type *parent, octave_idx_type nr, octave_idx_type nc)
Definition: colamd.cc:156
static double get_key(const std::string &key)
Definition: oct-spparms.cc:97
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
F77_RET_T const F77_INT F77_CMPLX * A
OCTAVE_EXPORT octave_value_list return the number of command line arguments passed to Octave If called with the optional argument the function xample nargout(@histc)
Definition: ov-usr-fcn.cc:997
#define CHOLMOD_NAME(name)
Definition: oct-sparse.h:125
int suitesparse_integer
Definition: oct-sparse.h:168
std::string str
Definition: hash.cc:118
double tmp
Definition: data.cc:6252
octave_value retval
Definition: data.cc:6246
octave_idx_type * xridx(void)
Definition: Sparse.h:501
void err_wrong_type_arg(const char *name, const char *s)
Definition: errwarn.cc:162
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:227
int SparseCholPrint(const char *fmt,...)
Definition: sparse-util.cc:73
octave::sys::time start
Definition: graphics.cc:12337
p
Definition: lu.cc:138
T * xdata(void)
Definition: Sparse.h:488
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition: oct-locbuf.h:41
args.length() nargin
Definition: file-io.cc:589
for i
Definition: data.cc:5264
#define DEFUN_DLD(name, args_name, nargout_name, doc)
Macro to define an at run time dynamically loadable builtin function.
Definition: defun-dld.h:58
octave_idx_type * xcidx(void)
Definition: Sparse.h:514
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:87
void err_disabled_feature(const std::string &fcn, const std::string &feature, const std::string &pkg)
Definition: errwarn.cc:50
If this string is the system will ring the terminal sometimes it is useful to be able to print the original representation of the string
Definition: utils.cc:888
SparseBoolMatrix transpose(void) const
Definition: boolSparse.h:89
suitesparse_integer * to_suitesparse_intptr(octave_idx_type *i)
Definition: oct-sparse.cc:48
void F(const TSRC *v, TRES *r, octave_idx_type m, octave_idx_type n)
Definition: mx-inlines.cc:756