|
Blender
V2.59
|
00001 00004 /* ========================================================================== */ 00005 /* === colamd - a sparse matrix column ordering algorithm =================== */ 00006 /* ========================================================================== */ 00007 00008 /* 00009 colamd: An approximate minimum degree column ordering algorithm. 00010 00011 Purpose: 00012 00013 Colamd computes a permutation Q such that the Cholesky factorization of 00014 (AQ)'(AQ) has less fill-in and requires fewer floating point operations 00015 than A'A. This also provides a good ordering for sparse partial 00016 pivoting methods, P(AQ) = LU, where Q is computed prior to numerical 00017 factorization, and P is computed during numerical factorization via 00018 conventional partial pivoting with row interchanges. Colamd is the 00019 column ordering method used in SuperLU, part of the ScaLAPACK library. 00020 It is also available as user-contributed software for Matlab 5.2, 00021 available from MathWorks, Inc. (http://www.mathworks.com). This 00022 routine can be used in place of COLMMD in Matlab. By default, the \ 00023 and / operators in Matlab perform a column ordering (using COLMMD) 00024 prior to LU factorization using sparse partial pivoting, in the 00025 built-in Matlab LU(A) routine. 00026 00027 Authors: 00028 00029 The authors of the code itself are Stefan I. Larimore and Timothy A. 00030 Davis (davis@cise.ufl.edu), University of Florida. The algorithm was 00031 developed in collaboration with John Gilbert, Xerox PARC, and Esmond 00032 Ng, Oak Ridge National Laboratory. 00033 00034 Date: 00035 00036 August 3, 1998. Version 1.0. 00037 00038 Acknowledgements: 00039 00040 This work was supported by the National Science Foundation, under 00041 grants DMS-9504974 and DMS-9803599. 00042 00043 Notice: 00044 00045 Copyright (c) 1998 by the University of Florida. All Rights Reserved. 00046 00047 THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY 00048 EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. 00049 00050 Permission is hereby granted to use or copy this program for any 00051 purpose, provided the above notices are retained on all copies. 00052 User documentation of any code that uses this code must cite the 00053 Authors, the Copyright, and "Used by permission." If this code is 00054 accessible from within Matlab, then typing "help colamd" or "colamd" 00055 (with no arguments) must cite the Authors. Permission to modify the 00056 code and to distribute modified code is granted, provided the above 00057 notices are retained, and a notice that the code was modified is 00058 included with the above copyright notice. You must also retain the 00059 Availability information below, of the original version. 00060 00061 This software is provided free of charge. 00062 00063 Availability: 00064 00065 This file is located at 00066 00067 http://www.cise.ufl.edu/~davis/colamd/colamd.c 00068 00069 The colamd.h file is required, located in the same directory. 00070 The colamdmex.c file provides a Matlab interface for colamd. 00071 The symamdmex.c file provides a Matlab interface for symamd, which is 00072 a symmetric ordering based on this code, colamd.c. All codes are 00073 purely ANSI C compliant (they use no Unix-specific routines, include 00074 files, etc.). 00075 */ 00076 00077 /* ========================================================================== */ 00078 /* === Description of user-callable routines ================================ */ 00079 /* ========================================================================== */ 00080 00081 /* 00082 Each user-callable routine (declared as PUBLIC) is briefly described below. 00083 Refer to the comments preceding each routine for more details. 00084 00085 ---------------------------------------------------------------------------- 00086 colamd_recommended: 00087 ---------------------------------------------------------------------------- 00088 00089 Usage: 00090 00091 Alen = colamd_recommended (nnz, n_row, n_col) ; 00092 00093 Purpose: 00094 00095 Returns recommended value of Alen for use by colamd. Returns -1 00096 if any input argument is negative. 00097 00098 Arguments: 00099 00100 int nnz ; Number of nonzeros in the matrix A. This must 00101 be the same value as p [n_col] in the call to 00102 colamd - otherwise you will get a wrong value 00103 of the recommended memory to use. 00104 int n_row ; Number of rows in the matrix A. 00105 int n_col ; Number of columns in the matrix A. 00106 00107 ---------------------------------------------------------------------------- 00108 colamd_set_defaults: 00109 ---------------------------------------------------------------------------- 00110 00111 Usage: 00112 00113 colamd_set_defaults (knobs) ; 00114 00115 Purpose: 00116 00117 Sets the default parameters. 00118 00119 Arguments: 00120 00121 double knobs [COLAMD_KNOBS] ; Output only. 00122 00123 Rows with more than (knobs [COLAMD_DENSE_ROW] * n_col) entries 00124 are removed prior to ordering. Columns with more than 00125 (knobs [COLAMD_DENSE_COL] * n_row) entries are removed 00126 prior to ordering, and placed last in the output column 00127 ordering. Default values of these two knobs are both 0.5. 00128 Currently, only knobs [0] and knobs [1] are used, but future 00129 versions may use more knobs. If so, they will be properly set 00130 to their defaults by the future version of colamd_set_defaults, 00131 so that the code that calls colamd will not need to change, 00132 assuming that you either use colamd_set_defaults, or pass a 00133 (double *) NULL pointer as the knobs array to colamd. 00134 00135 ---------------------------------------------------------------------------- 00136 colamd: 00137 ---------------------------------------------------------------------------- 00138 00139 Usage: 00140 00141 colamd (n_row, n_col, Alen, A, p, knobs) ; 00142 00143 Purpose: 00144 00145 Computes a column ordering (Q) of A such that P(AQ)=LU or 00146 (AQ)'AQ=LL' have less fill-in and require fewer floating point 00147 operations than factorizing the unpermuted matrix A or A'A, 00148 respectively. 00149 00150 Arguments: 00151 00152 int n_row ; 00153 00154 Number of rows in the matrix A. 00155 Restriction: n_row >= 0. 00156 Colamd returns FALSE if n_row is negative. 00157 00158 int n_col ; 00159 00160 Number of columns in the matrix A. 00161 Restriction: n_col >= 0. 00162 Colamd returns FALSE if n_col is negative. 00163 00164 int Alen ; 00165 00166 Restriction (see note): 00167 Alen >= 2*nnz + 6*(n_col+1) + 4*(n_row+1) + n_col + COLAMD_STATS 00168 Colamd returns FALSE if these conditions are not met. 00169 00170 Note: this restriction makes an modest assumption regarding 00171 the size of the two typedef'd structures, below. We do, 00172 however, guarantee that 00173 Alen >= colamd_recommended (nnz, n_row, n_col) 00174 will be sufficient. 00175 00176 int A [Alen] ; Input argument, stats on output. 00177 00178 A is an integer array of size Alen. Alen must be at least as 00179 large as the bare minimum value given above, but this is very 00180 low, and can result in excessive run time. For best 00181 performance, we recommend that Alen be greater than or equal to 00182 colamd_recommended (nnz, n_row, n_col), which adds 00183 nnz/5 to the bare minimum value given above. 00184 00185 On input, the row indices of the entries in column c of the 00186 matrix are held in A [(p [c]) ... (p [c+1]-1)]. The row indices 00187 in a given column c need not be in ascending order, and 00188 duplicate row indices may be be present. However, colamd will 00189 work a little faster if both of these conditions are met 00190 (Colamd puts the matrix into this format, if it finds that the 00191 the conditions are not met). 00192 00193 The matrix is 0-based. That is, rows are in the range 0 to 00194 n_row-1, and columns are in the range 0 to n_col-1. Colamd 00195 returns FALSE if any row index is out of range. 00196 00197 The contents of A are modified during ordering, and are thus 00198 undefined on output with the exception of a few statistics 00199 about the ordering (A [0..COLAMD_STATS-1]): 00200 A [0]: number of dense or empty rows ignored. 00201 A [1]: number of dense or empty columns ignored (and ordered 00202 last in the output permutation p) 00203 A [2]: number of garbage collections performed. 00204 A [3]: 0, if all row indices in each column were in sorted 00205 order, and no duplicates were present. 00206 1, otherwise (in which case colamd had to do more work) 00207 Note that a row can become "empty" if it contains only 00208 "dense" and/or "empty" columns, and similarly a column can 00209 become "empty" if it only contains "dense" and/or "empty" rows. 00210 Future versions may return more statistics in A, but the usage 00211 of these 4 entries in A will remain unchanged. 00212 00213 int p [n_col+1] ; Both input and output argument. 00214 00215 p is an integer array of size n_col+1. On input, it holds the 00216 "pointers" for the column form of the matrix A. Column c of 00217 the matrix A is held in A [(p [c]) ... (p [c+1]-1)]. The first 00218 entry, p [0], must be zero, and p [c] <= p [c+1] must hold 00219 for all c in the range 0 to n_col-1. The value p [n_col] is 00220 thus the total number of entries in the pattern of the matrix A. 00221 Colamd returns FALSE if these conditions are not met. 00222 00223 On output, if colamd returns TRUE, the array p holds the column 00224 permutation (Q, for P(AQ)=LU or (AQ)'(AQ)=LL'), where p [0] is 00225 the first column index in the new ordering, and p [n_col-1] is 00226 the last. That is, p [k] = j means that column j of A is the 00227 kth pivot column, in AQ, where k is in the range 0 to n_col-1 00228 (p [0] = j means that column j of A is the first column in AQ). 00229 00230 If colamd returns FALSE, then no permutation is returned, and 00231 p is undefined on output. 00232 00233 double knobs [COLAMD_KNOBS] ; Input only. 00234 00235 See colamd_set_defaults for a description. If the knobs array 00236 is not present (that is, if a (double *) NULL pointer is passed 00237 in its place), then the default values of the parameters are 00238 used instead. 00239 00240 */ 00241 00242 00243 /* ========================================================================== */ 00244 /* === Include files ======================================================== */ 00245 /* ========================================================================== */ 00246 00247 /* limits.h: the largest positive integer (INT_MAX) */ 00248 #include <limits.h> 00249 00250 /* colamd.h: knob array size, stats output size, and global prototypes */ 00251 #include "colamd.h" 00252 00253 /* ========================================================================== */ 00254 /* === Scaffolding code definitions ======================================== */ 00255 /* ========================================================================== */ 00256 00257 /* Ensure that debugging is turned off: */ 00258 #ifndef NDEBUG 00259 #define NDEBUG 00260 #endif 00261 00262 /* assert.h: the assert macro (no debugging if NDEBUG is defined) */ 00263 #include <assert.h> 00264 00265 /* 00266 Our "scaffolding code" philosophy: In our opinion, well-written library 00267 code should keep its "debugging" code, and just normally have it turned off 00268 by the compiler so as not to interfere with performance. This serves 00269 several purposes: 00270 00271 (1) assertions act as comments to the reader, telling you what the code 00272 expects at that point. All assertions will always be true (unless 00273 there really is a bug, of course). 00274 00275 (2) leaving in the scaffolding code assists anyone who would like to modify 00276 the code, or understand the algorithm (by reading the debugging output, 00277 one can get a glimpse into what the code is doing). 00278 00279 (3) (gasp!) for actually finding bugs. This code has been heavily tested 00280 and "should" be fully functional and bug-free ... but you never know... 00281 00282 To enable debugging, comment out the "#define NDEBUG" above. The code will 00283 become outrageously slow when debugging is enabled. To control the level of 00284 debugging output, set an environment variable D to 0 (little), 1 (some), 00285 2, 3, or 4 (lots). 00286 */ 00287 00288 /* ========================================================================== */ 00289 /* === Row and Column structures ============================================ */ 00290 /* ========================================================================== */ 00291 00292 typedef struct ColInfo_struct 00293 { 00294 int start ; /* index for A of first row in this column, or DEAD */ 00295 /* if column is dead */ 00296 int length ; /* number of rows in this column */ 00297 union 00298 { 00299 int thickness ; /* number of original columns represented by this */ 00300 /* col, if the column is alive */ 00301 int parent ; /* parent in parent tree super-column structure, if */ 00302 /* the column is dead */ 00303 } shared1 ; 00304 union 00305 { 00306 int score ; /* the score used to maintain heap, if col is alive */ 00307 int order ; /* pivot ordering of this column, if col is dead */ 00308 } shared2 ; 00309 union 00310 { 00311 int headhash ; /* head of a hash bucket, if col is at the head of */ 00312 /* a degree list */ 00313 int hash ; /* hash value, if col is not in a degree list */ 00314 int prev ; /* previous column in degree list, if col is in a */ 00315 /* degree list (but not at the head of a degree list) */ 00316 } shared3 ; 00317 union 00318 { 00319 int degree_next ; /* next column, if col is in a degree list */ 00320 int hash_next ; /* next column, if col is in a hash list */ 00321 } shared4 ; 00322 00323 } ColInfo ; 00324 00325 typedef struct RowInfo_struct 00326 { 00327 int start ; /* index for A of first col in this row */ 00328 int length ; /* number of principal columns in this row */ 00329 union 00330 { 00331 int degree ; /* number of principal & non-principal columns in row */ 00332 int p ; /* used as a row pointer in init_rows_cols () */ 00333 } shared1 ; 00334 union 00335 { 00336 int mark ; /* for computing set differences and marking dead rows*/ 00337 int first_column ;/* first column in row (used in garbage collection) */ 00338 } shared2 ; 00339 00340 } RowInfo ; 00341 00342 /* ========================================================================== */ 00343 /* === Definitions ========================================================== */ 00344 /* ========================================================================== */ 00345 00346 #define MAX(a,b) (((a) > (b)) ? (a) : (b)) 00347 #define MIN(a,b) (((a) < (b)) ? (a) : (b)) 00348 00349 #define ONES_COMPLEMENT(r) (-(r)-1) 00350 00351 #define TRUE (1) 00352 #define FALSE (0) 00353 #define EMPTY (-1) 00354 00355 /* Row and column status */ 00356 #define ALIVE (0) 00357 #define DEAD (-1) 00358 00359 /* Column status */ 00360 #define DEAD_PRINCIPAL (-1) 00361 #define DEAD_NON_PRINCIPAL (-2) 00362 00363 /* Macros for row and column status update and checking. */ 00364 #define ROW_IS_DEAD(r) ROW_IS_MARKED_DEAD (Row[r].shared2.mark) 00365 #define ROW_IS_MARKED_DEAD(row_mark) (row_mark < ALIVE) 00366 #define ROW_IS_ALIVE(r) (Row [r].shared2.mark >= ALIVE) 00367 #define COL_IS_DEAD(c) (Col [c].start < ALIVE) 00368 #define COL_IS_ALIVE(c) (Col [c].start >= ALIVE) 00369 #define COL_IS_DEAD_PRINCIPAL(c) (Col [c].start == DEAD_PRINCIPAL) 00370 #define KILL_ROW(r) { Row [r].shared2.mark = DEAD ; } 00371 #define KILL_PRINCIPAL_COL(c) { Col [c].start = DEAD_PRINCIPAL ; } 00372 #define KILL_NON_PRINCIPAL_COL(c) { Col [c].start = DEAD_NON_PRINCIPAL ; } 00373 00374 /* Routines are either PUBLIC (user-callable) or PRIVATE (not user-callable) */ 00375 #define PUBLIC 00376 #define PRIVATE static 00377 00378 /* ========================================================================== */ 00379 /* === Prototypes of PRIVATE routines ======================================= */ 00380 /* ========================================================================== */ 00381 00382 PRIVATE int init_rows_cols 00383 ( 00384 int n_row, 00385 int n_col, 00386 RowInfo Row [], 00387 ColInfo Col [], 00388 int A [], 00389 int p [] 00390 ) ; 00391 00392 PRIVATE void init_scoring 00393 ( 00394 int n_row, 00395 int n_col, 00396 RowInfo Row [], 00397 ColInfo Col [], 00398 int A [], 00399 int head [], 00400 double knobs [COLAMD_KNOBS], 00401 int *p_n_row2, 00402 int *p_n_col2, 00403 int *p_max_deg 00404 ) ; 00405 00406 PRIVATE int find_ordering 00407 ( 00408 int n_row, 00409 int n_col, 00410 int Alen, 00411 RowInfo Row [], 00412 ColInfo Col [], 00413 int A [], 00414 int head [], 00415 int n_col2, 00416 int max_deg, 00417 int pfree 00418 ) ; 00419 00420 PRIVATE void order_children 00421 ( 00422 int n_col, 00423 ColInfo Col [], 00424 int p [] 00425 ) ; 00426 00427 PRIVATE void detect_super_cols 00428 ( 00429 #ifndef NDEBUG 00430 int n_col, 00431 RowInfo Row [], 00432 #endif 00433 ColInfo Col [], 00434 int A [], 00435 int head [], 00436 int row_start, 00437 int row_length 00438 ) ; 00439 00440 PRIVATE int garbage_collection 00441 ( 00442 int n_row, 00443 int n_col, 00444 RowInfo Row [], 00445 ColInfo Col [], 00446 int A [], 00447 int *pfree 00448 ) ; 00449 00450 PRIVATE int clear_mark 00451 ( 00452 int n_row, 00453 RowInfo Row [] 00454 ) ; 00455 00456 /* ========================================================================== */ 00457 /* === Debugging definitions ================================================ */ 00458 /* ========================================================================== */ 00459 00460 #ifndef NDEBUG 00461 00462 /* === With debugging ======================================================= */ 00463 00464 /* stdlib.h: for getenv and atoi, to get debugging level from environment */ 00465 #include <stdlib.h> 00466 00467 /* stdio.h: for printf (no printing if debugging is turned off) */ 00468 #include <stdio.h> 00469 00470 PRIVATE void debug_deg_lists 00471 ( 00472 int n_row, 00473 int n_col, 00474 RowInfo Row [], 00475 ColInfo Col [], 00476 int head [], 00477 int min_score, 00478 int should, 00479 int max_deg 00480 ) ; 00481 00482 PRIVATE void debug_mark 00483 ( 00484 int n_row, 00485 RowInfo Row [], 00486 int tag_mark, 00487 int max_mark 00488 ) ; 00489 00490 PRIVATE void debug_matrix 00491 ( 00492 int n_row, 00493 int n_col, 00494 RowInfo Row [], 00495 ColInfo Col [], 00496 int A [] 00497 ) ; 00498 00499 PRIVATE void debug_structures 00500 ( 00501 int n_row, 00502 int n_col, 00503 RowInfo Row [], 00504 ColInfo Col [], 00505 int A [], 00506 int n_col2 00507 ) ; 00508 00509 /* the following is the *ONLY* global variable in this file, and is only */ 00510 /* present when debugging */ 00511 00512 PRIVATE int debug_colamd ; /* debug print level */ 00513 00514 #define DEBUG0(params) { (void) printf params ; } 00515 #define DEBUG1(params) { if (debug_colamd >= 1) (void) printf params ; } 00516 #define DEBUG2(params) { if (debug_colamd >= 2) (void) printf params ; } 00517 #define DEBUG3(params) { if (debug_colamd >= 3) (void) printf params ; } 00518 #define DEBUG4(params) { if (debug_colamd >= 4) (void) printf params ; } 00519 00520 #else 00521 00522 /* === No debugging ========================================================= */ 00523 00524 #define DEBUG0(params) ; 00525 #define DEBUG1(params) ; 00526 #define DEBUG2(params) ; 00527 #define DEBUG3(params) ; 00528 #define DEBUG4(params) ; 00529 00530 #endif 00531 00532 /* ========================================================================== */ 00533 00534 00535 /* ========================================================================== */ 00536 /* === USER-CALLABLE ROUTINES: ============================================== */ 00537 /* ========================================================================== */ 00538 00539 00540 /* ========================================================================== */ 00541 /* === colamd_recommended =================================================== */ 00542 /* ========================================================================== */ 00543 00544 /* 00545 The colamd_recommended routine returns the suggested size for Alen. This 00546 value has been determined to provide good balance between the number of 00547 garbage collections and the memory requirements for colamd. 00548 */ 00549 00550 PUBLIC int colamd_recommended /* returns recommended value of Alen. */ 00551 ( 00552 /* === Parameters ======================================================= */ 00553 00554 int nnz, /* number of nonzeros in A */ 00555 int n_row, /* number of rows in A */ 00556 int n_col /* number of columns in A */ 00557 ) 00558 { 00559 /* === Local variables ================================================== */ 00560 00561 int minimum ; /* bare minimum requirements */ 00562 int recommended ; /* recommended value of Alen */ 00563 00564 if (nnz < 0 || n_row < 0 || n_col < 0) 00565 { 00566 /* return -1 if any input argument is corrupted */ 00567 DEBUG0 (("colamd_recommended error!")) ; 00568 DEBUG0 ((" nnz: %d, n_row: %d, n_col: %d\n", nnz, n_row, n_col)) ; 00569 return (-1) ; 00570 } 00571 00572 minimum = 00573 2 * (nnz) /* for A */ 00574 + (((n_col) + 1) * sizeof (ColInfo) / sizeof (int)) /* for Col */ 00575 + (((n_row) + 1) * sizeof (RowInfo) / sizeof (int)) /* for Row */ 00576 + n_col /* minimum elbow room to guarrantee success */ 00577 + COLAMD_STATS ; /* for output statistics */ 00578 00579 /* recommended is equal to the minumum plus enough memory to keep the */ 00580 /* number garbage collections low */ 00581 recommended = minimum + nnz/5 ; 00582 00583 return (recommended) ; 00584 } 00585 00586 00587 /* ========================================================================== */ 00588 /* === colamd_set_defaults ================================================== */ 00589 /* ========================================================================== */ 00590 00591 /* 00592 The colamd_set_defaults routine sets the default values of the user- 00593 controllable parameters for colamd: 00594 00595 knobs [0] rows with knobs[0]*n_col entries or more are removed 00596 prior to ordering. 00597 00598 knobs [1] columns with knobs[1]*n_row entries or more are removed 00599 prior to ordering, and placed last in the column 00600 permutation. 00601 00602 knobs [2..19] unused, but future versions might use this 00603 */ 00604 00605 PUBLIC void colamd_set_defaults 00606 ( 00607 /* === Parameters ======================================================= */ 00608 00609 double knobs [COLAMD_KNOBS] /* knob array */ 00610 ) 00611 { 00612 /* === Local variables ================================================== */ 00613 00614 int i ; 00615 00616 if (!knobs) 00617 { 00618 return ; /* no knobs to initialize */ 00619 } 00620 for (i = 0 ; i < COLAMD_KNOBS ; i++) 00621 { 00622 knobs [i] = 0 ; 00623 } 00624 knobs [COLAMD_DENSE_ROW] = 0.5 ; /* ignore rows over 50% dense */ 00625 knobs [COLAMD_DENSE_COL] = 0.5 ; /* ignore columns over 50% dense */ 00626 } 00627 00628 00629 /* ========================================================================== */ 00630 /* === colamd =============================================================== */ 00631 /* ========================================================================== */ 00632 00633 /* 00634 The colamd routine computes a column ordering Q of a sparse matrix 00635 A such that the LU factorization P(AQ) = LU remains sparse, where P is 00636 selected via partial pivoting. The routine can also be viewed as 00637 providing a permutation Q such that the Cholesky factorization 00638 (AQ)'(AQ) = LL' remains sparse. 00639 00640 On input, the nonzero patterns of the columns of A are stored in the 00641 array A, in order 0 to n_col-1. A is held in 0-based form (rows in the 00642 range 0 to n_row-1 and columns in the range 0 to n_col-1). Row indices 00643 for column c are located in A [(p [c]) ... (p [c+1]-1)], where p [0] = 0, 00644 and thus p [n_col] is the number of entries in A. The matrix is 00645 destroyed on output. The row indices within each column do not have to 00646 be sorted (from small to large row indices), and duplicate row indices 00647 may be present. However, colamd will work a little faster if columns are 00648 sorted and no duplicates are present. Matlab 5.2 always passes the matrix 00649 with sorted columns, and no duplicates. 00650 00651 The integer array A is of size Alen. Alen must be at least of size 00652 (where nnz is the number of entries in A): 00653 00654 nnz for the input column form of A 00655 + nnz for a row form of A that colamd generates 00656 + 6*(n_col+1) for a ColInfo Col [0..n_col] array 00657 (this assumes sizeof (ColInfo) is 6 int's). 00658 + 4*(n_row+1) for a RowInfo Row [0..n_row] array 00659 (this assumes sizeof (RowInfo) is 4 int's). 00660 + elbow_room must be at least n_col. We recommend at least 00661 nnz/5 in addition to that. If sufficient, 00662 changes in the elbow room affect the ordering 00663 time only, not the ordering itself. 00664 + COLAMD_STATS for the output statistics 00665 00666 Colamd returns FALSE is memory is insufficient, or TRUE otherwise. 00667 00668 On input, the caller must specify: 00669 00670 n_row the number of rows of A 00671 n_col the number of columns of A 00672 Alen the size of the array A 00673 A [0 ... nnz-1] the row indices, where nnz = p [n_col] 00674 A [nnz ... Alen-1] (need not be initialized by the user) 00675 p [0 ... n_col] the column pointers, p [0] = 0, and p [n_col] 00676 is the number of entries in A. Column c of A 00677 is stored in A [p [c] ... p [c+1]-1]. 00678 knobs [0 ... 19] a set of parameters that control the behavior 00679 of colamd. If knobs is a NULL pointer the 00680 defaults are used. The user-callable 00681 colamd_set_defaults routine sets the default 00682 parameters. See that routine for a description 00683 of the user-controllable parameters. 00684 00685 If the return value of Colamd is TRUE, then on output: 00686 00687 p [0 ... n_col-1] the column permutation. p [0] is the first 00688 column index, and p [n_col-1] is the last. 00689 That is, p [k] = j means that column j of A 00690 is the kth column of AQ. 00691 00692 A is undefined on output (the matrix pattern is 00693 destroyed), except for the following statistics: 00694 00695 A [0] the number of dense (or empty) rows ignored 00696 A [1] the number of dense (or empty) columms. These 00697 are ordered last, in their natural order. 00698 A [2] the number of garbage collections performed. 00699 If this is excessive, then you would have 00700 gotten your results faster if Alen was larger. 00701 A [3] 0, if all row indices in each column were in 00702 sorted order and no duplicates were present. 00703 1, if there were unsorted or duplicate row 00704 indices in the input. You would have gotten 00705 your results faster if A [3] was returned as 0. 00706 00707 If the return value of Colamd is FALSE, then A and p are undefined on 00708 output. 00709 */ 00710 00711 PUBLIC int colamd /* returns TRUE if successful */ 00712 ( 00713 /* === Parameters ======================================================= */ 00714 00715 int n_row, /* number of rows in A */ 00716 int n_col, /* number of columns in A */ 00717 int Alen, /* length of A */ 00718 int A [], /* row indices of A */ 00719 int p [], /* pointers to columns in A */ 00720 double knobs [COLAMD_KNOBS] /* parameters (uses defaults if NULL) */ 00721 ) 00722 { 00723 /* === Local variables ================================================== */ 00724 00725 int i ; /* loop index */ 00726 int nnz ; /* nonzeros in A */ 00727 int Row_size ; /* size of Row [], in integers */ 00728 int Col_size ; /* size of Col [], in integers */ 00729 int elbow_room ; /* remaining free space */ 00730 RowInfo *Row ; /* pointer into A of Row [0..n_row] array */ 00731 ColInfo *Col ; /* pointer into A of Col [0..n_col] array */ 00732 int n_col2 ; /* number of non-dense, non-empty columns */ 00733 int n_row2 ; /* number of non-dense, non-empty rows */ 00734 int ngarbage ; /* number of garbage collections performed */ 00735 int max_deg ; /* maximum row degree */ 00736 double default_knobs [COLAMD_KNOBS] ; /* default knobs knobs array */ 00737 int init_result ; /* return code from initialization */ 00738 00739 #ifndef NDEBUG 00740 debug_colamd = 0 ; /* no debug printing */ 00741 /* get "D" environment variable, which gives the debug printing level */ 00742 if (getenv ("D")) debug_colamd = atoi (getenv ("D")) ; 00743 DEBUG0 (("debug version, D = %d (THIS WILL BE SLOOOOW!)\n", debug_colamd)) ; 00744 #endif 00745 00746 /* === Check the input arguments ======================================== */ 00747 00748 if (n_row < 0 || n_col < 0 || !A || !p) 00749 { 00750 /* n_row and n_col must be non-negative, A and p must be present */ 00751 DEBUG0 (("colamd error! %d %d %d\n", n_row, n_col, Alen)) ; 00752 return (FALSE) ; 00753 } 00754 nnz = p [n_col] ; 00755 if (nnz < 0 || p [0] != 0) 00756 { 00757 /* nnz must be non-negative, and p [0] must be zero */ 00758 DEBUG0 (("colamd error! %d %d\n", nnz, p [0])) ; 00759 return (FALSE) ; 00760 } 00761 00762 /* === If no knobs, set default parameters ============================== */ 00763 00764 if (!knobs) 00765 { 00766 knobs = default_knobs ; 00767 colamd_set_defaults (knobs) ; 00768 } 00769 00770 /* === Allocate the Row and Col arrays from array A ===================== */ 00771 00772 Col_size = (n_col + 1) * sizeof (ColInfo) / sizeof (int) ; 00773 Row_size = (n_row + 1) * sizeof (RowInfo) / sizeof (int) ; 00774 elbow_room = Alen - (2*nnz + Col_size + Row_size) ; 00775 if (elbow_room < n_col + COLAMD_STATS) 00776 { 00777 /* not enough space in array A to perform the ordering */ 00778 DEBUG0 (("colamd error! elbow_room %d, %d\n", elbow_room,n_col)) ; 00779 return (FALSE) ; 00780 } 00781 Alen = 2*nnz + elbow_room ; 00782 Col = (ColInfo *) &A [Alen] ; 00783 Row = (RowInfo *) &A [Alen + Col_size] ; 00784 00785 /* === Construct the row and column data structures ===================== */ 00786 00787 init_result = init_rows_cols (n_row, n_col, Row, Col, A, p) ; 00788 if (init_result == -1) 00789 { 00790 /* input matrix is invalid */ 00791 DEBUG0 (("colamd error! matrix invalid\n")) ; 00792 return (FALSE) ; 00793 } 00794 00795 /* === Initialize scores, kill dense rows/columns ======================= */ 00796 00797 init_scoring (n_row, n_col, Row, Col, A, p, knobs, 00798 &n_row2, &n_col2, &max_deg) ; 00799 00800 /* === Order the supercolumns =========================================== */ 00801 00802 ngarbage = find_ordering (n_row, n_col, Alen, Row, Col, A, p, 00803 n_col2, max_deg, 2*nnz) ; 00804 00805 /* === Order the non-principal columns ================================== */ 00806 00807 order_children (n_col, Col, p) ; 00808 00809 /* === Return statistics in A =========================================== */ 00810 00811 for (i = 0 ; i < COLAMD_STATS ; i++) 00812 { 00813 A [i] = 0 ; 00814 } 00815 A [COLAMD_DENSE_ROW] = n_row - n_row2 ; 00816 A [COLAMD_DENSE_COL] = n_col - n_col2 ; 00817 A [COLAMD_DEFRAG_COUNT] = ngarbage ; 00818 A [COLAMD_JUMBLED_COLS] = init_result ; 00819 00820 return (TRUE) ; 00821 } 00822 00823 00824 /* ========================================================================== */ 00825 /* === NON-USER-CALLABLE ROUTINES: ========================================== */ 00826 /* ========================================================================== */ 00827 00828 /* There are no user-callable routines beyond this point in the file */ 00829 00830 00831 /* ========================================================================== */ 00832 /* === init_rows_cols ======================================================= */ 00833 /* ========================================================================== */ 00834 00835 /* 00836 Takes the column form of the matrix in A and creates the row form of the 00837 matrix. Also, row and column attributes are stored in the Col and Row 00838 structs. If the columns are un-sorted or contain duplicate row indices, 00839 this routine will also sort and remove duplicate row indices from the 00840 column form of the matrix. Returns -1 on error, 1 if columns jumbled, 00841 or 0 if columns not jumbled. Not user-callable. 00842 */ 00843 00844 PRIVATE int init_rows_cols /* returns status code */ 00845 ( 00846 /* === Parameters ======================================================= */ 00847 00848 int n_row, /* number of rows of A */ 00849 int n_col, /* number of columns of A */ 00850 RowInfo Row [], /* of size n_row+1 */ 00851 ColInfo Col [], /* of size n_col+1 */ 00852 int A [], /* row indices of A, of size Alen */ 00853 int p [] /* pointers to columns in A, of size n_col+1 */ 00854 ) 00855 { 00856 /* === Local variables ================================================== */ 00857 00858 int col ; /* a column index */ 00859 int row ; /* a row index */ 00860 int *cp ; /* a column pointer */ 00861 int *cp_end ; /* a pointer to the end of a column */ 00862 int *rp ; /* a row pointer */ 00863 int *rp_end ; /* a pointer to the end of a row */ 00864 int last_start ; /* start index of previous column in A */ 00865 int start ; /* start index of column in A */ 00866 int last_row ; /* previous row */ 00867 int jumbled_columns ; /* indicates if columns are jumbled */ 00868 00869 /* === Initialize columns, and check column pointers ==================== */ 00870 00871 last_start = 0 ; 00872 for (col = 0 ; col < n_col ; col++) 00873 { 00874 start = p [col] ; 00875 if (start < last_start) 00876 { 00877 /* column pointers must be non-decreasing */ 00878 DEBUG0 (("colamd error! last p %d p [col] %d\n",last_start,start)); 00879 return (-1) ; 00880 } 00881 Col [col].start = start ; 00882 Col [col].length = p [col+1] - start ; 00883 Col [col].shared1.thickness = 1 ; 00884 Col [col].shared2.score = 0 ; 00885 Col [col].shared3.prev = EMPTY ; 00886 Col [col].shared4.degree_next = EMPTY ; 00887 last_start = start ; 00888 } 00889 /* must check the end pointer for last column */ 00890 if (p [n_col] < last_start) 00891 { 00892 /* column pointers must be non-decreasing */ 00893 DEBUG0 (("colamd error! last p %d p [n_col] %d\n",p[col],last_start)) ; 00894 return (-1) ; 00895 } 00896 00897 /* p [0..n_col] no longer needed, used as "head" in subsequent routines */ 00898 00899 /* === Scan columns, compute row degrees, and check row indices ========= */ 00900 00901 jumbled_columns = FALSE ; 00902 00903 for (row = 0 ; row < n_row ; row++) 00904 { 00905 Row [row].length = 0 ; 00906 Row [row].shared2.mark = -1 ; 00907 } 00908 00909 for (col = 0 ; col < n_col ; col++) 00910 { 00911 last_row = -1 ; 00912 00913 cp = &A [p [col]] ; 00914 cp_end = &A [p [col+1]] ; 00915 00916 while (cp < cp_end) 00917 { 00918 row = *cp++ ; 00919 00920 /* make sure row indices within range */ 00921 if (row < 0 || row >= n_row) 00922 { 00923 DEBUG0 (("colamd error! col %d row %d last_row %d\n", 00924 col, row, last_row)) ; 00925 return (-1) ; 00926 } 00927 else if (row <= last_row) 00928 { 00929 /* row indices are not sorted or repeated, thus cols */ 00930 /* are jumbled */ 00931 jumbled_columns = TRUE ; 00932 } 00933 /* prevent repeated row from being counted */ 00934 if (Row [row].shared2.mark != col) 00935 { 00936 Row [row].length++ ; 00937 Row [row].shared2.mark = col ; 00938 last_row = row ; 00939 } 00940 else 00941 { 00942 /* this is a repeated entry in the column, */ 00943 /* it will be removed */ 00944 Col [col].length-- ; 00945 } 00946 } 00947 } 00948 00949 /* === Compute row pointers ============================================= */ 00950 00951 /* row form of the matrix starts directly after the column */ 00952 /* form of matrix in A */ 00953 Row [0].start = p [n_col] ; 00954 Row [0].shared1.p = Row [0].start ; 00955 Row [0].shared2.mark = -1 ; 00956 for (row = 1 ; row < n_row ; row++) 00957 { 00958 Row [row].start = Row [row-1].start + Row [row-1].length ; 00959 Row [row].shared1.p = Row [row].start ; 00960 Row [row].shared2.mark = -1 ; 00961 } 00962 00963 /* === Create row form ================================================== */ 00964 00965 if (jumbled_columns) 00966 { 00967 /* if cols jumbled, watch for repeated row indices */ 00968 for (col = 0 ; col < n_col ; col++) 00969 { 00970 cp = &A [p [col]] ; 00971 cp_end = &A [p [col+1]] ; 00972 while (cp < cp_end) 00973 { 00974 row = *cp++ ; 00975 if (Row [row].shared2.mark != col) 00976 { 00977 A [(Row [row].shared1.p)++] = col ; 00978 Row [row].shared2.mark = col ; 00979 } 00980 } 00981 } 00982 } 00983 else 00984 { 00985 /* if cols not jumbled, we don't need the mark (this is faster) */ 00986 for (col = 0 ; col < n_col ; col++) 00987 { 00988 cp = &A [p [col]] ; 00989 cp_end = &A [p [col+1]] ; 00990 while (cp < cp_end) 00991 { 00992 A [(Row [*cp++].shared1.p)++] = col ; 00993 } 00994 } 00995 } 00996 00997 /* === Clear the row marks and set row degrees ========================== */ 00998 00999 for (row = 0 ; row < n_row ; row++) 01000 { 01001 Row [row].shared2.mark = 0 ; 01002 Row [row].shared1.degree = Row [row].length ; 01003 } 01004 01005 /* === See if we need to re-create columns ============================== */ 01006 01007 if (jumbled_columns) 01008 { 01009 01010 #ifndef NDEBUG 01011 /* make sure column lengths are correct */ 01012 for (col = 0 ; col < n_col ; col++) 01013 { 01014 p [col] = Col [col].length ; 01015 } 01016 for (row = 0 ; row < n_row ; row++) 01017 { 01018 rp = &A [Row [row].start] ; 01019 rp_end = rp + Row [row].length ; 01020 while (rp < rp_end) 01021 { 01022 p [*rp++]-- ; 01023 } 01024 } 01025 for (col = 0 ; col < n_col ; col++) 01026 { 01027 assert (p [col] == 0) ; 01028 } 01029 /* now p is all zero (different than when debugging is turned off) */ 01030 #endif 01031 01032 /* === Compute col pointers ========================================= */ 01033 01034 /* col form of the matrix starts at A [0]. */ 01035 /* Note, we may have a gap between the col form and the row */ 01036 /* form if there were duplicate entries, if so, it will be */ 01037 /* removed upon the first garbage collection */ 01038 Col [0].start = 0 ; 01039 p [0] = Col [0].start ; 01040 for (col = 1 ; col < n_col ; col++) 01041 { 01042 /* note that the lengths here are for pruned columns, i.e. */ 01043 /* no duplicate row indices will exist for these columns */ 01044 Col [col].start = Col [col-1].start + Col [col-1].length ; 01045 p [col] = Col [col].start ; 01046 } 01047 01048 /* === Re-create col form =========================================== */ 01049 01050 for (row = 0 ; row < n_row ; row++) 01051 { 01052 rp = &A [Row [row].start] ; 01053 rp_end = rp + Row [row].length ; 01054 while (rp < rp_end) 01055 { 01056 A [(p [*rp++])++] = row ; 01057 } 01058 } 01059 return (1) ; 01060 } 01061 else 01062 { 01063 /* no columns jumbled (this is faster) */ 01064 return (0) ; 01065 } 01066 } 01067 01068 01069 /* ========================================================================== */ 01070 /* === init_scoring ========================================================= */ 01071 /* ========================================================================== */ 01072 01073 /* 01074 Kills dense or empty columns and rows, calculates an initial score for 01075 each column, and places all columns in the degree lists. Not user-callable. 01076 */ 01077 01078 PRIVATE void init_scoring 01079 ( 01080 /* === Parameters ======================================================= */ 01081 01082 int n_row, /* number of rows of A */ 01083 int n_col, /* number of columns of A */ 01084 RowInfo Row [], /* of size n_row+1 */ 01085 ColInfo Col [], /* of size n_col+1 */ 01086 int A [], /* column form and row form of A */ 01087 int head [], /* of size n_col+1 */ 01088 double knobs [COLAMD_KNOBS],/* parameters */ 01089 int *p_n_row2, /* number of non-dense, non-empty rows */ 01090 int *p_n_col2, /* number of non-dense, non-empty columns */ 01091 int *p_max_deg /* maximum row degree */ 01092 ) 01093 { 01094 /* === Local variables ================================================== */ 01095 01096 int c ; /* a column index */ 01097 int r, row ; /* a row index */ 01098 int *cp ; /* a column pointer */ 01099 int deg ; /* degree (# entries) of a row or column */ 01100 int *cp_end ; /* a pointer to the end of a column */ 01101 int *new_cp ; /* new column pointer */ 01102 int col_length ; /* length of pruned column */ 01103 int score ; /* current column score */ 01104 int n_col2 ; /* number of non-dense, non-empty columns */ 01105 int n_row2 ; /* number of non-dense, non-empty rows */ 01106 int dense_row_count ; /* remove rows with more entries than this */ 01107 int dense_col_count ; /* remove cols with more entries than this */ 01108 int min_score ; /* smallest column score */ 01109 int max_deg ; /* maximum row degree */ 01110 int next_col ; /* Used to add to degree list.*/ 01111 #ifndef NDEBUG 01112 int debug_count ; /* debug only. */ 01113 #endif 01114 01115 /* === Extract knobs ==================================================== */ 01116 01117 dense_row_count = MAX (0, MIN (knobs [COLAMD_DENSE_ROW] * n_col, n_col)) ; 01118 dense_col_count = MAX (0, MIN (knobs [COLAMD_DENSE_COL] * n_row, n_row)) ; 01119 DEBUG0 (("densecount: %d %d\n", dense_row_count, dense_col_count)) ; 01120 max_deg = 0 ; 01121 n_col2 = n_col ; 01122 n_row2 = n_row ; 01123 01124 /* === Kill empty columns =============================================== */ 01125 01126 /* Put the empty columns at the end in their natural, so that LU */ 01127 /* factorization can proceed as far as possible. */ 01128 for (c = n_col-1 ; c >= 0 ; c--) 01129 { 01130 deg = Col [c].length ; 01131 if (deg == 0) 01132 { 01133 /* this is a empty column, kill and order it last */ 01134 Col [c].shared2.order = --n_col2 ; 01135 KILL_PRINCIPAL_COL (c) ; 01136 } 01137 } 01138 DEBUG0 (("null columns killed: %d\n", n_col - n_col2)) ; 01139 01140 /* === Kill dense columns =============================================== */ 01141 01142 /* Put the dense columns at the end, in their natural order */ 01143 for (c = n_col-1 ; c >= 0 ; c--) 01144 { 01145 /* skip any dead columns */ 01146 if (COL_IS_DEAD (c)) 01147 { 01148 continue ; 01149 } 01150 deg = Col [c].length ; 01151 if (deg > dense_col_count) 01152 { 01153 /* this is a dense column, kill and order it last */ 01154 Col [c].shared2.order = --n_col2 ; 01155 /* decrement the row degrees */ 01156 cp = &A [Col [c].start] ; 01157 cp_end = cp + Col [c].length ; 01158 while (cp < cp_end) 01159 { 01160 Row [*cp++].shared1.degree-- ; 01161 } 01162 KILL_PRINCIPAL_COL (c) ; 01163 } 01164 } 01165 DEBUG0 (("Dense and null columns killed: %d\n", n_col - n_col2)) ; 01166 01167 /* === Kill dense and empty rows ======================================== */ 01168 01169 for (r = 0 ; r < n_row ; r++) 01170 { 01171 deg = Row [r].shared1.degree ; 01172 assert (deg >= 0 && deg <= n_col) ; 01173 if (deg > dense_row_count || deg == 0) 01174 { 01175 /* kill a dense or empty row */ 01176 KILL_ROW (r) ; 01177 --n_row2 ; 01178 } 01179 else 01180 { 01181 /* keep track of max degree of remaining rows */ 01182 max_deg = MAX (max_deg, deg) ; 01183 } 01184 } 01185 DEBUG0 (("Dense and null rows killed: %d\n", n_row - n_row2)) ; 01186 01187 /* === Compute initial column scores ==================================== */ 01188 01189 /* At this point the row degrees are accurate. They reflect the number */ 01190 /* of "live" (non-dense) columns in each row. No empty rows exist. */ 01191 /* Some "live" columns may contain only dead rows, however. These are */ 01192 /* pruned in the code below. */ 01193 01194 /* now find the initial matlab score for each column */ 01195 for (c = n_col-1 ; c >= 0 ; c--) 01196 { 01197 /* skip dead column */ 01198 if (COL_IS_DEAD (c)) 01199 { 01200 continue ; 01201 } 01202 score = 0 ; 01203 cp = &A [Col [c].start] ; 01204 new_cp = cp ; 01205 cp_end = cp + Col [c].length ; 01206 while (cp < cp_end) 01207 { 01208 /* get a row */ 01209 row = *cp++ ; 01210 /* skip if dead */ 01211 if (ROW_IS_DEAD (row)) 01212 { 01213 continue ; 01214 } 01215 /* compact the column */ 01216 *new_cp++ = row ; 01217 /* add row's external degree */ 01218 score += Row [row].shared1.degree - 1 ; 01219 /* guard against integer overflow */ 01220 score = MIN (score, n_col) ; 01221 } 01222 /* determine pruned column length */ 01223 col_length = (int) (new_cp - &A [Col [c].start]) ; 01224 if (col_length == 0) 01225 { 01226 /* a newly-made null column (all rows in this col are "dense" */ 01227 /* and have already been killed) */ 01228 DEBUG0 (("Newly null killed: %d\n", c)) ; 01229 Col [c].shared2.order = --n_col2 ; 01230 KILL_PRINCIPAL_COL (c) ; 01231 } 01232 else 01233 { 01234 /* set column length and set score */ 01235 assert (score >= 0) ; 01236 assert (score <= n_col) ; 01237 Col [c].length = col_length ; 01238 Col [c].shared2.score = score ; 01239 } 01240 } 01241 DEBUG0 (("Dense, null, and newly-null columns killed: %d\n",n_col-n_col2)) ; 01242 01243 /* At this point, all empty rows and columns are dead. All live columns */ 01244 /* are "clean" (containing no dead rows) and simplicial (no supercolumns */ 01245 /* yet). Rows may contain dead columns, but all live rows contain at */ 01246 /* least one live column. */ 01247 01248 #ifndef NDEBUG 01249 debug_structures (n_row, n_col, Row, Col, A, n_col2) ; 01250 #endif 01251 01252 /* === Initialize degree lists ========================================== */ 01253 01254 #ifndef NDEBUG 01255 debug_count = 0 ; 01256 #endif 01257 01258 /* clear the hash buckets */ 01259 for (c = 0 ; c <= n_col ; c++) 01260 { 01261 head [c] = EMPTY ; 01262 } 01263 min_score = n_col ; 01264 /* place in reverse order, so low column indices are at the front */ 01265 /* of the lists. This is to encourage natural tie-breaking */ 01266 for (c = n_col-1 ; c >= 0 ; c--) 01267 { 01268 /* only add principal columns to degree lists */ 01269 if (COL_IS_ALIVE (c)) 01270 { 01271 DEBUG4 (("place %d score %d minscore %d ncol %d\n", 01272 c, Col [c].shared2.score, min_score, n_col)) ; 01273 01274 /* === Add columns score to DList =============================== */ 01275 01276 score = Col [c].shared2.score ; 01277 01278 assert (min_score >= 0) ; 01279 assert (min_score <= n_col) ; 01280 assert (score >= 0) ; 01281 assert (score <= n_col) ; 01282 assert (head [score] >= EMPTY) ; 01283 01284 /* now add this column to dList at proper score location */ 01285 next_col = head [score] ; 01286 Col [c].shared3.prev = EMPTY ; 01287 Col [c].shared4.degree_next = next_col ; 01288 01289 /* if there already was a column with the same score, set its */ 01290 /* previous pointer to this new column */ 01291 if (next_col != EMPTY) 01292 { 01293 Col [next_col].shared3.prev = c ; 01294 } 01295 head [score] = c ; 01296 01297 /* see if this score is less than current min */ 01298 min_score = MIN (min_score, score) ; 01299 01300 #ifndef NDEBUG 01301 debug_count++ ; 01302 #endif 01303 } 01304 } 01305 01306 #ifndef NDEBUG 01307 DEBUG0 (("Live cols %d out of %d, non-princ: %d\n", 01308 debug_count, n_col, n_col-debug_count)) ; 01309 assert (debug_count == n_col2) ; 01310 debug_deg_lists (n_row, n_col, Row, Col, head, min_score, n_col2, max_deg) ; 01311 #endif 01312 01313 /* === Return number of remaining columns, and max row degree =========== */ 01314 01315 *p_n_col2 = n_col2 ; 01316 *p_n_row2 = n_row2 ; 01317 *p_max_deg = max_deg ; 01318 } 01319 01320 01321 /* ========================================================================== */ 01322 /* === find_ordering ======================================================== */ 01323 /* ========================================================================== */ 01324 01325 /* 01326 Order the principal columns of the supercolumn form of the matrix 01327 (no supercolumns on input). Uses a minimum approximate column minimum 01328 degree ordering method. Not user-callable. 01329 */ 01330 01331 PRIVATE int find_ordering /* return the number of garbage collections */ 01332 ( 01333 /* === Parameters ======================================================= */ 01334 01335 int n_row, /* number of rows of A */ 01336 int n_col, /* number of columns of A */ 01337 int Alen, /* size of A, 2*nnz + elbow_room or larger */ 01338 RowInfo Row [], /* of size n_row+1 */ 01339 ColInfo Col [], /* of size n_col+1 */ 01340 int A [], /* column form and row form of A */ 01341 int head [], /* of size n_col+1 */ 01342 int n_col2, /* Remaining columns to order */ 01343 int max_deg, /* Maximum row degree */ 01344 int pfree /* index of first free slot (2*nnz on entry) */ 01345 ) 01346 { 01347 /* === Local variables ================================================== */ 01348 01349 int k ; /* current pivot ordering step */ 01350 int pivot_col ; /* current pivot column */ 01351 int *cp ; /* a column pointer */ 01352 int *rp ; /* a row pointer */ 01353 int pivot_row ; /* current pivot row */ 01354 int *new_cp ; /* modified column pointer */ 01355 int *new_rp ; /* modified row pointer */ 01356 int pivot_row_start ; /* pointer to start of pivot row */ 01357 int pivot_row_degree ; /* # of columns in pivot row */ 01358 int pivot_row_length ; /* # of supercolumns in pivot row */ 01359 int pivot_col_score ; /* score of pivot column */ 01360 int needed_memory ; /* free space needed for pivot row */ 01361 int *cp_end ; /* pointer to the end of a column */ 01362 int *rp_end ; /* pointer to the end of a row */ 01363 int row ; /* a row index */ 01364 int col ; /* a column index */ 01365 int max_score ; /* maximum possible score */ 01366 int cur_score ; /* score of current column */ 01367 unsigned int hash ; /* hash value for supernode detection */ 01368 int head_column ; /* head of hash bucket */ 01369 int first_col ; /* first column in hash bucket */ 01370 int tag_mark ; /* marker value for mark array */ 01371 int row_mark ; /* Row [row].shared2.mark */ 01372 int set_difference ; /* set difference size of row with pivot row */ 01373 int min_score ; /* smallest column score */ 01374 int col_thickness ; /* "thickness" (# of columns in a supercol) */ 01375 int max_mark ; /* maximum value of tag_mark */ 01376 int pivot_col_thickness ; /* number of columns represented by pivot col */ 01377 int prev_col ; /* Used by Dlist operations. */ 01378 int next_col ; /* Used by Dlist operations. */ 01379 int ngarbage ; /* number of garbage collections performed */ 01380 #ifndef NDEBUG 01381 int debug_d ; /* debug loop counter */ 01382 int debug_step = 0 ; /* debug loop counter */ 01383 #endif 01384 01385 /* === Initialization and clear mark ==================================== */ 01386 01387 max_mark = INT_MAX - n_col ; /* INT_MAX defined in <limits.h> */ 01388 tag_mark = clear_mark (n_row, Row) ; 01389 min_score = 0 ; 01390 ngarbage = 0 ; 01391 DEBUG0 (("Ordering.. n_col2=%d\n", n_col2)) ; 01392 01393 /* === Order the columns ================================================ */ 01394 01395 for (k = 0 ; k < n_col2 ; /* 'k' is incremented below */) 01396 { 01397 01398 #ifndef NDEBUG 01399 if (debug_step % 100 == 0) 01400 { 01401 DEBUG0 (("\n... Step k: %d out of n_col2: %d\n", k, n_col2)) ; 01402 } 01403 else 01404 { 01405 DEBUG1 (("\n----------Step k: %d out of n_col2: %d\n", k, n_col2)) ; 01406 } 01407 debug_step++ ; 01408 debug_deg_lists (n_row, n_col, Row, Col, head, 01409 min_score, n_col2-k, max_deg) ; 01410 debug_matrix (n_row, n_col, Row, Col, A) ; 01411 #endif 01412 01413 /* === Select pivot column, and order it ============================ */ 01414 01415 /* make sure degree list isn't empty */ 01416 assert (min_score >= 0) ; 01417 assert (min_score <= n_col) ; 01418 assert (head [min_score] >= EMPTY) ; 01419 01420 #ifndef NDEBUG 01421 for (debug_d = 0 ; debug_d < min_score ; debug_d++) 01422 { 01423 assert (head [debug_d] == EMPTY) ; 01424 } 01425 #endif 01426 01427 /* get pivot column from head of minimum degree list */ 01428 while (head [min_score] == EMPTY && min_score < n_col) 01429 { 01430 min_score++ ; 01431 } 01432 pivot_col = head [min_score] ; 01433 assert (pivot_col >= 0 && pivot_col <= n_col) ; 01434 next_col = Col [pivot_col].shared4.degree_next ; 01435 head [min_score] = next_col ; 01436 if (next_col != EMPTY) 01437 { 01438 Col [next_col].shared3.prev = EMPTY ; 01439 } 01440 01441 assert (COL_IS_ALIVE (pivot_col)) ; 01442 DEBUG3 (("Pivot col: %d\n", pivot_col)) ; 01443 01444 /* remember score for defrag check */ 01445 pivot_col_score = Col [pivot_col].shared2.score ; 01446 01447 /* the pivot column is the kth column in the pivot order */ 01448 Col [pivot_col].shared2.order = k ; 01449 01450 /* increment order count by column thickness */ 01451 pivot_col_thickness = Col [pivot_col].shared1.thickness ; 01452 k += pivot_col_thickness ; 01453 assert (pivot_col_thickness > 0) ; 01454 01455 /* === Garbage_collection, if necessary ============================= */ 01456 01457 needed_memory = MIN (pivot_col_score, n_col - k) ; 01458 if (pfree + needed_memory >= Alen) 01459 { 01460 pfree = garbage_collection (n_row, n_col, Row, Col, A, &A [pfree]) ; 01461 ngarbage++ ; 01462 /* after garbage collection we will have enough */ 01463 assert (pfree + needed_memory < Alen) ; 01464 /* garbage collection has wiped out the Row[].shared2.mark array */ 01465 tag_mark = clear_mark (n_row, Row) ; 01466 #ifndef NDEBUG 01467 debug_matrix (n_row, n_col, Row, Col, A) ; 01468 #endif 01469 } 01470 01471 /* === Compute pivot row pattern ==================================== */ 01472 01473 /* get starting location for this new merged row */ 01474 pivot_row_start = pfree ; 01475 01476 /* initialize new row counts to zero */ 01477 pivot_row_degree = 0 ; 01478 01479 /* tag pivot column as having been visited so it isn't included */ 01480 /* in merged pivot row */ 01481 Col [pivot_col].shared1.thickness = -pivot_col_thickness ; 01482 01483 /* pivot row is the union of all rows in the pivot column pattern */ 01484 cp = &A [Col [pivot_col].start] ; 01485 cp_end = cp + Col [pivot_col].length ; 01486 while (cp < cp_end) 01487 { 01488 /* get a row */ 01489 row = *cp++ ; 01490 DEBUG4 (("Pivot col pattern %d %d\n", ROW_IS_ALIVE (row), row)) ; 01491 /* skip if row is dead */ 01492 if (ROW_IS_DEAD (row)) 01493 { 01494 continue ; 01495 } 01496 rp = &A [Row [row].start] ; 01497 rp_end = rp + Row [row].length ; 01498 while (rp < rp_end) 01499 { 01500 /* get a column */ 01501 col = *rp++ ; 01502 /* add the column, if alive and untagged */ 01503 col_thickness = Col [col].shared1.thickness ; 01504 if (col_thickness > 0 && COL_IS_ALIVE (col)) 01505 { 01506 /* tag column in pivot row */ 01507 Col [col].shared1.thickness = -col_thickness ; 01508 assert (pfree < Alen) ; 01509 /* place column in pivot row */ 01510 A [pfree++] = col ; 01511 pivot_row_degree += col_thickness ; 01512 } 01513 } 01514 } 01515 01516 /* clear tag on pivot column */ 01517 Col [pivot_col].shared1.thickness = pivot_col_thickness ; 01518 max_deg = MAX (max_deg, pivot_row_degree) ; 01519 01520 #ifndef NDEBUG 01521 DEBUG3 (("check2\n")) ; 01522 debug_mark (n_row, Row, tag_mark, max_mark) ; 01523 #endif 01524 01525 /* === Kill all rows used to construct pivot row ==================== */ 01526 01527 /* also kill pivot row, temporarily */ 01528 cp = &A [Col [pivot_col].start] ; 01529 cp_end = cp + Col [pivot_col].length ; 01530 while (cp < cp_end) 01531 { 01532 /* may be killing an already dead row */ 01533 row = *cp++ ; 01534 DEBUG2 (("Kill row in pivot col: %d\n", row)) ; 01535 KILL_ROW (row) ; 01536 } 01537 01538 /* === Select a row index to use as the new pivot row =============== */ 01539 01540 pivot_row_length = pfree - pivot_row_start ; 01541 if (pivot_row_length > 0) 01542 { 01543 /* pick the "pivot" row arbitrarily (first row in col) */ 01544 pivot_row = A [Col [pivot_col].start] ; 01545 DEBUG2 (("Pivotal row is %d\n", pivot_row)) ; 01546 } 01547 else 01548 { 01549 /* there is no pivot row, since it is of zero length */ 01550 pivot_row = EMPTY ; 01551 assert (pivot_row_length == 0) ; 01552 } 01553 assert (Col [pivot_col].length > 0 || pivot_row_length == 0) ; 01554 01555 /* === Approximate degree computation =============================== */ 01556 01557 /* Here begins the computation of the approximate degree. The column */ 01558 /* score is the sum of the pivot row "length", plus the size of the */ 01559 /* set differences of each row in the column minus the pattern of the */ 01560 /* pivot row itself. The column ("thickness") itself is also */ 01561 /* excluded from the column score (we thus use an approximate */ 01562 /* external degree). */ 01563 01564 /* The time taken by the following code (compute set differences, and */ 01565 /* add them up) is proportional to the size of the data structure */ 01566 /* being scanned - that is, the sum of the sizes of each column in */ 01567 /* the pivot row. Thus, the amortized time to compute a column score */ 01568 /* is proportional to the size of that column (where size, in this */ 01569 /* context, is the column "length", or the number of row indices */ 01570 /* in that column). The number of row indices in a column is */ 01571 /* monotonically non-decreasing, from the length of the original */ 01572 /* column on input to colamd. */ 01573 01574 /* === Compute set differences ====================================== */ 01575 01576 DEBUG1 (("** Computing set differences phase. **\n")) ; 01577 01578 /* pivot row is currently dead - it will be revived later. */ 01579 01580 DEBUG2 (("Pivot row: ")) ; 01581 /* for each column in pivot row */ 01582 rp = &A [pivot_row_start] ; 01583 rp_end = rp + pivot_row_length ; 01584 while (rp < rp_end) 01585 { 01586 col = *rp++ ; 01587 assert (COL_IS_ALIVE (col) && col != pivot_col) ; 01588 DEBUG2 (("Col: %d\n", col)) ; 01589 01590 /* clear tags used to construct pivot row pattern */ 01591 col_thickness = -Col [col].shared1.thickness ; 01592 assert (col_thickness > 0) ; 01593 Col [col].shared1.thickness = col_thickness ; 01594 01595 /* === Remove column from degree list =========================== */ 01596 01597 cur_score = Col [col].shared2.score ; 01598 prev_col = Col [col].shared3.prev ; 01599 next_col = Col [col].shared4.degree_next ; 01600 assert (cur_score >= 0) ; 01601 assert (cur_score <= n_col) ; 01602 assert (cur_score >= EMPTY) ; 01603 if (prev_col == EMPTY) 01604 { 01605 head [cur_score] = next_col ; 01606 } 01607 else 01608 { 01609 Col [prev_col].shared4.degree_next = next_col ; 01610 } 01611 if (next_col != EMPTY) 01612 { 01613 Col [next_col].shared3.prev = prev_col ; 01614 } 01615 01616 /* === Scan the column ========================================== */ 01617 01618 cp = &A [Col [col].start] ; 01619 cp_end = cp + Col [col].length ; 01620 while (cp < cp_end) 01621 { 01622 /* get a row */ 01623 row = *cp++ ; 01624 row_mark = Row [row].shared2.mark ; 01625 /* skip if dead */ 01626 if (ROW_IS_MARKED_DEAD (row_mark)) 01627 { 01628 continue ; 01629 } 01630 assert (row != pivot_row) ; 01631 set_difference = row_mark - tag_mark ; 01632 /* check if the row has been seen yet */ 01633 if (set_difference < 0) 01634 { 01635 assert (Row [row].shared1.degree <= max_deg) ; 01636 set_difference = Row [row].shared1.degree ; 01637 } 01638 /* subtract column thickness from this row's set difference */ 01639 set_difference -= col_thickness ; 01640 assert (set_difference >= 0) ; 01641 /* absorb this row if the set difference becomes zero */ 01642 if (set_difference == 0) 01643 { 01644 DEBUG1 (("aggressive absorption. Row: %d\n", row)) ; 01645 KILL_ROW (row) ; 01646 } 01647 else 01648 { 01649 /* save the new mark */ 01650 Row [row].shared2.mark = set_difference + tag_mark ; 01651 } 01652 } 01653 } 01654 01655 #ifndef NDEBUG 01656 debug_deg_lists (n_row, n_col, Row, Col, head, 01657 min_score, n_col2-k-pivot_row_degree, max_deg) ; 01658 #endif 01659 01660 /* === Add up set differences for each column ======================= */ 01661 01662 DEBUG1 (("** Adding set differences phase. **\n")) ; 01663 01664 /* for each column in pivot row */ 01665 rp = &A [pivot_row_start] ; 01666 rp_end = rp + pivot_row_length ; 01667 while (rp < rp_end) 01668 { 01669 /* get a column */ 01670 col = *rp++ ; 01671 assert (COL_IS_ALIVE (col) && col != pivot_col) ; 01672 hash = 0 ; 01673 cur_score = 0 ; 01674 cp = &A [Col [col].start] ; 01675 /* compact the column */ 01676 new_cp = cp ; 01677 cp_end = cp + Col [col].length ; 01678 01679 DEBUG2 (("Adding set diffs for Col: %d.\n", col)) ; 01680 01681 while (cp < cp_end) 01682 { 01683 /* get a row */ 01684 row = *cp++ ; 01685 assert(row >= 0 && row < n_row) ; 01686 row_mark = Row [row].shared2.mark ; 01687 /* skip if dead */ 01688 if (ROW_IS_MARKED_DEAD (row_mark)) 01689 { 01690 continue ; 01691 } 01692 assert (row_mark > tag_mark) ; 01693 /* compact the column */ 01694 *new_cp++ = row ; 01695 /* compute hash function */ 01696 hash += row ; 01697 /* add set difference */ 01698 cur_score += row_mark - tag_mark ; 01699 /* integer overflow... */ 01700 cur_score = MIN (cur_score, n_col) ; 01701 } 01702 01703 /* recompute the column's length */ 01704 Col [col].length = (int) (new_cp - &A [Col [col].start]) ; 01705 01706 /* === Further mass elimination ================================= */ 01707 01708 if (Col [col].length == 0) 01709 { 01710 DEBUG1 (("further mass elimination. Col: %d\n", col)) ; 01711 /* nothing left but the pivot row in this column */ 01712 KILL_PRINCIPAL_COL (col) ; 01713 pivot_row_degree -= Col [col].shared1.thickness ; 01714 assert (pivot_row_degree >= 0) ; 01715 /* order it */ 01716 Col [col].shared2.order = k ; 01717 /* increment order count by column thickness */ 01718 k += Col [col].shared1.thickness ; 01719 } 01720 else 01721 { 01722 /* === Prepare for supercolumn detection ==================== */ 01723 01724 DEBUG2 (("Preparing supercol detection for Col: %d.\n", col)) ; 01725 01726 /* save score so far */ 01727 Col [col].shared2.score = cur_score ; 01728 01729 /* add column to hash table, for supercolumn detection */ 01730 hash %= n_col + 1 ; 01731 01732 DEBUG2 ((" Hash = %d, n_col = %d.\n", hash, n_col)) ; 01733 assert (hash <= n_col) ; 01734 01735 head_column = head [hash] ; 01736 if (head_column > EMPTY) 01737 { 01738 /* degree list "hash" is non-empty, use prev (shared3) of */ 01739 /* first column in degree list as head of hash bucket */ 01740 first_col = Col [head_column].shared3.headhash ; 01741 Col [head_column].shared3.headhash = col ; 01742 } 01743 else 01744 { 01745 /* degree list "hash" is empty, use head as hash bucket */ 01746 first_col = - (head_column + 2) ; 01747 head [hash] = - (col + 2) ; 01748 } 01749 Col [col].shared4.hash_next = first_col ; 01750 01751 /* save hash function in Col [col].shared3.hash */ 01752 Col [col].shared3.hash = (int) hash ; 01753 assert (COL_IS_ALIVE (col)) ; 01754 } 01755 } 01756 01757 /* The approximate external column degree is now computed. */ 01758 01759 /* === Supercolumn detection ======================================== */ 01760 01761 DEBUG1 (("** Supercolumn detection phase. **\n")) ; 01762 01763 detect_super_cols ( 01764 #ifndef NDEBUG 01765 n_col, Row, 01766 #endif 01767 Col, A, head, pivot_row_start, pivot_row_length) ; 01768 01769 /* === Kill the pivotal column ====================================== */ 01770 01771 KILL_PRINCIPAL_COL (pivot_col) ; 01772 01773 /* === Clear mark =================================================== */ 01774 01775 tag_mark += (max_deg + 1) ; 01776 if (tag_mark >= max_mark) 01777 { 01778 DEBUG1 (("clearing tag_mark\n")) ; 01779 tag_mark = clear_mark (n_row, Row) ; 01780 } 01781 #ifndef NDEBUG 01782 DEBUG3 (("check3\n")) ; 01783 debug_mark (n_row, Row, tag_mark, max_mark) ; 01784 #endif 01785 01786 /* === Finalize the new pivot row, and column scores ================ */ 01787 01788 DEBUG1 (("** Finalize scores phase. **\n")) ; 01789 01790 /* for each column in pivot row */ 01791 rp = &A [pivot_row_start] ; 01792 /* compact the pivot row */ 01793 new_rp = rp ; 01794 rp_end = rp + pivot_row_length ; 01795 while (rp < rp_end) 01796 { 01797 col = *rp++ ; 01798 /* skip dead columns */ 01799 if (COL_IS_DEAD (col)) 01800 { 01801 continue ; 01802 } 01803 *new_rp++ = col ; 01804 /* add new pivot row to column */ 01805 A [Col [col].start + (Col [col].length++)] = pivot_row ; 01806 01807 /* retrieve score so far and add on pivot row's degree. */ 01808 /* (we wait until here for this in case the pivot */ 01809 /* row's degree was reduced due to mass elimination). */ 01810 cur_score = Col [col].shared2.score + pivot_row_degree ; 01811 01812 /* calculate the max possible score as the number of */ 01813 /* external columns minus the 'k' value minus the */ 01814 /* columns thickness */ 01815 max_score = n_col - k - Col [col].shared1.thickness ; 01816 01817 /* make the score the external degree of the union-of-rows */ 01818 cur_score -= Col [col].shared1.thickness ; 01819 01820 /* make sure score is less or equal than the max score */ 01821 cur_score = MIN (cur_score, max_score) ; 01822 assert (cur_score >= 0) ; 01823 01824 /* store updated score */ 01825 Col [col].shared2.score = cur_score ; 01826 01827 /* === Place column back in degree list ========================= */ 01828 01829 assert (min_score >= 0) ; 01830 assert (min_score <= n_col) ; 01831 assert (cur_score >= 0) ; 01832 assert (cur_score <= n_col) ; 01833 assert (head [cur_score] >= EMPTY) ; 01834 next_col = head [cur_score] ; 01835 Col [col].shared4.degree_next = next_col ; 01836 Col [col].shared3.prev = EMPTY ; 01837 if (next_col != EMPTY) 01838 { 01839 Col [next_col].shared3.prev = col ; 01840 } 01841 head [cur_score] = col ; 01842 01843 /* see if this score is less than current min */ 01844 min_score = MIN (min_score, cur_score) ; 01845 01846 } 01847 01848 #ifndef NDEBUG 01849 debug_deg_lists (n_row, n_col, Row, Col, head, 01850 min_score, n_col2-k, max_deg) ; 01851 #endif 01852 01853 /* === Resurrect the new pivot row ================================== */ 01854 01855 if (pivot_row_degree > 0) 01856 { 01857 /* update pivot row length to reflect any cols that were killed */ 01858 /* during super-col detection and mass elimination */ 01859 Row [pivot_row].start = pivot_row_start ; 01860 Row [pivot_row].length = (int) (new_rp - &A[pivot_row_start]) ; 01861 Row [pivot_row].shared1.degree = pivot_row_degree ; 01862 Row [pivot_row].shared2.mark = 0 ; 01863 /* pivot row is no longer dead */ 01864 } 01865 } 01866 01867 /* === All principal columns have now been ordered ====================== */ 01868 01869 return (ngarbage) ; 01870 } 01871 01872 01873 /* ========================================================================== */ 01874 /* === order_children ======================================================= */ 01875 /* ========================================================================== */ 01876 01877 /* 01878 The find_ordering routine has ordered all of the principal columns (the 01879 representatives of the supercolumns). The non-principal columns have not 01880 yet been ordered. This routine orders those columns by walking up the 01881 parent tree (a column is a child of the column which absorbed it). The 01882 final permutation vector is then placed in p [0 ... n_col-1], with p [0] 01883 being the first column, and p [n_col-1] being the last. It doesn't look 01884 like it at first glance, but be assured that this routine takes time linear 01885 in the number of columns. Although not immediately obvious, the time 01886 taken by this routine is O (n_col), that is, linear in the number of 01887 columns. Not user-callable. 01888 */ 01889 01890 PRIVATE void order_children 01891 ( 01892 /* === Parameters ======================================================= */ 01893 01894 int n_col, /* number of columns of A */ 01895 ColInfo Col [], /* of size n_col+1 */ 01896 int p [] /* p [0 ... n_col-1] is the column permutation*/ 01897 ) 01898 { 01899 /* === Local variables ================================================== */ 01900 01901 int i ; /* loop counter for all columns */ 01902 int c ; /* column index */ 01903 int parent ; /* index of column's parent */ 01904 int order ; /* column's order */ 01905 01906 /* === Order each non-principal column ================================== */ 01907 01908 for (i = 0 ; i < n_col ; i++) 01909 { 01910 /* find an un-ordered non-principal column */ 01911 assert (COL_IS_DEAD (i)) ; 01912 if (!COL_IS_DEAD_PRINCIPAL (i) && Col [i].shared2.order == EMPTY) 01913 { 01914 parent = i ; 01915 /* once found, find its principal parent */ 01916 do 01917 { 01918 parent = Col [parent].shared1.parent ; 01919 } while (!COL_IS_DEAD_PRINCIPAL (parent)) ; 01920 01921 /* now, order all un-ordered non-principal columns along path */ 01922 /* to this parent. collapse tree at the same time */ 01923 c = i ; 01924 /* get order of parent */ 01925 order = Col [parent].shared2.order ; 01926 01927 do 01928 { 01929 assert (Col [c].shared2.order == EMPTY) ; 01930 01931 /* order this column */ 01932 Col [c].shared2.order = order++ ; 01933 /* collaps tree */ 01934 Col [c].shared1.parent = parent ; 01935 01936 /* get immediate parent of this column */ 01937 c = Col [c].shared1.parent ; 01938 01939 /* continue until we hit an ordered column. There are */ 01940 /* guarranteed not to be anymore unordered columns */ 01941 /* above an ordered column */ 01942 } while (Col [c].shared2.order == EMPTY) ; 01943 01944 /* re-order the super_col parent to largest order for this group */ 01945 Col [parent].shared2.order = order ; 01946 } 01947 } 01948 01949 /* === Generate the permutation ========================================= */ 01950 01951 for (c = 0 ; c < n_col ; c++) 01952 { 01953 p [Col [c].shared2.order] = c ; 01954 } 01955 } 01956 01957 01958 /* ========================================================================== */ 01959 /* === detect_super_cols ==================================================== */ 01960 /* ========================================================================== */ 01961 01962 /* 01963 Detects supercolumns by finding matches between columns in the hash buckets. 01964 Check amongst columns in the set A [row_start ... row_start + row_length-1]. 01965 The columns under consideration are currently *not* in the degree lists, 01966 and have already been placed in the hash buckets. 01967 01968 The hash bucket for columns whose hash function is equal to h is stored 01969 as follows: 01970 01971 if head [h] is >= 0, then head [h] contains a degree list, so: 01972 01973 head [h] is the first column in degree bucket h. 01974 Col [head [h]].headhash gives the first column in hash bucket h. 01975 01976 otherwise, the degree list is empty, and: 01977 01978 -(head [h] + 2) is the first column in hash bucket h. 01979 01980 For a column c in a hash bucket, Col [c].shared3.prev is NOT a "previous 01981 column" pointer. Col [c].shared3.hash is used instead as the hash number 01982 for that column. The value of Col [c].shared4.hash_next is the next column 01983 in the same hash bucket. 01984 01985 Assuming no, or "few" hash collisions, the time taken by this routine is 01986 linear in the sum of the sizes (lengths) of each column whose score has 01987 just been computed in the approximate degree computation. 01988 Not user-callable. 01989 */ 01990 01991 PRIVATE void detect_super_cols 01992 ( 01993 /* === Parameters ======================================================= */ 01994 01995 #ifndef NDEBUG 01996 /* these two parameters are only needed when debugging is enabled: */ 01997 int n_col, /* number of columns of A */ 01998 RowInfo Row [], /* of size n_row+1 */ 01999 #endif 02000 ColInfo Col [], /* of size n_col+1 */ 02001 int A [], /* row indices of A */ 02002 int head [], /* head of degree lists and hash buckets */ 02003 int row_start, /* pointer to set of columns to check */ 02004 int row_length /* number of columns to check */ 02005 ) 02006 { 02007 /* === Local variables ================================================== */ 02008 02009 int hash ; /* hash # for a column */ 02010 int *rp ; /* pointer to a row */ 02011 int c ; /* a column index */ 02012 int super_c ; /* column index of the column to absorb into */ 02013 int *cp1 ; /* column pointer for column super_c */ 02014 int *cp2 ; /* column pointer for column c */ 02015 int length ; /* length of column super_c */ 02016 int prev_c ; /* column preceding c in hash bucket */ 02017 int i ; /* loop counter */ 02018 int *rp_end ; /* pointer to the end of the row */ 02019 int col ; /* a column index in the row to check */ 02020 int head_column ; /* first column in hash bucket or degree list */ 02021 int first_col ; /* first column in hash bucket */ 02022 02023 /* === Consider each column in the row ================================== */ 02024 02025 rp = &A [row_start] ; 02026 rp_end = rp + row_length ; 02027 while (rp < rp_end) 02028 { 02029 col = *rp++ ; 02030 if (COL_IS_DEAD (col)) 02031 { 02032 continue ; 02033 } 02034 02035 /* get hash number for this column */ 02036 hash = Col [col].shared3.hash ; 02037 assert (hash <= n_col) ; 02038 02039 /* === Get the first column in this hash bucket ===================== */ 02040 02041 head_column = head [hash] ; 02042 if (head_column > EMPTY) 02043 { 02044 first_col = Col [head_column].shared3.headhash ; 02045 } 02046 else 02047 { 02048 first_col = - (head_column + 2) ; 02049 } 02050 02051 /* === Consider each column in the hash bucket ====================== */ 02052 02053 for (super_c = first_col ; super_c != EMPTY ; 02054 super_c = Col [super_c].shared4.hash_next) 02055 { 02056 assert (COL_IS_ALIVE (super_c)) ; 02057 assert (Col [super_c].shared3.hash == hash) ; 02058 length = Col [super_c].length ; 02059 02060 /* prev_c is the column preceding column c in the hash bucket */ 02061 prev_c = super_c ; 02062 02063 /* === Compare super_c with all columns after it ================ */ 02064 02065 for (c = Col [super_c].shared4.hash_next ; 02066 c != EMPTY ; c = Col [c].shared4.hash_next) 02067 { 02068 assert (c != super_c) ; 02069 assert (COL_IS_ALIVE (c)) ; 02070 assert (Col [c].shared3.hash == hash) ; 02071 02072 /* not identical if lengths or scores are different */ 02073 if (Col [c].length != length || 02074 Col [c].shared2.score != Col [super_c].shared2.score) 02075 { 02076 prev_c = c ; 02077 continue ; 02078 } 02079 02080 /* compare the two columns */ 02081 cp1 = &A [Col [super_c].start] ; 02082 cp2 = &A [Col [c].start] ; 02083 02084 for (i = 0 ; i < length ; i++) 02085 { 02086 /* the columns are "clean" (no dead rows) */ 02087 assert (ROW_IS_ALIVE (*cp1)) ; 02088 assert (ROW_IS_ALIVE (*cp2)) ; 02089 /* row indices will same order for both supercols, */ 02090 /* no gather scatter nessasary */ 02091 if (*cp1++ != *cp2++) 02092 { 02093 break ; 02094 } 02095 } 02096 02097 /* the two columns are different if the for-loop "broke" */ 02098 if (i != length) 02099 { 02100 prev_c = c ; 02101 continue ; 02102 } 02103 02104 /* === Got it! two columns are identical =================== */ 02105 02106 assert (Col [c].shared2.score == Col [super_c].shared2.score) ; 02107 02108 Col [super_c].shared1.thickness += Col [c].shared1.thickness ; 02109 Col [c].shared1.parent = super_c ; 02110 KILL_NON_PRINCIPAL_COL (c) ; 02111 /* order c later, in order_children() */ 02112 Col [c].shared2.order = EMPTY ; 02113 /* remove c from hash bucket */ 02114 Col [prev_c].shared4.hash_next = Col [c].shared4.hash_next ; 02115 } 02116 } 02117 02118 /* === Empty this hash bucket ======================================= */ 02119 02120 if (head_column > EMPTY) 02121 { 02122 /* corresponding degree list "hash" is not empty */ 02123 Col [head_column].shared3.headhash = EMPTY ; 02124 } 02125 else 02126 { 02127 /* corresponding degree list "hash" is empty */ 02128 head [hash] = EMPTY ; 02129 } 02130 } 02131 } 02132 02133 02134 /* ========================================================================== */ 02135 /* === garbage_collection =================================================== */ 02136 /* ========================================================================== */ 02137 02138 /* 02139 Defragments and compacts columns and rows in the workspace A. Used when 02140 all avaliable memory has been used while performing row merging. Returns 02141 the index of the first free position in A, after garbage collection. The 02142 time taken by this routine is linear is the size of the array A, which is 02143 itself linear in the number of nonzeros in the input matrix. 02144 Not user-callable. 02145 */ 02146 02147 PRIVATE int garbage_collection /* returns the new value of pfree */ 02148 ( 02149 /* === Parameters ======================================================= */ 02150 02151 int n_row, /* number of rows */ 02152 int n_col, /* number of columns */ 02153 RowInfo Row [], /* row info */ 02154 ColInfo Col [], /* column info */ 02155 int A [], /* A [0 ... Alen-1] holds the matrix */ 02156 int *pfree /* &A [0] ... pfree is in use */ 02157 ) 02158 { 02159 /* === Local variables ================================================== */ 02160 02161 int *psrc ; /* source pointer */ 02162 int *pdest ; /* destination pointer */ 02163 int j ; /* counter */ 02164 int r ; /* a row index */ 02165 int c ; /* a column index */ 02166 int length ; /* length of a row or column */ 02167 02168 #ifndef NDEBUG 02169 int debug_rows ; 02170 DEBUG0 (("Defrag..\n")) ; 02171 for (psrc = &A[0] ; psrc < pfree ; psrc++) assert (*psrc >= 0) ; 02172 debug_rows = 0 ; 02173 #endif 02174 02175 /* === Defragment the columns =========================================== */ 02176 02177 pdest = &A[0] ; 02178 for (c = 0 ; c < n_col ; c++) 02179 { 02180 if (COL_IS_ALIVE (c)) 02181 { 02182 psrc = &A [Col [c].start] ; 02183 02184 /* move and compact the column */ 02185 assert (pdest <= psrc) ; 02186 Col [c].start = (int) (pdest - &A [0]) ; 02187 length = Col [c].length ; 02188 for (j = 0 ; j < length ; j++) 02189 { 02190 r = *psrc++ ; 02191 if (ROW_IS_ALIVE (r)) 02192 { 02193 *pdest++ = r ; 02194 } 02195 } 02196 Col [c].length = (int) (pdest - &A [Col [c].start]) ; 02197 } 02198 } 02199 02200 /* === Prepare to defragment the rows =================================== */ 02201 02202 for (r = 0 ; r < n_row ; r++) 02203 { 02204 if (ROW_IS_ALIVE (r)) 02205 { 02206 if (Row [r].length == 0) 02207 { 02208 /* this row is of zero length. cannot compact it, so kill it */ 02209 DEBUG0 (("Defrag row kill\n")) ; 02210 KILL_ROW (r) ; 02211 } 02212 else 02213 { 02214 /* save first column index in Row [r].shared2.first_column */ 02215 psrc = &A [Row [r].start] ; 02216 Row [r].shared2.first_column = *psrc ; 02217 assert (ROW_IS_ALIVE (r)) ; 02218 /* flag the start of the row with the one's complement of row */ 02219 *psrc = ONES_COMPLEMENT (r) ; 02220 #ifndef NDEBUG 02221 debug_rows++ ; 02222 #endif 02223 } 02224 } 02225 } 02226 02227 /* === Defragment the rows ============================================== */ 02228 02229 psrc = pdest ; 02230 while (psrc < pfree) 02231 { 02232 /* find a negative number ... the start of a row */ 02233 if (*psrc++ < 0) 02234 { 02235 psrc-- ; 02236 /* get the row index */ 02237 r = ONES_COMPLEMENT (*psrc) ; 02238 assert (r >= 0 && r < n_row) ; 02239 /* restore first column index */ 02240 *psrc = Row [r].shared2.first_column ; 02241 assert (ROW_IS_ALIVE (r)) ; 02242 02243 /* move and compact the row */ 02244 assert (pdest <= psrc) ; 02245 Row [r].start = (int) (pdest - &A [0]) ; 02246 length = Row [r].length ; 02247 for (j = 0 ; j < length ; j++) 02248 { 02249 c = *psrc++ ; 02250 if (COL_IS_ALIVE (c)) 02251 { 02252 *pdest++ = c ; 02253 } 02254 } 02255 Row [r].length = (int) (pdest - &A [Row [r].start]) ; 02256 #ifndef NDEBUG 02257 debug_rows-- ; 02258 #endif 02259 } 02260 } 02261 /* ensure we found all the rows */ 02262 assert (debug_rows == 0) ; 02263 02264 /* === Return the new value of pfree ==================================== */ 02265 02266 return ((int) (pdest - &A [0])) ; 02267 } 02268 02269 02270 /* ========================================================================== */ 02271 /* === clear_mark =========================================================== */ 02272 /* ========================================================================== */ 02273 02274 /* 02275 Clears the Row [].shared2.mark array, and returns the new tag_mark. 02276 Return value is the new tag_mark. Not user-callable. 02277 */ 02278 02279 PRIVATE int clear_mark /* return the new value for tag_mark */ 02280 ( 02281 /* === Parameters ======================================================= */ 02282 02283 int n_row, /* number of rows in A */ 02284 RowInfo Row [] /* Row [0 ... n_row-1].shared2.mark is set to zero */ 02285 ) 02286 { 02287 /* === Local variables ================================================== */ 02288 02289 int r ; 02290 02291 DEBUG0 (("Clear mark\n")) ; 02292 for (r = 0 ; r < n_row ; r++) 02293 { 02294 if (ROW_IS_ALIVE (r)) 02295 { 02296 Row [r].shared2.mark = 0 ; 02297 } 02298 } 02299 return (1) ; 02300 } 02301 02302 02303 /* ========================================================================== */ 02304 /* === debugging routines =================================================== */ 02305 /* ========================================================================== */ 02306 02307 /* When debugging is disabled, the remainder of this file is ignored. */ 02308 02309 #ifndef NDEBUG 02310 02311 02312 /* ========================================================================== */ 02313 /* === debug_structures ===================================================== */ 02314 /* ========================================================================== */ 02315 02316 /* 02317 At this point, all empty rows and columns are dead. All live columns 02318 are "clean" (containing no dead rows) and simplicial (no supercolumns 02319 yet). Rows may contain dead columns, but all live rows contain at 02320 least one live column. 02321 */ 02322 02323 PRIVATE void debug_structures 02324 ( 02325 /* === Parameters ======================================================= */ 02326 02327 int n_row, 02328 int n_col, 02329 RowInfo Row [], 02330 ColInfo Col [], 02331 int A [], 02332 int n_col2 02333 ) 02334 { 02335 /* === Local variables ================================================== */ 02336 02337 int i ; 02338 int c ; 02339 int *cp ; 02340 int *cp_end ; 02341 int len ; 02342 int score ; 02343 int r ; 02344 int *rp ; 02345 int *rp_end ; 02346 int deg ; 02347 02348 /* === Check A, Row, and Col ============================================ */ 02349 02350 for (c = 0 ; c < n_col ; c++) 02351 { 02352 if (COL_IS_ALIVE (c)) 02353 { 02354 len = Col [c].length ; 02355 score = Col [c].shared2.score ; 02356 DEBUG4 (("initial live col %5d %5d %5d\n", c, len, score)) ; 02357 assert (len > 0) ; 02358 assert (score >= 0) ; 02359 assert (Col [c].shared1.thickness == 1) ; 02360 cp = &A [Col [c].start] ; 02361 cp_end = cp + len ; 02362 while (cp < cp_end) 02363 { 02364 r = *cp++ ; 02365 assert (ROW_IS_ALIVE (r)) ; 02366 } 02367 } 02368 else 02369 { 02370 i = Col [c].shared2.order ; 02371 assert (i >= n_col2 && i < n_col) ; 02372 } 02373 } 02374 02375 for (r = 0 ; r < n_row ; r++) 02376 { 02377 if (ROW_IS_ALIVE (r)) 02378 { 02379 i = 0 ; 02380 len = Row [r].length ; 02381 deg = Row [r].shared1.degree ; 02382 assert (len > 0) ; 02383 assert (deg > 0) ; 02384 rp = &A [Row [r].start] ; 02385 rp_end = rp + len ; 02386 while (rp < rp_end) 02387 { 02388 c = *rp++ ; 02389 if (COL_IS_ALIVE (c)) 02390 { 02391 i++ ; 02392 } 02393 } 02394 assert (i > 0) ; 02395 } 02396 } 02397 } 02398 02399 02400 /* ========================================================================== */ 02401 /* === debug_deg_lists ====================================================== */ 02402 /* ========================================================================== */ 02403 02404 /* 02405 Prints the contents of the degree lists. Counts the number of columns 02406 in the degree list and compares it to the total it should have. Also 02407 checks the row degrees. 02408 */ 02409 02410 PRIVATE void debug_deg_lists 02411 ( 02412 /* === Parameters ======================================================= */ 02413 02414 int n_row, 02415 int n_col, 02416 RowInfo Row [], 02417 ColInfo Col [], 02418 int head [], 02419 int min_score, 02420 int should, 02421 int max_deg 02422 ) 02423 { 02424 /* === Local variables ================================================== */ 02425 02426 int deg ; 02427 int col ; 02428 int have ; 02429 int row ; 02430 02431 /* === Check the degree lists =========================================== */ 02432 02433 if (n_col > 10000 && debug_colamd <= 0) 02434 { 02435 return ; 02436 } 02437 have = 0 ; 02438 DEBUG4 (("Degree lists: %d\n", min_score)) ; 02439 for (deg = 0 ; deg <= n_col ; deg++) 02440 { 02441 col = head [deg] ; 02442 if (col == EMPTY) 02443 { 02444 continue ; 02445 } 02446 DEBUG4 (("%d:", deg)) ; 02447 while (col != EMPTY) 02448 { 02449 DEBUG4 ((" %d", col)) ; 02450 have += Col [col].shared1.thickness ; 02451 assert (COL_IS_ALIVE (col)) ; 02452 col = Col [col].shared4.degree_next ; 02453 } 02454 DEBUG4 (("\n")) ; 02455 } 02456 DEBUG4 (("should %d have %d\n", should, have)) ; 02457 assert (should == have) ; 02458 02459 /* === Check the row degrees ============================================ */ 02460 02461 if (n_row > 10000 && debug_colamd <= 0) 02462 { 02463 return ; 02464 } 02465 for (row = 0 ; row < n_row ; row++) 02466 { 02467 if (ROW_IS_ALIVE (row)) 02468 { 02469 assert (Row [row].shared1.degree <= max_deg) ; 02470 } 02471 } 02472 } 02473 02474 02475 /* ========================================================================== */ 02476 /* === debug_mark =========================================================== */ 02477 /* ========================================================================== */ 02478 02479 /* 02480 Ensures that the tag_mark is less that the maximum and also ensures that 02481 each entry in the mark array is less than the tag mark. 02482 */ 02483 02484 PRIVATE void debug_mark 02485 ( 02486 /* === Parameters ======================================================= */ 02487 02488 int n_row, 02489 RowInfo Row [], 02490 int tag_mark, 02491 int max_mark 02492 ) 02493 { 02494 /* === Local variables ================================================== */ 02495 02496 int r ; 02497 02498 /* === Check the Row marks ============================================== */ 02499 02500 assert (tag_mark > 0 && tag_mark <= max_mark) ; 02501 if (n_row > 10000 && debug_colamd <= 0) 02502 { 02503 return ; 02504 } 02505 for (r = 0 ; r < n_row ; r++) 02506 { 02507 assert (Row [r].shared2.mark < tag_mark) ; 02508 } 02509 } 02510 02511 02512 /* ========================================================================== */ 02513 /* === debug_matrix ========================================================= */ 02514 /* ========================================================================== */ 02515 02516 /* 02517 Prints out the contents of the columns and the rows. 02518 */ 02519 02520 PRIVATE void debug_matrix 02521 ( 02522 /* === Parameters ======================================================= */ 02523 02524 int n_row, 02525 int n_col, 02526 RowInfo Row [], 02527 ColInfo Col [], 02528 int A [] 02529 ) 02530 { 02531 /* === Local variables ================================================== */ 02532 02533 int r ; 02534 int c ; 02535 int *rp ; 02536 int *rp_end ; 02537 int *cp ; 02538 int *cp_end ; 02539 02540 /* === Dump the rows and columns of the matrix ========================== */ 02541 02542 if (debug_colamd < 3) 02543 { 02544 return ; 02545 } 02546 DEBUG3 (("DUMP MATRIX:\n")) ; 02547 for (r = 0 ; r < n_row ; r++) 02548 { 02549 DEBUG3 (("Row %d alive? %d\n", r, ROW_IS_ALIVE (r))) ; 02550 if (ROW_IS_DEAD (r)) 02551 { 02552 continue ; 02553 } 02554 DEBUG3 (("start %d length %d degree %d\n", 02555 Row [r].start, Row [r].length, Row [r].shared1.degree)) ; 02556 rp = &A [Row [r].start] ; 02557 rp_end = rp + Row [r].length ; 02558 while (rp < rp_end) 02559 { 02560 c = *rp++ ; 02561 DEBUG3 ((" %d col %d\n", COL_IS_ALIVE (c), c)) ; 02562 } 02563 } 02564 02565 for (c = 0 ; c < n_col ; c++) 02566 { 02567 DEBUG3 (("Col %d alive? %d\n", c, COL_IS_ALIVE (c))) ; 02568 if (COL_IS_DEAD (c)) 02569 { 02570 continue ; 02571 } 02572 DEBUG3 (("start %d length %d shared1 %d shared2 %d\n", 02573 Col [c].start, Col [c].length, 02574 Col [c].shared1.thickness, Col [c].shared2.score)) ; 02575 cp = &A [Col [c].start] ; 02576 cp_end = cp + Col [c].length ; 02577 while (cp < cp_end) 02578 { 02579 r = *cp++ ; 02580 DEBUG3 ((" %d row %d\n", ROW_IS_ALIVE (r), r)) ; 02581 } 02582 } 02583 } 02584 02585 #endif 02586