blob: 8e339a704a1812c368e19f445aaec7deda219eb2 [file] [log] [blame]
Brian Silverman72890c22015-09-19 14:37:37 -04001// // This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2012 Desire Nuentsa Wakam <desire.nuentsa_wakam@inria.fr>
5//
6// This Source Code Form is subject to the terms of the Mozilla
7// Public License v. 2.0. If a copy of the MPL was not distributed
8// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9
10// This file is modified from the colamd/symamd library. The copyright is below
11
12// The authors of the code itself are Stefan I. Larimore and Timothy A.
13// Davis (davis@cise.ufl.edu), University of Florida. The algorithm was
14// developed in collaboration with John Gilbert, Xerox PARC, and Esmond
15// Ng, Oak Ridge National Laboratory.
Austin Schuhc55b0172022-02-20 17:52:35 -080016//
Brian Silverman72890c22015-09-19 14:37:37 -040017// Date:
Austin Schuhc55b0172022-02-20 17:52:35 -080018//
Brian Silverman72890c22015-09-19 14:37:37 -040019// September 8, 2003. Version 2.3.
Austin Schuhc55b0172022-02-20 17:52:35 -080020//
Brian Silverman72890c22015-09-19 14:37:37 -040021// Acknowledgements:
Austin Schuhc55b0172022-02-20 17:52:35 -080022//
Brian Silverman72890c22015-09-19 14:37:37 -040023// This work was supported by the National Science Foundation, under
24// grants DMS-9504974 and DMS-9803599.
Austin Schuhc55b0172022-02-20 17:52:35 -080025//
Brian Silverman72890c22015-09-19 14:37:37 -040026// Notice:
Austin Schuhc55b0172022-02-20 17:52:35 -080027//
Brian Silverman72890c22015-09-19 14:37:37 -040028// Copyright (c) 1998-2003 by the University of Florida.
29// All Rights Reserved.
Austin Schuhc55b0172022-02-20 17:52:35 -080030//
Brian Silverman72890c22015-09-19 14:37:37 -040031// THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY
32// EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
Austin Schuhc55b0172022-02-20 17:52:35 -080033//
Brian Silverman72890c22015-09-19 14:37:37 -040034// Permission is hereby granted to use, copy, modify, and/or distribute
35// this program, provided that the Copyright, this License, and the
36// Availability of the original version is retained on all copies and made
37// accessible to the end-user of any code or package that includes COLAMD
Austin Schuhc55b0172022-02-20 17:52:35 -080038// or any modified version of COLAMD.
39//
Brian Silverman72890c22015-09-19 14:37:37 -040040// Availability:
Austin Schuhc55b0172022-02-20 17:52:35 -080041//
Brian Silverman72890c22015-09-19 14:37:37 -040042// The colamd/symamd library is available at
Austin Schuhc55b0172022-02-20 17:52:35 -080043//
Austin Schuh189376f2018-12-20 22:11:15 +110044// http://www.suitesparse.com
Brian Silverman72890c22015-09-19 14:37:37 -040045
Austin Schuhc55b0172022-02-20 17:52:35 -080046
Brian Silverman72890c22015-09-19 14:37:37 -040047#ifndef EIGEN_COLAMD_H
48#define EIGEN_COLAMD_H
49
50namespace internal {
Austin Schuhc55b0172022-02-20 17:52:35 -080051
52namespace Colamd {
53
Brian Silverman72890c22015-09-19 14:37:37 -040054/* Ensure that debugging is turned off: */
55#ifndef COLAMD_NDEBUG
56#define COLAMD_NDEBUG
57#endif /* NDEBUG */
Austin Schuhc55b0172022-02-20 17:52:35 -080058
59
Brian Silverman72890c22015-09-19 14:37:37 -040060/* ========================================================================== */
61/* === Knob and statistics definitions ====================================== */
62/* ========================================================================== */
63
64/* size of the knobs [ ] array. Only knobs [0..1] are currently used. */
Austin Schuhc55b0172022-02-20 17:52:35 -080065const int NKnobs = 20;
Brian Silverman72890c22015-09-19 14:37:37 -040066
67/* number of output statistics. Only stats [0..6] are currently used. */
Austin Schuhc55b0172022-02-20 17:52:35 -080068const int NStats = 20;
Brian Silverman72890c22015-09-19 14:37:37 -040069
Austin Schuhc55b0172022-02-20 17:52:35 -080070/* Indices into knobs and stats array. */
71enum KnobsStatsIndex {
72 /* knobs [0] and stats [0]: dense row knob and output statistic. */
73 DenseRow = 0,
Brian Silverman72890c22015-09-19 14:37:37 -040074
Austin Schuhc55b0172022-02-20 17:52:35 -080075 /* knobs [1] and stats [1]: dense column knob and output statistic. */
76 DenseCol = 1,
Brian Silverman72890c22015-09-19 14:37:37 -040077
Austin Schuhc55b0172022-02-20 17:52:35 -080078 /* stats [2]: memory defragmentation count output statistic */
79 DefragCount = 2,
Brian Silverman72890c22015-09-19 14:37:37 -040080
Austin Schuhc55b0172022-02-20 17:52:35 -080081 /* stats [3]: colamd status: zero OK, > 0 warning or notice, < 0 error */
82 Status = 3,
Brian Silverman72890c22015-09-19 14:37:37 -040083
Austin Schuhc55b0172022-02-20 17:52:35 -080084 /* stats [4..6]: error info, or info on jumbled columns */
85 Info1 = 4,
86 Info2 = 5,
87 Info3 = 6
88};
Brian Silverman72890c22015-09-19 14:37:37 -040089
90/* error codes returned in stats [3]: */
Austin Schuhc55b0172022-02-20 17:52:35 -080091enum Status {
92 Ok = 0,
93 OkButJumbled = 1,
94 ErrorANotPresent = -1,
95 ErrorPNotPresent = -2,
96 ErrorNrowNegative = -3,
97 ErrorNcolNegative = -4,
98 ErrorNnzNegative = -5,
99 ErrorP0Nonzero = -6,
100 ErrorATooSmall = -7,
101 ErrorColLengthNegative = -8,
102 ErrorRowIndexOutOfBounds = -9,
103 ErrorOutOfMemory = -10,
104 ErrorInternalError = -999
105};
Brian Silverman72890c22015-09-19 14:37:37 -0400106/* ========================================================================== */
107/* === Definitions ========================================================== */
108/* ========================================================================== */
109
Austin Schuhc55b0172022-02-20 17:52:35 -0800110template <typename IndexType>
111IndexType ones_complement(const IndexType r) {
112 return (-(r)-1);
113}
Brian Silverman72890c22015-09-19 14:37:37 -0400114
115/* -------------------------------------------------------------------------- */
Austin Schuhc55b0172022-02-20 17:52:35 -0800116const int Empty = -1;
Brian Silverman72890c22015-09-19 14:37:37 -0400117
118/* Row and column status */
Austin Schuhc55b0172022-02-20 17:52:35 -0800119enum RowColumnStatus {
120 Alive = 0,
121 Dead = -1
122};
Brian Silverman72890c22015-09-19 14:37:37 -0400123
124/* Column status */
Austin Schuhc55b0172022-02-20 17:52:35 -0800125enum ColumnStatus {
126 DeadPrincipal = -1,
127 DeadNonPrincipal = -2
128};
Brian Silverman72890c22015-09-19 14:37:37 -0400129
130/* ========================================================================== */
131/* === Colamd reporting mechanism =========================================== */
132/* ========================================================================== */
133
134// == Row and Column structures ==
Austin Schuh189376f2018-12-20 22:11:15 +1100135template <typename IndexType>
Austin Schuhc55b0172022-02-20 17:52:35 -0800136struct ColStructure
Brian Silverman72890c22015-09-19 14:37:37 -0400137{
Austin Schuhc55b0172022-02-20 17:52:35 -0800138 IndexType start ; /* index for A of first row in this column, or Dead */
Brian Silverman72890c22015-09-19 14:37:37 -0400139 /* if column is dead */
Austin Schuh189376f2018-12-20 22:11:15 +1100140 IndexType length ; /* number of rows in this column */
Brian Silverman72890c22015-09-19 14:37:37 -0400141 union
142 {
Austin Schuh189376f2018-12-20 22:11:15 +1100143 IndexType thickness ; /* number of original columns represented by this */
Brian Silverman72890c22015-09-19 14:37:37 -0400144 /* col, if the column is alive */
Austin Schuh189376f2018-12-20 22:11:15 +1100145 IndexType parent ; /* parent in parent tree super-column structure, if */
Brian Silverman72890c22015-09-19 14:37:37 -0400146 /* the column is dead */
147 } shared1 ;
148 union
149 {
Austin Schuh189376f2018-12-20 22:11:15 +1100150 IndexType score ; /* the score used to maintain heap, if col is alive */
151 IndexType order ; /* pivot ordering of this column, if col is dead */
Brian Silverman72890c22015-09-19 14:37:37 -0400152 } shared2 ;
153 union
154 {
Austin Schuh189376f2018-12-20 22:11:15 +1100155 IndexType headhash ; /* head of a hash bucket, if col is at the head of */
Brian Silverman72890c22015-09-19 14:37:37 -0400156 /* a degree list */
Austin Schuh189376f2018-12-20 22:11:15 +1100157 IndexType hash ; /* hash value, if col is not in a degree list */
158 IndexType prev ; /* previous column in degree list, if col is in a */
Brian Silverman72890c22015-09-19 14:37:37 -0400159 /* degree list (but not at the head of a degree list) */
160 } shared3 ;
161 union
162 {
Austin Schuh189376f2018-12-20 22:11:15 +1100163 IndexType degree_next ; /* next column, if col is in a degree list */
164 IndexType hash_next ; /* next column, if col is in a hash list */
Brian Silverman72890c22015-09-19 14:37:37 -0400165 } shared4 ;
Austin Schuhc55b0172022-02-20 17:52:35 -0800166
167 inline bool is_dead() const { return start < Alive; }
168
169 inline bool is_alive() const { return start >= Alive; }
170
171 inline bool is_dead_principal() const { return start == DeadPrincipal; }
172
173 inline void kill_principal() { start = DeadPrincipal; }
174
175 inline void kill_non_principal() { start = DeadNonPrincipal; }
176
Brian Silverman72890c22015-09-19 14:37:37 -0400177};
Austin Schuhc55b0172022-02-20 17:52:35 -0800178
Austin Schuh189376f2018-12-20 22:11:15 +1100179template <typename IndexType>
Austin Schuhc55b0172022-02-20 17:52:35 -0800180struct RowStructure
Brian Silverman72890c22015-09-19 14:37:37 -0400181{
Austin Schuh189376f2018-12-20 22:11:15 +1100182 IndexType start ; /* index for A of first col in this row */
183 IndexType length ; /* number of principal columns in this row */
Brian Silverman72890c22015-09-19 14:37:37 -0400184 union
185 {
Austin Schuh189376f2018-12-20 22:11:15 +1100186 IndexType degree ; /* number of principal & non-principal columns in row */
187 IndexType p ; /* used as a row pointer in init_rows_cols () */
Brian Silverman72890c22015-09-19 14:37:37 -0400188 } shared1 ;
189 union
190 {
Austin Schuh189376f2018-12-20 22:11:15 +1100191 IndexType mark ; /* for computing set differences and marking dead rows*/
192 IndexType first_column ;/* first column in row (used in garbage collection) */
Brian Silverman72890c22015-09-19 14:37:37 -0400193 } shared2 ;
Austin Schuhc55b0172022-02-20 17:52:35 -0800194
195 inline bool is_dead() const { return shared2.mark < Alive; }
196
197 inline bool is_alive() const { return shared2.mark >= Alive; }
198
199 inline void kill() { shared2.mark = Dead; }
200
Brian Silverman72890c22015-09-19 14:37:37 -0400201};
Austin Schuhc55b0172022-02-20 17:52:35 -0800202
Brian Silverman72890c22015-09-19 14:37:37 -0400203/* ========================================================================== */
204/* === Colamd recommended memory size ======================================= */
205/* ========================================================================== */
Austin Schuhc55b0172022-02-20 17:52:35 -0800206
Brian Silverman72890c22015-09-19 14:37:37 -0400207/*
208 The recommended length Alen of the array A passed to colamd is given by
209 the COLAMD_RECOMMENDED (nnz, n_row, n_col) macro. It returns -1 if any
210 argument is negative. 2*nnz space is required for the row and column
211 indices of the matrix. colamd_c (n_col) + colamd_r (n_row) space is
212 required for the Col and Row arrays, respectively, which are internal to
213 colamd. An additional n_col space is the minimal amount of "elbow room",
214 and nnz/5 more space is recommended for run time efficiency.
Austin Schuhc55b0172022-02-20 17:52:35 -0800215
Brian Silverman72890c22015-09-19 14:37:37 -0400216 This macro is not needed when using symamd.
Austin Schuhc55b0172022-02-20 17:52:35 -0800217
Austin Schuh189376f2018-12-20 22:11:15 +1100218 Explicit typecast to IndexType added Sept. 23, 2002, COLAMD version 2.2, to avoid
Brian Silverman72890c22015-09-19 14:37:37 -0400219 gcc -pedantic warning messages.
220*/
Austin Schuh189376f2018-12-20 22:11:15 +1100221template <typename IndexType>
Austin Schuhc55b0172022-02-20 17:52:35 -0800222inline IndexType colamd_c(IndexType n_col)
223{ return IndexType( ((n_col) + 1) * sizeof (ColStructure<IndexType>) / sizeof (IndexType) ) ; }
Brian Silverman72890c22015-09-19 14:37:37 -0400224
Austin Schuh189376f2018-12-20 22:11:15 +1100225template <typename IndexType>
226inline IndexType colamd_r(IndexType n_row)
Austin Schuhc55b0172022-02-20 17:52:35 -0800227{ return IndexType(((n_row) + 1) * sizeof (RowStructure<IndexType>) / sizeof (IndexType)); }
Brian Silverman72890c22015-09-19 14:37:37 -0400228
229// Prototypes of non-user callable routines
Austin Schuh189376f2018-12-20 22:11:15 +1100230template <typename IndexType>
Austin Schuhc55b0172022-02-20 17:52:35 -0800231static IndexType init_rows_cols (IndexType n_row, IndexType n_col, RowStructure<IndexType> Row [], ColStructure<IndexType> col [], IndexType A [], IndexType p [], IndexType stats[NStats] );
Brian Silverman72890c22015-09-19 14:37:37 -0400232
Austin Schuh189376f2018-12-20 22:11:15 +1100233template <typename IndexType>
Austin Schuhc55b0172022-02-20 17:52:35 -0800234static void init_scoring (IndexType n_row, IndexType n_col, RowStructure<IndexType> Row [], ColStructure<IndexType> Col [], IndexType A [], IndexType head [], double knobs[NKnobs], IndexType *p_n_row2, IndexType *p_n_col2, IndexType *p_max_deg);
Brian Silverman72890c22015-09-19 14:37:37 -0400235
Austin Schuh189376f2018-12-20 22:11:15 +1100236template <typename IndexType>
Austin Schuhc55b0172022-02-20 17:52:35 -0800237static IndexType find_ordering (IndexType n_row, IndexType n_col, IndexType Alen, RowStructure<IndexType> Row [], ColStructure<IndexType> Col [], IndexType A [], IndexType head [], IndexType n_col2, IndexType max_deg, IndexType pfree);
Brian Silverman72890c22015-09-19 14:37:37 -0400238
Austin Schuh189376f2018-12-20 22:11:15 +1100239template <typename IndexType>
Austin Schuhc55b0172022-02-20 17:52:35 -0800240static void order_children (IndexType n_col, ColStructure<IndexType> Col [], IndexType p []);
Brian Silverman72890c22015-09-19 14:37:37 -0400241
Austin Schuh189376f2018-12-20 22:11:15 +1100242template <typename IndexType>
Austin Schuhc55b0172022-02-20 17:52:35 -0800243static void detect_super_cols (ColStructure<IndexType> Col [], IndexType A [], IndexType head [], IndexType row_start, IndexType row_length ) ;
Brian Silverman72890c22015-09-19 14:37:37 -0400244
Austin Schuh189376f2018-12-20 22:11:15 +1100245template <typename IndexType>
Austin Schuhc55b0172022-02-20 17:52:35 -0800246static IndexType garbage_collection (IndexType n_row, IndexType n_col, RowStructure<IndexType> Row [], ColStructure<IndexType> Col [], IndexType A [], IndexType *pfree) ;
Brian Silverman72890c22015-09-19 14:37:37 -0400247
Austin Schuh189376f2018-12-20 22:11:15 +1100248template <typename IndexType>
Austin Schuhc55b0172022-02-20 17:52:35 -0800249static inline IndexType clear_mark (IndexType n_row, RowStructure<IndexType> Row [] ) ;
Brian Silverman72890c22015-09-19 14:37:37 -0400250
251/* === No debugging ========================================================= */
252
253#define COLAMD_DEBUG0(params) ;
254#define COLAMD_DEBUG1(params) ;
255#define COLAMD_DEBUG2(params) ;
256#define COLAMD_DEBUG3(params) ;
257#define COLAMD_DEBUG4(params) ;
258
259#define COLAMD_ASSERT(expression) ((void) 0)
260
261
262/**
Austin Schuhc55b0172022-02-20 17:52:35 -0800263 * \brief Returns the recommended value of Alen
264 *
265 * Returns recommended value of Alen for use by colamd.
266 * Returns -1 if any input argument is negative.
267 * The use of this routine or macro is optional.
268 * Note that the macro uses its arguments more than once,
269 * so be careful for side effects, if you pass expressions as arguments to COLAMD_RECOMMENDED.
270 *
Brian Silverman72890c22015-09-19 14:37:37 -0400271 * \param nnz nonzeros in A
272 * \param n_row number of rows in A
273 * \param n_col number of columns in A
274 * \return recommended value of Alen for use by colamd
275 */
Austin Schuh189376f2018-12-20 22:11:15 +1100276template <typename IndexType>
Austin Schuhc55b0172022-02-20 17:52:35 -0800277inline IndexType recommended ( IndexType nnz, IndexType n_row, IndexType n_col)
Brian Silverman72890c22015-09-19 14:37:37 -0400278{
279 if ((nnz) < 0 || (n_row) < 0 || (n_col) < 0)
280 return (-1);
281 else
Austin Schuhc55b0172022-02-20 17:52:35 -0800282 return (2 * (nnz) + colamd_c (n_col) + colamd_r (n_row) + (n_col) + ((nnz) / 5));
Brian Silverman72890c22015-09-19 14:37:37 -0400283}
284
285/**
286 * \brief set default parameters The use of this routine is optional.
Brian Silverman72890c22015-09-19 14:37:37 -0400287 *
Austin Schuhc55b0172022-02-20 17:52:35 -0800288 * Colamd: rows with more than (knobs [DenseRow] * n_col)
289 * entries are removed prior to ordering. Columns with more than
290 * (knobs [DenseCol] * n_row) entries are removed prior to
291 * ordering, and placed last in the output column ordering.
292 *
293 * DenseRow and DenseCol are defined as 0 and 1,
Brian Silverman72890c22015-09-19 14:37:37 -0400294 * respectively, in colamd.h. Default values of these two knobs
295 * are both 0.5. Currently, only knobs [0] and knobs [1] are
296 * used, but future versions may use more knobs. If so, they will
297 * be properly set to their defaults by the future version of
298 * colamd_set_defaults, so that the code that calls colamd will
299 * not need to change, assuming that you either use
300 * colamd_set_defaults, or pass a (double *) NULL pointer as the
301 * knobs array to colamd or symamd.
Austin Schuhc55b0172022-02-20 17:52:35 -0800302 *
Brian Silverman72890c22015-09-19 14:37:37 -0400303 * \param knobs parameter settings for colamd
304 */
305
Austin Schuhc55b0172022-02-20 17:52:35 -0800306static inline void set_defaults(double knobs[NKnobs])
Brian Silverman72890c22015-09-19 14:37:37 -0400307{
308 /* === Local variables ================================================== */
Austin Schuhc55b0172022-02-20 17:52:35 -0800309
Brian Silverman72890c22015-09-19 14:37:37 -0400310 int i ;
311
312 if (!knobs)
313 {
314 return ; /* no knobs to initialize */
315 }
Austin Schuhc55b0172022-02-20 17:52:35 -0800316 for (i = 0 ; i < NKnobs ; i++)
Brian Silverman72890c22015-09-19 14:37:37 -0400317 {
318 knobs [i] = 0 ;
319 }
Austin Schuhc55b0172022-02-20 17:52:35 -0800320 knobs [Colamd::DenseRow] = 0.5 ; /* ignore rows over 50% dense */
321 knobs [Colamd::DenseCol] = 0.5 ; /* ignore columns over 50% dense */
Brian Silverman72890c22015-09-19 14:37:37 -0400322}
323
Austin Schuhc55b0172022-02-20 17:52:35 -0800324/**
Brian Silverman72890c22015-09-19 14:37:37 -0400325 * \brief Computes a column ordering using the column approximate minimum degree ordering
Austin Schuhc55b0172022-02-20 17:52:35 -0800326 *
Brian Silverman72890c22015-09-19 14:37:37 -0400327 * Computes a column ordering (Q) of A such that P(AQ)=LU or
328 * (AQ)'AQ=LL' have less fill-in and require fewer floating point
329 * operations than factorizing the unpermuted matrix A or A'A,
330 * respectively.
Austin Schuhc55b0172022-02-20 17:52:35 -0800331 *
332 *
Brian Silverman72890c22015-09-19 14:37:37 -0400333 * \param n_row number of rows in A
334 * \param n_col number of columns in A
335 * \param Alen, size of the array A
336 * \param A row indices of the matrix, of size ALen
337 * \param p column pointers of A, of size n_col+1
338 * \param knobs parameter settings for colamd
339 * \param stats colamd output statistics and error codes
340 */
Austin Schuh189376f2018-12-20 22:11:15 +1100341template <typename IndexType>
Austin Schuhc55b0172022-02-20 17:52:35 -0800342static bool compute_ordering(IndexType n_row, IndexType n_col, IndexType Alen, IndexType *A, IndexType *p, double knobs[NKnobs], IndexType stats[NStats])
Brian Silverman72890c22015-09-19 14:37:37 -0400343{
344 /* === Local variables ================================================== */
Austin Schuhc55b0172022-02-20 17:52:35 -0800345
Austin Schuh189376f2018-12-20 22:11:15 +1100346 IndexType i ; /* loop index */
347 IndexType nnz ; /* nonzeros in A */
348 IndexType Row_size ; /* size of Row [], in integers */
349 IndexType Col_size ; /* size of Col [], in integers */
350 IndexType need ; /* minimum required length of A */
Austin Schuhc55b0172022-02-20 17:52:35 -0800351 Colamd::RowStructure<IndexType> *Row ; /* pointer into A of Row [0..n_row] array */
352 Colamd::ColStructure<IndexType> *Col ; /* pointer into A of Col [0..n_col] array */
Austin Schuh189376f2018-12-20 22:11:15 +1100353 IndexType n_col2 ; /* number of non-dense, non-empty columns */
354 IndexType n_row2 ; /* number of non-dense, non-empty rows */
355 IndexType ngarbage ; /* number of garbage collections performed */
356 IndexType max_deg ; /* maximum row degree */
Austin Schuhc55b0172022-02-20 17:52:35 -0800357 double default_knobs [NKnobs] ; /* default knobs array */
358
359
Brian Silverman72890c22015-09-19 14:37:37 -0400360 /* === Check the input arguments ======================================== */
Austin Schuhc55b0172022-02-20 17:52:35 -0800361
Brian Silverman72890c22015-09-19 14:37:37 -0400362 if (!stats)
363 {
364 COLAMD_DEBUG0 (("colamd: stats not present\n")) ;
365 return (false) ;
366 }
Austin Schuhc55b0172022-02-20 17:52:35 -0800367 for (i = 0 ; i < NStats ; i++)
Brian Silverman72890c22015-09-19 14:37:37 -0400368 {
369 stats [i] = 0 ;
370 }
Austin Schuhc55b0172022-02-20 17:52:35 -0800371 stats [Colamd::Status] = Colamd::Ok ;
372 stats [Colamd::Info1] = -1 ;
373 stats [Colamd::Info2] = -1 ;
374
Brian Silverman72890c22015-09-19 14:37:37 -0400375 if (!A) /* A is not present */
376 {
Austin Schuhc55b0172022-02-20 17:52:35 -0800377 stats [Colamd::Status] = Colamd::ErrorANotPresent ;
Brian Silverman72890c22015-09-19 14:37:37 -0400378 COLAMD_DEBUG0 (("colamd: A not present\n")) ;
379 return (false) ;
380 }
Austin Schuhc55b0172022-02-20 17:52:35 -0800381
Brian Silverman72890c22015-09-19 14:37:37 -0400382 if (!p) /* p is not present */
383 {
Austin Schuhc55b0172022-02-20 17:52:35 -0800384 stats [Colamd::Status] = Colamd::ErrorPNotPresent ;
Brian Silverman72890c22015-09-19 14:37:37 -0400385 COLAMD_DEBUG0 (("colamd: p not present\n")) ;
386 return (false) ;
387 }
Austin Schuhc55b0172022-02-20 17:52:35 -0800388
Brian Silverman72890c22015-09-19 14:37:37 -0400389 if (n_row < 0) /* n_row must be >= 0 */
390 {
Austin Schuhc55b0172022-02-20 17:52:35 -0800391 stats [Colamd::Status] = Colamd::ErrorNrowNegative ;
392 stats [Colamd::Info1] = n_row ;
Brian Silverman72890c22015-09-19 14:37:37 -0400393 COLAMD_DEBUG0 (("colamd: nrow negative %d\n", n_row)) ;
394 return (false) ;
395 }
Austin Schuhc55b0172022-02-20 17:52:35 -0800396
Brian Silverman72890c22015-09-19 14:37:37 -0400397 if (n_col < 0) /* n_col must be >= 0 */
398 {
Austin Schuhc55b0172022-02-20 17:52:35 -0800399 stats [Colamd::Status] = Colamd::ErrorNcolNegative ;
400 stats [Colamd::Info1] = n_col ;
Brian Silverman72890c22015-09-19 14:37:37 -0400401 COLAMD_DEBUG0 (("colamd: ncol negative %d\n", n_col)) ;
402 return (false) ;
403 }
Austin Schuhc55b0172022-02-20 17:52:35 -0800404
Brian Silverman72890c22015-09-19 14:37:37 -0400405 nnz = p [n_col] ;
406 if (nnz < 0) /* nnz must be >= 0 */
407 {
Austin Schuhc55b0172022-02-20 17:52:35 -0800408 stats [Colamd::Status] = Colamd::ErrorNnzNegative ;
409 stats [Colamd::Info1] = nnz ;
Brian Silverman72890c22015-09-19 14:37:37 -0400410 COLAMD_DEBUG0 (("colamd: number of entries negative %d\n", nnz)) ;
411 return (false) ;
412 }
Austin Schuhc55b0172022-02-20 17:52:35 -0800413
Brian Silverman72890c22015-09-19 14:37:37 -0400414 if (p [0] != 0)
415 {
Austin Schuhc55b0172022-02-20 17:52:35 -0800416 stats [Colamd::Status] = Colamd::ErrorP0Nonzero ;
417 stats [Colamd::Info1] = p [0] ;
Brian Silverman72890c22015-09-19 14:37:37 -0400418 COLAMD_DEBUG0 (("colamd: p[0] not zero %d\n", p [0])) ;
419 return (false) ;
420 }
Austin Schuhc55b0172022-02-20 17:52:35 -0800421
Brian Silverman72890c22015-09-19 14:37:37 -0400422 /* === If no knobs, set default knobs =================================== */
Austin Schuhc55b0172022-02-20 17:52:35 -0800423
Brian Silverman72890c22015-09-19 14:37:37 -0400424 if (!knobs)
425 {
Austin Schuhc55b0172022-02-20 17:52:35 -0800426 set_defaults (default_knobs) ;
Brian Silverman72890c22015-09-19 14:37:37 -0400427 knobs = default_knobs ;
428 }
Austin Schuhc55b0172022-02-20 17:52:35 -0800429
Brian Silverman72890c22015-09-19 14:37:37 -0400430 /* === Allocate the Row and Col arrays from array A ===================== */
Austin Schuhc55b0172022-02-20 17:52:35 -0800431
Brian Silverman72890c22015-09-19 14:37:37 -0400432 Col_size = colamd_c (n_col) ;
433 Row_size = colamd_r (n_row) ;
434 need = 2*nnz + n_col + Col_size + Row_size ;
Austin Schuhc55b0172022-02-20 17:52:35 -0800435
Brian Silverman72890c22015-09-19 14:37:37 -0400436 if (need > Alen)
437 {
438 /* not enough space in array A to perform the ordering */
Austin Schuhc55b0172022-02-20 17:52:35 -0800439 stats [Colamd::Status] = Colamd::ErrorATooSmall ;
440 stats [Colamd::Info1] = need ;
441 stats [Colamd::Info2] = Alen ;
Brian Silverman72890c22015-09-19 14:37:37 -0400442 COLAMD_DEBUG0 (("colamd: Need Alen >= %d, given only Alen = %d\n", need,Alen));
443 return (false) ;
444 }
Austin Schuhc55b0172022-02-20 17:52:35 -0800445
Brian Silverman72890c22015-09-19 14:37:37 -0400446 Alen -= Col_size + Row_size ;
Austin Schuhc55b0172022-02-20 17:52:35 -0800447 Col = (ColStructure<IndexType> *) &A [Alen] ;
448 Row = (RowStructure<IndexType> *) &A [Alen + Col_size] ;
Brian Silverman72890c22015-09-19 14:37:37 -0400449
450 /* === Construct the row and column data structures ===================== */
Austin Schuhc55b0172022-02-20 17:52:35 -0800451
452 if (!Colamd::init_rows_cols (n_row, n_col, Row, Col, A, p, stats))
Brian Silverman72890c22015-09-19 14:37:37 -0400453 {
454 /* input matrix is invalid */
455 COLAMD_DEBUG0 (("colamd: Matrix invalid\n")) ;
456 return (false) ;
457 }
Austin Schuhc55b0172022-02-20 17:52:35 -0800458
Brian Silverman72890c22015-09-19 14:37:37 -0400459 /* === Initialize scores, kill dense rows/columns ======================= */
460
Austin Schuhc55b0172022-02-20 17:52:35 -0800461 Colamd::init_scoring (n_row, n_col, Row, Col, A, p, knobs,
Brian Silverman72890c22015-09-19 14:37:37 -0400462 &n_row2, &n_col2, &max_deg) ;
Austin Schuhc55b0172022-02-20 17:52:35 -0800463
Brian Silverman72890c22015-09-19 14:37:37 -0400464 /* === Order the supercolumns =========================================== */
Austin Schuhc55b0172022-02-20 17:52:35 -0800465
466 ngarbage = Colamd::find_ordering (n_row, n_col, Alen, Row, Col, A, p,
Brian Silverman72890c22015-09-19 14:37:37 -0400467 n_col2, max_deg, 2*nnz) ;
Austin Schuhc55b0172022-02-20 17:52:35 -0800468
Brian Silverman72890c22015-09-19 14:37:37 -0400469 /* === Order the non-principal columns ================================== */
Austin Schuhc55b0172022-02-20 17:52:35 -0800470
471 Colamd::order_children (n_col, Col, p) ;
472
Brian Silverman72890c22015-09-19 14:37:37 -0400473 /* === Return statistics in stats ======================================= */
Austin Schuhc55b0172022-02-20 17:52:35 -0800474
475 stats [Colamd::DenseRow] = n_row - n_row2 ;
476 stats [Colamd::DenseCol] = n_col - n_col2 ;
477 stats [Colamd::DefragCount] = ngarbage ;
478 COLAMD_DEBUG0 (("colamd: done.\n")) ;
Brian Silverman72890c22015-09-19 14:37:37 -0400479 return (true) ;
480}
481
482/* ========================================================================== */
483/* === NON-USER-CALLABLE ROUTINES: ========================================== */
484/* ========================================================================== */
485
486/* There are no user-callable routines beyond this point in the file */
487
Brian Silverman72890c22015-09-19 14:37:37 -0400488/* ========================================================================== */
489/* === init_rows_cols ======================================================= */
490/* ========================================================================== */
491
492/*
493 Takes the column form of the matrix in A and creates the row form of the
494 matrix. Also, row and column attributes are stored in the Col and Row
495 structs. If the columns are un-sorted or contain duplicate row indices,
496 this routine will also sort and remove duplicate row indices from the
497 column form of the matrix. Returns false if the matrix is invalid,
498 true otherwise. Not user-callable.
499*/
Austin Schuh189376f2018-12-20 22:11:15 +1100500template <typename IndexType>
501static IndexType init_rows_cols /* returns true if OK, or false otherwise */
Brian Silverman72890c22015-09-19 14:37:37 -0400502 (
503 /* === Parameters ======================================================= */
504
Austin Schuh189376f2018-12-20 22:11:15 +1100505 IndexType n_row, /* number of rows of A */
506 IndexType n_col, /* number of columns of A */
Austin Schuhc55b0172022-02-20 17:52:35 -0800507 RowStructure<IndexType> Row [], /* of size n_row+1 */
508 ColStructure<IndexType> Col [], /* of size n_col+1 */
Austin Schuh189376f2018-12-20 22:11:15 +1100509 IndexType A [], /* row indices of A, of size Alen */
510 IndexType p [], /* pointers to columns in A, of size n_col+1 */
Austin Schuhc55b0172022-02-20 17:52:35 -0800511 IndexType stats [NStats] /* colamd statistics */
Brian Silverman72890c22015-09-19 14:37:37 -0400512 )
513{
514 /* === Local variables ================================================== */
515
Austin Schuh189376f2018-12-20 22:11:15 +1100516 IndexType col ; /* a column index */
517 IndexType row ; /* a row index */
518 IndexType *cp ; /* a column pointer */
519 IndexType *cp_end ; /* a pointer to the end of a column */
520 IndexType *rp ; /* a row pointer */
521 IndexType *rp_end ; /* a pointer to the end of a row */
522 IndexType last_row ; /* previous row */
Brian Silverman72890c22015-09-19 14:37:37 -0400523
524 /* === Initialize columns, and check column pointers ==================== */
525
526 for (col = 0 ; col < n_col ; col++)
527 {
528 Col [col].start = p [col] ;
529 Col [col].length = p [col+1] - p [col] ;
530
Austin Schuh189376f2018-12-20 22:11:15 +1100531 if ((Col [col].length) < 0) // extra parentheses to work-around gcc bug 10200
Brian Silverman72890c22015-09-19 14:37:37 -0400532 {
533 /* column pointers must be non-decreasing */
Austin Schuhc55b0172022-02-20 17:52:35 -0800534 stats [Colamd::Status] = Colamd::ErrorColLengthNegative ;
535 stats [Colamd::Info1] = col ;
536 stats [Colamd::Info2] = Col [col].length ;
Brian Silverman72890c22015-09-19 14:37:37 -0400537 COLAMD_DEBUG0 (("colamd: col %d length %d < 0\n", col, Col [col].length)) ;
538 return (false) ;
539 }
540
541 Col [col].shared1.thickness = 1 ;
542 Col [col].shared2.score = 0 ;
Austin Schuhc55b0172022-02-20 17:52:35 -0800543 Col [col].shared3.prev = Empty ;
544 Col [col].shared4.degree_next = Empty ;
Brian Silverman72890c22015-09-19 14:37:37 -0400545 }
546
547 /* p [0..n_col] no longer needed, used as "head" in subsequent routines */
548
549 /* === Scan columns, compute row degrees, and check row indices ========= */
550
Austin Schuhc55b0172022-02-20 17:52:35 -0800551 stats [Info3] = 0 ; /* number of duplicate or unsorted row indices*/
Brian Silverman72890c22015-09-19 14:37:37 -0400552
553 for (row = 0 ; row < n_row ; row++)
554 {
555 Row [row].length = 0 ;
556 Row [row].shared2.mark = -1 ;
557 }
558
559 for (col = 0 ; col < n_col ; col++)
560 {
561 last_row = -1 ;
562
563 cp = &A [p [col]] ;
564 cp_end = &A [p [col+1]] ;
565
566 while (cp < cp_end)
567 {
568 row = *cp++ ;
569
570 /* make sure row indices within range */
571 if (row < 0 || row >= n_row)
572 {
Austin Schuhc55b0172022-02-20 17:52:35 -0800573 stats [Colamd::Status] = Colamd::ErrorRowIndexOutOfBounds ;
574 stats [Colamd::Info1] = col ;
575 stats [Colamd::Info2] = row ;
576 stats [Colamd::Info3] = n_row ;
Brian Silverman72890c22015-09-19 14:37:37 -0400577 COLAMD_DEBUG0 (("colamd: row %d col %d out of bounds\n", row, col)) ;
578 return (false) ;
579 }
580
581 if (row <= last_row || Row [row].shared2.mark == col)
582 {
583 /* row index are unsorted or repeated (or both), thus col */
584 /* is jumbled. This is a notice, not an error condition. */
Austin Schuhc55b0172022-02-20 17:52:35 -0800585 stats [Colamd::Status] = Colamd::OkButJumbled ;
586 stats [Colamd::Info1] = col ;
587 stats [Colamd::Info2] = row ;
588 (stats [Colamd::Info3]) ++ ;
Brian Silverman72890c22015-09-19 14:37:37 -0400589 COLAMD_DEBUG1 (("colamd: row %d col %d unsorted/duplicate\n",row,col));
590 }
591
592 if (Row [row].shared2.mark != col)
593 {
594 Row [row].length++ ;
595 }
596 else
597 {
598 /* this is a repeated entry in the column, */
599 /* it will be removed */
600 Col [col].length-- ;
601 }
602
603 /* mark the row as having been seen in this column */
604 Row [row].shared2.mark = col ;
605
606 last_row = row ;
607 }
608 }
609
610 /* === Compute row pointers ============================================= */
611
612 /* row form of the matrix starts directly after the column */
613 /* form of matrix in A */
614 Row [0].start = p [n_col] ;
615 Row [0].shared1.p = Row [0].start ;
616 Row [0].shared2.mark = -1 ;
617 for (row = 1 ; row < n_row ; row++)
618 {
619 Row [row].start = Row [row-1].start + Row [row-1].length ;
620 Row [row].shared1.p = Row [row].start ;
621 Row [row].shared2.mark = -1 ;
622 }
623
624 /* === Create row form ================================================== */
625
Austin Schuhc55b0172022-02-20 17:52:35 -0800626 if (stats [Status] == OkButJumbled)
Brian Silverman72890c22015-09-19 14:37:37 -0400627 {
628 /* if cols jumbled, watch for repeated row indices */
629 for (col = 0 ; col < n_col ; col++)
630 {
631 cp = &A [p [col]] ;
632 cp_end = &A [p [col+1]] ;
633 while (cp < cp_end)
634 {
635 row = *cp++ ;
636 if (Row [row].shared2.mark != col)
637 {
638 A [(Row [row].shared1.p)++] = col ;
639 Row [row].shared2.mark = col ;
640 }
641 }
642 }
643 }
644 else
645 {
646 /* if cols not jumbled, we don't need the mark (this is faster) */
647 for (col = 0 ; col < n_col ; col++)
648 {
649 cp = &A [p [col]] ;
650 cp_end = &A [p [col+1]] ;
651 while (cp < cp_end)
652 {
653 A [(Row [*cp++].shared1.p)++] = col ;
654 }
655 }
656 }
657
658 /* === Clear the row marks and set row degrees ========================== */
659
660 for (row = 0 ; row < n_row ; row++)
661 {
662 Row [row].shared2.mark = 0 ;
663 Row [row].shared1.degree = Row [row].length ;
664 }
665
666 /* === See if we need to re-create columns ============================== */
667
Austin Schuhc55b0172022-02-20 17:52:35 -0800668 if (stats [Status] == OkButJumbled)
Brian Silverman72890c22015-09-19 14:37:37 -0400669 {
670 COLAMD_DEBUG0 (("colamd: reconstructing column form, matrix jumbled\n")) ;
671
672
673 /* === Compute col pointers ========================================= */
674
675 /* col form of the matrix starts at A [0]. */
676 /* Note, we may have a gap between the col form and the row */
677 /* form if there were duplicate entries, if so, it will be */
678 /* removed upon the first garbage collection */
679 Col [0].start = 0 ;
680 p [0] = Col [0].start ;
681 for (col = 1 ; col < n_col ; col++)
682 {
683 /* note that the lengths here are for pruned columns, i.e. */
684 /* no duplicate row indices will exist for these columns */
685 Col [col].start = Col [col-1].start + Col [col-1].length ;
686 p [col] = Col [col].start ;
687 }
688
689 /* === Re-create col form =========================================== */
690
691 for (row = 0 ; row < n_row ; row++)
692 {
693 rp = &A [Row [row].start] ;
694 rp_end = rp + Row [row].length ;
695 while (rp < rp_end)
696 {
697 A [(p [*rp++])++] = row ;
698 }
699 }
700 }
701
702 /* === Done. Matrix is not (or no longer) jumbled ====================== */
703
704 return (true) ;
705}
706
707
708/* ========================================================================== */
709/* === init_scoring ========================================================= */
710/* ========================================================================== */
711
712/*
713 Kills dense or empty columns and rows, calculates an initial score for
714 each column, and places all columns in the degree lists. Not user-callable.
715*/
Austin Schuh189376f2018-12-20 22:11:15 +1100716template <typename IndexType>
Brian Silverman72890c22015-09-19 14:37:37 -0400717static void init_scoring
718 (
719 /* === Parameters ======================================================= */
720
Austin Schuh189376f2018-12-20 22:11:15 +1100721 IndexType n_row, /* number of rows of A */
722 IndexType n_col, /* number of columns of A */
Austin Schuhc55b0172022-02-20 17:52:35 -0800723 RowStructure<IndexType> Row [], /* of size n_row+1 */
724 ColStructure<IndexType> Col [], /* of size n_col+1 */
Austin Schuh189376f2018-12-20 22:11:15 +1100725 IndexType A [], /* column form and row form of A */
726 IndexType head [], /* of size n_col+1 */
Austin Schuhc55b0172022-02-20 17:52:35 -0800727 double knobs [NKnobs],/* parameters */
Austin Schuh189376f2018-12-20 22:11:15 +1100728 IndexType *p_n_row2, /* number of non-dense, non-empty rows */
729 IndexType *p_n_col2, /* number of non-dense, non-empty columns */
730 IndexType *p_max_deg /* maximum row degree */
Brian Silverman72890c22015-09-19 14:37:37 -0400731 )
732{
733 /* === Local variables ================================================== */
734
Austin Schuh189376f2018-12-20 22:11:15 +1100735 IndexType c ; /* a column index */
736 IndexType r, row ; /* a row index */
737 IndexType *cp ; /* a column pointer */
738 IndexType deg ; /* degree of a row or column */
739 IndexType *cp_end ; /* a pointer to the end of a column */
740 IndexType *new_cp ; /* new column pointer */
741 IndexType col_length ; /* length of pruned column */
742 IndexType score ; /* current column score */
743 IndexType n_col2 ; /* number of non-dense, non-empty columns */
744 IndexType n_row2 ; /* number of non-dense, non-empty rows */
745 IndexType dense_row_count ; /* remove rows with more entries than this */
746 IndexType dense_col_count ; /* remove cols with more entries than this */
747 IndexType min_score ; /* smallest column score */
748 IndexType max_deg ; /* maximum row degree */
749 IndexType next_col ; /* Used to add to degree list.*/
Brian Silverman72890c22015-09-19 14:37:37 -0400750
751
752 /* === Extract knobs ==================================================== */
753
Austin Schuhc55b0172022-02-20 17:52:35 -0800754 dense_row_count = numext::maxi(IndexType(0), numext::mini(IndexType(knobs [Colamd::DenseRow] * n_col), n_col)) ;
755 dense_col_count = numext::maxi(IndexType(0), numext::mini(IndexType(knobs [Colamd::DenseCol] * n_row), n_row)) ;
Brian Silverman72890c22015-09-19 14:37:37 -0400756 COLAMD_DEBUG1 (("colamd: densecount: %d %d\n", dense_row_count, dense_col_count)) ;
757 max_deg = 0 ;
758 n_col2 = n_col ;
759 n_row2 = n_row ;
760
761 /* === Kill empty columns =============================================== */
762
763 /* Put the empty columns at the end in their natural order, so that LU */
764 /* factorization can proceed as far as possible. */
765 for (c = n_col-1 ; c >= 0 ; c--)
766 {
767 deg = Col [c].length ;
768 if (deg == 0)
769 {
770 /* this is a empty column, kill and order it last */
771 Col [c].shared2.order = --n_col2 ;
Austin Schuhc55b0172022-02-20 17:52:35 -0800772 Col[c].kill_principal() ;
Brian Silverman72890c22015-09-19 14:37:37 -0400773 }
774 }
775 COLAMD_DEBUG1 (("colamd: null columns killed: %d\n", n_col - n_col2)) ;
776
777 /* === Kill dense columns =============================================== */
778
779 /* Put the dense columns at the end, in their natural order */
780 for (c = n_col-1 ; c >= 0 ; c--)
781 {
782 /* skip any dead columns */
Austin Schuhc55b0172022-02-20 17:52:35 -0800783 if (Col[c].is_dead())
Brian Silverman72890c22015-09-19 14:37:37 -0400784 {
785 continue ;
786 }
787 deg = Col [c].length ;
788 if (deg > dense_col_count)
789 {
790 /* this is a dense column, kill and order it last */
791 Col [c].shared2.order = --n_col2 ;
792 /* decrement the row degrees */
793 cp = &A [Col [c].start] ;
794 cp_end = cp + Col [c].length ;
795 while (cp < cp_end)
796 {
797 Row [*cp++].shared1.degree-- ;
798 }
Austin Schuhc55b0172022-02-20 17:52:35 -0800799 Col[c].kill_principal() ;
Brian Silverman72890c22015-09-19 14:37:37 -0400800 }
801 }
802 COLAMD_DEBUG1 (("colamd: Dense and null columns killed: %d\n", n_col - n_col2)) ;
803
804 /* === Kill dense and empty rows ======================================== */
805
806 for (r = 0 ; r < n_row ; r++)
807 {
808 deg = Row [r].shared1.degree ;
809 COLAMD_ASSERT (deg >= 0 && deg <= n_col) ;
810 if (deg > dense_row_count || deg == 0)
811 {
812 /* kill a dense or empty row */
Austin Schuhc55b0172022-02-20 17:52:35 -0800813 Row[r].kill() ;
Brian Silverman72890c22015-09-19 14:37:37 -0400814 --n_row2 ;
815 }
816 else
817 {
818 /* keep track of max degree of remaining rows */
Austin Schuh189376f2018-12-20 22:11:15 +1100819 max_deg = numext::maxi(max_deg, deg) ;
Brian Silverman72890c22015-09-19 14:37:37 -0400820 }
821 }
822 COLAMD_DEBUG1 (("colamd: Dense and null rows killed: %d\n", n_row - n_row2)) ;
823
824 /* === Compute initial column scores ==================================== */
825
826 /* At this point the row degrees are accurate. They reflect the number */
827 /* of "live" (non-dense) columns in each row. No empty rows exist. */
828 /* Some "live" columns may contain only dead rows, however. These are */
829 /* pruned in the code below. */
830
831 /* now find the initial matlab score for each column */
832 for (c = n_col-1 ; c >= 0 ; c--)
833 {
834 /* skip dead column */
Austin Schuhc55b0172022-02-20 17:52:35 -0800835 if (Col[c].is_dead())
Brian Silverman72890c22015-09-19 14:37:37 -0400836 {
837 continue ;
838 }
839 score = 0 ;
840 cp = &A [Col [c].start] ;
841 new_cp = cp ;
842 cp_end = cp + Col [c].length ;
843 while (cp < cp_end)
844 {
845 /* get a row */
846 row = *cp++ ;
847 /* skip if dead */
Austin Schuhc55b0172022-02-20 17:52:35 -0800848 if (Row[row].is_dead())
Brian Silverman72890c22015-09-19 14:37:37 -0400849 {
850 continue ;
851 }
852 /* compact the column */
853 *new_cp++ = row ;
854 /* add row's external degree */
855 score += Row [row].shared1.degree - 1 ;
856 /* guard against integer overflow */
Austin Schuh189376f2018-12-20 22:11:15 +1100857 score = numext::mini(score, n_col) ;
Brian Silverman72890c22015-09-19 14:37:37 -0400858 }
859 /* determine pruned column length */
Austin Schuh189376f2018-12-20 22:11:15 +1100860 col_length = (IndexType) (new_cp - &A [Col [c].start]) ;
Brian Silverman72890c22015-09-19 14:37:37 -0400861 if (col_length == 0)
862 {
863 /* a newly-made null column (all rows in this col are "dense" */
864 /* and have already been killed) */
865 COLAMD_DEBUG2 (("Newly null killed: %d\n", c)) ;
866 Col [c].shared2.order = --n_col2 ;
Austin Schuhc55b0172022-02-20 17:52:35 -0800867 Col[c].kill_principal() ;
Brian Silverman72890c22015-09-19 14:37:37 -0400868 }
869 else
870 {
871 /* set column length and set score */
872 COLAMD_ASSERT (score >= 0) ;
873 COLAMD_ASSERT (score <= n_col) ;
874 Col [c].length = col_length ;
875 Col [c].shared2.score = score ;
876 }
877 }
878 COLAMD_DEBUG1 (("colamd: Dense, null, and newly-null columns killed: %d\n",
879 n_col-n_col2)) ;
880
881 /* At this point, all empty rows and columns are dead. All live columns */
882 /* are "clean" (containing no dead rows) and simplicial (no supercolumns */
883 /* yet). Rows may contain dead columns, but all live rows contain at */
884 /* least one live column. */
885
886 /* === Initialize degree lists ========================================== */
887
888
889 /* clear the hash buckets */
890 for (c = 0 ; c <= n_col ; c++)
891 {
Austin Schuhc55b0172022-02-20 17:52:35 -0800892 head [c] = Empty ;
Brian Silverman72890c22015-09-19 14:37:37 -0400893 }
894 min_score = n_col ;
895 /* place in reverse order, so low column indices are at the front */
896 /* of the lists. This is to encourage natural tie-breaking */
897 for (c = n_col-1 ; c >= 0 ; c--)
898 {
899 /* only add principal columns to degree lists */
Austin Schuhc55b0172022-02-20 17:52:35 -0800900 if (Col[c].is_alive())
Brian Silverman72890c22015-09-19 14:37:37 -0400901 {
902 COLAMD_DEBUG4 (("place %d score %d minscore %d ncol %d\n",
903 c, Col [c].shared2.score, min_score, n_col)) ;
904
905 /* === Add columns score to DList =============================== */
906
907 score = Col [c].shared2.score ;
908
909 COLAMD_ASSERT (min_score >= 0) ;
910 COLAMD_ASSERT (min_score <= n_col) ;
911 COLAMD_ASSERT (score >= 0) ;
912 COLAMD_ASSERT (score <= n_col) ;
Austin Schuhc55b0172022-02-20 17:52:35 -0800913 COLAMD_ASSERT (head [score] >= Empty) ;
Brian Silverman72890c22015-09-19 14:37:37 -0400914
915 /* now add this column to dList at proper score location */
916 next_col = head [score] ;
Austin Schuhc55b0172022-02-20 17:52:35 -0800917 Col [c].shared3.prev = Empty ;
Brian Silverman72890c22015-09-19 14:37:37 -0400918 Col [c].shared4.degree_next = next_col ;
919
920 /* if there already was a column with the same score, set its */
921 /* previous pointer to this new column */
Austin Schuhc55b0172022-02-20 17:52:35 -0800922 if (next_col != Empty)
Brian Silverman72890c22015-09-19 14:37:37 -0400923 {
924 Col [next_col].shared3.prev = c ;
925 }
926 head [score] = c ;
927
928 /* see if this score is less than current min */
Austin Schuh189376f2018-12-20 22:11:15 +1100929 min_score = numext::mini(min_score, score) ;
Brian Silverman72890c22015-09-19 14:37:37 -0400930
931
932 }
933 }
934
935
936 /* === Return number of remaining columns, and max row degree =========== */
937
938 *p_n_col2 = n_col2 ;
939 *p_n_row2 = n_row2 ;
940 *p_max_deg = max_deg ;
941}
942
943
944/* ========================================================================== */
945/* === find_ordering ======================================================== */
946/* ========================================================================== */
947
948/*
949 Order the principal columns of the supercolumn form of the matrix
950 (no supercolumns on input). Uses a minimum approximate column minimum
951 degree ordering method. Not user-callable.
952*/
Austin Schuh189376f2018-12-20 22:11:15 +1100953template <typename IndexType>
954static IndexType find_ordering /* return the number of garbage collections */
Brian Silverman72890c22015-09-19 14:37:37 -0400955 (
956 /* === Parameters ======================================================= */
957
Austin Schuh189376f2018-12-20 22:11:15 +1100958 IndexType n_row, /* number of rows of A */
959 IndexType n_col, /* number of columns of A */
960 IndexType Alen, /* size of A, 2*nnz + n_col or larger */
Austin Schuhc55b0172022-02-20 17:52:35 -0800961 RowStructure<IndexType> Row [], /* of size n_row+1 */
962 ColStructure<IndexType> Col [], /* of size n_col+1 */
Austin Schuh189376f2018-12-20 22:11:15 +1100963 IndexType A [], /* column form and row form of A */
964 IndexType head [], /* of size n_col+1 */
965 IndexType n_col2, /* Remaining columns to order */
966 IndexType max_deg, /* Maximum row degree */
967 IndexType pfree /* index of first free slot (2*nnz on entry) */
Brian Silverman72890c22015-09-19 14:37:37 -0400968 )
969{
970 /* === Local variables ================================================== */
971
Austin Schuh189376f2018-12-20 22:11:15 +1100972 IndexType k ; /* current pivot ordering step */
973 IndexType pivot_col ; /* current pivot column */
974 IndexType *cp ; /* a column pointer */
975 IndexType *rp ; /* a row pointer */
976 IndexType pivot_row ; /* current pivot row */
977 IndexType *new_cp ; /* modified column pointer */
978 IndexType *new_rp ; /* modified row pointer */
979 IndexType pivot_row_start ; /* pointer to start of pivot row */
980 IndexType pivot_row_degree ; /* number of columns in pivot row */
981 IndexType pivot_row_length ; /* number of supercolumns in pivot row */
982 IndexType pivot_col_score ; /* score of pivot column */
983 IndexType needed_memory ; /* free space needed for pivot row */
984 IndexType *cp_end ; /* pointer to the end of a column */
985 IndexType *rp_end ; /* pointer to the end of a row */
986 IndexType row ; /* a row index */
987 IndexType col ; /* a column index */
988 IndexType max_score ; /* maximum possible score */
989 IndexType cur_score ; /* score of current column */
Brian Silverman72890c22015-09-19 14:37:37 -0400990 unsigned int hash ; /* hash value for supernode detection */
Austin Schuh189376f2018-12-20 22:11:15 +1100991 IndexType head_column ; /* head of hash bucket */
992 IndexType first_col ; /* first column in hash bucket */
993 IndexType tag_mark ; /* marker value for mark array */
994 IndexType row_mark ; /* Row [row].shared2.mark */
995 IndexType set_difference ; /* set difference size of row with pivot row */
996 IndexType min_score ; /* smallest column score */
997 IndexType col_thickness ; /* "thickness" (no. of columns in a supercol) */
998 IndexType max_mark ; /* maximum value of tag_mark */
999 IndexType pivot_col_thickness ; /* number of columns represented by pivot col */
1000 IndexType prev_col ; /* Used by Dlist operations. */
1001 IndexType next_col ; /* Used by Dlist operations. */
1002 IndexType ngarbage ; /* number of garbage collections performed */
Brian Silverman72890c22015-09-19 14:37:37 -04001003
1004
1005 /* === Initialization and clear mark ==================================== */
1006
1007 max_mark = INT_MAX - n_col ; /* INT_MAX defined in <limits.h> */
Austin Schuhc55b0172022-02-20 17:52:35 -08001008 tag_mark = Colamd::clear_mark (n_row, Row) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001009 min_score = 0 ;
1010 ngarbage = 0 ;
1011 COLAMD_DEBUG1 (("colamd: Ordering, n_col2=%d\n", n_col2)) ;
1012
1013 /* === Order the columns ================================================ */
1014
1015 for (k = 0 ; k < n_col2 ; /* 'k' is incremented below */)
1016 {
1017
1018 /* === Select pivot column, and order it ============================ */
1019
1020 /* make sure degree list isn't empty */
1021 COLAMD_ASSERT (min_score >= 0) ;
1022 COLAMD_ASSERT (min_score <= n_col) ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001023 COLAMD_ASSERT (head [min_score] >= Empty) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001024
1025 /* get pivot column from head of minimum degree list */
Austin Schuhc55b0172022-02-20 17:52:35 -08001026 while (min_score < n_col && head [min_score] == Empty)
Brian Silverman72890c22015-09-19 14:37:37 -04001027 {
1028 min_score++ ;
1029 }
1030 pivot_col = head [min_score] ;
1031 COLAMD_ASSERT (pivot_col >= 0 && pivot_col <= n_col) ;
1032 next_col = Col [pivot_col].shared4.degree_next ;
1033 head [min_score] = next_col ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001034 if (next_col != Empty)
Brian Silverman72890c22015-09-19 14:37:37 -04001035 {
Austin Schuhc55b0172022-02-20 17:52:35 -08001036 Col [next_col].shared3.prev = Empty ;
Brian Silverman72890c22015-09-19 14:37:37 -04001037 }
1038
Austin Schuhc55b0172022-02-20 17:52:35 -08001039 COLAMD_ASSERT (Col[pivot_col].is_alive()) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001040 COLAMD_DEBUG3 (("Pivot col: %d\n", pivot_col)) ;
1041
1042 /* remember score for defrag check */
1043 pivot_col_score = Col [pivot_col].shared2.score ;
1044
1045 /* the pivot column is the kth column in the pivot order */
1046 Col [pivot_col].shared2.order = k ;
1047
1048 /* increment order count by column thickness */
1049 pivot_col_thickness = Col [pivot_col].shared1.thickness ;
1050 k += pivot_col_thickness ;
1051 COLAMD_ASSERT (pivot_col_thickness > 0) ;
1052
1053 /* === Garbage_collection, if necessary ============================= */
1054
Austin Schuh189376f2018-12-20 22:11:15 +11001055 needed_memory = numext::mini(pivot_col_score, n_col - k) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001056 if (pfree + needed_memory >= Alen)
1057 {
Austin Schuhc55b0172022-02-20 17:52:35 -08001058 pfree = Colamd::garbage_collection (n_row, n_col, Row, Col, A, &A [pfree]) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001059 ngarbage++ ;
1060 /* after garbage collection we will have enough */
1061 COLAMD_ASSERT (pfree + needed_memory < Alen) ;
1062 /* garbage collection has wiped out the Row[].shared2.mark array */
Austin Schuhc55b0172022-02-20 17:52:35 -08001063 tag_mark = Colamd::clear_mark (n_row, Row) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001064
1065 }
1066
1067 /* === Compute pivot row pattern ==================================== */
1068
1069 /* get starting location for this new merged row */
1070 pivot_row_start = pfree ;
1071
1072 /* initialize new row counts to zero */
1073 pivot_row_degree = 0 ;
1074
1075 /* tag pivot column as having been visited so it isn't included */
1076 /* in merged pivot row */
1077 Col [pivot_col].shared1.thickness = -pivot_col_thickness ;
1078
1079 /* pivot row is the union of all rows in the pivot column pattern */
1080 cp = &A [Col [pivot_col].start] ;
1081 cp_end = cp + Col [pivot_col].length ;
1082 while (cp < cp_end)
1083 {
1084 /* get a row */
1085 row = *cp++ ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001086 COLAMD_DEBUG4 (("Pivot col pattern %d %d\n", Row[row].is_alive(), row)) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001087 /* skip if row is dead */
Austin Schuhc55b0172022-02-20 17:52:35 -08001088 if (Row[row].is_dead())
Brian Silverman72890c22015-09-19 14:37:37 -04001089 {
1090 continue ;
1091 }
1092 rp = &A [Row [row].start] ;
1093 rp_end = rp + Row [row].length ;
1094 while (rp < rp_end)
1095 {
1096 /* get a column */
1097 col = *rp++ ;
1098 /* add the column, if alive and untagged */
1099 col_thickness = Col [col].shared1.thickness ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001100 if (col_thickness > 0 && Col[col].is_alive())
Brian Silverman72890c22015-09-19 14:37:37 -04001101 {
1102 /* tag column in pivot row */
1103 Col [col].shared1.thickness = -col_thickness ;
1104 COLAMD_ASSERT (pfree < Alen) ;
1105 /* place column in pivot row */
1106 A [pfree++] = col ;
1107 pivot_row_degree += col_thickness ;
1108 }
1109 }
1110 }
1111
1112 /* clear tag on pivot column */
1113 Col [pivot_col].shared1.thickness = pivot_col_thickness ;
Austin Schuh189376f2018-12-20 22:11:15 +11001114 max_deg = numext::maxi(max_deg, pivot_row_degree) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001115
1116
1117 /* === Kill all rows used to construct pivot row ==================== */
1118
1119 /* also kill pivot row, temporarily */
1120 cp = &A [Col [pivot_col].start] ;
1121 cp_end = cp + Col [pivot_col].length ;
1122 while (cp < cp_end)
1123 {
1124 /* may be killing an already dead row */
1125 row = *cp++ ;
1126 COLAMD_DEBUG3 (("Kill row in pivot col: %d\n", row)) ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001127 Row[row].kill() ;
Brian Silverman72890c22015-09-19 14:37:37 -04001128 }
1129
1130 /* === Select a row index to use as the new pivot row =============== */
1131
1132 pivot_row_length = pfree - pivot_row_start ;
1133 if (pivot_row_length > 0)
1134 {
1135 /* pick the "pivot" row arbitrarily (first row in col) */
1136 pivot_row = A [Col [pivot_col].start] ;
1137 COLAMD_DEBUG3 (("Pivotal row is %d\n", pivot_row)) ;
1138 }
1139 else
1140 {
1141 /* there is no pivot row, since it is of zero length */
Austin Schuhc55b0172022-02-20 17:52:35 -08001142 pivot_row = Empty ;
Brian Silverman72890c22015-09-19 14:37:37 -04001143 COLAMD_ASSERT (pivot_row_length == 0) ;
1144 }
1145 COLAMD_ASSERT (Col [pivot_col].length > 0 || pivot_row_length == 0) ;
1146
1147 /* === Approximate degree computation =============================== */
1148
1149 /* Here begins the computation of the approximate degree. The column */
1150 /* score is the sum of the pivot row "length", plus the size of the */
1151 /* set differences of each row in the column minus the pattern of the */
1152 /* pivot row itself. The column ("thickness") itself is also */
1153 /* excluded from the column score (we thus use an approximate */
1154 /* external degree). */
1155
1156 /* The time taken by the following code (compute set differences, and */
1157 /* add them up) is proportional to the size of the data structure */
1158 /* being scanned - that is, the sum of the sizes of each column in */
1159 /* the pivot row. Thus, the amortized time to compute a column score */
1160 /* is proportional to the size of that column (where size, in this */
1161 /* context, is the column "length", or the number of row indices */
1162 /* in that column). The number of row indices in a column is */
1163 /* monotonically non-decreasing, from the length of the original */
1164 /* column on input to colamd. */
1165
1166 /* === Compute set differences ====================================== */
1167
1168 COLAMD_DEBUG3 (("** Computing set differences phase. **\n")) ;
1169
1170 /* pivot row is currently dead - it will be revived later. */
1171
1172 COLAMD_DEBUG3 (("Pivot row: ")) ;
1173 /* for each column in pivot row */
1174 rp = &A [pivot_row_start] ;
1175 rp_end = rp + pivot_row_length ;
1176 while (rp < rp_end)
1177 {
1178 col = *rp++ ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001179 COLAMD_ASSERT (Col[col].is_alive() && col != pivot_col) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001180 COLAMD_DEBUG3 (("Col: %d\n", col)) ;
1181
1182 /* clear tags used to construct pivot row pattern */
1183 col_thickness = -Col [col].shared1.thickness ;
1184 COLAMD_ASSERT (col_thickness > 0) ;
1185 Col [col].shared1.thickness = col_thickness ;
1186
1187 /* === Remove column from degree list =========================== */
1188
1189 cur_score = Col [col].shared2.score ;
1190 prev_col = Col [col].shared3.prev ;
1191 next_col = Col [col].shared4.degree_next ;
1192 COLAMD_ASSERT (cur_score >= 0) ;
1193 COLAMD_ASSERT (cur_score <= n_col) ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001194 COLAMD_ASSERT (cur_score >= Empty) ;
1195 if (prev_col == Empty)
Brian Silverman72890c22015-09-19 14:37:37 -04001196 {
1197 head [cur_score] = next_col ;
1198 }
1199 else
1200 {
1201 Col [prev_col].shared4.degree_next = next_col ;
1202 }
Austin Schuhc55b0172022-02-20 17:52:35 -08001203 if (next_col != Empty)
Brian Silverman72890c22015-09-19 14:37:37 -04001204 {
1205 Col [next_col].shared3.prev = prev_col ;
1206 }
1207
1208 /* === Scan the column ========================================== */
1209
1210 cp = &A [Col [col].start] ;
1211 cp_end = cp + Col [col].length ;
1212 while (cp < cp_end)
1213 {
1214 /* get a row */
1215 row = *cp++ ;
Brian Silverman72890c22015-09-19 14:37:37 -04001216 /* skip if dead */
Austin Schuhc55b0172022-02-20 17:52:35 -08001217 if (Row[row].is_dead())
Brian Silverman72890c22015-09-19 14:37:37 -04001218 {
1219 continue ;
1220 }
Austin Schuhc55b0172022-02-20 17:52:35 -08001221 row_mark = Row [row].shared2.mark ;
Brian Silverman72890c22015-09-19 14:37:37 -04001222 COLAMD_ASSERT (row != pivot_row) ;
1223 set_difference = row_mark - tag_mark ;
1224 /* check if the row has been seen yet */
1225 if (set_difference < 0)
1226 {
1227 COLAMD_ASSERT (Row [row].shared1.degree <= max_deg) ;
1228 set_difference = Row [row].shared1.degree ;
1229 }
1230 /* subtract column thickness from this row's set difference */
1231 set_difference -= col_thickness ;
1232 COLAMD_ASSERT (set_difference >= 0) ;
1233 /* absorb this row if the set difference becomes zero */
1234 if (set_difference == 0)
1235 {
1236 COLAMD_DEBUG3 (("aggressive absorption. Row: %d\n", row)) ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001237 Row[row].kill() ;
Brian Silverman72890c22015-09-19 14:37:37 -04001238 }
1239 else
1240 {
1241 /* save the new mark */
1242 Row [row].shared2.mark = set_difference + tag_mark ;
1243 }
1244 }
1245 }
1246
1247
1248 /* === Add up set differences for each column ======================= */
1249
1250 COLAMD_DEBUG3 (("** Adding set differences phase. **\n")) ;
1251
1252 /* for each column in pivot row */
1253 rp = &A [pivot_row_start] ;
1254 rp_end = rp + pivot_row_length ;
1255 while (rp < rp_end)
1256 {
1257 /* get a column */
1258 col = *rp++ ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001259 COLAMD_ASSERT (Col[col].is_alive() && col != pivot_col) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001260 hash = 0 ;
1261 cur_score = 0 ;
1262 cp = &A [Col [col].start] ;
1263 /* compact the column */
1264 new_cp = cp ;
1265 cp_end = cp + Col [col].length ;
1266
1267 COLAMD_DEBUG4 (("Adding set diffs for Col: %d.\n", col)) ;
1268
1269 while (cp < cp_end)
1270 {
1271 /* get a row */
1272 row = *cp++ ;
1273 COLAMD_ASSERT(row >= 0 && row < n_row) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001274 /* skip if dead */
Austin Schuhc55b0172022-02-20 17:52:35 -08001275 if (Row [row].is_dead())
Brian Silverman72890c22015-09-19 14:37:37 -04001276 {
1277 continue ;
1278 }
Austin Schuhc55b0172022-02-20 17:52:35 -08001279 row_mark = Row [row].shared2.mark ;
Brian Silverman72890c22015-09-19 14:37:37 -04001280 COLAMD_ASSERT (row_mark > tag_mark) ;
1281 /* compact the column */
1282 *new_cp++ = row ;
1283 /* compute hash function */
1284 hash += row ;
1285 /* add set difference */
1286 cur_score += row_mark - tag_mark ;
1287 /* integer overflow... */
Austin Schuh189376f2018-12-20 22:11:15 +11001288 cur_score = numext::mini(cur_score, n_col) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001289 }
1290
1291 /* recompute the column's length */
Austin Schuh189376f2018-12-20 22:11:15 +11001292 Col [col].length = (IndexType) (new_cp - &A [Col [col].start]) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001293
1294 /* === Further mass elimination ================================= */
1295
1296 if (Col [col].length == 0)
1297 {
1298 COLAMD_DEBUG4 (("further mass elimination. Col: %d\n", col)) ;
1299 /* nothing left but the pivot row in this column */
Austin Schuhc55b0172022-02-20 17:52:35 -08001300 Col[col].kill_principal() ;
Brian Silverman72890c22015-09-19 14:37:37 -04001301 pivot_row_degree -= Col [col].shared1.thickness ;
1302 COLAMD_ASSERT (pivot_row_degree >= 0) ;
1303 /* order it */
1304 Col [col].shared2.order = k ;
1305 /* increment order count by column thickness */
1306 k += Col [col].shared1.thickness ;
1307 }
1308 else
1309 {
1310 /* === Prepare for supercolumn detection ==================== */
1311
1312 COLAMD_DEBUG4 (("Preparing supercol detection for Col: %d.\n", col)) ;
1313
1314 /* save score so far */
1315 Col [col].shared2.score = cur_score ;
1316
1317 /* add column to hash table, for supercolumn detection */
1318 hash %= n_col + 1 ;
1319
1320 COLAMD_DEBUG4 ((" Hash = %d, n_col = %d.\n", hash, n_col)) ;
1321 COLAMD_ASSERT (hash <= n_col) ;
1322
1323 head_column = head [hash] ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001324 if (head_column > Empty)
Brian Silverman72890c22015-09-19 14:37:37 -04001325 {
1326 /* degree list "hash" is non-empty, use prev (shared3) of */
1327 /* first column in degree list as head of hash bucket */
1328 first_col = Col [head_column].shared3.headhash ;
1329 Col [head_column].shared3.headhash = col ;
1330 }
1331 else
1332 {
1333 /* degree list "hash" is empty, use head as hash bucket */
1334 first_col = - (head_column + 2) ;
1335 head [hash] = - (col + 2) ;
1336 }
1337 Col [col].shared4.hash_next = first_col ;
1338
1339 /* save hash function in Col [col].shared3.hash */
Austin Schuh189376f2018-12-20 22:11:15 +11001340 Col [col].shared3.hash = (IndexType) hash ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001341 COLAMD_ASSERT (Col[col].is_alive()) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001342 }
1343 }
1344
1345 /* The approximate external column degree is now computed. */
1346
1347 /* === Supercolumn detection ======================================== */
1348
1349 COLAMD_DEBUG3 (("** Supercolumn detection phase. **\n")) ;
1350
Austin Schuhc55b0172022-02-20 17:52:35 -08001351 Colamd::detect_super_cols (Col, A, head, pivot_row_start, pivot_row_length) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001352
1353 /* === Kill the pivotal column ====================================== */
1354
Austin Schuhc55b0172022-02-20 17:52:35 -08001355 Col[pivot_col].kill_principal() ;
Brian Silverman72890c22015-09-19 14:37:37 -04001356
1357 /* === Clear mark =================================================== */
1358
1359 tag_mark += (max_deg + 1) ;
1360 if (tag_mark >= max_mark)
1361 {
1362 COLAMD_DEBUG2 (("clearing tag_mark\n")) ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001363 tag_mark = Colamd::clear_mark (n_row, Row) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001364 }
1365
1366 /* === Finalize the new pivot row, and column scores ================ */
1367
1368 COLAMD_DEBUG3 (("** Finalize scores phase. **\n")) ;
1369
1370 /* for each column in pivot row */
1371 rp = &A [pivot_row_start] ;
1372 /* compact the pivot row */
1373 new_rp = rp ;
1374 rp_end = rp + pivot_row_length ;
1375 while (rp < rp_end)
1376 {
1377 col = *rp++ ;
1378 /* skip dead columns */
Austin Schuhc55b0172022-02-20 17:52:35 -08001379 if (Col[col].is_dead())
Brian Silverman72890c22015-09-19 14:37:37 -04001380 {
1381 continue ;
1382 }
1383 *new_rp++ = col ;
1384 /* add new pivot row to column */
1385 A [Col [col].start + (Col [col].length++)] = pivot_row ;
1386
1387 /* retrieve score so far and add on pivot row's degree. */
1388 /* (we wait until here for this in case the pivot */
1389 /* row's degree was reduced due to mass elimination). */
1390 cur_score = Col [col].shared2.score + pivot_row_degree ;
1391
1392 /* calculate the max possible score as the number of */
1393 /* external columns minus the 'k' value minus the */
1394 /* columns thickness */
1395 max_score = n_col - k - Col [col].shared1.thickness ;
1396
1397 /* make the score the external degree of the union-of-rows */
1398 cur_score -= Col [col].shared1.thickness ;
1399
1400 /* make sure score is less or equal than the max score */
Austin Schuh189376f2018-12-20 22:11:15 +11001401 cur_score = numext::mini(cur_score, max_score) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001402 COLAMD_ASSERT (cur_score >= 0) ;
1403
1404 /* store updated score */
1405 Col [col].shared2.score = cur_score ;
1406
1407 /* === Place column back in degree list ========================= */
1408
1409 COLAMD_ASSERT (min_score >= 0) ;
1410 COLAMD_ASSERT (min_score <= n_col) ;
1411 COLAMD_ASSERT (cur_score >= 0) ;
1412 COLAMD_ASSERT (cur_score <= n_col) ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001413 COLAMD_ASSERT (head [cur_score] >= Empty) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001414 next_col = head [cur_score] ;
1415 Col [col].shared4.degree_next = next_col ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001416 Col [col].shared3.prev = Empty ;
1417 if (next_col != Empty)
Brian Silverman72890c22015-09-19 14:37:37 -04001418 {
1419 Col [next_col].shared3.prev = col ;
1420 }
1421 head [cur_score] = col ;
1422
1423 /* see if this score is less than current min */
Austin Schuh189376f2018-12-20 22:11:15 +11001424 min_score = numext::mini(min_score, cur_score) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001425
1426 }
1427
1428 /* === Resurrect the new pivot row ================================== */
1429
1430 if (pivot_row_degree > 0)
1431 {
1432 /* update pivot row length to reflect any cols that were killed */
1433 /* during super-col detection and mass elimination */
1434 Row [pivot_row].start = pivot_row_start ;
Austin Schuh189376f2018-12-20 22:11:15 +11001435 Row [pivot_row].length = (IndexType) (new_rp - &A[pivot_row_start]) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001436 Row [pivot_row].shared1.degree = pivot_row_degree ;
1437 Row [pivot_row].shared2.mark = 0 ;
1438 /* pivot row is no longer dead */
1439 }
1440 }
1441
1442 /* === All principal columns have now been ordered ====================== */
1443
1444 return (ngarbage) ;
1445}
1446
1447
1448/* ========================================================================== */
1449/* === order_children ======================================================= */
1450/* ========================================================================== */
1451
1452/*
1453 The find_ordering routine has ordered all of the principal columns (the
1454 representatives of the supercolumns). The non-principal columns have not
1455 yet been ordered. This routine orders those columns by walking up the
1456 parent tree (a column is a child of the column which absorbed it). The
1457 final permutation vector is then placed in p [0 ... n_col-1], with p [0]
1458 being the first column, and p [n_col-1] being the last. It doesn't look
1459 like it at first glance, but be assured that this routine takes time linear
1460 in the number of columns. Although not immediately obvious, the time
1461 taken by this routine is O (n_col), that is, linear in the number of
1462 columns. Not user-callable.
1463*/
Austin Schuh189376f2018-12-20 22:11:15 +11001464template <typename IndexType>
Brian Silverman72890c22015-09-19 14:37:37 -04001465static inline void order_children
1466(
1467 /* === Parameters ======================================================= */
1468
Austin Schuh189376f2018-12-20 22:11:15 +11001469 IndexType n_col, /* number of columns of A */
Austin Schuhc55b0172022-02-20 17:52:35 -08001470 ColStructure<IndexType> Col [], /* of size n_col+1 */
Austin Schuh189376f2018-12-20 22:11:15 +11001471 IndexType p [] /* p [0 ... n_col-1] is the column permutation*/
Brian Silverman72890c22015-09-19 14:37:37 -04001472 )
1473{
1474 /* === Local variables ================================================== */
1475
Austin Schuh189376f2018-12-20 22:11:15 +11001476 IndexType i ; /* loop counter for all columns */
1477 IndexType c ; /* column index */
1478 IndexType parent ; /* index of column's parent */
1479 IndexType order ; /* column's order */
Brian Silverman72890c22015-09-19 14:37:37 -04001480
1481 /* === Order each non-principal column ================================== */
1482
1483 for (i = 0 ; i < n_col ; i++)
1484 {
1485 /* find an un-ordered non-principal column */
Austin Schuhc55b0172022-02-20 17:52:35 -08001486 COLAMD_ASSERT (col_is_dead(Col, i)) ;
1487 if (!Col[i].is_dead_principal() && Col [i].shared2.order == Empty)
Brian Silverman72890c22015-09-19 14:37:37 -04001488 {
1489 parent = i ;
1490 /* once found, find its principal parent */
1491 do
1492 {
1493 parent = Col [parent].shared1.parent ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001494 } while (!Col[parent].is_dead_principal()) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001495
1496 /* now, order all un-ordered non-principal columns along path */
1497 /* to this parent. collapse tree at the same time */
1498 c = i ;
1499 /* get order of parent */
1500 order = Col [parent].shared2.order ;
1501
1502 do
1503 {
Austin Schuhc55b0172022-02-20 17:52:35 -08001504 COLAMD_ASSERT (Col [c].shared2.order == Empty) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001505
1506 /* order this column */
1507 Col [c].shared2.order = order++ ;
1508 /* collaps tree */
1509 Col [c].shared1.parent = parent ;
1510
1511 /* get immediate parent of this column */
1512 c = Col [c].shared1.parent ;
1513
1514 /* continue until we hit an ordered column. There are */
Austin Schuhc55b0172022-02-20 17:52:35 -08001515 /* guaranteed not to be anymore unordered columns */
Brian Silverman72890c22015-09-19 14:37:37 -04001516 /* above an ordered column */
Austin Schuhc55b0172022-02-20 17:52:35 -08001517 } while (Col [c].shared2.order == Empty) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001518
1519 /* re-order the super_col parent to largest order for this group */
1520 Col [parent].shared2.order = order ;
1521 }
1522 }
1523
1524 /* === Generate the permutation ========================================= */
1525
1526 for (c = 0 ; c < n_col ; c++)
1527 {
1528 p [Col [c].shared2.order] = c ;
1529 }
1530}
1531
1532
1533/* ========================================================================== */
1534/* === detect_super_cols ==================================================== */
1535/* ========================================================================== */
1536
1537/*
1538 Detects supercolumns by finding matches between columns in the hash buckets.
1539 Check amongst columns in the set A [row_start ... row_start + row_length-1].
1540 The columns under consideration are currently *not* in the degree lists,
1541 and have already been placed in the hash buckets.
1542
1543 The hash bucket for columns whose hash function is equal to h is stored
1544 as follows:
1545
1546 if head [h] is >= 0, then head [h] contains a degree list, so:
1547
1548 head [h] is the first column in degree bucket h.
1549 Col [head [h]].headhash gives the first column in hash bucket h.
1550
1551 otherwise, the degree list is empty, and:
1552
1553 -(head [h] + 2) is the first column in hash bucket h.
1554
1555 For a column c in a hash bucket, Col [c].shared3.prev is NOT a "previous
1556 column" pointer. Col [c].shared3.hash is used instead as the hash number
1557 for that column. The value of Col [c].shared4.hash_next is the next column
1558 in the same hash bucket.
1559
1560 Assuming no, or "few" hash collisions, the time taken by this routine is
1561 linear in the sum of the sizes (lengths) of each column whose score has
1562 just been computed in the approximate degree computation.
1563 Not user-callable.
1564*/
Austin Schuh189376f2018-12-20 22:11:15 +11001565template <typename IndexType>
Brian Silverman72890c22015-09-19 14:37:37 -04001566static void detect_super_cols
1567(
1568 /* === Parameters ======================================================= */
Austin Schuhc55b0172022-02-20 17:52:35 -08001569
1570 ColStructure<IndexType> Col [], /* of size n_col+1 */
Austin Schuh189376f2018-12-20 22:11:15 +11001571 IndexType A [], /* row indices of A */
1572 IndexType head [], /* head of degree lists and hash buckets */
1573 IndexType row_start, /* pointer to set of columns to check */
1574 IndexType row_length /* number of columns to check */
Brian Silverman72890c22015-09-19 14:37:37 -04001575)
1576{
1577 /* === Local variables ================================================== */
1578
Austin Schuh189376f2018-12-20 22:11:15 +11001579 IndexType hash ; /* hash value for a column */
1580 IndexType *rp ; /* pointer to a row */
1581 IndexType c ; /* a column index */
1582 IndexType super_c ; /* column index of the column to absorb into */
1583 IndexType *cp1 ; /* column pointer for column super_c */
1584 IndexType *cp2 ; /* column pointer for column c */
1585 IndexType length ; /* length of column super_c */
1586 IndexType prev_c ; /* column preceding c in hash bucket */
1587 IndexType i ; /* loop counter */
1588 IndexType *rp_end ; /* pointer to the end of the row */
1589 IndexType col ; /* a column index in the row to check */
1590 IndexType head_column ; /* first column in hash bucket or degree list */
1591 IndexType first_col ; /* first column in hash bucket */
Brian Silverman72890c22015-09-19 14:37:37 -04001592
1593 /* === Consider each column in the row ================================== */
1594
1595 rp = &A [row_start] ;
1596 rp_end = rp + row_length ;
1597 while (rp < rp_end)
1598 {
1599 col = *rp++ ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001600 if (Col[col].is_dead())
Brian Silverman72890c22015-09-19 14:37:37 -04001601 {
1602 continue ;
1603 }
1604
1605 /* get hash number for this column */
1606 hash = Col [col].shared3.hash ;
1607 COLAMD_ASSERT (hash <= n_col) ;
1608
1609 /* === Get the first column in this hash bucket ===================== */
1610
1611 head_column = head [hash] ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001612 if (head_column > Empty)
Brian Silverman72890c22015-09-19 14:37:37 -04001613 {
1614 first_col = Col [head_column].shared3.headhash ;
1615 }
1616 else
1617 {
1618 first_col = - (head_column + 2) ;
1619 }
1620
1621 /* === Consider each column in the hash bucket ====================== */
1622
Austin Schuhc55b0172022-02-20 17:52:35 -08001623 for (super_c = first_col ; super_c != Empty ;
Brian Silverman72890c22015-09-19 14:37:37 -04001624 super_c = Col [super_c].shared4.hash_next)
1625 {
Austin Schuhc55b0172022-02-20 17:52:35 -08001626 COLAMD_ASSERT (Col [super_c].is_alive()) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001627 COLAMD_ASSERT (Col [super_c].shared3.hash == hash) ;
1628 length = Col [super_c].length ;
1629
1630 /* prev_c is the column preceding column c in the hash bucket */
1631 prev_c = super_c ;
1632
1633 /* === Compare super_c with all columns after it ================ */
1634
1635 for (c = Col [super_c].shared4.hash_next ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001636 c != Empty ; c = Col [c].shared4.hash_next)
Brian Silverman72890c22015-09-19 14:37:37 -04001637 {
1638 COLAMD_ASSERT (c != super_c) ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001639 COLAMD_ASSERT (Col[c].is_alive()) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001640 COLAMD_ASSERT (Col [c].shared3.hash == hash) ;
1641
1642 /* not identical if lengths or scores are different */
1643 if (Col [c].length != length ||
1644 Col [c].shared2.score != Col [super_c].shared2.score)
1645 {
1646 prev_c = c ;
1647 continue ;
1648 }
1649
1650 /* compare the two columns */
1651 cp1 = &A [Col [super_c].start] ;
1652 cp2 = &A [Col [c].start] ;
1653
1654 for (i = 0 ; i < length ; i++)
1655 {
1656 /* the columns are "clean" (no dead rows) */
Austin Schuhc55b0172022-02-20 17:52:35 -08001657 COLAMD_ASSERT ( cp1->is_alive() );
1658 COLAMD_ASSERT ( cp2->is_alive() );
Brian Silverman72890c22015-09-19 14:37:37 -04001659 /* row indices will same order for both supercols, */
Austin Schuhc55b0172022-02-20 17:52:35 -08001660 /* no gather scatter necessary */
Brian Silverman72890c22015-09-19 14:37:37 -04001661 if (*cp1++ != *cp2++)
1662 {
1663 break ;
1664 }
1665 }
1666
1667 /* the two columns are different if the for-loop "broke" */
1668 if (i != length)
1669 {
1670 prev_c = c ;
1671 continue ;
1672 }
1673
1674 /* === Got it! two columns are identical =================== */
1675
1676 COLAMD_ASSERT (Col [c].shared2.score == Col [super_c].shared2.score) ;
1677
1678 Col [super_c].shared1.thickness += Col [c].shared1.thickness ;
1679 Col [c].shared1.parent = super_c ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001680 Col[c].kill_non_principal() ;
Brian Silverman72890c22015-09-19 14:37:37 -04001681 /* order c later, in order_children() */
Austin Schuhc55b0172022-02-20 17:52:35 -08001682 Col [c].shared2.order = Empty ;
Brian Silverman72890c22015-09-19 14:37:37 -04001683 /* remove c from hash bucket */
1684 Col [prev_c].shared4.hash_next = Col [c].shared4.hash_next ;
1685 }
1686 }
1687
1688 /* === Empty this hash bucket ======================================= */
1689
Austin Schuhc55b0172022-02-20 17:52:35 -08001690 if (head_column > Empty)
Brian Silverman72890c22015-09-19 14:37:37 -04001691 {
1692 /* corresponding degree list "hash" is not empty */
Austin Schuhc55b0172022-02-20 17:52:35 -08001693 Col [head_column].shared3.headhash = Empty ;
Brian Silverman72890c22015-09-19 14:37:37 -04001694 }
1695 else
1696 {
1697 /* corresponding degree list "hash" is empty */
Austin Schuhc55b0172022-02-20 17:52:35 -08001698 head [hash] = Empty ;
Brian Silverman72890c22015-09-19 14:37:37 -04001699 }
1700 }
1701}
1702
1703
1704/* ========================================================================== */
1705/* === garbage_collection =================================================== */
1706/* ========================================================================== */
1707
1708/*
1709 Defragments and compacts columns and rows in the workspace A. Used when
Austin Schuhc55b0172022-02-20 17:52:35 -08001710 all available memory has been used while performing row merging. Returns
Brian Silverman72890c22015-09-19 14:37:37 -04001711 the index of the first free position in A, after garbage collection. The
1712 time taken by this routine is linear is the size of the array A, which is
1713 itself linear in the number of nonzeros in the input matrix.
1714 Not user-callable.
1715*/
Austin Schuh189376f2018-12-20 22:11:15 +11001716template <typename IndexType>
1717static IndexType garbage_collection /* returns the new value of pfree */
Brian Silverman72890c22015-09-19 14:37:37 -04001718 (
1719 /* === Parameters ======================================================= */
Austin Schuhc55b0172022-02-20 17:52:35 -08001720
Austin Schuh189376f2018-12-20 22:11:15 +11001721 IndexType n_row, /* number of rows */
1722 IndexType n_col, /* number of columns */
Austin Schuhc55b0172022-02-20 17:52:35 -08001723 RowStructure<IndexType> Row [], /* row info */
1724 ColStructure<IndexType> Col [], /* column info */
Austin Schuh189376f2018-12-20 22:11:15 +11001725 IndexType A [], /* A [0 ... Alen-1] holds the matrix */
1726 IndexType *pfree /* &A [0] ... pfree is in use */
Brian Silverman72890c22015-09-19 14:37:37 -04001727 )
1728{
1729 /* === Local variables ================================================== */
1730
Austin Schuh189376f2018-12-20 22:11:15 +11001731 IndexType *psrc ; /* source pointer */
1732 IndexType *pdest ; /* destination pointer */
1733 IndexType j ; /* counter */
1734 IndexType r ; /* a row index */
1735 IndexType c ; /* a column index */
1736 IndexType length ; /* length of a row or column */
Brian Silverman72890c22015-09-19 14:37:37 -04001737
1738 /* === Defragment the columns =========================================== */
1739
1740 pdest = &A[0] ;
1741 for (c = 0 ; c < n_col ; c++)
1742 {
Austin Schuhc55b0172022-02-20 17:52:35 -08001743 if (Col[c].is_alive())
Brian Silverman72890c22015-09-19 14:37:37 -04001744 {
1745 psrc = &A [Col [c].start] ;
1746
1747 /* move and compact the column */
1748 COLAMD_ASSERT (pdest <= psrc) ;
Austin Schuh189376f2018-12-20 22:11:15 +11001749 Col [c].start = (IndexType) (pdest - &A [0]) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001750 length = Col [c].length ;
1751 for (j = 0 ; j < length ; j++)
1752 {
1753 r = *psrc++ ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001754 if (Row[r].is_alive())
Brian Silverman72890c22015-09-19 14:37:37 -04001755 {
1756 *pdest++ = r ;
1757 }
1758 }
Austin Schuh189376f2018-12-20 22:11:15 +11001759 Col [c].length = (IndexType) (pdest - &A [Col [c].start]) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001760 }
1761 }
1762
1763 /* === Prepare to defragment the rows =================================== */
1764
1765 for (r = 0 ; r < n_row ; r++)
1766 {
Austin Schuhc55b0172022-02-20 17:52:35 -08001767 if (Row[r].is_alive())
Brian Silverman72890c22015-09-19 14:37:37 -04001768 {
1769 if (Row [r].length == 0)
1770 {
Austin Schuhc55b0172022-02-20 17:52:35 -08001771 /* this row is of zero length. cannot compact it, so kill it */
1772 COLAMD_DEBUG3 (("Defrag row kill\n")) ;
1773 Row[r].kill() ;
Brian Silverman72890c22015-09-19 14:37:37 -04001774 }
1775 else
1776 {
Austin Schuhc55b0172022-02-20 17:52:35 -08001777 /* save first column index in Row [r].shared2.first_column */
1778 psrc = &A [Row [r].start] ;
1779 Row [r].shared2.first_column = *psrc ;
1780 COLAMD_ASSERT (Row[r].is_alive()) ;
1781 /* flag the start of the row with the one's complement of row */
1782 *psrc = ones_complement(r) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001783
1784 }
1785 }
1786 }
1787
1788 /* === Defragment the rows ============================================== */
1789
1790 psrc = pdest ;
1791 while (psrc < pfree)
1792 {
1793 /* find a negative number ... the start of a row */
1794 if (*psrc++ < 0)
1795 {
1796 psrc-- ;
1797 /* get the row index */
Austin Schuhc55b0172022-02-20 17:52:35 -08001798 r = ones_complement(*psrc) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001799 COLAMD_ASSERT (r >= 0 && r < n_row) ;
1800 /* restore first column index */
1801 *psrc = Row [r].shared2.first_column ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001802 COLAMD_ASSERT (Row[r].is_alive()) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001803
1804 /* move and compact the row */
1805 COLAMD_ASSERT (pdest <= psrc) ;
Austin Schuh189376f2018-12-20 22:11:15 +11001806 Row [r].start = (IndexType) (pdest - &A [0]) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001807 length = Row [r].length ;
1808 for (j = 0 ; j < length ; j++)
1809 {
1810 c = *psrc++ ;
Austin Schuhc55b0172022-02-20 17:52:35 -08001811 if (Col[c].is_alive())
Brian Silverman72890c22015-09-19 14:37:37 -04001812 {
1813 *pdest++ = c ;
1814 }
1815 }
Austin Schuh189376f2018-12-20 22:11:15 +11001816 Row [r].length = (IndexType) (pdest - &A [Row [r].start]) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001817
1818 }
1819 }
1820 /* ensure we found all the rows */
1821 COLAMD_ASSERT (debug_rows == 0) ;
1822
1823 /* === Return the new value of pfree ==================================== */
1824
Austin Schuh189376f2018-12-20 22:11:15 +11001825 return ((IndexType) (pdest - &A [0])) ;
Brian Silverman72890c22015-09-19 14:37:37 -04001826}
1827
1828
1829/* ========================================================================== */
1830/* === clear_mark =========================================================== */
1831/* ========================================================================== */
1832
1833/*
1834 Clears the Row [].shared2.mark array, and returns the new tag_mark.
1835 Return value is the new tag_mark. Not user-callable.
1836*/
Austin Schuh189376f2018-12-20 22:11:15 +11001837template <typename IndexType>
1838static inline IndexType clear_mark /* return the new value for tag_mark */
Brian Silverman72890c22015-09-19 14:37:37 -04001839 (
1840 /* === Parameters ======================================================= */
1841
Austin Schuh189376f2018-12-20 22:11:15 +11001842 IndexType n_row, /* number of rows in A */
Austin Schuhc55b0172022-02-20 17:52:35 -08001843 RowStructure<IndexType> Row [] /* Row [0 ... n_row-1].shared2.mark is set to zero */
Brian Silverman72890c22015-09-19 14:37:37 -04001844 )
1845{
1846 /* === Local variables ================================================== */
1847
Austin Schuh189376f2018-12-20 22:11:15 +11001848 IndexType r ;
Brian Silverman72890c22015-09-19 14:37:37 -04001849
1850 for (r = 0 ; r < n_row ; r++)
1851 {
Austin Schuhc55b0172022-02-20 17:52:35 -08001852 if (Row[r].is_alive())
Brian Silverman72890c22015-09-19 14:37:37 -04001853 {
1854 Row [r].shared2.mark = 0 ;
1855 }
1856 }
1857 return (1) ;
1858}
1859
Austin Schuhc55b0172022-02-20 17:52:35 -08001860} // namespace Colamd
Brian Silverman72890c22015-09-19 14:37:37 -04001861
Austin Schuhc55b0172022-02-20 17:52:35 -08001862} // namespace internal
Brian Silverman72890c22015-09-19 14:37:37 -04001863#endif