GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
ccolamd.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2005-2013 David Bateman
4 
5 This file is part of Octave.
6 
7 Octave is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 // This is the octave interface to ccolamd, which bore the copyright given
24 // in the help of the functions.
25 
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29 
30 #include <cstdlib>
31 
32 #include <string>
33 #include <vector>
34 
35 #include "ov.h"
36 #include "defun-dld.h"
37 #include "pager.h"
38 #include "ov-re-mat.h"
39 
40 #include "ov-re-sparse.h"
41 #include "ov-cx-sparse.h"
42 
43 #include "oct-sparse.h"
44 #include "oct-locbuf.h"
45 
46 #ifdef USE_64_BIT_IDX_T
47 #define CCOLAMD_NAME(name) ccolamd_l ## name
48 #define CSYMAMD_NAME(name) csymamd_l ## name
49 #else
50 #define CCOLAMD_NAME(name) ccolamd ## name
51 #define CSYMAMD_NAME(name) csymamd ## name
52 #endif
53 
54 DEFUN_DLD (ccolamd, args, nargout,
55  "-*- texinfo -*-\n\
56 @deftypefn {Loadable Function} {@var{p} =} ccolamd (@var{S})\n\
57 @deftypefnx {Loadable Function} {@var{p} =} ccolamd (@var{S}, @var{knobs})\n\
58 @deftypefnx {Loadable Function} {@var{p} =} ccolamd (@var{S}, @var{knobs}, @var{cmember})\n\
59 @deftypefnx {Loadable Function} {[@var{p}, @var{stats}] =} ccolamd (@dots{})\n\
60 \n\
61 Constrained column approximate minimum degree permutation.\n\
62 @code{@var{p} = ccolamd (@var{S})} returns the column approximate minimum\n\
63 degree permutation vector for the sparse matrix @var{S}. For a non-symmetric\n\
64 matrix\n\
65 @var{S},\n\
66 @code{@var{S}(:, @var{p})} tends to have sparser LU@tie{}factors than\n\
67 @var{S}. @code{chol (@var{S}(:, @var{p})' * @var{S}(:, @var{p}))} also\n\
68 tends to be sparser than @code{chol (@var{S}' * @var{S})}. @code{@var{p} =\n\
69 ccolamd (@var{S}, 1)} optimizes the ordering for @code{lu (@var{S}(:,\n\
70 @var{p}))}. The ordering is followed by a column elimination tree\n\
71 post-ordering.\n\
72 \n\
73 @var{knobs} is an optional 1-element to 5-element input vector, with a\n\
74 default value of @code{[0 10 10 1 0]} if not present or empty. Entries not\n\
75 present are set to their defaults.\n\
76 \n\
77 @table @code\n\
78 @item @var{knobs}(1)\n\
79 if nonzero, the ordering is optimized for @code{lu (S(:, p))}. It will be a\n\
80 poor ordering for @code{chol (@var{S}(:, @var{p})' * @var{S}(:,\n\
81 @var{p}))}. This is the most important knob for ccolamd.\n\
82 \n\
83 @item @var{knobs}(2)\n\
84 if @var{S} is m-by-n, rows with more than @code{max (16, @var{knobs}(2) *\n\
85 sqrt (n))} entries are ignored.\n\
86 \n\
87 @item @var{knobs}(3)\n\
88 columns with more than @code{max (16, @var{knobs}(3) * sqrt (min (@var{m},\n\
89 @var{n})))} entries are ignored and ordered last in the output permutation\n\
90 (subject to the cmember constraints).\n\
91 \n\
92 @item @var{knobs}(4)\n\
93 if nonzero, aggressive absorption is performed.\n\
94 \n\
95 @item @var{knobs}(5)\n\
96 if nonzero, statistics and knobs are printed.\n\
97 \n\
98 @end table\n\
99 \n\
100 @var{cmember} is an optional vector of length @math{n}. It defines the\n\
101 constraints on the column ordering. If @code{@var{cmember}(j) = @var{c}},\n\
102 then column @var{j} is in constraint set @var{c} (@var{c} must be in the\n\
103 range 1 to\n\
104 n). In the output permutation @var{p}, all columns in set 1 appear\n\
105 first, followed by all columns in set 2, and so on. @code{@var{cmember} =\n\
106 ones (1,n)} if not present or empty.\n\
107 @code{ccolamd (@var{S}, [], 1 : n)} returns @code{1 : n}\n\
108 \n\
109 @code{@var{p} = ccolamd (@var{S})} is about the same as\n\
110 @code{@var{p} = colamd (@var{S})}. @var{knobs} and its default values\n\
111 differ. @code{colamd} always does aggressive absorption, and it finds an\n\
112 ordering suitable for both @code{lu (@var{S}(:, @var{p}))} and @code{chol\n\
113 (@var{S}(:, @var{p})' * @var{S}(:, @var{p}))}; it cannot optimize its\n\
114 ordering for @code{lu (@var{S}(:, @var{p}))} to the extent that\n\
115 @code{ccolamd (@var{S}, 1)} can.\n\
116 \n\
117 @var{stats} is an optional 20-element output vector that provides data\n\
118 about the ordering and the validity of the input matrix @var{S}. Ordering\n\
119 statistics are in @code{@var{stats}(1 : 3)}. @code{@var{stats}(1)} and\n\
120 @code{@var{stats}(2)} are the number of dense or empty rows and columns\n\
121 ignored by @sc{ccolamd} and @code{@var{stats}(3)} is the number of garbage\n\
122 collections performed on the internal data structure used by @sc{ccolamd}\n\
123 (roughly of size @code{2.2 * nnz (@var{S}) + 4 * @var{m} + 7 * @var{n}}\n\
124 integers).\n\
125 \n\
126 @code{@var{stats}(4 : 7)} provide information if CCOLAMD was able to\n\
127 continue. The matrix is OK if @code{@var{stats}(4)} is zero, or 1 if\n\
128 invalid. @code{@var{stats}(5)} is the rightmost column index that is\n\
129 unsorted or contains duplicate entries, or zero if no such column exists.\n\
130 @code{@var{stats}(6)} is the last seen duplicate or out-of-order row\n\
131 index in the column index given by @code{@var{stats}(5)}, or zero if no\n\
132 such row index exists. @code{@var{stats}(7)} is the number of duplicate\n\
133 or out-of-order row indices. @code{@var{stats}(8 : 20)} is always zero in\n\
134 the current version of @sc{ccolamd} (reserved for future use).\n\
135 \n\
136 The authors of the code itself are S. Larimore, T. Davis (Univ. of Florida)\n\
137 and S. Rajamanickam in collaboration with J. Bilbert and E. Ng. Supported\n\
138 by the National Science Foundation\n\
139 @nospell{(DMS-9504974, DMS-9803599, CCR-0203270)}, and a grant from Sandia\n\
140 National Lab. See @url{http://www.cise.ufl.edu/research/sparse} for\n\
141 ccolamd, csymamd, amd, colamd, symamd, and other related orderings.\n\
142 @seealso{colamd, csymamd}\n\
143 @end deftypefn")
144 {
145  octave_value_list retval;
146 
147 #ifdef HAVE_CCOLAMD
148 
149  int nargin = args.length ();
150  int spumoni = 0;
151 
152  if (nargout > 2 || nargin < 1 || nargin > 3)
153  usage ("ccolamd: incorrect number of input and/or output arguments");
154  else
155  {
156  // Get knobs
157  OCTAVE_LOCAL_BUFFER (double, knobs, CCOLAMD_KNOBS);
158  CCOLAMD_NAME (_set_defaults) (knobs);
159 
160  // Check for user-passed knobs
161  if (nargin > 1)
162  {
163  NDArray User_knobs = args(1).array_value ();
164  int nel_User_knobs = User_knobs.length ();
165 
166  if (nel_User_knobs > 0)
167  knobs[CCOLAMD_LU] = (User_knobs(0) != 0);
168  if (nel_User_knobs > 1)
169  knobs[CCOLAMD_DENSE_ROW] = User_knobs(1);
170  if (nel_User_knobs > 2)
171  knobs[CCOLAMD_DENSE_COL] = User_knobs(2);
172  if (nel_User_knobs > 3)
173  knobs[CCOLAMD_AGGRESSIVE] = (User_knobs(3) != 0);
174  if (nel_User_knobs > 4)
175  spumoni = (User_knobs(4) != 0);
176 
177  // print knob settings if spumoni is set
178  if (spumoni)
179  {
180  octave_stdout << "\nccolamd version " << CCOLAMD_MAIN_VERSION << "."
181  << CCOLAMD_SUB_VERSION << ", " << CCOLAMD_DATE
182  << ":\nknobs(1): " << User_knobs(0) << ", order for ";
183  if (knobs[CCOLAMD_LU] != 0)
184  octave_stdout << "lu (A)\n";
185  else
186  octave_stdout << "chol (A'*A)\n";
187 
188  if (knobs[CCOLAMD_DENSE_ROW] >= 0)
189  octave_stdout << "knobs(2): " << User_knobs(1)
190  << ", rows with > max (16,"
191  << knobs[CCOLAMD_DENSE_ROW]
192  << "*sqrt (size(A,2)))"
193  << " entries removed\n";
194  else
195  octave_stdout << "knobs(2): " << User_knobs(1)
196  << ", no dense rows removed\n";
197 
198  if (knobs[CCOLAMD_DENSE_COL] >= 0)
199  octave_stdout << "knobs(3): " << User_knobs(2)
200  << ", cols with > max (16,"
201  << knobs[CCOLAMD_DENSE_COL] << "*sqrt (size(A)))"
202  << " entries removed\n";
203  else
204  octave_stdout << "knobs(3): " << User_knobs(2)
205  << ", no dense columns removed\n";
206 
207  if (knobs[CCOLAMD_AGGRESSIVE] != 0)
208  octave_stdout << "knobs(4): " << User_knobs(3)
209  << ", aggressive absorption: yes";
210  else
211  octave_stdout << "knobs(4): " << User_knobs(3)
212  << ", aggressive absorption: no";
213 
214  octave_stdout << "knobs(5): " << User_knobs(4)
215  << ", statistics and knobs printed\n";
216  }
217  }
218 
219  octave_idx_type n_row, n_col, nnz;
220  octave_idx_type *ridx, *cidx;
222  SparseMatrix sm;
223 
224  if (args(0).is_sparse_type ())
225  {
226  if (args(0).is_complex_type ())
227  {
228  scm = args(0). sparse_complex_matrix_value ();
229  n_row = scm.rows ();
230  n_col = scm.cols ();
231  nnz = scm.nnz ();
232  ridx = scm.xridx ();
233  cidx = scm.xcidx ();
234  }
235  else
236  {
237  sm = args(0).sparse_matrix_value ();
238 
239  n_row = sm.rows ();
240  n_col = sm.cols ();
241  nnz = sm.nnz ();
242  ridx = sm.xridx ();
243  cidx = sm.xcidx ();
244  }
245  }
246  else
247  {
248  if (args(0).is_complex_type ())
249  sm = SparseMatrix (real (args(0).complex_matrix_value ()));
250  else
251  sm = SparseMatrix (args(0).matrix_value ());
252 
253  n_row = sm.rows ();
254  n_col = sm.cols ();
255  nnz = sm.nnz ();
256  ridx = sm.xridx ();
257  cidx = sm.xcidx ();
258  }
259 
260  // Allocate workspace for ccolamd
261  OCTAVE_LOCAL_BUFFER (octave_idx_type, p, n_col+1);
262  for (octave_idx_type i = 0; i < n_col+1; i++)
263  p[i] = cidx[i];
264 
265  octave_idx_type Alen = CCOLAMD_NAME (_recommended) (nnz, n_row, n_col);
267  for (octave_idx_type i = 0; i < nnz; i++)
268  A[i] = ridx[i];
269 
270  OCTAVE_LOCAL_BUFFER (octave_idx_type, stats, CCOLAMD_STATS);
271 
272  if (nargin > 2)
273  {
274  NDArray in_cmember = args(2).array_value ();
275  octave_idx_type cslen = in_cmember.length ();
276  OCTAVE_LOCAL_BUFFER (octave_idx_type, cmember, cslen);
277  for (octave_idx_type i = 0; i < cslen; i++)
278  // convert cmember from 1-based to 0-based
279  cmember[i] = static_cast<octave_idx_type>(in_cmember(i) - 1);
280 
281  if (cslen != n_col)
282  error ("ccolamd: CMEMBER must be of length equal to #cols of A");
283  else
284  // Order the columns (destroys A)
285  if (! CCOLAMD_NAME () (n_row, n_col, Alen, A, p,
286  knobs, stats, cmember))
287  {
288  CCOLAMD_NAME (_report) (stats) ;
289  error ("ccolamd: internal error!");
290  return retval;
291  }
292  }
293  else
294  {
295  // Order the columns (destroys A)
296  if (! CCOLAMD_NAME () (n_row, n_col, Alen, A, p, knobs, stats, 0))
297  {
298  CCOLAMD_NAME (_report) (stats) ;
299  error ("ccolamd: internal error!");
300  return retval;
301  }
302  }
303 
304  // return the permutation vector
305  NDArray out_perm (dim_vector (1, n_col));
306  for (octave_idx_type i = 0; i < n_col; i++)
307  out_perm(i) = p[i] + 1;
308 
309  retval(0) = out_perm;
310 
311  // print stats if spumoni > 0
312  if (spumoni > 0)
313  CCOLAMD_NAME (_report) (stats) ;
314 
315  // Return the stats vector
316  if (nargout == 2)
317  {
318  NDArray out_stats (dim_vector (1, CCOLAMD_STATS));
319  for (octave_idx_type i = 0 ; i < CCOLAMD_STATS ; i++)
320  out_stats(i) = stats[i] ;
321  retval(1) = out_stats;
322 
323  // fix stats (5) and (6), for 1-based information on
324  // jumbled matrix. note that this correction doesn't
325  // occur if symamd returns FALSE
326  out_stats (CCOLAMD_INFO1) ++ ;
327  out_stats (CCOLAMD_INFO2) ++ ;
328  }
329  }
330 
331 #else
332 
333  error ("ccolamd: not available in this version of Octave");
334 
335 #endif
336 
337  return retval;
338 }
339 
340 DEFUN_DLD (csymamd, args, nargout,
341  "-*- texinfo -*-\n\
342 @deftypefn {Loadable Function} {@var{p} =} csymamd (@var{S})\n\
343 @deftypefnx {Loadable Function} {@var{p} =} csymamd (@var{S}, @var{knobs})\n\
344 @deftypefnx {Loadable Function} {@var{p} =} csymamd (@var{S}, @var{knobs}, @var{cmember})\n\
345 @deftypefnx {Loadable Function} {[@var{p}, @var{stats}] =} csymamd (@dots{})\n\
346 \n\
347 For a symmetric positive definite matrix @var{S}, returns the permutation\n\
348 vector @var{p} such that @code{@var{S}(@var{p},@var{p})} tends to have a\n\
349 sparser Cholesky@tie{}factor than @var{S}. Sometimes @code{csymamd} works\n\
350 well for symmetric indefinite matrices too. The matrix @var{S} is assumed\n\
351 to be symmetric; only the strictly lower triangular part is referenced.\n\
352 @var{S} must be square. The ordering is followed by an elimination tree\n\
353 post-ordering.\n\
354 \n\
355 @var{knobs} is an optional 1-element to 3-element input vector, with a\n\
356 default value of @code{[10 1 0]} if present or empty. Entries not\n\
357 present are set to their defaults.\n\
358 \n\
359 @table @code\n\
360 @item @var{knobs}(1)\n\
361 If @var{S} is n-by-n, then rows and columns with more than\n\
362 @code{max(16,@var{knobs}(1)*sqrt(n))} entries are ignored, and ordered\n\
363 last in the output permutation (subject to the cmember constraints).\n\
364 \n\
365 @item @var{knobs}(2)\n\
366 If nonzero, aggressive absorption is performed.\n\
367 \n\
368 @item @var{knobs}(3)\n\
369 If nonzero, statistics and knobs are printed.\n\
370 \n\
371 @end table\n\
372 \n\
373 @var{cmember} is an optional vector of length n. It defines the constraints\n\
374 on the ordering. If @code{@var{cmember}(j) = @var{S}}, then row/column j is\n\
375 in constraint set @var{c} (@var{c} must be in the range 1 to n). In the\n\
376 output permutation @var{p}, rows/columns in set 1 appear first, followed\n\
377 by all rows/columns in set 2, and so on. @code{@var{cmember} = ones (1,n)}\n\
378 if not present or empty. @code{csymamd (@var{S},[],1:n)} returns @code{1:n}.\n\
379 \n\
380 @code{@var{p} = csymamd (@var{S})} is about the same as @code{@var{p} =\n\
381 symamd (@var{S})}. @var{knobs} and its default values differ.\n\
382 \n\
383 @code{@var{stats}(4:7)} provide information if CCOLAMD was able to\n\
384 continue. The matrix is OK if @code{@var{stats}(4)} is zero, or 1 if\n\
385 invalid. @code{@var{stats}(5)} is the rightmost column index that is\n\
386 unsorted or contains duplicate entries, or zero if no such column exists.\n\
387 @code{@var{stats}(6)} is the last seen duplicate or out-of-order row\n\
388 index in the column index given by @code{@var{stats}(5)}, or zero if no\n\
389 such row index exists. @code{@var{stats}(7)} is the number of duplicate\n\
390 or out-of-order row indices. @code{@var{stats}(8:20)} is always zero in\n\
391 the current version of @sc{ccolamd} (reserved for future use).\n\
392 \n\
393 The authors of the code itself are S. Larimore, T. Davis (Uni of Florida)\n\
394 and S. Rajamanickam in collaboration with J. Bilbert and E. Ng. Supported\n\
395 by the National Science Foundation\n\
396 @nospell{(DMS-9504974, DMS-9803599, CCR-0203270)}, and a grant from Sandia\n\
397 National Lab. See @url{http://www.cise.ufl.edu/research/sparse} for\n\
398 ccolamd, csymamd, amd, colamd, symamd, and other related orderings.\n\
399 @seealso{symamd, ccolamd}\n\
400 @end deftypefn")
401 {
402  octave_value_list retval;
403 
404 #if HAVE_CCOLAMD
405 
406  int nargin = args.length ();
407  int spumoni = 0;
408 
409  if (nargout > 2 || nargin < 1 || nargin > 3)
410  usage ("ccolamd: incorrect number of input and/or output arguments");
411  else
412  {
413  // Get knobs
414  OCTAVE_LOCAL_BUFFER (double, knobs, CCOLAMD_KNOBS);
415  CCOLAMD_NAME (_set_defaults) (knobs);
416 
417  // Check for user-passed knobs
418  if (nargin > 1)
419  {
420  NDArray User_knobs = args(1).array_value ();
421  int nel_User_knobs = User_knobs.length ();
422 
423  if (nel_User_knobs > 0)
424  knobs[CCOLAMD_DENSE_ROW] = User_knobs(0);
425  if (nel_User_knobs > 0)
426  knobs[CCOLAMD_AGGRESSIVE] = User_knobs(1);
427  if (nel_User_knobs > 1)
428  spumoni = static_cast<int> (User_knobs(2));
429 
430  // print knob settings if spumoni is set
431  if (spumoni)
432  {
433  octave_stdout << "\ncsymamd version " << CCOLAMD_MAIN_VERSION
434  << "." << CCOLAMD_SUB_VERSION
435  << ", " << CCOLAMD_DATE << "\n";
436 
437  if (knobs[CCOLAMD_DENSE_ROW] >= 0)
438  octave_stdout << "knobs(1): " << User_knobs(0)
439  << ", rows/cols with > max (16,"
440  << knobs[CCOLAMD_DENSE_ROW]
441  << "*sqrt (size(A,2)))"
442  << " entries removed\n";
443  else
444  octave_stdout << "knobs(1): " << User_knobs(0)
445  << ", no dense rows/cols removed\n";
446 
447  if (knobs[CCOLAMD_AGGRESSIVE] != 0)
448  octave_stdout << "knobs(2): " << User_knobs(1)
449  << ", aggressive absorption: yes";
450  else
451  octave_stdout << "knobs(2): " << User_knobs(1)
452  << ", aggressive absorption: no";
453 
454 
455  octave_stdout << "knobs(3): " << User_knobs(2)
456  << ", statistics and knobs printed\n";
457  }
458  }
459 
460  octave_idx_type n_row, n_col;
461  octave_idx_type *ridx, *cidx;
462  SparseMatrix sm;
464 
465  if (args(0).is_sparse_type ())
466  {
467  if (args(0).is_complex_type ())
468  {
469  scm = args(0).sparse_complex_matrix_value ();
470  n_row = scm.rows ();
471  n_col = scm.cols ();
472  ridx = scm.xridx ();
473  cidx = scm.xcidx ();
474  }
475  else
476  {
477  sm = args(0).sparse_matrix_value ();
478  n_row = sm.rows ();
479  n_col = sm.cols ();
480  ridx = sm.xridx ();
481  cidx = sm.xcidx ();
482  }
483  }
484  else
485  {
486  if (args(0).is_complex_type ())
487  sm = SparseMatrix (real (args(0).complex_matrix_value ()));
488  else
489  sm = SparseMatrix (args(0).matrix_value ());
490 
491  n_row = sm.rows ();
492  n_col = sm.cols ();
493  ridx = sm.xridx ();
494  cidx = sm.xcidx ();
495  }
496 
497  if (n_row != n_col)
498  {
499  error ("csymamd: matrix S must be square");
500  return retval;
501  }
502 
503  // Allocate workspace for symamd
504  OCTAVE_LOCAL_BUFFER (octave_idx_type, perm, n_col+1);
505  OCTAVE_LOCAL_BUFFER (octave_idx_type, stats, CCOLAMD_STATS);
506 
507  if (nargin > 2)
508  {
509  NDArray in_cmember = args(2).array_value ();
510  octave_idx_type cslen = in_cmember.length ();
511  OCTAVE_LOCAL_BUFFER (octave_idx_type, cmember, cslen);
512  for (octave_idx_type i = 0; i < cslen; i++)
513  // convert cmember from 1-based to 0-based
514  cmember[i] = static_cast<octave_idx_type>(in_cmember(i) - 1);
515 
516  if (cslen != n_col)
517  error ("csymamd: CMEMBER must be of length equal to #cols of A");
518  else if (!CSYMAMD_NAME () (n_col, ridx, cidx, perm, knobs, stats,
519  &calloc, &free, cmember, -1))
520  {
521  CSYMAMD_NAME (_report) (stats) ;
522  error ("csymamd: internal error!") ;
523  return retval;
524  }
525  }
526  else
527  {
528  if (!CSYMAMD_NAME () (n_col, ridx, cidx, perm, knobs, stats,
529  &calloc, &free, 0, -1))
530  {
531  CSYMAMD_NAME (_report) (stats) ;
532  error ("csymamd: internal error!") ;
533  return retval;
534  }
535  }
536 
537  // return the permutation vector
538  NDArray out_perm (dim_vector (1, n_col));
539  for (octave_idx_type i = 0; i < n_col; i++)
540  out_perm(i) = perm[i] + 1;
541 
542  retval(0) = out_perm;
543 
544  // Return the stats vector
545  if (nargout == 2)
546  {
547  NDArray out_stats (dim_vector (1, CCOLAMD_STATS));
548  for (octave_idx_type i = 0 ; i < CCOLAMD_STATS ; i++)
549  out_stats(i) = stats[i] ;
550  retval(1) = out_stats;
551 
552  // fix stats (5) and (6), for 1-based information on
553  // jumbled matrix. note that this correction doesn't
554  // occur if symamd returns FALSE
555  out_stats (CCOLAMD_INFO1) ++ ;
556  out_stats (CCOLAMD_INFO2) ++ ;
557  }
558 
559  // print stats if spumoni > 0
560  if (spumoni > 0)
561  CSYMAMD_NAME (_report) (stats) ;
562 
563  // Return the stats vector
564  if (nargout == 2)
565  {
566  NDArray out_stats (dim_vector (1, CCOLAMD_STATS));
567  for (octave_idx_type i = 0 ; i < CCOLAMD_STATS ; i++)
568  out_stats(i) = stats[i] ;
569  retval(1) = out_stats;
570 
571  // fix stats (5) and (6), for 1-based information on
572  // jumbled matrix. note that this correction doesn't
573  // occur if symamd returns FALSE
574  out_stats (CCOLAMD_INFO1) ++ ;
575  out_stats (CCOLAMD_INFO2) ++ ;
576  }
577  }
578 
579 #else
580 
581  error ("csymamd: not available in this version of Octave");
582 
583 #endif
584 
585  return retval;
586 }