Blender  V2.59
colamd.c
Go to the documentation of this file.
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