GNU Octave 7.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-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 <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
51OCTAVE_NAMESPACE_BEGIN
52
53DEFUN (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
59Perform a symbolic factorization analysis of the sparse matrix @var{S}.
60
61The input variables are
62
63@table @var
64@item S
65@var{S} is a real or complex sparse matrix.
66
67@item typ
68Is the type of the factorization and can be one of
69
70@table @asis
71@item @qcode{"sym"} (default)
72Factorize @var{S}. Assumes @var{S} is symmetric and uses the upper
73triangular portion of the matrix.
74
75@item @qcode{"col"}
76Factorize @tcode{@var{S}' * @var{S}}.
77
78@item @qcode{"row"}
79Factorize @tcode{@var{S} * @var{S}'}.
80
81@item @qcode{"lo"}
82Factorize @tcode{@var{S}'}. Assumes @var{S} is symmetric and uses the lower
83triangular portion of the matrix.
84@end table
85
86@item mode
87When @var{mode} is unspecified return the Cholesky@tie{}factorization for
88@var{R}. If @var{mode} is @qcode{"lower"} or @qcode{"L"} then return
89the conjugate transpose @tcode{@var{R}'} which is a lower triangular factor.
90The conjugate transpose version is faster and uses less memory, but still
91returns the same values for all other outputs: @var{count}, @var{h},
92@var{parent}, and @var{post}.
93@end table
94
95The output variables are:
96
97@table @var
98@item count
99The row counts of the Cholesky@tie{}factorization as determined by
100@var{typ}. The computational difficulty of performing the true
101factorization using @code{chol} is @code{sum (@var{count} .^ 2)}.
102
103@item h
104The height of the elimination tree.
105
106@item parent
107The elimination tree itself.
108
109@item post
110A sparse boolean matrix whose structure is that of the
111Cholesky@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
383cleanup:
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")', 'unrecognized MODE "foobar"');
431%! fail ("symbfact (sparse ([1, 2; 3, 4; 5, 6]))", "S must be a square matrix");
432
433*/
434
435OCTAVE_NAMESPACE_END
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
SparseBoolMatrix transpose(void) const
Definition: boolSparse.h:89
octave_idx_type rows(void) const
Definition: Sparse.h:351
T * data(void)
Definition: Sparse.h:574
T * xdata(void)
Definition: Sparse.h:576
octave_idx_type nnz(void) const
Actual number of nonzero terms.
Definition: Sparse.h:339
octave_idx_type * xridx(void)
Definition: Sparse.h:589
octave_idx_type * ridx(void)
Definition: Sparse.h:583
octave_idx_type * cidx(void)
Definition: Sparse.h:596
octave_idx_type * xcidx(void)
Definition: Sparse.h:602
octave_idx_type cols(void) const
Definition: Sparse.h:352
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
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:159
OCTINTERP_API 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:980
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
void F(const TSRC *v, TRES *r, octave_idx_type m, octave_idx_type n)
Definition: mx-inlines.cc:757
int suitesparse_integer
Definition: oct-sparse.h:173
suitesparse_integer * to_suitesparse_intptr(octave_idx_type *i)
Definition: oct-sparse.cc:51
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition: oct-locbuf.h:44
#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
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:391