3Copyright (C) 2005, 2006, 2007, 2008 David Bateman
5This file is part of Octave.
7Octave is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by the
9Free Software Foundation; either version 3 of the License, or (at your
10option) any later version.
12Octave is distributed in the hope that it will be useful, but WITHOUT
13ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17You should have received a copy of the GNU General Public License
18along with Octave; see the file COPYING. If not, see
19<http://www.gnu.org/licenses/>.
23// This is the octave interface to ccolamd, which bore the copyright given
24// in the help of the functions.
40#include "ov-re-sparse.h"
41#include "ov-cx-sparse.h"
43#include "oct-sparse.h"
44#include "oct-locbuf.h"
47#define CCOLAMD_NAME(name) ccolamd_l ## name
48#define CSYMAMD_NAME(name) csymamd_l ## name
50#define CCOLAMD_NAME(name) ccolamd ## name
51#define CSYMAMD_NAME(name) csymamd ## name
54DEFUN_DLD (ccolamd, args, nargout,
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\
61Constrained column approximate minimum degree permutation. @code{@var{p} =\n\
62ccolamd (@var{s})} returns the column approximate minimum degree permutation\n\
63vector for the sparse matrix @var{s}. For a non-symmetric matrix @var{s},\n\
64@code{@var{s} (:, @var{p})} tends to have sparser LU factors than @var{s}.\n\
65@code{chol (@var{s} (:, @var{p})' * @var{s} (:, @var{p}))} also tends to be\n\
66sparser than @code{chol (@var{s}' * @var{s})}. @code{@var{p} = ccolamd\n\
67(@var{s}, 1)} optimizes the ordering for @code{lu (@var{s} (:, @var{p}))}.\n\
68The ordering is followed by a column elimination tree post-ordering.\n\
70@var{knobs} is an optional one- to five-element input vector, with a default\n\
71value of @code{[0 10 10 1 0]} if not present or empty. Entries not present\n\
72are set to their defaults.\n\
75@item @var{knobs}(1)\n\
76if nonzero, the ordering is optimized for @code{lu (S (:, p))}. It will be a\n\
77poor ordering for @code{chol (@var{s} (:, @var{p})' * @var{s} (:,\n\
78@var{p}))}. This is the most important knob for ccolamd.\n\
81if @var{s} is m-by-n, rows with more than @code{max (16, @var{knobs} (2) *\n\
82sqrt (n))} entries are ignored.\n\
85columns with more than @code{max (16, @var{knobs} (3) * sqrt (min (@var{m},\n\
86@var{n})))} entries are ignored and ordered last in the output permutation\n\
87(subject to the cmember constraints).\n\
90if nonzero, aggressive absorption is performed.\n\
93if nonzero, statistics and knobs are printed.\n\
97@var{cmember} is an optional vector of length n. It defines the constraints\n\
98on the column ordering. If @code{@var{cmember} (j) = @var{c}}, then column\n\
99@var{j} is in constraint set @var{c} (@var{c} must be in the range 1 to\n\
100@var{n}). In the output permutation @var{p}, all columns in set 1 appear\n\
101first, followed by all columns in set 2, and so on. @code{@var{cmember} =\n\
102ones(1,n)} if not present or empty. @code{ccolamd (@var{s}, [], 1 :\n\
103@var{n})} returns @code{1 : @var{n}}\n\
105@code{@var{p} = ccolamd (@var{s})} is about the same as @code{@var{p} =\n\
106colamd (@var{s})}. @var{knobs} and its default values differ. @code{colamd}\n\
107always does aggressive absorption, and it finds an ordering suitable for\n\
108both @code{lu (@var{s} (:, @var{p}))} and @code{chol (@var{S} (:, @var{p})'\n\
109* @var{s} (:, @var{p}))}; it cannot optimize its ordering for\n\
110@code{lu (@var{s} (:, @var{p}))} to the extent that\n\
111@code{ccolamd (@var{s}, 1)} can.\n\
113@var{stats} is an optional 20-element output vector that provides data\n\
114about the ordering and the validity of the input matrix @var{s}. Ordering\n\
115statistics are in @code{@var{stats} (1 : 3)}. @code{@var{stats} (1)} and\n\
116@code{@var{stats} (2)} are the number of dense or empty rows and columns\n\
117ignored by CCOLAMD and @code{@var{stats} (3)} is the number of garbage\n\
118collections performed on the internal data structure used by CCOLAMD\n\
119(roughly of size @code{2.2 * nnz (@var{s}) + 4 * @var{m} + 7 * @var{n}}\n\
122@code{@var{stats} (4 : 7)} provide information if CCOLAMD was able to\n\
123continue. The matrix is OK if @code{@var{stats} (4)} is zero, or 1 if\n\
124invalid. @code{@var{stats} (5)} is the rightmost column index that is\n\
125unsorted or contains duplicate entries, or zero if no such column exists.\n\
126@code{@var{stats} (6)} is the last seen duplicate or out-of-order row\n\
127index in the column index given by @code{@var{stats} (5)}, or zero if no\n\
128such row index exists. @code{@var{stats} (7)} is the number of duplicate\n\
129or out-of-order row indices. @code{@var{stats} (8 : 20)} is always zero in\n\
130the current version of CCOLAMD (reserved for future use).\n\
132The authors of the code itself are S. Larimore, T. Davis (Uni of Florida)\n\
133and S. Rajamanickam in collaboration with J. Bilbert and E. Ng. Supported\n\
134by the National Science Foundation (DMS-9504974, DMS-9803599, CCR-0203270),\n\
135and a grant from Sandia National Lab. See\n\
136@url{http://www.cise.ufl.edu/research/sparse} for ccolamd, csymamd, amd,\n\
137colamd, symamd, and other related orderings.\n\
138@seealso{colamd, csymamd}\n\
141 octave_value_list retval;
145 int nargin = args.length ();
148 if (nargout > 2 || nargin < 1 || nargin > 3)
149 usage ("ccolamd: incorrect number of input and/or output arguments");
153 OCTAVE_LOCAL_BUFFER (double, knobs, CCOLAMD_KNOBS);
154 CCOLAMD_NAME (_set_defaults) (knobs);
156 // Check for user-passed knobs
159 NDArray User_knobs = args(1).array_value ();
160 int nel_User_knobs = User_knobs.length ();
162 if (nel_User_knobs > 0)
163 knobs [CCOLAMD_LU] = (User_knobs (0) != 0);
164 if (nel_User_knobs > 1)
165 knobs [CCOLAMD_DENSE_ROW] = User_knobs (1);
166 if (nel_User_knobs > 2)
167 knobs [CCOLAMD_DENSE_COL] = User_knobs (2);
168 if (nel_User_knobs > 3)
169 knobs [CCOLAMD_AGGRESSIVE] = (User_knobs (3) != 0);
170 if (nel_User_knobs > 4)
171 spumoni = (User_knobs (4) != 0);
173 // print knob settings if spumoni is set
176 octave_stdout << "\nccolamd version " << CCOLAMD_MAIN_VERSION << "."
177 << CCOLAMD_SUB_VERSION << ", " << CCOLAMD_DATE
178 << ":\nknobs(1): " << User_knobs (0) << ", order for ";
179 if ( knobs [CCOLAMD_LU] != 0)
180 octave_stdout << "lu(A)\n";
182 octave_stdout << "chol(A'*A)\n";
184 if (knobs [CCOLAMD_DENSE_ROW] >= 0)
185 octave_stdout << "knobs(2): " << User_knobs (1)
186 << ", rows with > max(16,"
187 << knobs [CCOLAMD_DENSE_ROW] << "*sqrt(size(A,2)))"
188 << " entries removed\n";
190 octave_stdout << "knobs(2): " << User_knobs (1)
191 << ", no dense rows removed\n";
193 if (knobs [CCOLAMD_DENSE_COL] >= 0)
194 octave_stdout << "knobs(3): " << User_knobs (2)
195 << ", cols with > max(16,"
196 << knobs [CCOLAMD_DENSE_COL] << "*sqrt(size(A)))"
197 << " entries removed\n";
199 octave_stdout << "knobs(3): " << User_knobs (2)
200 << ", no dense columns removed\n";
202 if (knobs [CCOLAMD_AGGRESSIVE] != 0)
203 octave_stdout << "knobs(4): " << User_knobs(3)
204 << ", aggressive absorption: yes";
206 octave_stdout << "knobs(4): " << User_knobs(3)
207 << ", aggressive absorption: no";
209 octave_stdout << "knobs(5): " << User_knobs (4)
210 << ", statistics and knobs printed\n";
214 octave_idx_type n_row, n_col, nnz;
215 octave_idx_type *ridx, *cidx;
216 SparseComplexMatrix scm;
219 if (args(0).is_sparse_type ())
221 if (args(0).is_complex_type ())
223 scm = args(0). sparse_complex_matrix_value ();
232 sm = args(0).sparse_matrix_value ();
243 if (args(0).is_complex_type ())
244 sm = SparseMatrix (real (args(0).complex_matrix_value ()));
246 sm = SparseMatrix (args(0).matrix_value ());
255 // Allocate workspace for ccolamd
256 OCTAVE_LOCAL_BUFFER (octave_idx_type, p, n_col+1);
257 for (octave_idx_type i = 0; i < n_col+1; i++)
260 octave_idx_type Alen = CCOLAMD_NAME (_recommended) (nnz, n_row, n_col);
261 OCTAVE_LOCAL_BUFFER (octave_idx_type, A, Alen);
262 for (octave_idx_type i = 0; i < nnz; i++)
265 OCTAVE_LOCAL_BUFFER (octave_idx_type, stats, CCOLAMD_STATS);
269 NDArray in_cmember = args(2).array_value();
270 octave_idx_type cslen = in_cmember.length();
271 OCTAVE_LOCAL_BUFFER (octave_idx_type, cmember, cslen);
272 for (octave_idx_type i = 0; i < cslen; i++)
273 // convert cmember from 1-based to 0-based
274 cmember[i] = static_cast<octave_idx_type>(in_cmember(i) - 1);
277 error ("ccolamd: cmember must be of length equal to #cols of A");
279 // Order the columns (destroys A)
280 if (! CCOLAMD_NAME () (n_row, n_col, Alen, A, p, knobs, stats, cmember))
282 CCOLAMD_NAME (_report) (stats) ;
283 error ("ccolamd: internal error!");
289 // Order the columns (destroys A)
290 if (! CCOLAMD_NAME () (n_row, n_col, Alen, A, p, knobs, stats, 0))
292 CCOLAMD_NAME (_report) (stats) ;
293 error ("ccolamd: internal error!");
298 // return the permutation vector
299 NDArray out_perm (dim_vector (1, n_col));
300 for (octave_idx_type i = 0; i < n_col; i++)
301 out_perm(i) = p [i] + 1;
303 retval (0) = out_perm;
305 // print stats if spumoni > 0
307 CCOLAMD_NAME (_report) (stats) ;
309 // Return the stats vector
312 NDArray out_stats (dim_vector (1, CCOLAMD_STATS));
313 for (octave_idx_type i = 0 ; i < CCOLAMD_STATS ; i++)
314 out_stats (i) = stats [i] ;
315 retval(1) = out_stats;
317 // fix stats (5) and (6), for 1-based information on
318 // jumbled matrix. note that this correction doesn't
319 // occur if symamd returns FALSE
320 out_stats (CCOLAMD_INFO1) ++ ;
321 out_stats (CCOLAMD_INFO2) ++ ;
327 error ("ccolamd: not available in this version of Octave");
334DEFUN_DLD (csymamd, args, nargout,
336@deftypefn {Loadable Function} {@var{p} =} csymamd (@var{s})\n\
337@deftypefnx {Loadable Function} {@var{p} =} csymamd (@var{s}, @var{knobs})\n\
338@deftypefnx {Loadable Function} {@var{p} =} csymamd (@var{s}, @var{knobs}, @var{cmember})\n\
339@deftypefnx {Loadable Function} {[@var{p}, @var{stats}] =} csymamd (@dots{})\n\
341For a symmetric positive definite matrix @var{s}, returns the permutation\n\
342vector @var{p} such that @code{@var{s}(@var{p},@var{p})} tends to have a\n\
343sparser Cholesky factor than @var{s}. Sometimes @code{csymamd} works well\n\
344for symmetric indefinite matrices too. The matrix @var{s} is assumed to\n\
345be symmetric; only the strictly lower triangular part is referenced.\n\
346@var{s} must be square. The ordering is followed by an elimination tree\n\
349@var{knobs} is an optional one- to three-element input vector, with a\n\
350default value of @code{[10 1 0]} if present or empty. Entries not\n\
351present are set to their defaults.\n\
354@item @var{knobs}(1)\n\
355If @var{s} is n-by-n, then rows and columns with more than\n\
356@code{max(16,@var{knobs}(1)*sqrt(n))} entries are ignored, and ordered\n\
357last in the output permutation (subject to the cmember constraints).\n\
359@item @var{knobs}(2)\n\
360If nonzero, aggressive absorption is performed.\n\
362@item @var{knobs}(3)\n\
363If nonzero, statistics and knobs are printed.\n\
367@var{cmember} is an optional vector of length n. It defines the constraints\n\
368on the ordering. If @code{@var{cmember}(j) = @var{s}}, then row/column j is\n\
369in constraint set @var{c} (@var{c} must be in the range 1 to n). In the\n\
370output permutation @var{p}, rows/columns in set 1 appear first, followed\n\
371by all rows/columns in set 2, and so on. @code{@var{cmember} = ones(1,n)}\n\
372if not present or empty. @code{csymamd(@var{s},[],1:n)} returns @code{1:n}.\n\
374@code{@var{p} = csymamd(@var{s})} is about the same as @code{@var{p} =\n\
375symamd(@var{s})}. @var{knobs} and its default values differ.\n\
377@code{@var{stats} (4:7)} provide information if CCOLAMD was able to\n\
378continue. The matrix is OK if @code{@var{stats} (4)} is zero, or 1 if\n\
379invalid. @code{@var{stats} (5)} is the rightmost column index that is\n\
380unsorted or contains duplicate entries, or zero if no such column exists.\n\
381@code{@var{stats} (6)} is the last seen duplicate or out-of-order row\n\
382index in the column index given by @code{@var{stats} (5)}, or zero if no\n\
383such row index exists. @code{@var{stats} (7)} is the number of duplicate\n\
384or out-of-order row indices. @code{@var{stats} (8:20)} is always zero in\n\
385the current version of CCOLAMD (reserved for future use).\n\
387The authors of the code itself are S. Larimore, T. Davis (Uni of Florida)\n\
388and S. Rajamanickam in collaboration with J. Bilbert and E. Ng. Supported\n\
389by the National Science Foundation (DMS-9504974, DMS-9803599, CCR-0203270),\n\
390and a grant from Sandia National Lab. See\n\
391@url{http://www.cise.ufl.edu/research/sparse} for ccolamd, csymamd, amd,\n\
392colamd, symamd, and other related orderings.\n\
393@seealso{symamd, ccolamd}\n\
396 octave_value_list retval;
400 int nargin = args.length ();
403 if (nargout > 2 || nargin < 1 || nargin > 3)
404 usage ("ccolamd: incorrect number of input and/or output arguments");
408 OCTAVE_LOCAL_BUFFER (double, knobs, CCOLAMD_KNOBS);
409 CCOLAMD_NAME (_set_defaults) (knobs);
411 // Check for user-passed knobs
414 NDArray User_knobs = args(1).array_value ();
415 int nel_User_knobs = User_knobs.length ();
417 if (nel_User_knobs > 0)
418 knobs [CCOLAMD_DENSE_ROW] = User_knobs (0);
419 if (nel_User_knobs > 0)
420 knobs [CCOLAMD_AGGRESSIVE] = User_knobs (1);
421 if (nel_User_knobs > 1)
422 spumoni = static_cast<int> (User_knobs (2));
424 // print knob settings if spumoni is set
427 octave_stdout << "\ncsymamd version " << CCOLAMD_MAIN_VERSION << "."
428 << CCOLAMD_SUB_VERSION << ", " << CCOLAMD_DATE << "\n";
430 if (knobs [CCOLAMD_DENSE_ROW] >= 0)
431 octave_stdout << "knobs(1): " << User_knobs (0)
432 << ", rows/cols with > max(16,"
433 << knobs [CCOLAMD_DENSE_ROW] << "*sqrt(size(A,2)))"
434 << " entries removed\n";
436 octave_stdout << "knobs(1): " << User_knobs (0)
437 << ", no dense rows/cols removed\n";
439 if (knobs [CCOLAMD_AGGRESSIVE] != 0)
440 octave_stdout << "knobs(2): " << User_knobs(1)
441 << ", aggressive absorption: yes";
443 octave_stdout << "knobs(2): " << User_knobs(1)
444 << ", aggressive absorption: no";
447 octave_stdout << "knobs(3): " << User_knobs (2)
448 << ", statistics and knobs printed\n";
452 octave_idx_type n_row, n_col, nnz;
453 octave_idx_type *ridx, *cidx;
455 SparseComplexMatrix scm;
457 if (args(0).is_sparse_type ())
459 if (args(0).is_complex_type ())
461 scm = args(0).sparse_complex_matrix_value ();
470 sm = args(0).sparse_matrix_value ();
480 if (args(0).is_complex_type ())
481 sm = SparseMatrix (real (args(0).complex_matrix_value ()));
483 sm = SparseMatrix (args(0).matrix_value ());
494 error ("symamd: matrix must be square");
498 // Allocate workspace for symamd
499 OCTAVE_LOCAL_BUFFER (octave_idx_type, perm, n_col+1);
500 OCTAVE_LOCAL_BUFFER (octave_idx_type, stats, CCOLAMD_STATS);
504 NDArray in_cmember = args(2).array_value();
505 octave_idx_type cslen = in_cmember.length();
506 OCTAVE_LOCAL_BUFFER (octave_idx_type, cmember, cslen);
507 for (octave_idx_type i = 0; i < cslen; i++)
508 // convert cmember from 1-based to 0-based
509 cmember[i] = static_cast<octave_idx_type>(in_cmember(i) - 1);
512 error ("ccolamd: cmember must be of length equal to #cols of A");
514 if (!CSYMAMD_NAME () (n_col, ridx, cidx, perm, knobs, stats,
515 &calloc, &free, cmember, -1))
517 CSYMAMD_NAME (_report) (stats) ;
518 error ("symamd: internal error!") ;
524 if (!CSYMAMD_NAME () (n_col, ridx, cidx, perm, knobs, stats,
525 &calloc, &free, 0, -1))
527 CSYMAMD_NAME (_report) (stats) ;
528 error ("symamd: internal error!") ;
533 // return the permutation vector
534 NDArray out_perm (dim_vector (1, n_col));
535 for (octave_idx_type i = 0; i < n_col; i++)
536 out_perm(i) = perm [i] + 1;
538 retval (0) = out_perm;
540 // Return the stats vector
543 NDArray out_stats (dim_vector (1, CCOLAMD_STATS));
544 for (octave_idx_type i = 0 ; i < CCOLAMD_STATS ; i++)
545 out_stats (i) = stats [i] ;
546 retval(1) = out_stats;
548 // fix stats (5) and (6), for 1-based information on
549 // jumbled matrix. note that this correction doesn't
550 // occur if symamd returns FALSE
551 out_stats (CCOLAMD_INFO1) ++ ;
552 out_stats (CCOLAMD_INFO2) ++ ;
555 // print stats if spumoni > 0
557 CSYMAMD_NAME (_report) (stats) ;
559 // Return the stats vector
562 NDArray out_stats (dim_vector (1, CCOLAMD_STATS));
563 for (octave_idx_type i = 0 ; i < CCOLAMD_STATS ; i++)
564 out_stats (i) = stats [i] ;
565 retval(1) = out_stats;
567 // fix stats (5) and (6), for 1-based information on
568 // jumbled matrix. note that this correction doesn't
569 // occur if symamd returns FALSE
570 out_stats (CCOLAMD_INFO1) ++ ;
571 out_stats (CCOLAMD_INFO2) ++ ;
577 error ("csymamd: not available in this version of Octave");