3612 lines
		
	
	
		
			106 KiB
		
	
	
	
		
			C
		
	
	
		
		
			
		
	
	
			3612 lines
		
	
	
		
			106 KiB
		
	
	
	
		
			C
		
	
	
|  | /* ========================================================================== */ | ||
|  | /* === colamd/symamd - a sparse matrix column ordering algorithm ============ */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /* COLAMD / SYMAMD
 | ||
|  | 
 | ||
|  |     colamd:  an approximate minimum degree column ordering algorithm, | ||
|  |     	for LU factorization of symmetric or unsymmetric matrices, | ||
|  | 	QR factorization, least squares, interior point methods for | ||
|  | 	linear programming problems, and other related problems. | ||
|  | 
 | ||
|  |     symamd:  an approximate minimum degree ordering algorithm for Cholesky | ||
|  |     	factorization of symmetric matrices. | ||
|  | 
 | ||
|  |     Purpose: | ||
|  | 
 | ||
|  | 	Colamd computes a permutation Q such that the Cholesky factorization of | ||
|  | 	(AQ)'(AQ) has less fill-in and requires fewer floating point operations | ||
|  | 	than A'A.  This also provides a good ordering for sparse partial | ||
|  | 	pivoting methods, P(AQ) = LU, where Q is computed prior to numerical | ||
|  | 	factorization, and P is computed during numerical factorization via | ||
|  | 	conventional partial pivoting with row interchanges.  Colamd is the | ||
|  | 	column ordering method used in SuperLU, part of the ScaLAPACK library. | ||
|  | 	It is also available as built-in function in MATLAB Version 6, | ||
|  | 	available from MathWorks, Inc. (http://www.mathworks.com).  This
 | ||
|  | 	routine can be used in place of colmmd in MATLAB. | ||
|  | 
 | ||
|  |     	Symamd computes a permutation P of a symmetric matrix A such that the | ||
|  | 	Cholesky factorization of PAP' has less fill-in and requires fewer | ||
|  | 	floating point operations than A.  Symamd constructs a matrix M such | ||
|  | 	that M'M has the same nonzero pattern of A, and then orders the columns | ||
|  | 	of M using colmmd.  The column ordering of M is then returned as the | ||
|  | 	row and column ordering P of A.  | ||
|  | 
 | ||
|  |     Authors: | ||
|  | 
 | ||
|  | 	The authors of the code itself are Stefan I. Larimore and Timothy A. | ||
|  | 	Davis (davis at cise.ufl.edu), University of Florida.  The algorithm was | ||
|  | 	developed in collaboration with John Gilbert, Xerox PARC, and Esmond | ||
|  | 	Ng, Oak Ridge National Laboratory. | ||
|  | 
 | ||
|  |     Acknowledgements: | ||
|  | 
 | ||
|  | 	This work was supported by the National Science Foundation, under | ||
|  | 	grants DMS-9504974 and DMS-9803599. | ||
|  | 
 | ||
|  |     Copyright and License: | ||
|  | 
 | ||
|  | 	Copyright (c) 1998-2007, Timothy A. Davis, All Rights Reserved. | ||
|  | 	COLAMD is also available under alternate licenses, contact T. Davis | ||
|  | 	for details. | ||
|  | 
 | ||
|  | 	This library is free software; you can redistribute it and/or | ||
|  | 	modify it under the terms of the GNU Lesser General Public | ||
|  | 	License as published by the Free Software Foundation; either | ||
|  | 	version 2.1 of the License, or (at your option) any later version. | ||
|  | 
 | ||
|  | 	This library is distributed in the hope that it will be useful, | ||
|  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
|  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||
|  | 	Lesser General Public License for more details. | ||
|  | 
 | ||
|  | 	You should have received a copy of the GNU Lesser General Public | ||
|  | 	License along with this library; if not, write to the Free Software | ||
|  | 	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 | ||
|  | 	USA | ||
|  | 
 | ||
|  | 	Permission is hereby granted to use or copy this program under the | ||
|  | 	terms of the GNU LGPL, provided that the Copyright, this License, | ||
|  | 	and the Availability of the original version is retained on all copies. | ||
|  | 	User documentation of any code that uses this code or any modified | ||
|  | 	version of this code must cite the Copyright, this License, the | ||
|  | 	Availability note, and "Used by permission." Permission to modify | ||
|  | 	the code and to distribute modified code is granted, provided the | ||
|  | 	Copyright, this License, and the Availability note are retained, | ||
|  | 	and a notice that the code was modified is included. | ||
|  | 
 | ||
|  |     Availability: | ||
|  | 
 | ||
|  | 	The colamd/symamd library is available at | ||
|  | 
 | ||
|  | 	    http://www.cise.ufl.edu/research/sparse/colamd/
 | ||
|  | 
 | ||
|  | 	This is the http://www.cise.ufl.edu/research/sparse/colamd/colamd.c
 | ||
|  | 	file.  It requires the colamd.h file.  It is required by the colamdmex.c | ||
|  | 	and symamdmex.c files, for the MATLAB interface to colamd and symamd. | ||
|  | 	Appears as ACM Algorithm 836. | ||
|  | 
 | ||
|  |     See the ChangeLog file for changes since Version 1.0. | ||
|  | 
 | ||
|  |     References: | ||
|  | 
 | ||
|  | 	T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, An approximate column | ||
|  | 	minimum degree ordering algorithm, ACM Transactions on Mathematical | ||
|  | 	Software, vol. 30, no. 3., pp. 353-376, 2004. | ||
|  | 
 | ||
|  | 	T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, Algorithm 836: COLAMD, | ||
|  | 	an approximate column minimum degree ordering algorithm, ACM | ||
|  | 	Transactions on Mathematical Software, vol. 30, no. 3., pp. 377-380, | ||
|  | 	2004. | ||
|  | 
 | ||
|  | */ | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === Description of user-callable routines ================================ */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /* COLAMD includes both int and UF_long versions of all its routines.  The
 | ||
|  |  * description below is for the int version.  For UF_long, all int arguments | ||
|  |  * become UF_long.  UF_long is normally defined as long, except for WIN64. | ||
|  | 
 | ||
|  |     ---------------------------------------------------------------------------- | ||
|  |     colamd_recommended: | ||
|  |     ---------------------------------------------------------------------------- | ||
|  | 
 | ||
|  | 	C syntax: | ||
|  | 
 | ||
|  | 	    #include "colamd.h"
 | ||
|  | 	    size_t colamd_recommended (int nnz, int n_row, int n_col) ; | ||
|  | 	    size_t colamd_l_recommended (UF_long nnz, UF_long n_row, | ||
|  | 		UF_long n_col) ; | ||
|  | 
 | ||
|  | 	Purpose: | ||
|  | 
 | ||
|  | 	    Returns recommended value of Alen for use by colamd.  Returns 0 | ||
|  | 	    if any input argument is negative.  The use of this routine | ||
|  | 	    is optional.  Not needed for symamd, which dynamically allocates | ||
|  | 	    its own memory. | ||
|  | 
 | ||
|  | 	    Note that in v2.4 and earlier, these routines returned int or long. | ||
|  | 	    They now return a value of type size_t. | ||
|  | 
 | ||
|  | 	Arguments (all input arguments): | ||
|  | 
 | ||
|  | 	    int nnz ;		Number of nonzeros in the matrix A.  This must | ||
|  | 				be the same value as p [n_col] in the call to | ||
|  | 				colamd - otherwise you will get a wrong value | ||
|  | 				of the recommended memory to use. | ||
|  | 
 | ||
|  | 	    int n_row ;		Number of rows in the matrix A. | ||
|  | 
 | ||
|  | 	    int n_col ;		Number of columns in the matrix A. | ||
|  | 
 | ||
|  |     ---------------------------------------------------------------------------- | ||
|  |     colamd_set_defaults: | ||
|  |     ---------------------------------------------------------------------------- | ||
|  | 
 | ||
|  | 	C syntax: | ||
|  | 
 | ||
|  | 	    #include "colamd.h"
 | ||
|  | 	    colamd_set_defaults (double knobs [COLAMD_KNOBS]) ; | ||
|  | 	    colamd_l_set_defaults (double knobs [COLAMD_KNOBS]) ; | ||
|  | 
 | ||
|  | 	Purpose: | ||
|  | 
 | ||
|  | 	    Sets the default parameters.  The use of this routine is optional. | ||
|  | 
 | ||
|  | 	Arguments: | ||
|  | 
 | ||
|  | 	    double knobs [COLAMD_KNOBS] ;	Output only. | ||
|  | 
 | ||
|  | 		NOTE: the meaning of the dense row/col knobs has changed in v2.4 | ||
|  | 
 | ||
|  | 		knobs [0] and knobs [1] control dense row and col detection: | ||
|  | 
 | ||
|  | 		Colamd: rows with more than | ||
|  | 		max (16, knobs [COLAMD_DENSE_ROW] * sqrt (n_col)) | ||
|  | 		entries are removed prior to ordering.  Columns with more than | ||
|  | 		max (16, knobs [COLAMD_DENSE_COL] * sqrt (MIN (n_row,n_col))) | ||
|  | 		entries are removed prior to | ||
|  | 		ordering, and placed last in the output column ordering.  | ||
|  | 
 | ||
|  | 		Symamd: uses only knobs [COLAMD_DENSE_ROW], which is knobs [0]. | ||
|  | 		Rows and columns with more than | ||
|  | 		max (16, knobs [COLAMD_DENSE_ROW] * sqrt (n)) | ||
|  | 		entries are removed prior to ordering, and placed last in the | ||
|  | 		output ordering. | ||
|  | 
 | ||
|  | 		COLAMD_DENSE_ROW and COLAMD_DENSE_COL are defined as 0 and 1, | ||
|  | 		respectively, in colamd.h.  Default values of these two knobs | ||
|  | 		are both 10.  Currently, only knobs [0] and knobs [1] are | ||
|  | 		used, but future versions may use more knobs.  If so, they will | ||
|  | 		be properly set to their defaults by the future version of | ||
|  | 		colamd_set_defaults, so that the code that calls colamd will | ||
|  | 		not need to change, assuming that you either use | ||
|  | 		colamd_set_defaults, or pass a (double *) NULL pointer as the | ||
|  | 		knobs array to colamd or symamd. | ||
|  | 
 | ||
|  | 	    knobs [2]: aggressive absorption | ||
|  | 
 | ||
|  | 	        knobs [COLAMD_AGGRESSIVE] controls whether or not to do | ||
|  | 	        aggressive absorption during the ordering.  Default is TRUE. | ||
|  | 
 | ||
|  | 
 | ||
|  |     ---------------------------------------------------------------------------- | ||
|  |     colamd: | ||
|  |     ---------------------------------------------------------------------------- | ||
|  | 
 | ||
|  | 	C syntax: | ||
|  | 
 | ||
|  | 	    #include "colamd.h"
 | ||
|  | 	    int colamd (int n_row, int n_col, int Alen, int *A, int *p, | ||
|  | 	    	double knobs [COLAMD_KNOBS], int stats [COLAMD_STATS]) ; | ||
|  | 	    UF_long colamd_l (UF_long n_row, UF_long n_col, UF_long Alen, | ||
|  | 		UF_long *A, UF_long *p, double knobs [COLAMD_KNOBS], | ||
|  | 		UF_long stats [COLAMD_STATS]) ; | ||
|  | 
 | ||
|  | 	Purpose: | ||
|  | 
 | ||
|  | 	    Computes a column ordering (Q) of A such that P(AQ)=LU or | ||
|  | 	    (AQ)'AQ=LL' have less fill-in and require fewer floating point | ||
|  | 	    operations than factorizing the unpermuted matrix A or A'A, | ||
|  | 	    respectively. | ||
|  | 	     | ||
|  | 	Returns: | ||
|  | 
 | ||
|  | 	    TRUE (1) if successful, FALSE (0) otherwise. | ||
|  | 
 | ||
|  | 	Arguments: | ||
|  | 
 | ||
|  | 	    int n_row ;		Input argument. | ||
|  | 
 | ||
|  | 		Number of rows in the matrix A. | ||
|  | 		Restriction:  n_row >= 0. | ||
|  | 		Colamd returns FALSE if n_row is negative. | ||
|  | 
 | ||
|  | 	    int n_col ;		Input argument. | ||
|  | 
 | ||
|  | 		Number of columns in the matrix A. | ||
|  | 		Restriction:  n_col >= 0. | ||
|  | 		Colamd returns FALSE if n_col is negative. | ||
|  | 
 | ||
|  | 	    int Alen ;		Input argument. | ||
|  | 
 | ||
|  | 		Restriction (see note): | ||
|  | 		Alen >= 2*nnz + 6*(n_col+1) + 4*(n_row+1) + n_col | ||
|  | 		Colamd returns FALSE if these conditions are not met. | ||
|  | 
 | ||
|  | 		Note:  this restriction makes an modest assumption regarding | ||
|  | 		the size of the two typedef's structures in colamd.h. | ||
|  | 		We do, however, guarantee that | ||
|  | 
 | ||
|  | 			Alen >= colamd_recommended (nnz, n_row, n_col) | ||
|  | 
 | ||
|  | 		will be sufficient.  Note: the macro version does not check | ||
|  | 		for integer overflow, and thus is not recommended.  Use | ||
|  | 		the colamd_recommended routine instead. | ||
|  | 
 | ||
|  | 	    int A [Alen] ;	Input argument, undefined on output. | ||
|  | 
 | ||
|  | 		A is an integer array of size Alen.  Alen must be at least as | ||
|  | 		large as the bare minimum value given above, but this is very | ||
|  | 		low, and can result in excessive run time.  For best | ||
|  | 		performance, we recommend that Alen be greater than or equal to | ||
|  | 		colamd_recommended (nnz, n_row, n_col), which adds | ||
|  | 		nnz/5 to the bare minimum value given above. | ||
|  | 
 | ||
|  | 		On input, the row indices of the entries in column c of the | ||
|  | 		matrix are held in A [(p [c]) ... (p [c+1]-1)].  The row indices | ||
|  | 		in a given column c need not be in ascending order, and | ||
|  | 		duplicate row indices may be be present.  However, colamd will | ||
|  | 		work a little faster if both of these conditions are met | ||
|  | 		(Colamd puts the matrix into this format, if it finds that the | ||
|  | 		the conditions are not met). | ||
|  | 
 | ||
|  | 		The matrix is 0-based.  That is, rows are in the range 0 to | ||
|  | 		n_row-1, and columns are in the range 0 to n_col-1.  Colamd | ||
|  | 		returns FALSE if any row index is out of range. | ||
|  | 
 | ||
|  | 		The contents of A are modified during ordering, and are | ||
|  | 		undefined on output. | ||
|  | 
 | ||
|  | 	    int p [n_col+1] ;	Both input and output argument. | ||
|  | 
 | ||
|  | 		p is an integer array of size n_col+1.  On input, it holds the | ||
|  | 		"pointers" for the column form of the matrix A.  Column c of | ||
|  | 		the matrix A is held in A [(p [c]) ... (p [c+1]-1)].  The first | ||
|  | 		entry, p [0], must be zero, and p [c] <= p [c+1] must hold | ||
|  | 		for all c in the range 0 to n_col-1.  The value p [n_col] is | ||
|  | 		thus the total number of entries in the pattern of the matrix A. | ||
|  | 		Colamd returns FALSE if these conditions are not met. | ||
|  | 
 | ||
|  | 		On output, if colamd returns TRUE, the array p holds the column | ||
|  | 		permutation (Q, for P(AQ)=LU or (AQ)'(AQ)=LL'), where p [0] is | ||
|  | 		the first column index in the new ordering, and p [n_col-1] is | ||
|  | 		the last.  That is, p [k] = j means that column j of A is the | ||
|  | 		kth pivot column, in AQ, where k is in the range 0 to n_col-1 | ||
|  | 		(p [0] = j means that column j of A is the first column in AQ). | ||
|  | 
 | ||
|  | 		If colamd returns FALSE, then no permutation is returned, and | ||
|  | 		p is undefined on output. | ||
|  | 
 | ||
|  | 	    double knobs [COLAMD_KNOBS] ;	Input argument. | ||
|  | 
 | ||
|  | 		See colamd_set_defaults for a description. | ||
|  | 
 | ||
|  | 	    int stats [COLAMD_STATS] ;		Output argument. | ||
|  | 
 | ||
|  | 		Statistics on the ordering, and error status. | ||
|  | 		See colamd.h for related definitions. | ||
|  | 		Colamd returns FALSE if stats is not present. | ||
|  | 
 | ||
|  | 		stats [0]:  number of dense or empty rows ignored. | ||
|  | 
 | ||
|  | 		stats [1]:  number of dense or empty columns ignored (and | ||
|  | 				ordered last in the output permutation p) | ||
|  | 				Note that a row can become "empty" if it | ||
|  | 				contains only "dense" and/or "empty" columns, | ||
|  | 				and similarly a column can become "empty" if it | ||
|  | 				only contains "dense" and/or "empty" rows. | ||
|  | 
 | ||
|  | 		stats [2]:  number of garbage collections performed. | ||
|  | 				This can be excessively high if Alen is close | ||
|  | 				to the minimum required value. | ||
|  | 
 | ||
|  | 		stats [3]:  status code.  < 0 is an error code. | ||
|  | 			    > 1 is a warning or notice. | ||
|  | 
 | ||
|  | 			0	OK.  Each column of the input matrix contained | ||
|  | 				row indices in increasing order, with no | ||
|  | 				duplicates. | ||
|  | 
 | ||
|  | 			1	OK, but columns of input matrix were jumbled | ||
|  | 				(unsorted columns or duplicate entries).  Colamd | ||
|  | 				had to do some extra work to sort the matrix | ||
|  | 				first and remove duplicate entries, but it | ||
|  | 				still was able to return a valid permutation | ||
|  | 				(return value of colamd was TRUE). | ||
|  | 
 | ||
|  | 					stats [4]: highest numbered column that | ||
|  | 						is unsorted or has duplicate | ||
|  | 						entries. | ||
|  | 					stats [5]: last seen duplicate or | ||
|  | 						unsorted row index. | ||
|  | 					stats [6]: number of duplicate or | ||
|  | 						unsorted row indices. | ||
|  | 
 | ||
|  | 			-1	A is a null pointer | ||
|  | 
 | ||
|  | 			-2	p is a null pointer | ||
|  | 
 | ||
|  | 			-3 	n_row is negative | ||
|  | 
 | ||
|  | 					stats [4]: n_row | ||
|  | 
 | ||
|  | 			-4	n_col is negative | ||
|  | 
 | ||
|  | 					stats [4]: n_col | ||
|  | 
 | ||
|  | 			-5	number of nonzeros in matrix is negative | ||
|  | 
 | ||
|  | 					stats [4]: number of nonzeros, p [n_col] | ||
|  | 
 | ||
|  | 			-6	p [0] is nonzero | ||
|  | 
 | ||
|  | 					stats [4]: p [0] | ||
|  | 
 | ||
|  | 			-7	A is too small | ||
|  | 
 | ||
|  | 					stats [4]: required size | ||
|  | 					stats [5]: actual size (Alen) | ||
|  | 
 | ||
|  | 			-8	a column has a negative number of entries | ||
|  | 
 | ||
|  | 					stats [4]: column with < 0 entries | ||
|  | 					stats [5]: number of entries in col | ||
|  | 
 | ||
|  | 			-9	a row index is out of bounds | ||
|  | 
 | ||
|  | 					stats [4]: column with bad row index | ||
|  | 					stats [5]: bad row index | ||
|  | 					stats [6]: n_row, # of rows of matrx | ||
|  | 
 | ||
|  | 			-10	(unused; see symamd.c) | ||
|  | 
 | ||
|  | 			-999	(unused; see symamd.c) | ||
|  | 
 | ||
|  | 		Future versions may return more statistics in the stats array. | ||
|  | 
 | ||
|  | 	Example: | ||
|  | 	 | ||
|  | 	    See http://www.cise.ufl.edu/research/sparse/colamd/example.c
 | ||
|  | 	    for a complete example. | ||
|  | 
 | ||
|  | 	    To order the columns of a 5-by-4 matrix with 11 nonzero entries in | ||
|  | 	    the following nonzero pattern | ||
|  | 
 | ||
|  | 	    	x 0 x 0 | ||
|  | 		x 0 x x | ||
|  | 		0 x x 0 | ||
|  | 		0 0 x x | ||
|  | 		x x 0 0 | ||
|  | 
 | ||
|  | 	    with default knobs and no output statistics, do the following: | ||
|  | 
 | ||
|  | 		#include "colamd.h"
 | ||
|  | 		#define ALEN 100
 | ||
|  | 		int A [ALEN] = {0, 1, 4, 2, 4, 0, 1, 2, 3, 1, 3} ; | ||
|  | 		int p [ ] = {0, 3, 5, 9, 11} ; | ||
|  | 		int stats [COLAMD_STATS] ; | ||
|  | 		colamd (5, 4, ALEN, A, p, (double *) NULL, stats) ; | ||
|  | 
 | ||
|  | 	    The permutation is returned in the array p, and A is destroyed. | ||
|  | 
 | ||
|  |     ---------------------------------------------------------------------------- | ||
|  |     symamd: | ||
|  |     ---------------------------------------------------------------------------- | ||
|  | 
 | ||
|  | 	C syntax: | ||
|  | 
 | ||
|  | 	    #include "colamd.h"
 | ||
|  | 	    int symamd (int n, int *A, int *p, int *perm, | ||
|  | 	    	double knobs [COLAMD_KNOBS], int stats [COLAMD_STATS], | ||
|  | 		void (*allocate) (size_t, size_t), void (*release) (void *)) ; | ||
|  | 	    UF_long symamd_l (UF_long n, UF_long *A, UF_long *p, UF_long *perm, | ||
|  | 	    	double knobs [COLAMD_KNOBS], UF_long stats [COLAMD_STATS], | ||
|  | 		void (*allocate) (size_t, size_t), void (*release) (void *)) ; | ||
|  | 
 | ||
|  | 	Purpose: | ||
|  | 
 | ||
|  |     	    The symamd routine computes an ordering P of a symmetric sparse | ||
|  | 	    matrix A such that the Cholesky factorization PAP' = LL' remains | ||
|  | 	    sparse.  It is based on a column ordering of a matrix M constructed | ||
|  | 	    so that the nonzero pattern of M'M is the same as A.  The matrix A | ||
|  | 	    is assumed to be symmetric; only the strictly lower triangular part | ||
|  | 	    is accessed.  You must pass your selected memory allocator (usually | ||
|  | 	    calloc/free or mxCalloc/mxFree) to symamd, for it to allocate | ||
|  | 	    memory for the temporary matrix M. | ||
|  | 
 | ||
|  | 	Returns: | ||
|  | 
 | ||
|  | 	    TRUE (1) if successful, FALSE (0) otherwise. | ||
|  | 
 | ||
|  | 	Arguments: | ||
|  | 
 | ||
|  | 	    int n ;		Input argument. | ||
|  | 
 | ||
|  | 	    	Number of rows and columns in the symmetrix matrix A. | ||
|  | 		Restriction:  n >= 0. | ||
|  | 		Symamd returns FALSE if n is negative. | ||
|  | 
 | ||
|  | 	    int A [nnz] ;	Input argument. | ||
|  | 
 | ||
|  | 	    	A is an integer array of size nnz, where nnz = p [n]. | ||
|  | 		 | ||
|  | 		The row indices of the entries in column c of the matrix are | ||
|  | 		held in A [(p [c]) ... (p [c+1]-1)].  The row indices in a | ||
|  | 		given column c need not be in ascending order, and duplicate | ||
|  | 		row indices may be present.  However, symamd will run faster | ||
|  | 		if the columns are in sorted order with no duplicate entries.  | ||
|  | 
 | ||
|  | 		The matrix is 0-based.  That is, rows are in the range 0 to | ||
|  | 		n-1, and columns are in the range 0 to n-1.  Symamd | ||
|  | 		returns FALSE if any row index is out of range. | ||
|  | 
 | ||
|  | 		The contents of A are not modified. | ||
|  | 
 | ||
|  | 	    int p [n+1] ;   	Input argument. | ||
|  | 
 | ||
|  | 		p is an integer array of size n+1.  On input, it holds the | ||
|  | 		"pointers" for the column form of the matrix A.  Column c of | ||
|  | 		the matrix A is held in A [(p [c]) ... (p [c+1]-1)].  The first | ||
|  | 		entry, p [0], must be zero, and p [c] <= p [c+1] must hold | ||
|  | 		for all c in the range 0 to n-1.  The value p [n] is | ||
|  | 		thus the total number of entries in the pattern of the matrix A. | ||
|  | 		Symamd returns FALSE if these conditions are not met. | ||
|  | 
 | ||
|  | 		The contents of p are not modified. | ||
|  | 
 | ||
|  | 	    int perm [n+1] ;   	Output argument. | ||
|  | 
 | ||
|  | 		On output, if symamd returns TRUE, the array perm holds the | ||
|  | 		permutation P, where perm [0] is the first index in the new | ||
|  | 		ordering, and perm [n-1] is the last.  That is, perm [k] = j | ||
|  | 		means that row and column j of A is the kth column in PAP', | ||
|  | 		where k is in the range 0 to n-1 (perm [0] = j means | ||
|  | 		that row and column j of A are the first row and column in | ||
|  | 		PAP').  The array is used as a workspace during the ordering, | ||
|  | 		which is why it must be of length n+1, not just n. | ||
|  | 
 | ||
|  | 	    double knobs [COLAMD_KNOBS] ;	Input argument. | ||
|  | 
 | ||
|  | 		See colamd_set_defaults for a description. | ||
|  | 
 | ||
|  | 	    int stats [COLAMD_STATS] ;		Output argument. | ||
|  | 
 | ||
|  | 		Statistics on the ordering, and error status. | ||
|  | 		See colamd.h for related definitions. | ||
|  | 		Symamd returns FALSE if stats is not present. | ||
|  | 
 | ||
|  | 		stats [0]:  number of dense or empty row and columns ignored | ||
|  | 				(and ordered last in the output permutation  | ||
|  | 				perm).  Note that a row/column can become | ||
|  | 				"empty" if it contains only "dense" and/or | ||
|  | 				"empty" columns/rows. | ||
|  | 
 | ||
|  | 		stats [1]:  (same as stats [0]) | ||
|  | 
 | ||
|  | 		stats [2]:  number of garbage collections performed. | ||
|  | 
 | ||
|  | 		stats [3]:  status code.  < 0 is an error code. | ||
|  | 			    > 1 is a warning or notice. | ||
|  | 
 | ||
|  | 			0	OK.  Each column of the input matrix contained | ||
|  | 				row indices in increasing order, with no | ||
|  | 				duplicates. | ||
|  | 
 | ||
|  | 			1	OK, but columns of input matrix were jumbled | ||
|  | 				(unsorted columns or duplicate entries).  Symamd | ||
|  | 				had to do some extra work to sort the matrix | ||
|  | 				first and remove duplicate entries, but it | ||
|  | 				still was able to return a valid permutation | ||
|  | 				(return value of symamd was TRUE). | ||
|  | 
 | ||
|  | 					stats [4]: highest numbered column that | ||
|  | 						is unsorted or has duplicate | ||
|  | 						entries. | ||
|  | 					stats [5]: last seen duplicate or | ||
|  | 						unsorted row index. | ||
|  | 					stats [6]: number of duplicate or | ||
|  | 						unsorted row indices. | ||
|  | 
 | ||
|  | 			-1	A is a null pointer | ||
|  | 
 | ||
|  | 			-2	p is a null pointer | ||
|  | 
 | ||
|  | 			-3	(unused, see colamd.c) | ||
|  | 
 | ||
|  | 			-4 	n is negative | ||
|  | 
 | ||
|  | 					stats [4]: n | ||
|  | 
 | ||
|  | 			-5	number of nonzeros in matrix is negative | ||
|  | 
 | ||
|  | 					stats [4]: # of nonzeros (p [n]). | ||
|  | 
 | ||
|  | 			-6	p [0] is nonzero | ||
|  | 
 | ||
|  | 					stats [4]: p [0] | ||
|  | 
 | ||
|  | 			-7	(unused) | ||
|  | 
 | ||
|  | 			-8	a column has a negative number of entries | ||
|  | 
 | ||
|  | 					stats [4]: column with < 0 entries | ||
|  | 					stats [5]: number of entries in col | ||
|  | 
 | ||
|  | 			-9	a row index is out of bounds | ||
|  | 
 | ||
|  | 					stats [4]: column with bad row index | ||
|  | 					stats [5]: bad row index | ||
|  | 					stats [6]: n_row, # of rows of matrx | ||
|  | 
 | ||
|  | 			-10	out of memory (unable to allocate temporary | ||
|  | 				workspace for M or count arrays using the | ||
|  | 				"allocate" routine passed into symamd). | ||
|  | 
 | ||
|  | 		Future versions may return more statistics in the stats array. | ||
|  | 
 | ||
|  | 	    void * (*allocate) (size_t, size_t) | ||
|  | 
 | ||
|  | 	    	A pointer to a function providing memory allocation.  The | ||
|  | 		allocated memory must be returned initialized to zero.  For a | ||
|  | 		C application, this argument should normally be a pointer to | ||
|  | 		calloc.  For a MATLAB mexFunction, the routine mxCalloc is | ||
|  | 		passed instead. | ||
|  | 
 | ||
|  | 	    void (*release) (size_t, size_t) | ||
|  | 
 | ||
|  | 	    	A pointer to a function that frees memory allocated by the | ||
|  | 		memory allocation routine above.  For a C application, this | ||
|  | 		argument should normally be a pointer to free.  For a MATLAB | ||
|  | 		mexFunction, the routine mxFree is passed instead. | ||
|  | 
 | ||
|  | 
 | ||
|  |     ---------------------------------------------------------------------------- | ||
|  |     colamd_report: | ||
|  |     ---------------------------------------------------------------------------- | ||
|  | 
 | ||
|  | 	C syntax: | ||
|  | 
 | ||
|  | 	    #include "colamd.h"
 | ||
|  | 	    colamd_report (int stats [COLAMD_STATS]) ; | ||
|  | 	    colamd_l_report (UF_long stats [COLAMD_STATS]) ; | ||
|  | 
 | ||
|  | 	Purpose: | ||
|  | 
 | ||
|  | 	    Prints the error status and statistics recorded in the stats | ||
|  | 	    array on the standard error output (for a standard C routine) | ||
|  | 	    or on the MATLAB output (for a mexFunction). | ||
|  | 
 | ||
|  | 	Arguments: | ||
|  | 
 | ||
|  | 	    int stats [COLAMD_STATS] ;	Input only.  Statistics from colamd. | ||
|  | 
 | ||
|  | 
 | ||
|  |     ---------------------------------------------------------------------------- | ||
|  |     symamd_report: | ||
|  |     ---------------------------------------------------------------------------- | ||
|  | 
 | ||
|  | 	C syntax: | ||
|  | 
 | ||
|  | 	    #include "colamd.h"
 | ||
|  | 	    symamd_report (int stats [COLAMD_STATS]) ; | ||
|  | 	    symamd_l_report (UF_long stats [COLAMD_STATS]) ; | ||
|  | 
 | ||
|  | 	Purpose: | ||
|  | 
 | ||
|  | 	    Prints the error status and statistics recorded in the stats | ||
|  | 	    array on the standard error output (for a standard C routine) | ||
|  | 	    or on the MATLAB output (for a mexFunction). | ||
|  | 
 | ||
|  | 	Arguments: | ||
|  | 
 | ||
|  | 	    int stats [COLAMD_STATS] ;	Input only.  Statistics from symamd. | ||
|  | 
 | ||
|  | 
 | ||
|  | */ | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === Scaffolding code definitions  ======================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /* Ensure that debugging is turned off: */ | ||
|  | #ifndef NDEBUG
 | ||
|  | #define NDEBUG
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | /* turn on debugging by uncommenting the following line
 | ||
|  |  #undef NDEBUG
 | ||
|  | */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |    Our "scaffolding code" philosophy:  In our opinion, well-written library | ||
|  |    code should keep its "debugging" code, and just normally have it turned off | ||
|  |    by the compiler so as not to interfere with performance.  This serves | ||
|  |    several purposes: | ||
|  | 
 | ||
|  |    (1) assertions act as comments to the reader, telling you what the code | ||
|  | 	expects at that point.  All assertions will always be true (unless | ||
|  | 	there really is a bug, of course). | ||
|  | 
 | ||
|  |    (2) leaving in the scaffolding code assists anyone who would like to modify | ||
|  | 	the code, or understand the algorithm (by reading the debugging output, | ||
|  | 	one can get a glimpse into what the code is doing). | ||
|  | 
 | ||
|  |    (3) (gasp!) for actually finding bugs.  This code has been heavily tested | ||
|  | 	and "should" be fully functional and bug-free ... but you never know... | ||
|  | 
 | ||
|  |     The code will become outrageously slow when debugging is | ||
|  |     enabled.  To control the level of debugging output, set an environment | ||
|  |     variable D to 0 (little), 1 (some), 2, 3, or 4 (lots).  When debugging, | ||
|  |     you should see the following message on the standard output: | ||
|  | 
 | ||
|  |     	colamd: debug version, D = 1 (THIS WILL BE SLOW!) | ||
|  | 
 | ||
|  |     or a similar message for symamd.  If you don't, then debugging has not | ||
|  |     been enabled. | ||
|  | 
 | ||
|  | */ | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === Include files ======================================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | #include "colamd.h"
 | ||
|  | #include <limits.h>
 | ||
|  | #include <math.h>
 | ||
|  | 
 | ||
|  | #ifdef MATLAB_MEX_FILE
 | ||
|  | #include "mex.h"
 | ||
|  | #include "matrix.h"
 | ||
|  | #endif /* MATLAB_MEX_FILE */
 | ||
|  | 
 | ||
|  | #if !defined (NPRINT) || !defined (NDEBUG)
 | ||
|  | #include <stdio.h>
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #ifndef NULL
 | ||
|  | #define NULL ((void *) 0)
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === int or UF_long ======================================================= */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /* define UF_long */ | ||
|  | #include "UFconfig.h"
 | ||
|  | 
 | ||
|  | #ifdef DLONG
 | ||
|  | 
 | ||
|  | #define Int UF_long
 | ||
|  | #define ID  UF_long_id
 | ||
|  | #define Int_MAX UF_long_max
 | ||
|  | 
 | ||
|  | #define COLAMD_recommended colamd_l_recommended
 | ||
|  | #define COLAMD_set_defaults colamd_l_set_defaults
 | ||
|  | #define COLAMD_MAIN colamd_l
 | ||
|  | #define SYMAMD_MAIN symamd_l
 | ||
|  | #define COLAMD_report colamd_l_report
 | ||
|  | #define SYMAMD_report symamd_l_report
 | ||
|  | 
 | ||
|  | #else
 | ||
|  | 
 | ||
|  | #define Int int
 | ||
|  | #define ID "%d"
 | ||
|  | #define Int_MAX INT_MAX
 | ||
|  | 
 | ||
|  | #define COLAMD_recommended colamd_recommended
 | ||
|  | #define COLAMD_set_defaults colamd_set_defaults
 | ||
|  | #define COLAMD_MAIN colamd
 | ||
|  | #define SYMAMD_MAIN symamd
 | ||
|  | #define COLAMD_report colamd_report
 | ||
|  | #define SYMAMD_report symamd_report
 | ||
|  | 
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === Row and Column structures ============================================ */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /* User code that makes use of the colamd/symamd routines need not directly */ | ||
|  | /* reference these structures.  They are used only for colamd_recommended. */ | ||
|  | 
 | ||
|  | typedef struct Colamd_Col_struct | ||
|  | { | ||
|  |     Int start ;		/* index for A of first row in this column, or DEAD */ | ||
|  | 			/* if column is dead */ | ||
|  |     Int length ;	/* number of rows in this column */ | ||
|  |     union | ||
|  |     { | ||
|  | 	Int thickness ;	/* number of original columns represented by this */ | ||
|  | 			/* col, if the column is alive */ | ||
|  | 	Int parent ;	/* parent in parent tree super-column structure, if */ | ||
|  | 			/* the column is dead */ | ||
|  |     } shared1 ; | ||
|  |     union | ||
|  |     { | ||
|  | 	Int score ;	/* the score used to maintain heap, if col is alive */ | ||
|  | 	Int order ;	/* pivot ordering of this column, if col is dead */ | ||
|  |     } shared2 ; | ||
|  |     union | ||
|  |     { | ||
|  | 	Int headhash ;	/* head of a hash bucket, if col is at the head of */ | ||
|  | 			/* a degree list */ | ||
|  | 	Int hash ;	/* hash value, if col is not in a degree list */ | ||
|  | 	Int prev ;	/* previous column in degree list, if col is in a */ | ||
|  | 			/* degree list (but not at the head of a degree list) */ | ||
|  |     } shared3 ; | ||
|  |     union | ||
|  |     { | ||
|  | 	Int degree_next ;	/* next column, if col is in a degree list */ | ||
|  | 	Int hash_next ;		/* next column, if col is in a hash list */ | ||
|  |     } shared4 ; | ||
|  | 
 | ||
|  | } Colamd_Col ; | ||
|  | 
 | ||
|  | typedef struct Colamd_Row_struct | ||
|  | { | ||
|  |     Int start ;		/* index for A of first col in this row */ | ||
|  |     Int length ;	/* number of principal columns in this row */ | ||
|  |     union | ||
|  |     { | ||
|  | 	Int degree ;	/* number of principal & non-principal columns in row */ | ||
|  | 	Int p ;		/* used as a row pointer in init_rows_cols () */ | ||
|  |     } shared1 ; | ||
|  |     union | ||
|  |     { | ||
|  | 	Int mark ;	/* for computing set differences and marking dead rows*/ | ||
|  | 	Int first_column ;/* first column in row (used in garbage collection) */ | ||
|  |     } shared2 ; | ||
|  | 
 | ||
|  | } Colamd_Row ; | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === Definitions ========================================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /* Routines are either PUBLIC (user-callable) or PRIVATE (not user-callable) */ | ||
|  | #define PUBLIC
 | ||
|  | #define PRIVATE static
 | ||
|  | 
 | ||
|  | #define DENSE_DEGREE(alpha,n) \
 | ||
|  |     ((Int) MAX (16.0, (alpha) * sqrt ((double) (n)))) | ||
|  | 
 | ||
|  | #define MAX(a,b) (((a) > (b)) ? (a) : (b))
 | ||
|  | #define MIN(a,b) (((a) < (b)) ? (a) : (b))
 | ||
|  | 
 | ||
|  | #define ONES_COMPLEMENT(r) (-(r)-1)
 | ||
|  | 
 | ||
|  | /* -------------------------------------------------------------------------- */ | ||
|  | /* Change for version 2.1:  define TRUE and FALSE only if not yet defined */   | ||
|  | /* -------------------------------------------------------------------------- */ | ||
|  | 
 | ||
|  | #ifndef TRUE
 | ||
|  | #define TRUE (1)
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #ifndef FALSE
 | ||
|  | #define FALSE (0)
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | /* -------------------------------------------------------------------------- */ | ||
|  | 
 | ||
|  | #define EMPTY	(-1)
 | ||
|  | 
 | ||
|  | /* Row and column status */ | ||
|  | #define ALIVE	(0)
 | ||
|  | #define DEAD	(-1)
 | ||
|  | 
 | ||
|  | /* Column status */ | ||
|  | #define DEAD_PRINCIPAL		(-1)
 | ||
|  | #define DEAD_NON_PRINCIPAL	(-2)
 | ||
|  | 
 | ||
|  | /* Macros for row and column status update and checking. */ | ||
|  | #define ROW_IS_DEAD(r)			ROW_IS_MARKED_DEAD (Row[r].shared2.mark)
 | ||
|  | #define ROW_IS_MARKED_DEAD(row_mark)	(row_mark < ALIVE)
 | ||
|  | #define ROW_IS_ALIVE(r)			(Row [r].shared2.mark >= ALIVE)
 | ||
|  | #define COL_IS_DEAD(c)			(Col [c].start < ALIVE)
 | ||
|  | #define COL_IS_ALIVE(c)			(Col [c].start >= ALIVE)
 | ||
|  | #define COL_IS_DEAD_PRINCIPAL(c)	(Col [c].start == DEAD_PRINCIPAL)
 | ||
|  | #define KILL_ROW(r)			{ Row [r].shared2.mark = DEAD ; }
 | ||
|  | #define KILL_PRINCIPAL_COL(c)		{ Col [c].start = DEAD_PRINCIPAL ; }
 | ||
|  | #define KILL_NON_PRINCIPAL_COL(c)	{ Col [c].start = DEAD_NON_PRINCIPAL ; }
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === Colamd reporting mechanism =========================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | #if defined (MATLAB_MEX_FILE) || defined (MATHWORKS)
 | ||
|  | /* In MATLAB, matrices are 1-based to the user, but 0-based internally */ | ||
|  | #define INDEX(i) ((i)+1)
 | ||
|  | #else
 | ||
|  | /* In C, matrices are 0-based and indices are reported as such in *_report */ | ||
|  | #define INDEX(i) (i)
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | /* All output goes through the PRINTF macro.  */ | ||
|  | #define PRINTF(params) { if (colamd_printf != NULL) (void) colamd_printf params ; }
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === Prototypes of PRIVATE routines ======================================= */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | PRIVATE Int init_rows_cols | ||
|  | ( | ||
|  |     Int n_row, | ||
|  |     Int n_col, | ||
|  |     Colamd_Row Row [], | ||
|  |     Colamd_Col Col [], | ||
|  |     Int A [], | ||
|  |     Int p [], | ||
|  |     Int stats [COLAMD_STATS] | ||
|  | ) ; | ||
|  | 
 | ||
|  | PRIVATE void init_scoring | ||
|  | ( | ||
|  |     Int n_row, | ||
|  |     Int n_col, | ||
|  |     Colamd_Row Row [], | ||
|  |     Colamd_Col Col [], | ||
|  |     Int A [], | ||
|  |     Int head [], | ||
|  |     double knobs [COLAMD_KNOBS], | ||
|  |     Int *p_n_row2, | ||
|  |     Int *p_n_col2, | ||
|  |     Int *p_max_deg | ||
|  | ) ; | ||
|  | 
 | ||
|  | PRIVATE Int find_ordering | ||
|  | ( | ||
|  |     Int n_row, | ||
|  |     Int n_col, | ||
|  |     Int Alen, | ||
|  |     Colamd_Row Row [], | ||
|  |     Colamd_Col Col [], | ||
|  |     Int A [], | ||
|  |     Int head [], | ||
|  |     Int n_col2, | ||
|  |     Int max_deg, | ||
|  |     Int pfree, | ||
|  |     Int aggressive | ||
|  | ) ; | ||
|  | 
 | ||
|  | PRIVATE void order_children | ||
|  | ( | ||
|  |     Int n_col, | ||
|  |     Colamd_Col Col [], | ||
|  |     Int p [] | ||
|  | ) ; | ||
|  | 
 | ||
|  | PRIVATE void detect_super_cols | ||
|  | ( | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  |     Int n_col, | ||
|  |     Colamd_Row Row [], | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  |     Colamd_Col Col [], | ||
|  |     Int A [], | ||
|  |     Int head [], | ||
|  |     Int row_start, | ||
|  |     Int row_length | ||
|  | ) ; | ||
|  | 
 | ||
|  | PRIVATE Int garbage_collection | ||
|  | ( | ||
|  |     Int n_row, | ||
|  |     Int n_col, | ||
|  |     Colamd_Row Row [], | ||
|  |     Colamd_Col Col [], | ||
|  |     Int A [], | ||
|  |     Int *pfree | ||
|  | ) ; | ||
|  | 
 | ||
|  | PRIVATE Int clear_mark | ||
|  | ( | ||
|  |     Int tag_mark, | ||
|  |     Int max_mark, | ||
|  |     Int n_row, | ||
|  |     Colamd_Row Row [] | ||
|  | ) ; | ||
|  | 
 | ||
|  | PRIVATE void print_report | ||
|  | ( | ||
|  |     char *method, | ||
|  |     Int stats [COLAMD_STATS] | ||
|  | ) ; | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === Debugging prototypes and definitions ================================= */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  | 
 | ||
|  | #include <assert.h>
 | ||
|  | 
 | ||
|  | /* colamd_debug is the *ONLY* global variable, and is only */ | ||
|  | /* present when debugging */ | ||
|  | 
 | ||
|  | PRIVATE Int colamd_debug = 0 ;	/* debug print level */ | ||
|  | 
 | ||
|  | #define DEBUG0(params) { PRINTF (params) ; }
 | ||
|  | #define DEBUG1(params) { if (colamd_debug >= 1) PRINTF (params) ; }
 | ||
|  | #define DEBUG2(params) { if (colamd_debug >= 2) PRINTF (params) ; }
 | ||
|  | #define DEBUG3(params) { if (colamd_debug >= 3) PRINTF (params) ; }
 | ||
|  | #define DEBUG4(params) { if (colamd_debug >= 4) PRINTF (params) ; }
 | ||
|  | 
 | ||
|  | #ifdef MATLAB_MEX_FILE
 | ||
|  | #define ASSERT(expression) (mxAssert ((expression), ""))
 | ||
|  | #else
 | ||
|  | #define ASSERT(expression) (assert (expression))
 | ||
|  | #endif /* MATLAB_MEX_FILE */
 | ||
|  | 
 | ||
|  | PRIVATE void colamd_get_debug	/* gets the debug print level from getenv */ | ||
|  | ( | ||
|  |     char *method | ||
|  | ) ; | ||
|  | 
 | ||
|  | PRIVATE void debug_deg_lists | ||
|  | ( | ||
|  |     Int n_row, | ||
|  |     Int n_col, | ||
|  |     Colamd_Row Row [], | ||
|  |     Colamd_Col Col [], | ||
|  |     Int head [], | ||
|  |     Int min_score, | ||
|  |     Int should, | ||
|  |     Int max_deg | ||
|  | ) ; | ||
|  | 
 | ||
|  | PRIVATE void debug_mark | ||
|  | ( | ||
|  |     Int n_row, | ||
|  |     Colamd_Row Row [], | ||
|  |     Int tag_mark, | ||
|  |     Int max_mark | ||
|  | ) ; | ||
|  | 
 | ||
|  | PRIVATE void debug_matrix | ||
|  | ( | ||
|  |     Int n_row, | ||
|  |     Int n_col, | ||
|  |     Colamd_Row Row [], | ||
|  |     Colamd_Col Col [], | ||
|  |     Int A [] | ||
|  | ) ; | ||
|  | 
 | ||
|  | PRIVATE void debug_structures | ||
|  | ( | ||
|  |     Int n_row, | ||
|  |     Int n_col, | ||
|  |     Colamd_Row Row [], | ||
|  |     Colamd_Col Col [], | ||
|  |     Int A [], | ||
|  |     Int n_col2 | ||
|  | ) ; | ||
|  | 
 | ||
|  | #else /* NDEBUG */
 | ||
|  | 
 | ||
|  | /* === No debugging ========================================================= */ | ||
|  | 
 | ||
|  | #define DEBUG0(params) ;
 | ||
|  | #define DEBUG1(params) ;
 | ||
|  | #define DEBUG2(params) ;
 | ||
|  | #define DEBUG3(params) ;
 | ||
|  | #define DEBUG4(params) ;
 | ||
|  | 
 | ||
|  | #define ASSERT(expression)
 | ||
|  | 
 | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === USER-CALLABLE ROUTINES: ============================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === colamd_recommended =================================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |     The colamd_recommended routine returns the suggested size for Alen.  This | ||
|  |     value has been determined to provide good balance between the number of | ||
|  |     garbage collections and the memory requirements for colamd.  If any | ||
|  |     argument is negative, or if integer overflow occurs, a 0 is returned as an | ||
|  |     error condition.  2*nnz space is required for the row and column | ||
|  |     indices of the matrix. COLAMD_C (n_col) + COLAMD_R (n_row) space is | ||
|  |     required for the Col and Row arrays, respectively, which are internal to | ||
|  |     colamd (roughly 6*n_col + 4*n_row).  An additional n_col space is the | ||
|  |     minimal amount of "elbow room", and nnz/5 more space is recommended for | ||
|  |     run time efficiency. | ||
|  | 
 | ||
|  |     Alen is approximately 2.2*nnz + 7*n_col + 4*n_row + 10. | ||
|  | 
 | ||
|  |     This function is not needed when using symamd. | ||
|  | */ | ||
|  | 
 | ||
|  | /* add two values of type size_t, and check for integer overflow */ | ||
|  | static size_t t_add (size_t a, size_t b, int *ok) | ||
|  | { | ||
|  |     (*ok) = (*ok) && ((a + b) >= MAX (a,b)) ; | ||
|  |     return ((*ok) ? (a + b) : 0) ; | ||
|  | } | ||
|  | 
 | ||
|  | /* compute a*k where k is a small integer, and check for integer overflow */ | ||
|  | static size_t t_mult (size_t a, size_t k, int *ok) | ||
|  | { | ||
|  |     size_t i, s = 0 ; | ||
|  |     for (i = 0 ; i < k ; i++) | ||
|  |     { | ||
|  | 	s = t_add (s, a, ok) ; | ||
|  |     } | ||
|  |     return (s) ; | ||
|  | } | ||
|  | 
 | ||
|  | /* size of the Col and Row structures */ | ||
|  | #define COLAMD_C(n_col,ok) \
 | ||
|  |     ((t_mult (t_add (n_col, 1, ok), sizeof (Colamd_Col), ok) / sizeof (Int))) | ||
|  | 
 | ||
|  | #define COLAMD_R(n_row,ok) \
 | ||
|  |     ((t_mult (t_add (n_row, 1, ok), sizeof (Colamd_Row), ok) / sizeof (Int))) | ||
|  | 
 | ||
|  | 
 | ||
|  | PUBLIC size_t COLAMD_recommended	/* returns recommended value of Alen. */ | ||
|  | ( | ||
|  |     /* === Parameters ======================================================= */ | ||
|  | 
 | ||
|  |     Int nnz,			/* number of nonzeros in A */ | ||
|  |     Int n_row,			/* number of rows in A */ | ||
|  |     Int n_col			/* number of columns in A */ | ||
|  | ) | ||
|  | { | ||
|  |     size_t s, c, r ; | ||
|  |     int ok = TRUE ; | ||
|  |     if (nnz < 0 || n_row < 0 || n_col < 0) | ||
|  |     { | ||
|  | 	return (0) ; | ||
|  |     } | ||
|  |     s = t_mult (nnz, 2, &ok) ;	    /* 2*nnz */ | ||
|  |     c = COLAMD_C (n_col, &ok) ;	    /* size of column structures */ | ||
|  |     r = COLAMD_R (n_row, &ok) ;	    /* size of row structures */ | ||
|  |     s = t_add (s, c, &ok) ; | ||
|  |     s = t_add (s, r, &ok) ; | ||
|  |     s = t_add (s, n_col, &ok) ;	    /* elbow room */ | ||
|  |     s = t_add (s, nnz/5, &ok) ;	    /* elbow room */ | ||
|  |     ok = ok && (s < Int_MAX) ; | ||
|  |     return (ok ? s : 0) ; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === colamd_set_defaults ================================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |     The colamd_set_defaults routine sets the default values of the user- | ||
|  |     controllable parameters for colamd and symamd: | ||
|  | 
 | ||
|  | 	Colamd: rows with more than max (16, knobs [0] * sqrt (n_col)) | ||
|  | 	entries are removed prior to ordering.  Columns with more than | ||
|  | 	max (16, knobs [1] * sqrt (MIN (n_row,n_col))) entries are removed | ||
|  | 	prior to ordering, and placed last in the output column ordering.  | ||
|  | 
 | ||
|  | 	Symamd: Rows and columns with more than max (16, knobs [0] * sqrt (n)) | ||
|  | 	entries are removed prior to ordering, and placed last in the | ||
|  | 	output ordering. | ||
|  | 
 | ||
|  | 	knobs [0]	dense row control | ||
|  | 
 | ||
|  | 	knobs [1]	dense column control | ||
|  | 
 | ||
|  | 	knobs [2]	if nonzero, do aggresive absorption | ||
|  | 
 | ||
|  | 	knobs [3..19]	unused, but future versions might use this | ||
|  | 
 | ||
|  | */ | ||
|  | 
 | ||
|  | PUBLIC void COLAMD_set_defaults | ||
|  | ( | ||
|  |     /* === Parameters ======================================================= */ | ||
|  | 
 | ||
|  |     double knobs [COLAMD_KNOBS]		/* knob array */ | ||
|  | ) | ||
|  | { | ||
|  |     /* === Local variables ================================================== */ | ||
|  | 
 | ||
|  |     Int i ; | ||
|  | 
 | ||
|  |     if (!knobs) | ||
|  |     { | ||
|  | 	return ;			/* no knobs to initialize */ | ||
|  |     } | ||
|  |     for (i = 0 ; i < COLAMD_KNOBS ; i++) | ||
|  |     { | ||
|  | 	knobs [i] = 0 ; | ||
|  |     } | ||
|  |     knobs [COLAMD_DENSE_ROW] = 10 ; | ||
|  |     knobs [COLAMD_DENSE_COL] = 10 ; | ||
|  |     knobs [COLAMD_AGGRESSIVE] = TRUE ;	/* default: do aggressive absorption*/ | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === symamd =============================================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | PUBLIC Int SYMAMD_MAIN			/* return TRUE if OK, FALSE otherwise */ | ||
|  | ( | ||
|  |     /* === Parameters ======================================================= */ | ||
|  | 
 | ||
|  |     Int n,				/* number of rows and columns of A */ | ||
|  |     Int A [],				/* row indices of A */ | ||
|  |     Int p [],				/* column pointers of A */ | ||
|  |     Int perm [],			/* output permutation, size n+1 */ | ||
|  |     double knobs [COLAMD_KNOBS],	/* parameters (uses defaults if NULL) */ | ||
|  |     Int stats [COLAMD_STATS],		/* output statistics and error codes */ | ||
|  |     void * (*allocate) (size_t, size_t), | ||
|  |     					/* pointer to calloc (ANSI C) or */ | ||
|  | 					/* mxCalloc (for MATLAB mexFunction) */ | ||
|  |     void (*release) (void *) | ||
|  |     					/* pointer to free (ANSI C) or */ | ||
|  |     					/* mxFree (for MATLAB mexFunction) */ | ||
|  | ) | ||
|  | { | ||
|  |     /* === Local variables ================================================== */ | ||
|  | 
 | ||
|  |     Int *count ;		/* length of each column of M, and col pointer*/ | ||
|  |     Int *mark ;			/* mark array for finding duplicate entries */ | ||
|  |     Int *M ;			/* row indices of matrix M */ | ||
|  |     size_t Mlen ;		/* length of M */ | ||
|  |     Int n_row ;			/* number of rows in M */ | ||
|  |     Int nnz ;			/* number of entries in A */ | ||
|  |     Int i ;			/* row index of A */ | ||
|  |     Int j ;			/* column index of A */ | ||
|  |     Int k ;			/* row index of M */  | ||
|  |     Int mnz ;			/* number of nonzeros in M */ | ||
|  |     Int pp ;			/* index into a column of A */ | ||
|  |     Int last_row ;		/* last row seen in the current column */ | ||
|  |     Int length ;		/* number of nonzeros in a column */ | ||
|  | 
 | ||
|  |     double cknobs [COLAMD_KNOBS] ;		/* knobs for colamd */ | ||
|  |     double default_knobs [COLAMD_KNOBS] ;	/* default knobs for colamd */ | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  |     colamd_get_debug ("symamd") ; | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  |     /* === Check the input arguments ======================================== */ | ||
|  | 
 | ||
|  |     if (!stats) | ||
|  |     { | ||
|  | 	DEBUG0 (("symamd: stats not present\n")) ; | ||
|  | 	return (FALSE) ; | ||
|  |     } | ||
|  |     for (i = 0 ; i < COLAMD_STATS ; i++) | ||
|  |     { | ||
|  | 	stats [i] = 0 ; | ||
|  |     } | ||
|  |     stats [COLAMD_STATUS] = COLAMD_OK ; | ||
|  |     stats [COLAMD_INFO1] = -1 ; | ||
|  |     stats [COLAMD_INFO2] = -1 ; | ||
|  | 
 | ||
|  |     if (!A) | ||
|  |     { | ||
|  |     	stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ; | ||
|  | 	DEBUG0 (("symamd: A not present\n")) ; | ||
|  | 	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (!p)		/* p is not present */ | ||
|  |     { | ||
|  | 	stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ; | ||
|  | 	DEBUG0 (("symamd: p not present\n")) ; | ||
|  |     	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (n < 0)		/* n must be >= 0 */ | ||
|  |     { | ||
|  | 	stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ; | ||
|  | 	stats [COLAMD_INFO1] = n ; | ||
|  | 	DEBUG0 (("symamd: n negative %d\n", n)) ; | ||
|  |     	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     nnz = p [n] ; | ||
|  |     if (nnz < 0)	/* nnz must be >= 0 */ | ||
|  |     { | ||
|  | 	stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ; | ||
|  | 	stats [COLAMD_INFO1] = nnz ; | ||
|  | 	DEBUG0 (("symamd: number of entries negative %d\n", nnz)) ; | ||
|  | 	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (p [0] != 0) | ||
|  |     { | ||
|  | 	stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero ; | ||
|  | 	stats [COLAMD_INFO1] = p [0] ; | ||
|  | 	DEBUG0 (("symamd: p[0] not zero %d\n", p [0])) ; | ||
|  | 	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* === If no knobs, set default knobs =================================== */ | ||
|  | 
 | ||
|  |     if (!knobs) | ||
|  |     { | ||
|  | 	COLAMD_set_defaults (default_knobs) ; | ||
|  | 	knobs = default_knobs ; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* === Allocate count and mark ========================================== */ | ||
|  | 
 | ||
|  |     count = (Int *) ((*allocate) (n+1, sizeof (Int))) ; | ||
|  |     if (!count) | ||
|  |     { | ||
|  | 	stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ; | ||
|  | 	DEBUG0 (("symamd: allocate count (size %d) failed\n", n+1)) ; | ||
|  | 	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     mark = (Int *) ((*allocate) (n+1, sizeof (Int))) ; | ||
|  |     if (!mark) | ||
|  |     { | ||
|  | 	stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ; | ||
|  | 	(*release) ((void *) count) ; | ||
|  | 	DEBUG0 (("symamd: allocate mark (size %d) failed\n", n+1)) ; | ||
|  | 	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* === Compute column counts of M, check if A is valid ================== */ | ||
|  | 
 | ||
|  |     stats [COLAMD_INFO3] = 0 ;  /* number of duplicate or unsorted row indices*/ | ||
|  | 
 | ||
|  |     for (i = 0 ; i < n ; i++) | ||
|  |     { | ||
|  |     	mark [i] = -1 ; | ||
|  |     } | ||
|  | 
 | ||
|  |     for (j = 0 ; j < n ; j++) | ||
|  |     { | ||
|  | 	last_row = -1 ; | ||
|  | 
 | ||
|  | 	length = p [j+1] - p [j] ; | ||
|  | 	if (length < 0) | ||
|  | 	{ | ||
|  | 	    /* column pointers must be non-decreasing */ | ||
|  | 	    stats [COLAMD_STATUS] = COLAMD_ERROR_col_length_negative ; | ||
|  | 	    stats [COLAMD_INFO1] = j ; | ||
|  | 	    stats [COLAMD_INFO2] = length ; | ||
|  | 	    (*release) ((void *) count) ; | ||
|  | 	    (*release) ((void *) mark) ; | ||
|  | 	    DEBUG0 (("symamd: col %d negative length %d\n", j, length)) ; | ||
|  | 	    return (FALSE) ; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	for (pp = p [j] ; pp < p [j+1] ; pp++) | ||
|  | 	{ | ||
|  | 	    i = A [pp] ; | ||
|  | 	    if (i < 0 || i >= n) | ||
|  | 	    { | ||
|  | 		/* row index i, in column j, is out of bounds */ | ||
|  | 		stats [COLAMD_STATUS] = COLAMD_ERROR_row_index_out_of_bounds ; | ||
|  | 		stats [COLAMD_INFO1] = j ; | ||
|  | 		stats [COLAMD_INFO2] = i ; | ||
|  | 		stats [COLAMD_INFO3] = n ; | ||
|  | 		(*release) ((void *) count) ; | ||
|  | 		(*release) ((void *) mark) ; | ||
|  | 		DEBUG0 (("symamd: row %d col %d out of bounds\n", i, j)) ; | ||
|  | 		return (FALSE) ; | ||
|  | 	    } | ||
|  | 
 | ||
|  | 	    if (i <= last_row || mark [i] == j) | ||
|  | 	    { | ||
|  | 		/* row index is unsorted or repeated (or both), thus col */ | ||
|  | 		/* is jumbled.  This is a notice, not an error condition. */ | ||
|  | 		stats [COLAMD_STATUS] = COLAMD_OK_BUT_JUMBLED ; | ||
|  | 		stats [COLAMD_INFO1] = j ; | ||
|  | 		stats [COLAMD_INFO2] = i ; | ||
|  | 		(stats [COLAMD_INFO3]) ++ ; | ||
|  | 		DEBUG1 (("symamd: row %d col %d unsorted/duplicate\n", i, j)) ; | ||
|  | 	    } | ||
|  | 
 | ||
|  | 	    if (i > j && mark [i] != j) | ||
|  | 	    { | ||
|  | 		/* row k of M will contain column indices i and j */ | ||
|  | 		count [i]++ ; | ||
|  | 		count [j]++ ; | ||
|  | 	    } | ||
|  | 
 | ||
|  | 	    /* mark the row as having been seen in this column */ | ||
|  | 	    mark [i] = j ; | ||
|  | 
 | ||
|  | 	    last_row = i ; | ||
|  | 	} | ||
|  |     } | ||
|  | 
 | ||
|  |     /* v2.4: removed free(mark) */ | ||
|  | 
 | ||
|  |     /* === Compute column pointers of M ===================================== */ | ||
|  | 
 | ||
|  |     /* use output permutation, perm, for column pointers of M */ | ||
|  |     perm [0] = 0 ; | ||
|  |     for (j = 1 ; j <= n ; j++) | ||
|  |     { | ||
|  | 	perm [j] = perm [j-1] + count [j-1] ; | ||
|  |     } | ||
|  |     for (j = 0 ; j < n ; j++) | ||
|  |     { | ||
|  | 	count [j] = perm [j] ; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* === Construct M ====================================================== */ | ||
|  | 
 | ||
|  |     mnz = perm [n] ; | ||
|  |     n_row = mnz / 2 ; | ||
|  |     Mlen = COLAMD_recommended (mnz, n_row, n) ; | ||
|  |     M = (Int *) ((*allocate) (Mlen, sizeof (Int))) ; | ||
|  |     DEBUG0 (("symamd: M is %d-by-%d with %d entries, Mlen = %g\n", | ||
|  |     	n_row, n, mnz, (double) Mlen)) ; | ||
|  | 
 | ||
|  |     if (!M) | ||
|  |     { | ||
|  | 	stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ; | ||
|  | 	(*release) ((void *) count) ; | ||
|  | 	(*release) ((void *) mark) ; | ||
|  | 	DEBUG0 (("symamd: allocate M (size %g) failed\n", (double) Mlen)) ; | ||
|  | 	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     k = 0 ; | ||
|  | 
 | ||
|  |     if (stats [COLAMD_STATUS] == COLAMD_OK) | ||
|  |     { | ||
|  | 	/* Matrix is OK */ | ||
|  | 	for (j = 0 ; j < n ; j++) | ||
|  | 	{ | ||
|  | 	    ASSERT (p [j+1] - p [j] >= 0) ; | ||
|  | 	    for (pp = p [j] ; pp < p [j+1] ; pp++) | ||
|  | 	    { | ||
|  | 		i = A [pp] ; | ||
|  | 		ASSERT (i >= 0 && i < n) ; | ||
|  | 		if (i > j) | ||
|  | 		{ | ||
|  | 		    /* row k of M contains column indices i and j */ | ||
|  | 		    M [count [i]++] = k ; | ||
|  | 		    M [count [j]++] = k ; | ||
|  | 		    k++ ; | ||
|  | 		} | ||
|  | 	    } | ||
|  | 	} | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  | 	/* Matrix is jumbled.  Do not add duplicates to M.  Unsorted cols OK. */ | ||
|  | 	DEBUG0 (("symamd: Duplicates in A.\n")) ; | ||
|  | 	for (i = 0 ; i < n ; i++) | ||
|  | 	{ | ||
|  | 	    mark [i] = -1 ; | ||
|  | 	} | ||
|  | 	for (j = 0 ; j < n ; j++) | ||
|  | 	{ | ||
|  | 	    ASSERT (p [j+1] - p [j] >= 0) ; | ||
|  | 	    for (pp = p [j] ; pp < p [j+1] ; pp++) | ||
|  | 	    { | ||
|  | 		i = A [pp] ; | ||
|  | 		ASSERT (i >= 0 && i < n) ; | ||
|  | 		if (i > j && mark [i] != j) | ||
|  | 		{ | ||
|  | 		    /* row k of M contains column indices i and j */ | ||
|  | 		    M [count [i]++] = k ; | ||
|  | 		    M [count [j]++] = k ; | ||
|  | 		    k++ ; | ||
|  | 		    mark [i] = j ; | ||
|  | 		} | ||
|  | 	    } | ||
|  | 	} | ||
|  | 	/* v2.4: free(mark) moved below */ | ||
|  |     } | ||
|  | 
 | ||
|  |     /* count and mark no longer needed */ | ||
|  |     (*release) ((void *) count) ; | ||
|  |     (*release) ((void *) mark) ;	/* v2.4: free (mark) moved here */ | ||
|  |     ASSERT (k == n_row) ; | ||
|  | 
 | ||
|  |     /* === Adjust the knobs for M =========================================== */ | ||
|  | 
 | ||
|  |     for (i = 0 ; i < COLAMD_KNOBS ; i++) | ||
|  |     { | ||
|  | 	cknobs [i] = knobs [i] ; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* there are no dense rows in M */ | ||
|  |     cknobs [COLAMD_DENSE_ROW] = -1 ; | ||
|  |     cknobs [COLAMD_DENSE_COL] = knobs [COLAMD_DENSE_ROW] ; | ||
|  | 
 | ||
|  |     /* === Order the columns of M =========================================== */ | ||
|  | 
 | ||
|  |     /* v2.4: colamd cannot fail here, so the error check is removed */ | ||
|  |     (void) COLAMD_MAIN (n_row, n, (Int) Mlen, M, perm, cknobs, stats) ; | ||
|  | 
 | ||
|  |     /* Note that the output permutation is now in perm */ | ||
|  | 
 | ||
|  |     /* === get the statistics for symamd from colamd ======================== */ | ||
|  | 
 | ||
|  |     /* a dense column in colamd means a dense row and col in symamd */ | ||
|  |     stats [COLAMD_DENSE_ROW] = stats [COLAMD_DENSE_COL] ; | ||
|  | 
 | ||
|  |     /* === Free M =========================================================== */ | ||
|  | 
 | ||
|  |     (*release) ((void *) M) ; | ||
|  |     DEBUG0 (("symamd: done.\n")) ; | ||
|  |     return (TRUE) ; | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === colamd =============================================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |     The colamd routine computes a column ordering Q of a sparse matrix | ||
|  |     A such that the LU factorization P(AQ) = LU remains sparse, where P is | ||
|  |     selected via partial pivoting.   The routine can also be viewed as | ||
|  |     providing a permutation Q such that the Cholesky factorization | ||
|  |     (AQ)'(AQ) = LL' remains sparse. | ||
|  | */ | ||
|  | 
 | ||
|  | PUBLIC Int COLAMD_MAIN		/* returns TRUE if successful, FALSE otherwise*/ | ||
|  | ( | ||
|  |     /* === Parameters ======================================================= */ | ||
|  | 
 | ||
|  |     Int n_row,			/* number of rows in A */ | ||
|  |     Int n_col,			/* number of columns in A */ | ||
|  |     Int Alen,			/* length of A */ | ||
|  |     Int A [],			/* row indices of A */ | ||
|  |     Int p [],			/* pointers to columns in A */ | ||
|  |     double knobs [COLAMD_KNOBS],/* parameters (uses defaults if NULL) */ | ||
|  |     Int stats [COLAMD_STATS]	/* output statistics and error codes */ | ||
|  | ) | ||
|  | { | ||
|  |     /* === Local variables ================================================== */ | ||
|  | 
 | ||
|  |     Int i ;			/* loop index */ | ||
|  |     Int nnz ;			/* nonzeros in A */ | ||
|  |     size_t Row_size ;		/* size of Row [], in integers */ | ||
|  |     size_t Col_size ;		/* size of Col [], in integers */ | ||
|  |     size_t need ;		/* minimum required length of A */ | ||
|  |     Colamd_Row *Row ;		/* pointer into A of Row [0..n_row] array */ | ||
|  |     Colamd_Col *Col ;		/* pointer into A of Col [0..n_col] array */ | ||
|  |     Int n_col2 ;		/* number of non-dense, non-empty columns */ | ||
|  |     Int n_row2 ;		/* number of non-dense, non-empty rows */ | ||
|  |     Int ngarbage ;		/* number of garbage collections performed */ | ||
|  |     Int max_deg ;		/* maximum row degree */ | ||
|  |     double default_knobs [COLAMD_KNOBS] ;	/* default knobs array */ | ||
|  |     Int aggressive ;		/* do aggressive absorption */ | ||
|  |     int ok ; | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  |     colamd_get_debug ("colamd") ; | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  |     /* === Check the input arguments ======================================== */ | ||
|  | 
 | ||
|  |     if (!stats) | ||
|  |     { | ||
|  | 	DEBUG0 (("colamd: stats not present\n")) ; | ||
|  | 	return (FALSE) ; | ||
|  |     } | ||
|  |     for (i = 0 ; i < COLAMD_STATS ; i++) | ||
|  |     { | ||
|  | 	stats [i] = 0 ; | ||
|  |     } | ||
|  |     stats [COLAMD_STATUS] = COLAMD_OK ; | ||
|  |     stats [COLAMD_INFO1] = -1 ; | ||
|  |     stats [COLAMD_INFO2] = -1 ; | ||
|  | 
 | ||
|  |     if (!A)		/* A is not present */ | ||
|  |     { | ||
|  | 	stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ; | ||
|  | 	DEBUG0 (("colamd: A not present\n")) ; | ||
|  | 	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (!p)		/* p is not present */ | ||
|  |     { | ||
|  | 	stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ; | ||
|  | 	DEBUG0 (("colamd: p not present\n")) ; | ||
|  |     	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (n_row < 0)	/* n_row must be >= 0 */ | ||
|  |     { | ||
|  | 	stats [COLAMD_STATUS] = COLAMD_ERROR_nrow_negative ; | ||
|  | 	stats [COLAMD_INFO1] = n_row ; | ||
|  | 	DEBUG0 (("colamd: nrow negative %d\n", n_row)) ; | ||
|  |     	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (n_col < 0)	/* n_col must be >= 0 */ | ||
|  |     { | ||
|  | 	stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ; | ||
|  | 	stats [COLAMD_INFO1] = n_col ; | ||
|  | 	DEBUG0 (("colamd: ncol negative %d\n", n_col)) ; | ||
|  |     	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     nnz = p [n_col] ; | ||
|  |     if (nnz < 0)	/* nnz must be >= 0 */ | ||
|  |     { | ||
|  | 	stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ; | ||
|  | 	stats [COLAMD_INFO1] = nnz ; | ||
|  | 	DEBUG0 (("colamd: number of entries negative %d\n", nnz)) ; | ||
|  | 	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (p [0] != 0) | ||
|  |     { | ||
|  | 	stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero	; | ||
|  | 	stats [COLAMD_INFO1] = p [0] ; | ||
|  | 	DEBUG0 (("colamd: p[0] not zero %d\n", p [0])) ; | ||
|  | 	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* === If no knobs, set default knobs =================================== */ | ||
|  | 
 | ||
|  |     if (!knobs) | ||
|  |     { | ||
|  | 	COLAMD_set_defaults (default_knobs) ; | ||
|  | 	knobs = default_knobs ; | ||
|  |     } | ||
|  | 
 | ||
|  |     aggressive = (knobs [COLAMD_AGGRESSIVE] != FALSE) ; | ||
|  | 
 | ||
|  |     /* === Allocate the Row and Col arrays from array A ===================== */ | ||
|  | 
 | ||
|  |     ok = TRUE ; | ||
|  |     Col_size = COLAMD_C (n_col, &ok) ;	    /* size of Col array of structs */ | ||
|  |     Row_size = COLAMD_R (n_row, &ok) ;	    /* size of Row array of structs */ | ||
|  | 
 | ||
|  |     /* need = 2*nnz + n_col + Col_size + Row_size ; */ | ||
|  |     need = t_mult (nnz, 2, &ok) ; | ||
|  |     need = t_add (need, n_col, &ok) ; | ||
|  |     need = t_add (need, Col_size, &ok) ; | ||
|  |     need = t_add (need, Row_size, &ok) ; | ||
|  | 
 | ||
|  |     if (!ok || need > (size_t) Alen || need > Int_MAX) | ||
|  |     { | ||
|  | 	/* not enough space in array A to perform the ordering */ | ||
|  | 	stats [COLAMD_STATUS] = COLAMD_ERROR_A_too_small ; | ||
|  | 	stats [COLAMD_INFO1] = need ; | ||
|  | 	stats [COLAMD_INFO2] = Alen ; | ||
|  | 	DEBUG0 (("colamd: Need Alen >= %d, given only Alen = %d\n", need,Alen)); | ||
|  | 	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     Alen -= Col_size + Row_size ; | ||
|  |     Col = (Colamd_Col *) &A [Alen] ; | ||
|  |     Row = (Colamd_Row *) &A [Alen + Col_size] ; | ||
|  | 
 | ||
|  |     /* === Construct the row and column data structures ===================== */ | ||
|  | 
 | ||
|  |     if (!init_rows_cols (n_row, n_col, Row, Col, A, p, stats)) | ||
|  |     { | ||
|  | 	/* input matrix is invalid */ | ||
|  | 	DEBUG0 (("colamd: Matrix invalid\n")) ; | ||
|  | 	return (FALSE) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* === Initialize scores, kill dense rows/columns ======================= */ | ||
|  | 
 | ||
|  |     init_scoring (n_row, n_col, Row, Col, A, p, knobs, | ||
|  | 	&n_row2, &n_col2, &max_deg) ; | ||
|  | 
 | ||
|  |     /* === Order the supercolumns =========================================== */ | ||
|  | 
 | ||
|  |     ngarbage = find_ordering (n_row, n_col, Alen, Row, Col, A, p, | ||
|  | 	n_col2, max_deg, 2*nnz, aggressive) ; | ||
|  | 
 | ||
|  |     /* === Order the non-principal columns ================================== */ | ||
|  | 
 | ||
|  |     order_children (n_col, Col, p) ; | ||
|  | 
 | ||
|  |     /* === Return statistics in stats ======================================= */ | ||
|  | 
 | ||
|  |     stats [COLAMD_DENSE_ROW] = n_row - n_row2 ; | ||
|  |     stats [COLAMD_DENSE_COL] = n_col - n_col2 ; | ||
|  |     stats [COLAMD_DEFRAG_COUNT] = ngarbage ; | ||
|  |     DEBUG0 (("colamd: done.\n")) ;  | ||
|  |     return (TRUE) ; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === colamd_report ======================================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | PUBLIC void COLAMD_report | ||
|  | ( | ||
|  |     Int stats [COLAMD_STATS] | ||
|  | ) | ||
|  | { | ||
|  |     print_report ("colamd", stats) ; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === symamd_report ======================================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | PUBLIC void SYMAMD_report | ||
|  | ( | ||
|  |     Int stats [COLAMD_STATS] | ||
|  | ) | ||
|  | { | ||
|  |     print_report ("symamd", stats) ; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === NON-USER-CALLABLE ROUTINES: ========================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /* There are no user-callable routines beyond this point in the file */ | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === init_rows_cols ======================================================= */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |     Takes the column form of the matrix in A and creates the row form of the | ||
|  |     matrix.  Also, row and column attributes are stored in the Col and Row | ||
|  |     structs.  If the columns are un-sorted or contain duplicate row indices, | ||
|  |     this routine will also sort and remove duplicate row indices from the | ||
|  |     column form of the matrix.  Returns FALSE if the matrix is invalid, | ||
|  |     TRUE otherwise.  Not user-callable. | ||
|  | */ | ||
|  | 
 | ||
|  | PRIVATE Int init_rows_cols	/* returns TRUE if OK, or FALSE otherwise */ | ||
|  | ( | ||
|  |     /* === Parameters ======================================================= */ | ||
|  | 
 | ||
|  |     Int n_row,			/* number of rows of A */ | ||
|  |     Int n_col,			/* number of columns of A */ | ||
|  |     Colamd_Row Row [],		/* of size n_row+1 */ | ||
|  |     Colamd_Col Col [],		/* of size n_col+1 */ | ||
|  |     Int A [],			/* row indices of A, of size Alen */ | ||
|  |     Int p [],			/* pointers to columns in A, of size n_col+1 */ | ||
|  |     Int stats [COLAMD_STATS]	/* colamd statistics */  | ||
|  | ) | ||
|  | { | ||
|  |     /* === Local variables ================================================== */ | ||
|  | 
 | ||
|  |     Int col ;			/* a column index */ | ||
|  |     Int row ;			/* a row index */ | ||
|  |     Int *cp ;			/* a column pointer */ | ||
|  |     Int *cp_end ;		/* a pointer to the end of a column */ | ||
|  |     Int *rp ;			/* a row pointer */ | ||
|  |     Int *rp_end ;		/* a pointer to the end of a row */ | ||
|  |     Int last_row ;		/* previous row */ | ||
|  | 
 | ||
|  |     /* === Initialize columns, and check column pointers ==================== */ | ||
|  | 
 | ||
|  |     for (col = 0 ; col < n_col ; col++) | ||
|  |     { | ||
|  | 	Col [col].start = p [col] ; | ||
|  | 	Col [col].length = p [col+1] - p [col] ; | ||
|  | 
 | ||
|  | 	if (Col [col].length < 0) | ||
|  | 	{ | ||
|  | 	    /* column pointers must be non-decreasing */ | ||
|  | 	    stats [COLAMD_STATUS] = COLAMD_ERROR_col_length_negative ; | ||
|  | 	    stats [COLAMD_INFO1] = col ; | ||
|  | 	    stats [COLAMD_INFO2] = Col [col].length ; | ||
|  | 	    DEBUG0 (("colamd: col %d length %d < 0\n", col, Col [col].length)) ; | ||
|  | 	    return (FALSE) ; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	Col [col].shared1.thickness = 1 ; | ||
|  | 	Col [col].shared2.score = 0 ; | ||
|  | 	Col [col].shared3.prev = EMPTY ; | ||
|  | 	Col [col].shared4.degree_next = EMPTY ; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* p [0..n_col] no longer needed, used as "head" in subsequent routines */ | ||
|  | 
 | ||
|  |     /* === Scan columns, compute row degrees, and check row indices ========= */ | ||
|  | 
 | ||
|  |     stats [COLAMD_INFO3] = 0 ;	/* number of duplicate or unsorted row indices*/ | ||
|  | 
 | ||
|  |     for (row = 0 ; row < n_row ; row++) | ||
|  |     { | ||
|  | 	Row [row].length = 0 ; | ||
|  | 	Row [row].shared2.mark = -1 ; | ||
|  |     } | ||
|  | 
 | ||
|  |     for (col = 0 ; col < n_col ; col++) | ||
|  |     { | ||
|  | 	last_row = -1 ; | ||
|  | 
 | ||
|  | 	cp = &A [p [col]] ; | ||
|  | 	cp_end = &A [p [col+1]] ; | ||
|  | 
 | ||
|  | 	while (cp < cp_end) | ||
|  | 	{ | ||
|  | 	    row = *cp++ ; | ||
|  | 
 | ||
|  | 	    /* make sure row indices within range */ | ||
|  | 	    if (row < 0 || row >= n_row) | ||
|  | 	    { | ||
|  | 		stats [COLAMD_STATUS] = COLAMD_ERROR_row_index_out_of_bounds ; | ||
|  | 		stats [COLAMD_INFO1] = col ; | ||
|  | 		stats [COLAMD_INFO2] = row ; | ||
|  | 		stats [COLAMD_INFO3] = n_row ; | ||
|  | 		DEBUG0 (("colamd: row %d col %d out of bounds\n", row, col)) ; | ||
|  | 		return (FALSE) ; | ||
|  | 	    } | ||
|  | 
 | ||
|  | 	    if (row <= last_row || Row [row].shared2.mark == col) | ||
|  | 	    { | ||
|  | 		/* row index are unsorted or repeated (or both), thus col */ | ||
|  | 		/* is jumbled.  This is a notice, not an error condition. */ | ||
|  | 		stats [COLAMD_STATUS] = COLAMD_OK_BUT_JUMBLED ; | ||
|  | 		stats [COLAMD_INFO1] = col ; | ||
|  | 		stats [COLAMD_INFO2] = row ; | ||
|  | 		(stats [COLAMD_INFO3]) ++ ; | ||
|  | 		DEBUG1 (("colamd: row %d col %d unsorted/duplicate\n",row,col)); | ||
|  | 	    } | ||
|  | 
 | ||
|  | 	    if (Row [row].shared2.mark != col) | ||
|  | 	    { | ||
|  | 		Row [row].length++ ; | ||
|  | 	    } | ||
|  | 	    else | ||
|  | 	    { | ||
|  | 		/* this is a repeated entry in the column, */ | ||
|  | 		/* it will be removed */ | ||
|  | 		Col [col].length-- ; | ||
|  | 	    } | ||
|  | 
 | ||
|  | 	    /* mark the row as having been seen in this column */ | ||
|  | 	    Row [row].shared2.mark = col ; | ||
|  | 
 | ||
|  | 	    last_row = row ; | ||
|  | 	} | ||
|  |     } | ||
|  | 
 | ||
|  |     /* === Compute row pointers ============================================= */ | ||
|  | 
 | ||
|  |     /* row form of the matrix starts directly after the column */ | ||
|  |     /* form of matrix in A */ | ||
|  |     Row [0].start = p [n_col] ; | ||
|  |     Row [0].shared1.p = Row [0].start ; | ||
|  |     Row [0].shared2.mark = -1 ; | ||
|  |     for (row = 1 ; row < n_row ; row++) | ||
|  |     { | ||
|  | 	Row [row].start = Row [row-1].start + Row [row-1].length ; | ||
|  | 	Row [row].shared1.p = Row [row].start ; | ||
|  | 	Row [row].shared2.mark = -1 ; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* === Create row form ================================================== */ | ||
|  | 
 | ||
|  |     if (stats [COLAMD_STATUS] == COLAMD_OK_BUT_JUMBLED) | ||
|  |     { | ||
|  | 	/* if cols jumbled, watch for repeated row indices */ | ||
|  | 	for (col = 0 ; col < n_col ; col++) | ||
|  | 	{ | ||
|  | 	    cp = &A [p [col]] ; | ||
|  | 	    cp_end = &A [p [col+1]] ; | ||
|  | 	    while (cp < cp_end) | ||
|  | 	    { | ||
|  | 		row = *cp++ ; | ||
|  | 		if (Row [row].shared2.mark != col) | ||
|  | 		{ | ||
|  | 		    A [(Row [row].shared1.p)++] = col ; | ||
|  | 		    Row [row].shared2.mark = col ; | ||
|  | 		} | ||
|  | 	    } | ||
|  | 	} | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  | 	/* if cols not jumbled, we don't need the mark (this is faster) */ | ||
|  | 	for (col = 0 ; col < n_col ; col++) | ||
|  | 	{ | ||
|  | 	    cp = &A [p [col]] ; | ||
|  | 	    cp_end = &A [p [col+1]] ; | ||
|  | 	    while (cp < cp_end) | ||
|  | 	    { | ||
|  | 		A [(Row [*cp++].shared1.p)++] = col ; | ||
|  | 	    } | ||
|  | 	} | ||
|  |     } | ||
|  | 
 | ||
|  |     /* === Clear the row marks and set row degrees ========================== */ | ||
|  | 
 | ||
|  |     for (row = 0 ; row < n_row ; row++) | ||
|  |     { | ||
|  | 	Row [row].shared2.mark = 0 ; | ||
|  | 	Row [row].shared1.degree = Row [row].length ; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* === See if we need to re-create columns ============================== */ | ||
|  | 
 | ||
|  |     if (stats [COLAMD_STATUS] == COLAMD_OK_BUT_JUMBLED) | ||
|  |     { | ||
|  |     	DEBUG0 (("colamd: reconstructing column form, matrix jumbled\n")) ; | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  | 	/* make sure column lengths are correct */ | ||
|  | 	for (col = 0 ; col < n_col ; col++) | ||
|  | 	{ | ||
|  | 	    p [col] = Col [col].length ; | ||
|  | 	} | ||
|  | 	for (row = 0 ; row < n_row ; row++) | ||
|  | 	{ | ||
|  | 	    rp = &A [Row [row].start] ; | ||
|  | 	    rp_end = rp + Row [row].length ; | ||
|  | 	    while (rp < rp_end) | ||
|  | 	    { | ||
|  | 		p [*rp++]-- ; | ||
|  | 	    } | ||
|  | 	} | ||
|  | 	for (col = 0 ; col < n_col ; col++) | ||
|  | 	{ | ||
|  | 	    ASSERT (p [col] == 0) ; | ||
|  | 	} | ||
|  | 	/* now p is all zero (different than when debugging is turned off) */ | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  | 	/* === Compute col pointers ========================================= */ | ||
|  | 
 | ||
|  | 	/* col form of the matrix starts at A [0]. */ | ||
|  | 	/* Note, we may have a gap between the col form and the row */ | ||
|  | 	/* form if there were duplicate entries, if so, it will be */ | ||
|  | 	/* removed upon the first garbage collection */ | ||
|  | 	Col [0].start = 0 ; | ||
|  | 	p [0] = Col [0].start ; | ||
|  | 	for (col = 1 ; col < n_col ; col++) | ||
|  | 	{ | ||
|  | 	    /* note that the lengths here are for pruned columns, i.e. */ | ||
|  | 	    /* no duplicate row indices will exist for these columns */ | ||
|  | 	    Col [col].start = Col [col-1].start + Col [col-1].length ; | ||
|  | 	    p [col] = Col [col].start ; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* === Re-create col form =========================================== */ | ||
|  | 
 | ||
|  | 	for (row = 0 ; row < n_row ; row++) | ||
|  | 	{ | ||
|  | 	    rp = &A [Row [row].start] ; | ||
|  | 	    rp_end = rp + Row [row].length ; | ||
|  | 	    while (rp < rp_end) | ||
|  | 	    { | ||
|  | 		A [(p [*rp++])++] = row ; | ||
|  | 	    } | ||
|  | 	} | ||
|  |     } | ||
|  | 
 | ||
|  |     /* === Done.  Matrix is not (or no longer) jumbled ====================== */ | ||
|  | 
 | ||
|  |     return (TRUE) ; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === init_scoring ========================================================= */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |     Kills dense or empty columns and rows, calculates an initial score for | ||
|  |     each column, and places all columns in the degree lists.  Not user-callable. | ||
|  | */ | ||
|  | 
 | ||
|  | PRIVATE void init_scoring | ||
|  | ( | ||
|  |     /* === Parameters ======================================================= */ | ||
|  | 
 | ||
|  |     Int n_row,			/* number of rows of A */ | ||
|  |     Int n_col,			/* number of columns of A */ | ||
|  |     Colamd_Row Row [],		/* of size n_row+1 */ | ||
|  |     Colamd_Col Col [],		/* of size n_col+1 */ | ||
|  |     Int A [],			/* column form and row form of A */ | ||
|  |     Int head [],		/* of size n_col+1 */ | ||
|  |     double knobs [COLAMD_KNOBS],/* parameters */ | ||
|  |     Int *p_n_row2,		/* number of non-dense, non-empty rows */ | ||
|  |     Int *p_n_col2,		/* number of non-dense, non-empty columns */ | ||
|  |     Int *p_max_deg		/* maximum row degree */ | ||
|  | ) | ||
|  | { | ||
|  |     /* === Local variables ================================================== */ | ||
|  | 
 | ||
|  |     Int c ;			/* a column index */ | ||
|  |     Int r, row ;		/* a row index */ | ||
|  |     Int *cp ;			/* a column pointer */ | ||
|  |     Int deg ;			/* degree of a row or column */ | ||
|  |     Int *cp_end ;		/* a pointer to the end of a column */ | ||
|  |     Int *new_cp ;		/* new column pointer */ | ||
|  |     Int col_length ;		/* length of pruned column */ | ||
|  |     Int score ;			/* current column score */ | ||
|  |     Int n_col2 ;		/* number of non-dense, non-empty columns */ | ||
|  |     Int n_row2 ;		/* number of non-dense, non-empty rows */ | ||
|  |     Int dense_row_count ;	/* remove rows with more entries than this */ | ||
|  |     Int dense_col_count ;	/* remove cols with more entries than this */ | ||
|  |     Int min_score ;		/* smallest column score */ | ||
|  |     Int max_deg ;		/* maximum row degree */ | ||
|  |     Int next_col ;		/* Used to add to degree list.*/ | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  |     Int debug_count ;		/* debug only. */ | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  |     /* === Extract knobs ==================================================== */ | ||
|  | 
 | ||
|  |     /* Note: if knobs contains a NaN, this is undefined: */ | ||
|  |     if (knobs [COLAMD_DENSE_ROW] < 0) | ||
|  |     { | ||
|  | 	/* only remove completely dense rows */ | ||
|  | 	dense_row_count = n_col-1 ; | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  | 	dense_row_count = DENSE_DEGREE (knobs [COLAMD_DENSE_ROW], n_col) ; | ||
|  |     } | ||
|  |     if (knobs [COLAMD_DENSE_COL] < 0) | ||
|  |     { | ||
|  | 	/* only remove completely dense columns */ | ||
|  | 	dense_col_count = n_row-1 ; | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  | 	dense_col_count = | ||
|  | 	    DENSE_DEGREE (knobs [COLAMD_DENSE_COL], MIN (n_row, n_col)) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     DEBUG1 (("colamd: densecount: %d %d\n", dense_row_count, dense_col_count)) ; | ||
|  |     max_deg = 0 ; | ||
|  |     n_col2 = n_col ; | ||
|  |     n_row2 = n_row ; | ||
|  | 
 | ||
|  |     /* === Kill empty columns =============================================== */ | ||
|  | 
 | ||
|  |     /* Put the empty columns at the end in their natural order, so that LU */ | ||
|  |     /* factorization can proceed as far as possible. */ | ||
|  |     for (c = n_col-1 ; c >= 0 ; c--) | ||
|  |     { | ||
|  | 	deg = Col [c].length ; | ||
|  | 	if (deg == 0) | ||
|  | 	{ | ||
|  | 	    /* this is a empty column, kill and order it last */ | ||
|  | 	    Col [c].shared2.order = --n_col2 ; | ||
|  | 	    KILL_PRINCIPAL_COL (c) ; | ||
|  | 	} | ||
|  |     } | ||
|  |     DEBUG1 (("colamd: null columns killed: %d\n", n_col - n_col2)) ; | ||
|  | 
 | ||
|  |     /* === Kill dense columns =============================================== */ | ||
|  | 
 | ||
|  |     /* Put the dense columns at the end, in their natural order */ | ||
|  |     for (c = n_col-1 ; c >= 0 ; c--) | ||
|  |     { | ||
|  | 	/* skip any dead columns */ | ||
|  | 	if (COL_IS_DEAD (c)) | ||
|  | 	{ | ||
|  | 	    continue ; | ||
|  | 	} | ||
|  | 	deg = Col [c].length ; | ||
|  | 	if (deg > dense_col_count) | ||
|  | 	{ | ||
|  | 	    /* this is a dense column, kill and order it last */ | ||
|  | 	    Col [c].shared2.order = --n_col2 ; | ||
|  | 	    /* decrement the row degrees */ | ||
|  | 	    cp = &A [Col [c].start] ; | ||
|  | 	    cp_end = cp + Col [c].length ; | ||
|  | 	    while (cp < cp_end) | ||
|  | 	    { | ||
|  | 		Row [*cp++].shared1.degree-- ; | ||
|  | 	    } | ||
|  | 	    KILL_PRINCIPAL_COL (c) ; | ||
|  | 	} | ||
|  |     } | ||
|  |     DEBUG1 (("colamd: Dense and null columns killed: %d\n", n_col - n_col2)) ; | ||
|  | 
 | ||
|  |     /* === Kill dense and empty rows ======================================== */ | ||
|  | 
 | ||
|  |     for (r = 0 ; r < n_row ; r++) | ||
|  |     { | ||
|  | 	deg = Row [r].shared1.degree ; | ||
|  | 	ASSERT (deg >= 0 && deg <= n_col) ; | ||
|  | 	if (deg > dense_row_count || deg == 0) | ||
|  | 	{ | ||
|  | 	    /* kill a dense or empty row */ | ||
|  | 	    KILL_ROW (r) ; | ||
|  | 	    --n_row2 ; | ||
|  | 	} | ||
|  | 	else | ||
|  | 	{ | ||
|  | 	    /* keep track of max degree of remaining rows */ | ||
|  | 	    max_deg = MAX (max_deg, deg) ; | ||
|  | 	} | ||
|  |     } | ||
|  |     DEBUG1 (("colamd: Dense and null rows killed: %d\n", n_row - n_row2)) ; | ||
|  | 
 | ||
|  |     /* === Compute initial column scores ==================================== */ | ||
|  | 
 | ||
|  |     /* At this point the row degrees are accurate.  They reflect the number */ | ||
|  |     /* of "live" (non-dense) columns in each row.  No empty rows exist. */ | ||
|  |     /* Some "live" columns may contain only dead rows, however.  These are */ | ||
|  |     /* pruned in the code below. */ | ||
|  | 
 | ||
|  |     /* now find the initial matlab score for each column */ | ||
|  |     for (c = n_col-1 ; c >= 0 ; c--) | ||
|  |     { | ||
|  | 	/* skip dead column */ | ||
|  | 	if (COL_IS_DEAD (c)) | ||
|  | 	{ | ||
|  | 	    continue ; | ||
|  | 	} | ||
|  | 	score = 0 ; | ||
|  | 	cp = &A [Col [c].start] ; | ||
|  | 	new_cp = cp ; | ||
|  | 	cp_end = cp + Col [c].length ; | ||
|  | 	while (cp < cp_end) | ||
|  | 	{ | ||
|  | 	    /* get a row */ | ||
|  | 	    row = *cp++ ; | ||
|  | 	    /* skip if dead */ | ||
|  | 	    if (ROW_IS_DEAD (row)) | ||
|  | 	    { | ||
|  | 		continue ; | ||
|  | 	    } | ||
|  | 	    /* compact the column */ | ||
|  | 	    *new_cp++ = row ; | ||
|  | 	    /* add row's external degree */ | ||
|  | 	    score += Row [row].shared1.degree - 1 ; | ||
|  | 	    /* guard against integer overflow */ | ||
|  | 	    score = MIN (score, n_col) ; | ||
|  | 	} | ||
|  | 	/* determine pruned column length */ | ||
|  | 	col_length = (Int) (new_cp - &A [Col [c].start]) ; | ||
|  | 	if (col_length == 0) | ||
|  | 	{ | ||
|  | 	    /* a newly-made null column (all rows in this col are "dense" */ | ||
|  | 	    /* and have already been killed) */ | ||
|  | 	    DEBUG2 (("Newly null killed: %d\n", c)) ; | ||
|  | 	    Col [c].shared2.order = --n_col2 ; | ||
|  | 	    KILL_PRINCIPAL_COL (c) ; | ||
|  | 	} | ||
|  | 	else | ||
|  | 	{ | ||
|  | 	    /* set column length and set score */ | ||
|  | 	    ASSERT (score >= 0) ; | ||
|  | 	    ASSERT (score <= n_col) ; | ||
|  | 	    Col [c].length = col_length ; | ||
|  | 	    Col [c].shared2.score = score ; | ||
|  | 	} | ||
|  |     } | ||
|  |     DEBUG1 (("colamd: Dense, null, and newly-null columns killed: %d\n", | ||
|  |     	n_col-n_col2)) ; | ||
|  | 
 | ||
|  |     /* At this point, all empty rows and columns are dead.  All live columns */ | ||
|  |     /* are "clean" (containing no dead rows) and simplicial (no supercolumns */ | ||
|  |     /* yet).  Rows may contain dead columns, but all live rows contain at */ | ||
|  |     /* least one live column. */ | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  |     debug_structures (n_row, n_col, Row, Col, A, n_col2) ; | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  |     /* === Initialize degree lists ========================================== */ | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  |     debug_count = 0 ; | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  |     /* clear the hash buckets */ | ||
|  |     for (c = 0 ; c <= n_col ; c++) | ||
|  |     { | ||
|  | 	head [c] = EMPTY ; | ||
|  |     } | ||
|  |     min_score = n_col ; | ||
|  |     /* place in reverse order, so low column indices are at the front */ | ||
|  |     /* of the lists.  This is to encourage natural tie-breaking */ | ||
|  |     for (c = n_col-1 ; c >= 0 ; c--) | ||
|  |     { | ||
|  | 	/* only add principal columns to degree lists */ | ||
|  | 	if (COL_IS_ALIVE (c)) | ||
|  | 	{ | ||
|  | 	    DEBUG4 (("place %d score %d minscore %d ncol %d\n", | ||
|  | 		c, Col [c].shared2.score, min_score, n_col)) ; | ||
|  | 
 | ||
|  | 	    /* === Add columns score to DList =============================== */ | ||
|  | 
 | ||
|  | 	    score = Col [c].shared2.score ; | ||
|  | 
 | ||
|  | 	    ASSERT (min_score >= 0) ; | ||
|  | 	    ASSERT (min_score <= n_col) ; | ||
|  | 	    ASSERT (score >= 0) ; | ||
|  | 	    ASSERT (score <= n_col) ; | ||
|  | 	    ASSERT (head [score] >= EMPTY) ; | ||
|  | 
 | ||
|  | 	    /* now add this column to dList at proper score location */ | ||
|  | 	    next_col = head [score] ; | ||
|  | 	    Col [c].shared3.prev = EMPTY ; | ||
|  | 	    Col [c].shared4.degree_next = next_col ; | ||
|  | 
 | ||
|  | 	    /* if there already was a column with the same score, set its */ | ||
|  | 	    /* previous pointer to this new column */ | ||
|  | 	    if (next_col != EMPTY) | ||
|  | 	    { | ||
|  | 		Col [next_col].shared3.prev = c ; | ||
|  | 	    } | ||
|  | 	    head [score] = c ; | ||
|  | 
 | ||
|  | 	    /* see if this score is less than current min */ | ||
|  | 	    min_score = MIN (min_score, score) ; | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  | 	    debug_count++ ; | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  | 	} | ||
|  |     } | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  |     DEBUG1 (("colamd: Live cols %d out of %d, non-princ: %d\n", | ||
|  | 	debug_count, n_col, n_col-debug_count)) ; | ||
|  |     ASSERT (debug_count == n_col2) ; | ||
|  |     debug_deg_lists (n_row, n_col, Row, Col, head, min_score, n_col2, max_deg) ; | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  |     /* === Return number of remaining columns, and max row degree =========== */ | ||
|  | 
 | ||
|  |     *p_n_col2 = n_col2 ; | ||
|  |     *p_n_row2 = n_row2 ; | ||
|  |     *p_max_deg = max_deg ; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === find_ordering ======================================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |     Order the principal columns of the supercolumn form of the matrix | ||
|  |     (no supercolumns on input).  Uses a minimum approximate column minimum | ||
|  |     degree ordering method.  Not user-callable. | ||
|  | */ | ||
|  | 
 | ||
|  | PRIVATE Int find_ordering	/* return the number of garbage collections */ | ||
|  | ( | ||
|  |     /* === Parameters ======================================================= */ | ||
|  | 
 | ||
|  |     Int n_row,			/* number of rows of A */ | ||
|  |     Int n_col,			/* number of columns of A */ | ||
|  |     Int Alen,			/* size of A, 2*nnz + n_col or larger */ | ||
|  |     Colamd_Row Row [],		/* of size n_row+1 */ | ||
|  |     Colamd_Col Col [],		/* of size n_col+1 */ | ||
|  |     Int A [],			/* column form and row form of A */ | ||
|  |     Int head [],		/* of size n_col+1 */ | ||
|  |     Int n_col2,			/* Remaining columns to order */ | ||
|  |     Int max_deg,		/* Maximum row degree */ | ||
|  |     Int pfree,			/* index of first free slot (2*nnz on entry) */ | ||
|  |     Int aggressive | ||
|  | ) | ||
|  | { | ||
|  |     /* === Local variables ================================================== */ | ||
|  | 
 | ||
|  |     Int k ;			/* current pivot ordering step */ | ||
|  |     Int pivot_col ;		/* current pivot column */ | ||
|  |     Int *cp ;			/* a column pointer */ | ||
|  |     Int *rp ;			/* a row pointer */ | ||
|  |     Int pivot_row ;		/* current pivot row */ | ||
|  |     Int *new_cp ;		/* modified column pointer */ | ||
|  |     Int *new_rp ;		/* modified row pointer */ | ||
|  |     Int pivot_row_start ;	/* pointer to start of pivot row */ | ||
|  |     Int pivot_row_degree ;	/* number of columns in pivot row */ | ||
|  |     Int pivot_row_length ;	/* number of supercolumns in pivot row */ | ||
|  |     Int pivot_col_score ;	/* score of pivot column */ | ||
|  |     Int needed_memory ;		/* free space needed for pivot row */ | ||
|  |     Int *cp_end ;		/* pointer to the end of a column */ | ||
|  |     Int *rp_end ;		/* pointer to the end of a row */ | ||
|  |     Int row ;			/* a row index */ | ||
|  |     Int col ;			/* a column index */ | ||
|  |     Int max_score ;		/* maximum possible score */ | ||
|  |     Int cur_score ;		/* score of current column */ | ||
|  |     unsigned Int hash ;		/* hash value for supernode detection */ | ||
|  |     Int head_column ;		/* head of hash bucket */ | ||
|  |     Int first_col ;		/* first column in hash bucket */ | ||
|  |     Int tag_mark ;		/* marker value for mark array */ | ||
|  |     Int row_mark ;		/* Row [row].shared2.mark */ | ||
|  |     Int set_difference ;	/* set difference size of row with pivot row */ | ||
|  |     Int min_score ;		/* smallest column score */ | ||
|  |     Int col_thickness ;		/* "thickness" (no. of columns in a supercol) */ | ||
|  |     Int max_mark ;		/* maximum value of tag_mark */ | ||
|  |     Int pivot_col_thickness ;	/* number of columns represented by pivot col */ | ||
|  |     Int prev_col ;		/* Used by Dlist operations. */ | ||
|  |     Int next_col ;		/* Used by Dlist operations. */ | ||
|  |     Int ngarbage ;		/* number of garbage collections performed */ | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  |     Int debug_d ;		/* debug loop counter */ | ||
|  |     Int debug_step = 0 ;	/* debug loop counter */ | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  |     /* === Initialization and clear mark ==================================== */ | ||
|  | 
 | ||
|  |     max_mark = INT_MAX - n_col ;	/* INT_MAX defined in <limits.h> */ | ||
|  |     tag_mark = clear_mark (0, max_mark, n_row, Row) ; | ||
|  |     min_score = 0 ; | ||
|  |     ngarbage = 0 ; | ||
|  |     DEBUG1 (("colamd: Ordering, n_col2=%d\n", n_col2)) ; | ||
|  | 
 | ||
|  |     /* === Order the columns ================================================ */ | ||
|  | 
 | ||
|  |     for (k = 0 ; k < n_col2 ; /* 'k' is incremented below */) | ||
|  |     { | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  | 	if (debug_step % 100 == 0) | ||
|  | 	{ | ||
|  | 	    DEBUG2 (("\n...       Step k: %d out of n_col2: %d\n", k, n_col2)) ; | ||
|  | 	} | ||
|  | 	else | ||
|  | 	{ | ||
|  | 	    DEBUG3 (("\n----------Step k: %d out of n_col2: %d\n", k, n_col2)) ; | ||
|  | 	} | ||
|  | 	debug_step++ ; | ||
|  | 	debug_deg_lists (n_row, n_col, Row, Col, head, | ||
|  | 		min_score, n_col2-k, max_deg) ; | ||
|  | 	debug_matrix (n_row, n_col, Row, Col, A) ; | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  | 	/* === Select pivot column, and order it ============================ */ | ||
|  | 
 | ||
|  | 	/* make sure degree list isn't empty */ | ||
|  | 	ASSERT (min_score >= 0) ; | ||
|  | 	ASSERT (min_score <= n_col) ; | ||
|  | 	ASSERT (head [min_score] >= EMPTY) ; | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  | 	for (debug_d = 0 ; debug_d < min_score ; debug_d++) | ||
|  | 	{ | ||
|  | 	    ASSERT (head [debug_d] == EMPTY) ; | ||
|  | 	} | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  | 	/* get pivot column from head of minimum degree list */ | ||
|  | 	while (head [min_score] == EMPTY && min_score < n_col) | ||
|  | 	{ | ||
|  | 	    min_score++ ; | ||
|  | 	} | ||
|  | 	pivot_col = head [min_score] ; | ||
|  | 	ASSERT (pivot_col >= 0 && pivot_col <= n_col) ; | ||
|  | 	next_col = Col [pivot_col].shared4.degree_next ; | ||
|  | 	head [min_score] = next_col ; | ||
|  | 	if (next_col != EMPTY) | ||
|  | 	{ | ||
|  | 	    Col [next_col].shared3.prev = EMPTY ; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	ASSERT (COL_IS_ALIVE (pivot_col)) ; | ||
|  | 
 | ||
|  | 	/* remember score for defrag check */ | ||
|  | 	pivot_col_score = Col [pivot_col].shared2.score ; | ||
|  | 
 | ||
|  | 	/* the pivot column is the kth column in the pivot order */ | ||
|  | 	Col [pivot_col].shared2.order = k ; | ||
|  | 
 | ||
|  | 	/* increment order count by column thickness */ | ||
|  | 	pivot_col_thickness = Col [pivot_col].shared1.thickness ; | ||
|  | 	k += pivot_col_thickness ; | ||
|  | 	ASSERT (pivot_col_thickness > 0) ; | ||
|  | 	DEBUG3 (("Pivot col: %d thick %d\n", pivot_col, pivot_col_thickness)) ; | ||
|  | 
 | ||
|  | 	/* === Garbage_collection, if necessary ============================= */ | ||
|  | 
 | ||
|  | 	needed_memory = MIN (pivot_col_score, n_col - k) ; | ||
|  | 	if (pfree + needed_memory >= Alen) | ||
|  | 	{ | ||
|  | 	    pfree = garbage_collection (n_row, n_col, Row, Col, A, &A [pfree]) ; | ||
|  | 	    ngarbage++ ; | ||
|  | 	    /* after garbage collection we will have enough */ | ||
|  | 	    ASSERT (pfree + needed_memory < Alen) ; | ||
|  | 	    /* garbage collection has wiped out the Row[].shared2.mark array */ | ||
|  | 	    tag_mark = clear_mark (0, max_mark, n_row, Row) ; | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  | 	    debug_matrix (n_row, n_col, Row, Col, A) ; | ||
|  | #endif /* NDEBUG */
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* === Compute pivot row pattern ==================================== */ | ||
|  | 
 | ||
|  | 	/* get starting location for this new merged row */ | ||
|  | 	pivot_row_start = pfree ; | ||
|  | 
 | ||
|  | 	/* initialize new row counts to zero */ | ||
|  | 	pivot_row_degree = 0 ; | ||
|  | 
 | ||
|  | 	/* tag pivot column as having been visited so it isn't included */ | ||
|  | 	/* in merged pivot row */ | ||
|  | 	Col [pivot_col].shared1.thickness = -pivot_col_thickness ; | ||
|  | 
 | ||
|  | 	/* pivot row is the union of all rows in the pivot column pattern */ | ||
|  | 	cp = &A [Col [pivot_col].start] ; | ||
|  | 	cp_end = cp + Col [pivot_col].length ; | ||
|  | 	while (cp < cp_end) | ||
|  | 	{ | ||
|  | 	    /* get a row */ | ||
|  | 	    row = *cp++ ; | ||
|  | 	    DEBUG4 (("Pivot col pattern %d %d\n", ROW_IS_ALIVE (row), row)) ; | ||
|  | 	    /* skip if row is dead */ | ||
|  | 	    if (ROW_IS_ALIVE (row)) | ||
|  | 	    { | ||
|  | 		rp = &A [Row [row].start] ; | ||
|  | 		rp_end = rp + Row [row].length ; | ||
|  | 		while (rp < rp_end) | ||
|  | 		{ | ||
|  | 		    /* get a column */ | ||
|  | 		    col = *rp++ ; | ||
|  | 		    /* add the column, if alive and untagged */ | ||
|  | 		    col_thickness = Col [col].shared1.thickness ; | ||
|  | 		    if (col_thickness > 0 && COL_IS_ALIVE (col)) | ||
|  | 		    { | ||
|  | 			/* tag column in pivot row */ | ||
|  | 			Col [col].shared1.thickness = -col_thickness ; | ||
|  | 			ASSERT (pfree < Alen) ; | ||
|  | 			/* place column in pivot row */ | ||
|  | 			A [pfree++] = col ; | ||
|  | 			pivot_row_degree += col_thickness ; | ||
|  | 		    } | ||
|  | 		} | ||
|  | 	    } | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* clear tag on pivot column */ | ||
|  | 	Col [pivot_col].shared1.thickness = pivot_col_thickness ; | ||
|  | 	max_deg = MAX (max_deg, pivot_row_degree) ; | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  | 	DEBUG3 (("check2\n")) ; | ||
|  | 	debug_mark (n_row, Row, tag_mark, max_mark) ; | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  | 	/* === Kill all rows used to construct pivot row ==================== */ | ||
|  | 
 | ||
|  | 	/* also kill pivot row, temporarily */ | ||
|  | 	cp = &A [Col [pivot_col].start] ; | ||
|  | 	cp_end = cp + Col [pivot_col].length ; | ||
|  | 	while (cp < cp_end) | ||
|  | 	{ | ||
|  | 	    /* may be killing an already dead row */ | ||
|  | 	    row = *cp++ ; | ||
|  | 	    DEBUG3 (("Kill row in pivot col: %d\n", row)) ; | ||
|  | 	    KILL_ROW (row) ; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* === Select a row index to use as the new pivot row =============== */ | ||
|  | 
 | ||
|  | 	pivot_row_length = pfree - pivot_row_start ; | ||
|  | 	if (pivot_row_length > 0) | ||
|  | 	{ | ||
|  | 	    /* pick the "pivot" row arbitrarily (first row in col) */ | ||
|  | 	    pivot_row = A [Col [pivot_col].start] ; | ||
|  | 	    DEBUG3 (("Pivotal row is %d\n", pivot_row)) ; | ||
|  | 	} | ||
|  | 	else | ||
|  | 	{ | ||
|  | 	    /* there is no pivot row, since it is of zero length */ | ||
|  | 	    pivot_row = EMPTY ; | ||
|  | 	    ASSERT (pivot_row_length == 0) ; | ||
|  | 	} | ||
|  | 	ASSERT (Col [pivot_col].length > 0 || pivot_row_length == 0) ; | ||
|  | 
 | ||
|  | 	/* === Approximate degree computation =============================== */ | ||
|  | 
 | ||
|  | 	/* Here begins the computation of the approximate degree.  The column */ | ||
|  | 	/* score is the sum of the pivot row "length", plus the size of the */ | ||
|  | 	/* set differences of each row in the column minus the pattern of the */ | ||
|  | 	/* pivot row itself.  The column ("thickness") itself is also */ | ||
|  | 	/* excluded from the column score (we thus use an approximate */ | ||
|  | 	/* external degree). */ | ||
|  | 
 | ||
|  | 	/* The time taken by the following code (compute set differences, and */ | ||
|  | 	/* add them up) is proportional to the size of the data structure */ | ||
|  | 	/* being scanned - that is, the sum of the sizes of each column in */ | ||
|  | 	/* the pivot row.  Thus, the amortized time to compute a column score */ | ||
|  | 	/* is proportional to the size of that column (where size, in this */ | ||
|  | 	/* context, is the column "length", or the number of row indices */ | ||
|  | 	/* in that column).  The number of row indices in a column is */ | ||
|  | 	/* monotonically non-decreasing, from the length of the original */ | ||
|  | 	/* column on input to colamd. */ | ||
|  | 
 | ||
|  | 	/* === Compute set differences ====================================== */ | ||
|  | 
 | ||
|  | 	DEBUG3 (("** Computing set differences phase. **\n")) ; | ||
|  | 
 | ||
|  | 	/* pivot row is currently dead - it will be revived later. */ | ||
|  | 
 | ||
|  | 	DEBUG3 (("Pivot row: ")) ; | ||
|  | 	/* for each column in pivot row */ | ||
|  | 	rp = &A [pivot_row_start] ; | ||
|  | 	rp_end = rp + pivot_row_length ; | ||
|  | 	while (rp < rp_end) | ||
|  | 	{ | ||
|  | 	    col = *rp++ ; | ||
|  | 	    ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ; | ||
|  | 	    DEBUG3 (("Col: %d\n", col)) ; | ||
|  | 
 | ||
|  | 	    /* clear tags used to construct pivot row pattern */ | ||
|  | 	    col_thickness = -Col [col].shared1.thickness ; | ||
|  | 	    ASSERT (col_thickness > 0) ; | ||
|  | 	    Col [col].shared1.thickness = col_thickness ; | ||
|  | 
 | ||
|  | 	    /* === Remove column from degree list =========================== */ | ||
|  | 
 | ||
|  | 	    cur_score = Col [col].shared2.score ; | ||
|  | 	    prev_col = Col [col].shared3.prev ; | ||
|  | 	    next_col = Col [col].shared4.degree_next ; | ||
|  | 	    ASSERT (cur_score >= 0) ; | ||
|  | 	    ASSERT (cur_score <= n_col) ; | ||
|  | 	    ASSERT (cur_score >= EMPTY) ; | ||
|  | 	    if (prev_col == EMPTY) | ||
|  | 	    { | ||
|  | 		head [cur_score] = next_col ; | ||
|  | 	    } | ||
|  | 	    else | ||
|  | 	    { | ||
|  | 		Col [prev_col].shared4.degree_next = next_col ; | ||
|  | 	    } | ||
|  | 	    if (next_col != EMPTY) | ||
|  | 	    { | ||
|  | 		Col [next_col].shared3.prev = prev_col ; | ||
|  | 	    } | ||
|  | 
 | ||
|  | 	    /* === Scan the column ========================================== */ | ||
|  | 
 | ||
|  | 	    cp = &A [Col [col].start] ; | ||
|  | 	    cp_end = cp + Col [col].length ; | ||
|  | 	    while (cp < cp_end) | ||
|  | 	    { | ||
|  | 		/* get a row */ | ||
|  | 		row = *cp++ ; | ||
|  | 		row_mark = Row [row].shared2.mark ; | ||
|  | 		/* skip if dead */ | ||
|  | 		if (ROW_IS_MARKED_DEAD (row_mark)) | ||
|  | 		{ | ||
|  | 		    continue ; | ||
|  | 		} | ||
|  | 		ASSERT (row != pivot_row) ; | ||
|  | 		set_difference = row_mark - tag_mark ; | ||
|  | 		/* check if the row has been seen yet */ | ||
|  | 		if (set_difference < 0) | ||
|  | 		{ | ||
|  | 		    ASSERT (Row [row].shared1.degree <= max_deg) ; | ||
|  | 		    set_difference = Row [row].shared1.degree ; | ||
|  | 		} | ||
|  | 		/* subtract column thickness from this row's set difference */ | ||
|  | 		set_difference -= col_thickness ; | ||
|  | 		ASSERT (set_difference >= 0) ; | ||
|  | 		/* absorb this row if the set difference becomes zero */ | ||
|  | 		if (set_difference == 0 && aggressive) | ||
|  | 		{ | ||
|  | 		    DEBUG3 (("aggressive absorption. Row: %d\n", row)) ; | ||
|  | 		    KILL_ROW (row) ; | ||
|  | 		} | ||
|  | 		else | ||
|  | 		{ | ||
|  | 		    /* save the new mark */ | ||
|  | 		    Row [row].shared2.mark = set_difference + tag_mark ; | ||
|  | 		} | ||
|  | 	    } | ||
|  | 	} | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  | 	debug_deg_lists (n_row, n_col, Row, Col, head, | ||
|  | 		min_score, n_col2-k-pivot_row_degree, max_deg) ; | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  | 	/* === Add up set differences for each column ======================= */ | ||
|  | 
 | ||
|  | 	DEBUG3 (("** Adding set differences phase. **\n")) ; | ||
|  | 
 | ||
|  | 	/* for each column in pivot row */ | ||
|  | 	rp = &A [pivot_row_start] ; | ||
|  | 	rp_end = rp + pivot_row_length ; | ||
|  | 	while (rp < rp_end) | ||
|  | 	{ | ||
|  | 	    /* get a column */ | ||
|  | 	    col = *rp++ ; | ||
|  | 	    ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ; | ||
|  | 	    hash = 0 ; | ||
|  | 	    cur_score = 0 ; | ||
|  | 	    cp = &A [Col [col].start] ; | ||
|  | 	    /* compact the column */ | ||
|  | 	    new_cp = cp ; | ||
|  | 	    cp_end = cp + Col [col].length ; | ||
|  | 
 | ||
|  | 	    DEBUG4 (("Adding set diffs for Col: %d.\n", col)) ; | ||
|  | 
 | ||
|  | 	    while (cp < cp_end) | ||
|  | 	    { | ||
|  | 		/* get a row */ | ||
|  | 		row = *cp++ ; | ||
|  | 		ASSERT(row >= 0 && row < n_row) ; | ||
|  | 		row_mark = Row [row].shared2.mark ; | ||
|  | 		/* skip if dead */ | ||
|  | 		if (ROW_IS_MARKED_DEAD (row_mark)) | ||
|  | 		{ | ||
|  | 		    DEBUG4 ((" Row %d, dead\n", row)) ; | ||
|  | 		    continue ; | ||
|  | 		} | ||
|  | 		DEBUG4 ((" Row %d, set diff %d\n", row, row_mark-tag_mark)); | ||
|  | 		ASSERT (row_mark >= tag_mark) ; | ||
|  | 		/* compact the column */ | ||
|  | 		*new_cp++ = row ; | ||
|  | 		/* compute hash function */ | ||
|  | 		hash += row ; | ||
|  | 		/* add set difference */ | ||
|  | 		cur_score += row_mark - tag_mark ; | ||
|  | 		/* integer overflow... */ | ||
|  | 		cur_score = MIN (cur_score, n_col) ; | ||
|  | 	    } | ||
|  | 
 | ||
|  | 	    /* recompute the column's length */ | ||
|  | 	    Col [col].length = (Int) (new_cp - &A [Col [col].start]) ; | ||
|  | 
 | ||
|  | 	    /* === Further mass elimination ================================= */ | ||
|  | 
 | ||
|  | 	    if (Col [col].length == 0) | ||
|  | 	    { | ||
|  | 		DEBUG4 (("further mass elimination. Col: %d\n", col)) ; | ||
|  | 		/* nothing left but the pivot row in this column */ | ||
|  | 		KILL_PRINCIPAL_COL (col) ; | ||
|  | 		pivot_row_degree -= Col [col].shared1.thickness ; | ||
|  | 		ASSERT (pivot_row_degree >= 0) ; | ||
|  | 		/* order it */ | ||
|  | 		Col [col].shared2.order = k ; | ||
|  | 		/* increment order count by column thickness */ | ||
|  | 		k += Col [col].shared1.thickness ; | ||
|  | 	    } | ||
|  | 	    else | ||
|  | 	    { | ||
|  | 		/* === Prepare for supercolumn detection ==================== */ | ||
|  | 
 | ||
|  | 		DEBUG4 (("Preparing supercol detection for Col: %d.\n", col)) ; | ||
|  | 
 | ||
|  | 		/* save score so far */ | ||
|  | 		Col [col].shared2.score = cur_score ; | ||
|  | 
 | ||
|  | 		/* add column to hash table, for supercolumn detection */ | ||
|  | 		hash %= n_col + 1 ; | ||
|  | 
 | ||
|  | 		DEBUG4 ((" Hash = %d, n_col = %d.\n", hash, n_col)) ; | ||
|  | 		ASSERT (((Int) hash) <= n_col) ; | ||
|  | 
 | ||
|  | 		head_column = head [hash] ; | ||
|  | 		if (head_column > EMPTY) | ||
|  | 		{ | ||
|  | 		    /* degree list "hash" is non-empty, use prev (shared3) of */ | ||
|  | 		    /* first column in degree list as head of hash bucket */ | ||
|  | 		    first_col = Col [head_column].shared3.headhash ; | ||
|  | 		    Col [head_column].shared3.headhash = col ; | ||
|  | 		} | ||
|  | 		else | ||
|  | 		{ | ||
|  | 		    /* degree list "hash" is empty, use head as hash bucket */ | ||
|  | 		    first_col = - (head_column + 2) ; | ||
|  | 		    head [hash] = - (col + 2) ; | ||
|  | 		} | ||
|  | 		Col [col].shared4.hash_next = first_col ; | ||
|  | 
 | ||
|  | 		/* save hash function in Col [col].shared3.hash */ | ||
|  | 		Col [col].shared3.hash = (Int) hash ; | ||
|  | 		ASSERT (COL_IS_ALIVE (col)) ; | ||
|  | 	    } | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* The approximate external column degree is now computed.  */ | ||
|  | 
 | ||
|  | 	/* === Supercolumn detection ======================================== */ | ||
|  | 
 | ||
|  | 	DEBUG3 (("** Supercolumn detection phase. **\n")) ; | ||
|  | 
 | ||
|  | 	detect_super_cols ( | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  | 		n_col, Row, | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  | 		Col, A, head, pivot_row_start, pivot_row_length) ; | ||
|  | 
 | ||
|  | 	/* === Kill the pivotal column ====================================== */ | ||
|  | 
 | ||
|  | 	KILL_PRINCIPAL_COL (pivot_col) ; | ||
|  | 
 | ||
|  | 	/* === Clear mark =================================================== */ | ||
|  | 
 | ||
|  | 	tag_mark = clear_mark (tag_mark+max_deg+1, max_mark, n_row, Row) ; | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  | 	DEBUG3 (("check3\n")) ; | ||
|  | 	debug_mark (n_row, Row, tag_mark, max_mark) ; | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  | 	/* === Finalize the new pivot row, and column scores ================ */ | ||
|  | 
 | ||
|  | 	DEBUG3 (("** Finalize scores phase. **\n")) ; | ||
|  | 
 | ||
|  | 	/* for each column in pivot row */ | ||
|  | 	rp = &A [pivot_row_start] ; | ||
|  | 	/* compact the pivot row */ | ||
|  | 	new_rp = rp ; | ||
|  | 	rp_end = rp + pivot_row_length ; | ||
|  | 	while (rp < rp_end) | ||
|  | 	{ | ||
|  | 	    col = *rp++ ; | ||
|  | 	    /* skip dead columns */ | ||
|  | 	    if (COL_IS_DEAD (col)) | ||
|  | 	    { | ||
|  | 		continue ; | ||
|  | 	    } | ||
|  | 	    *new_rp++ = col ; | ||
|  | 	    /* add new pivot row to column */ | ||
|  | 	    A [Col [col].start + (Col [col].length++)] = pivot_row ; | ||
|  | 
 | ||
|  | 	    /* retrieve score so far and add on pivot row's degree. */ | ||
|  | 	    /* (we wait until here for this in case the pivot */ | ||
|  | 	    /* row's degree was reduced due to mass elimination). */ | ||
|  | 	    cur_score = Col [col].shared2.score + pivot_row_degree ; | ||
|  | 
 | ||
|  | 	    /* calculate the max possible score as the number of */ | ||
|  | 	    /* external columns minus the 'k' value minus the */ | ||
|  | 	    /* columns thickness */ | ||
|  | 	    max_score = n_col - k - Col [col].shared1.thickness ; | ||
|  | 
 | ||
|  | 	    /* make the score the external degree of the union-of-rows */ | ||
|  | 	    cur_score -= Col [col].shared1.thickness ; | ||
|  | 
 | ||
|  | 	    /* make sure score is less or equal than the max score */ | ||
|  | 	    cur_score = MIN (cur_score, max_score) ; | ||
|  | 	    ASSERT (cur_score >= 0) ; | ||
|  | 
 | ||
|  | 	    /* store updated score */ | ||
|  | 	    Col [col].shared2.score = cur_score ; | ||
|  | 
 | ||
|  | 	    /* === Place column back in degree list ========================= */ | ||
|  | 
 | ||
|  | 	    ASSERT (min_score >= 0) ; | ||
|  | 	    ASSERT (min_score <= n_col) ; | ||
|  | 	    ASSERT (cur_score >= 0) ; | ||
|  | 	    ASSERT (cur_score <= n_col) ; | ||
|  | 	    ASSERT (head [cur_score] >= EMPTY) ; | ||
|  | 	    next_col = head [cur_score] ; | ||
|  | 	    Col [col].shared4.degree_next = next_col ; | ||
|  | 	    Col [col].shared3.prev = EMPTY ; | ||
|  | 	    if (next_col != EMPTY) | ||
|  | 	    { | ||
|  | 		Col [next_col].shared3.prev = col ; | ||
|  | 	    } | ||
|  | 	    head [cur_score] = col ; | ||
|  | 
 | ||
|  | 	    /* see if this score is less than current min */ | ||
|  | 	    min_score = MIN (min_score, cur_score) ; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  | 	debug_deg_lists (n_row, n_col, Row, Col, head, | ||
|  | 		min_score, n_col2-k, max_deg) ; | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  | 	/* === Resurrect the new pivot row ================================== */ | ||
|  | 
 | ||
|  | 	if (pivot_row_degree > 0) | ||
|  | 	{ | ||
|  | 	    /* update pivot row length to reflect any cols that were killed */ | ||
|  | 	    /* during super-col detection and mass elimination */ | ||
|  | 	    Row [pivot_row].start  = pivot_row_start ; | ||
|  | 	    Row [pivot_row].length = (Int) (new_rp - &A[pivot_row_start]) ; | ||
|  | 	    ASSERT (Row [pivot_row].length > 0) ; | ||
|  | 	    Row [pivot_row].shared1.degree = pivot_row_degree ; | ||
|  | 	    Row [pivot_row].shared2.mark = 0 ; | ||
|  | 	    /* pivot row is no longer dead */ | ||
|  | 
 | ||
|  | 	    DEBUG1 (("Resurrect Pivot_row %d deg: %d\n", | ||
|  | 			pivot_row, pivot_row_degree)) ; | ||
|  | 	} | ||
|  |     } | ||
|  | 
 | ||
|  |     /* === All principal columns have now been ordered ====================== */ | ||
|  | 
 | ||
|  |     return (ngarbage) ; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === order_children ======================================================= */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |     The find_ordering routine has ordered all of the principal columns (the | ||
|  |     representatives of the supercolumns).  The non-principal columns have not | ||
|  |     yet been ordered.  This routine orders those columns by walking up the | ||
|  |     parent tree (a column is a child of the column which absorbed it).  The | ||
|  |     final permutation vector is then placed in p [0 ... n_col-1], with p [0] | ||
|  |     being the first column, and p [n_col-1] being the last.  It doesn't look | ||
|  |     like it at first glance, but be assured that this routine takes time linear | ||
|  |     in the number of columns.  Although not immediately obvious, the time | ||
|  |     taken by this routine is O (n_col), that is, linear in the number of | ||
|  |     columns.  Not user-callable. | ||
|  | */ | ||
|  | 
 | ||
|  | PRIVATE void order_children | ||
|  | ( | ||
|  |     /* === Parameters ======================================================= */ | ||
|  | 
 | ||
|  |     Int n_col,			/* number of columns of A */ | ||
|  |     Colamd_Col Col [],		/* of size n_col+1 */ | ||
|  |     Int p []			/* p [0 ... n_col-1] is the column permutation*/ | ||
|  | ) | ||
|  | { | ||
|  |     /* === Local variables ================================================== */ | ||
|  | 
 | ||
|  |     Int i ;			/* loop counter for all columns */ | ||
|  |     Int c ;			/* column index */ | ||
|  |     Int parent ;		/* index of column's parent */ | ||
|  |     Int order ;			/* column's order */ | ||
|  | 
 | ||
|  |     /* === Order each non-principal column ================================== */ | ||
|  | 
 | ||
|  |     for (i = 0 ; i < n_col ; i++) | ||
|  |     { | ||
|  | 	/* find an un-ordered non-principal column */ | ||
|  | 	ASSERT (COL_IS_DEAD (i)) ; | ||
|  | 	if (!COL_IS_DEAD_PRINCIPAL (i) && Col [i].shared2.order == EMPTY) | ||
|  | 	{ | ||
|  | 	    parent = i ; | ||
|  | 	    /* once found, find its principal parent */ | ||
|  | 	    do | ||
|  | 	    { | ||
|  | 		parent = Col [parent].shared1.parent ; | ||
|  | 	    } while (!COL_IS_DEAD_PRINCIPAL (parent)) ; | ||
|  | 
 | ||
|  | 	    /* now, order all un-ordered non-principal columns along path */ | ||
|  | 	    /* to this parent.  collapse tree at the same time */ | ||
|  | 	    c = i ; | ||
|  | 	    /* get order of parent */ | ||
|  | 	    order = Col [parent].shared2.order ; | ||
|  | 
 | ||
|  | 	    do | ||
|  | 	    { | ||
|  | 		ASSERT (Col [c].shared2.order == EMPTY) ; | ||
|  | 
 | ||
|  | 		/* order this column */ | ||
|  | 		Col [c].shared2.order = order++ ; | ||
|  | 		/* collaps tree */ | ||
|  | 		Col [c].shared1.parent = parent ; | ||
|  | 
 | ||
|  | 		/* get immediate parent of this column */ | ||
|  | 		c = Col [c].shared1.parent ; | ||
|  | 
 | ||
|  | 		/* continue until we hit an ordered column.  There are */ | ||
|  | 		/* guarranteed not to be anymore unordered columns */ | ||
|  | 		/* above an ordered column */ | ||
|  | 	    } while (Col [c].shared2.order == EMPTY) ; | ||
|  | 
 | ||
|  | 	    /* re-order the super_col parent to largest order for this group */ | ||
|  | 	    Col [parent].shared2.order = order ; | ||
|  | 	} | ||
|  |     } | ||
|  | 
 | ||
|  |     /* === Generate the permutation ========================================= */ | ||
|  | 
 | ||
|  |     for (c = 0 ; c < n_col ; c++) | ||
|  |     { | ||
|  | 	p [Col [c].shared2.order] = c ; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === detect_super_cols ==================================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |     Detects supercolumns by finding matches between columns in the hash buckets. | ||
|  |     Check amongst columns in the set A [row_start ... row_start + row_length-1]. | ||
|  |     The columns under consideration are currently *not* in the degree lists, | ||
|  |     and have already been placed in the hash buckets. | ||
|  | 
 | ||
|  |     The hash bucket for columns whose hash function is equal to h is stored | ||
|  |     as follows: | ||
|  | 
 | ||
|  | 	if head [h] is >= 0, then head [h] contains a degree list, so: | ||
|  | 
 | ||
|  | 		head [h] is the first column in degree bucket h. | ||
|  | 		Col [head [h]].headhash gives the first column in hash bucket h. | ||
|  | 
 | ||
|  | 	otherwise, the degree list is empty, and: | ||
|  | 
 | ||
|  | 		-(head [h] + 2) is the first column in hash bucket h. | ||
|  | 
 | ||
|  |     For a column c in a hash bucket, Col [c].shared3.prev is NOT a "previous | ||
|  |     column" pointer.  Col [c].shared3.hash is used instead as the hash number | ||
|  |     for that column.  The value of Col [c].shared4.hash_next is the next column | ||
|  |     in the same hash bucket. | ||
|  | 
 | ||
|  |     Assuming no, or "few" hash collisions, the time taken by this routine is | ||
|  |     linear in the sum of the sizes (lengths) of each column whose score has | ||
|  |     just been computed in the approximate degree computation. | ||
|  |     Not user-callable. | ||
|  | */ | ||
|  | 
 | ||
|  | PRIVATE void detect_super_cols | ||
|  | ( | ||
|  |     /* === Parameters ======================================================= */ | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  |     /* these two parameters are only needed when debugging is enabled: */ | ||
|  |     Int n_col,			/* number of columns of A */ | ||
|  |     Colamd_Row Row [],		/* of size n_row+1 */ | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  |     Colamd_Col Col [],		/* of size n_col+1 */ | ||
|  |     Int A [],			/* row indices of A */ | ||
|  |     Int head [],		/* head of degree lists and hash buckets */ | ||
|  |     Int row_start,		/* pointer to set of columns to check */ | ||
|  |     Int row_length		/* number of columns to check */ | ||
|  | ) | ||
|  | { | ||
|  |     /* === Local variables ================================================== */ | ||
|  | 
 | ||
|  |     Int hash ;			/* hash value for a column */ | ||
|  |     Int *rp ;			/* pointer to a row */ | ||
|  |     Int c ;			/* a column index */ | ||
|  |     Int super_c ;		/* column index of the column to absorb into */ | ||
|  |     Int *cp1 ;			/* column pointer for column super_c */ | ||
|  |     Int *cp2 ;			/* column pointer for column c */ | ||
|  |     Int length ;		/* length of column super_c */ | ||
|  |     Int prev_c ;		/* column preceding c in hash bucket */ | ||
|  |     Int i ;			/* loop counter */ | ||
|  |     Int *rp_end ;		/* pointer to the end of the row */ | ||
|  |     Int col ;			/* a column index in the row to check */ | ||
|  |     Int head_column ;		/* first column in hash bucket or degree list */ | ||
|  |     Int first_col ;		/* first column in hash bucket */ | ||
|  | 
 | ||
|  |     /* === Consider each column in the row ================================== */ | ||
|  | 
 | ||
|  |     rp = &A [row_start] ; | ||
|  |     rp_end = rp + row_length ; | ||
|  |     while (rp < rp_end) | ||
|  |     { | ||
|  | 	col = *rp++ ; | ||
|  | 	if (COL_IS_DEAD (col)) | ||
|  | 	{ | ||
|  | 	    continue ; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* get hash number for this column */ | ||
|  | 	hash = Col [col].shared3.hash ; | ||
|  | 	ASSERT (hash <= n_col) ; | ||
|  | 
 | ||
|  | 	/* === Get the first column in this hash bucket ===================== */ | ||
|  | 
 | ||
|  | 	head_column = head [hash] ; | ||
|  | 	if (head_column > EMPTY) | ||
|  | 	{ | ||
|  | 	    first_col = Col [head_column].shared3.headhash ; | ||
|  | 	} | ||
|  | 	else | ||
|  | 	{ | ||
|  | 	    first_col = - (head_column + 2) ; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* === Consider each column in the hash bucket ====================== */ | ||
|  | 
 | ||
|  | 	for (super_c = first_col ; super_c != EMPTY ; | ||
|  | 	    super_c = Col [super_c].shared4.hash_next) | ||
|  | 	{ | ||
|  | 	    ASSERT (COL_IS_ALIVE (super_c)) ; | ||
|  | 	    ASSERT (Col [super_c].shared3.hash == hash) ; | ||
|  | 	    length = Col [super_c].length ; | ||
|  | 
 | ||
|  | 	    /* prev_c is the column preceding column c in the hash bucket */ | ||
|  | 	    prev_c = super_c ; | ||
|  | 
 | ||
|  | 	    /* === Compare super_c with all columns after it ================ */ | ||
|  | 
 | ||
|  | 	    for (c = Col [super_c].shared4.hash_next ; | ||
|  | 		 c != EMPTY ; c = Col [c].shared4.hash_next) | ||
|  | 	    { | ||
|  | 		ASSERT (c != super_c) ; | ||
|  | 		ASSERT (COL_IS_ALIVE (c)) ; | ||
|  | 		ASSERT (Col [c].shared3.hash == hash) ; | ||
|  | 
 | ||
|  | 		/* not identical if lengths or scores are different */ | ||
|  | 		if (Col [c].length != length || | ||
|  | 		    Col [c].shared2.score != Col [super_c].shared2.score) | ||
|  | 		{ | ||
|  | 		    prev_c = c ; | ||
|  | 		    continue ; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		/* compare the two columns */ | ||
|  | 		cp1 = &A [Col [super_c].start] ; | ||
|  | 		cp2 = &A [Col [c].start] ; | ||
|  | 
 | ||
|  | 		for (i = 0 ; i < length ; i++) | ||
|  | 		{ | ||
|  | 		    /* the columns are "clean" (no dead rows) */ | ||
|  | 		    ASSERT (ROW_IS_ALIVE (*cp1))  ; | ||
|  | 		    ASSERT (ROW_IS_ALIVE (*cp2))  ; | ||
|  | 		    /* row indices will same order for both supercols, */ | ||
|  | 		    /* no gather scatter nessasary */ | ||
|  | 		    if (*cp1++ != *cp2++) | ||
|  | 		    { | ||
|  | 			break ; | ||
|  | 		    } | ||
|  | 		} | ||
|  | 
 | ||
|  | 		/* the two columns are different if the for-loop "broke" */ | ||
|  | 		if (i != length) | ||
|  | 		{ | ||
|  | 		    prev_c = c ; | ||
|  | 		    continue ; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		/* === Got it!  two columns are identical =================== */ | ||
|  | 
 | ||
|  | 		ASSERT (Col [c].shared2.score == Col [super_c].shared2.score) ; | ||
|  | 
 | ||
|  | 		Col [super_c].shared1.thickness += Col [c].shared1.thickness ; | ||
|  | 		Col [c].shared1.parent = super_c ; | ||
|  | 		KILL_NON_PRINCIPAL_COL (c) ; | ||
|  | 		/* order c later, in order_children() */ | ||
|  | 		Col [c].shared2.order = EMPTY ; | ||
|  | 		/* remove c from hash bucket */ | ||
|  | 		Col [prev_c].shared4.hash_next = Col [c].shared4.hash_next ; | ||
|  | 	    } | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* === Empty this hash bucket ======================================= */ | ||
|  | 
 | ||
|  | 	if (head_column > EMPTY) | ||
|  | 	{ | ||
|  | 	    /* corresponding degree list "hash" is not empty */ | ||
|  | 	    Col [head_column].shared3.headhash = EMPTY ; | ||
|  | 	} | ||
|  | 	else | ||
|  | 	{ | ||
|  | 	    /* corresponding degree list "hash" is empty */ | ||
|  | 	    head [hash] = EMPTY ; | ||
|  | 	} | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === garbage_collection =================================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |     Defragments and compacts columns and rows in the workspace A.  Used when | ||
|  |     all avaliable memory has been used while performing row merging.  Returns | ||
|  |     the index of the first free position in A, after garbage collection.  The | ||
|  |     time taken by this routine is linear is the size of the array A, which is | ||
|  |     itself linear in the number of nonzeros in the input matrix. | ||
|  |     Not user-callable. | ||
|  | */ | ||
|  | 
 | ||
|  | PRIVATE Int garbage_collection  /* returns the new value of pfree */ | ||
|  | ( | ||
|  |     /* === Parameters ======================================================= */ | ||
|  | 
 | ||
|  |     Int n_row,			/* number of rows */ | ||
|  |     Int n_col,			/* number of columns */ | ||
|  |     Colamd_Row Row [],		/* row info */ | ||
|  |     Colamd_Col Col [],		/* column info */ | ||
|  |     Int A [],			/* A [0 ... Alen-1] holds the matrix */ | ||
|  |     Int *pfree			/* &A [0] ... pfree is in use */ | ||
|  | ) | ||
|  | { | ||
|  |     /* === Local variables ================================================== */ | ||
|  | 
 | ||
|  |     Int *psrc ;			/* source pointer */ | ||
|  |     Int *pdest ;		/* destination pointer */ | ||
|  |     Int j ;			/* counter */ | ||
|  |     Int r ;			/* a row index */ | ||
|  |     Int c ;			/* a column index */ | ||
|  |     Int length ;		/* length of a row or column */ | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  |     Int debug_rows ; | ||
|  |     DEBUG2 (("Defrag..\n")) ; | ||
|  |     for (psrc = &A[0] ; psrc < pfree ; psrc++) ASSERT (*psrc >= 0) ; | ||
|  |     debug_rows = 0 ; | ||
|  | #endif /* NDEBUG */
 | ||
|  | 
 | ||
|  |     /* === Defragment the columns =========================================== */ | ||
|  | 
 | ||
|  |     pdest = &A[0] ; | ||
|  |     for (c = 0 ; c < n_col ; c++) | ||
|  |     { | ||
|  | 	if (COL_IS_ALIVE (c)) | ||
|  | 	{ | ||
|  | 	    psrc = &A [Col [c].start] ; | ||
|  | 
 | ||
|  | 	    /* move and compact the column */ | ||
|  | 	    ASSERT (pdest <= psrc) ; | ||
|  | 	    Col [c].start = (Int) (pdest - &A [0]) ; | ||
|  | 	    length = Col [c].length ; | ||
|  | 	    for (j = 0 ; j < length ; j++) | ||
|  | 	    { | ||
|  | 		r = *psrc++ ; | ||
|  | 		if (ROW_IS_ALIVE (r)) | ||
|  | 		{ | ||
|  | 		    *pdest++ = r ; | ||
|  | 		} | ||
|  | 	    } | ||
|  | 	    Col [c].length = (Int) (pdest - &A [Col [c].start]) ; | ||
|  | 	} | ||
|  |     } | ||
|  | 
 | ||
|  |     /* === Prepare to defragment the rows =================================== */ | ||
|  | 
 | ||
|  |     for (r = 0 ; r < n_row ; r++) | ||
|  |     { | ||
|  | 	if (ROW_IS_DEAD (r) || (Row [r].length == 0)) | ||
|  | 	{ | ||
|  | 	    /* This row is already dead, or is of zero length.  Cannot compact
 | ||
|  | 	     * a row of zero length, so kill it.  NOTE: in the current version, | ||
|  | 	     * there are no zero-length live rows.  Kill the row (for the first | ||
|  | 	     * time, or again) just to be safe. */ | ||
|  | 	    KILL_ROW (r) ; | ||
|  | 	} | ||
|  | 	else | ||
|  | 	{ | ||
|  | 	    /* save first column index in Row [r].shared2.first_column */ | ||
|  | 	    psrc = &A [Row [r].start] ; | ||
|  | 	    Row [r].shared2.first_column = *psrc ; | ||
|  | 	    ASSERT (ROW_IS_ALIVE (r)) ; | ||
|  | 	    /* flag the start of the row with the one's complement of row */ | ||
|  | 	    *psrc = ONES_COMPLEMENT (r) ; | ||
|  | #ifndef NDEBUG
 | ||
|  | 	    debug_rows++ ; | ||
|  | #endif /* NDEBUG */
 | ||
|  | 	} | ||
|  |     } | ||
|  | 
 | ||
|  |     /* === Defragment the rows ============================================== */ | ||
|  | 
 | ||
|  |     psrc = pdest ; | ||
|  |     while (psrc < pfree) | ||
|  |     { | ||
|  | 	/* find a negative number ... the start of a row */ | ||
|  | 	if (*psrc++ < 0) | ||
|  | 	{ | ||
|  | 	    psrc-- ; | ||
|  | 	    /* get the row index */ | ||
|  | 	    r = ONES_COMPLEMENT (*psrc) ; | ||
|  | 	    ASSERT (r >= 0 && r < n_row) ; | ||
|  | 	    /* restore first column index */ | ||
|  | 	    *psrc = Row [r].shared2.first_column ; | ||
|  | 	    ASSERT (ROW_IS_ALIVE (r)) ; | ||
|  | 	    ASSERT (Row [r].length > 0) ; | ||
|  | 	    /* move and compact the row */ | ||
|  | 	    ASSERT (pdest <= psrc) ; | ||
|  | 	    Row [r].start = (Int) (pdest - &A [0]) ; | ||
|  | 	    length = Row [r].length ; | ||
|  | 	    for (j = 0 ; j < length ; j++) | ||
|  | 	    { | ||
|  | 		c = *psrc++ ; | ||
|  | 		if (COL_IS_ALIVE (c)) | ||
|  | 		{ | ||
|  | 		    *pdest++ = c ; | ||
|  | 		} | ||
|  | 	    } | ||
|  | 	    Row [r].length = (Int) (pdest - &A [Row [r].start]) ; | ||
|  | 	    ASSERT (Row [r].length > 0) ; | ||
|  | #ifndef NDEBUG
 | ||
|  | 	    debug_rows-- ; | ||
|  | #endif /* NDEBUG */
 | ||
|  | 	} | ||
|  |     } | ||
|  |     /* ensure we found all the rows */ | ||
|  |     ASSERT (debug_rows == 0) ; | ||
|  | 
 | ||
|  |     /* === Return the new value of pfree ==================================== */ | ||
|  | 
 | ||
|  |     return ((Int) (pdest - &A [0])) ; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === clear_mark =========================================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |     Clears the Row [].shared2.mark array, and returns the new tag_mark. | ||
|  |     Return value is the new tag_mark.  Not user-callable. | ||
|  | */ | ||
|  | 
 | ||
|  | PRIVATE Int clear_mark	/* return the new value for tag_mark */ | ||
|  | ( | ||
|  |     /* === Parameters ======================================================= */ | ||
|  | 
 | ||
|  |     Int tag_mark,	/* new value of tag_mark */ | ||
|  |     Int max_mark,	/* max allowed value of tag_mark */ | ||
|  | 
 | ||
|  |     Int n_row,		/* number of rows in A */ | ||
|  |     Colamd_Row Row []	/* Row [0 ... n_row-1].shared2.mark is set to zero */ | ||
|  | ) | ||
|  | { | ||
|  |     /* === Local variables ================================================== */ | ||
|  | 
 | ||
|  |     Int r ; | ||
|  | 
 | ||
|  |     if (tag_mark <= 0 || tag_mark >= max_mark) | ||
|  |     { | ||
|  | 	for (r = 0 ; r < n_row ; r++) | ||
|  | 	{ | ||
|  | 	    if (ROW_IS_ALIVE (r)) | ||
|  | 	    { | ||
|  | 		Row [r].shared2.mark = 0 ; | ||
|  | 	    } | ||
|  | 	} | ||
|  | 	tag_mark = 1 ; | ||
|  |     } | ||
|  | 
 | ||
|  |     return (tag_mark) ; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === print_report ========================================================= */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | PRIVATE void print_report | ||
|  | ( | ||
|  |     char *method, | ||
|  |     Int stats [COLAMD_STATS] | ||
|  | ) | ||
|  | { | ||
|  | 
 | ||
|  |     Int i1, i2, i3 ; | ||
|  | 
 | ||
|  |     PRINTF (("\n%s version %d.%d, %s: ", method, | ||
|  | 	    COLAMD_MAIN_VERSION, COLAMD_SUB_VERSION, COLAMD_DATE)) ; | ||
|  | 
 | ||
|  |     if (!stats) | ||
|  |     { | ||
|  |     	PRINTF (("No statistics available.\n")) ; | ||
|  | 	return ; | ||
|  |     } | ||
|  | 
 | ||
|  |     i1 = stats [COLAMD_INFO1] ; | ||
|  |     i2 = stats [COLAMD_INFO2] ; | ||
|  |     i3 = stats [COLAMD_INFO3] ; | ||
|  | 
 | ||
|  |     if (stats [COLAMD_STATUS] >= 0) | ||
|  |     { | ||
|  |     	PRINTF (("OK.  ")) ; | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  |     	PRINTF (("ERROR.  ")) ; | ||
|  |     } | ||
|  | 
 | ||
|  |     switch (stats [COLAMD_STATUS]) | ||
|  |     { | ||
|  | 
 | ||
|  | 	case COLAMD_OK_BUT_JUMBLED: | ||
|  | 
 | ||
|  | 	    PRINTF(("Matrix has unsorted or duplicate row indices.\n")) ; | ||
|  | 
 | ||
|  | 	    PRINTF(("%s: number of duplicate or out-of-order row indices: %d\n", | ||
|  | 	    method, i3)) ; | ||
|  | 
 | ||
|  | 	    PRINTF(("%s: last seen duplicate or out-of-order row index:   %d\n", | ||
|  | 	    method, INDEX (i2))) ; | ||
|  | 
 | ||
|  | 	    PRINTF(("%s: last seen in column:                             %d", | ||
|  | 	    method, INDEX (i1))) ; | ||
|  | 
 | ||
|  | 	    /* no break - fall through to next case instead */ | ||
|  | 
 | ||
|  | 	case COLAMD_OK: | ||
|  | 
 | ||
|  | 	    PRINTF(("\n")) ; | ||
|  | 
 | ||
|  |  	    PRINTF(("%s: number of dense or empty rows ignored:           %d\n", | ||
|  | 	    method, stats [COLAMD_DENSE_ROW])) ; | ||
|  | 
 | ||
|  | 	    PRINTF(("%s: number of dense or empty columns ignored:        %d\n", | ||
|  | 	    method, stats [COLAMD_DENSE_COL])) ; | ||
|  | 
 | ||
|  | 	    PRINTF(("%s: number of garbage collections performed:         %d\n", | ||
|  | 	    method, stats [COLAMD_DEFRAG_COUNT])) ; | ||
|  | 	    break ; | ||
|  | 
 | ||
|  | 	case COLAMD_ERROR_A_not_present: | ||
|  | 
 | ||
|  | 	    PRINTF(("Array A (row indices of matrix) not present.\n")) ; | ||
|  | 	    break ; | ||
|  | 
 | ||
|  | 	case COLAMD_ERROR_p_not_present: | ||
|  | 
 | ||
|  | 	    PRINTF(("Array p (column pointers for matrix) not present.\n")) ; | ||
|  | 	    break ; | ||
|  | 
 | ||
|  | 	case COLAMD_ERROR_nrow_negative: | ||
|  | 
 | ||
|  | 	    PRINTF(("Invalid number of rows (%d).\n", i1)) ; | ||
|  | 	    break ; | ||
|  | 
 | ||
|  | 	case COLAMD_ERROR_ncol_negative: | ||
|  | 
 | ||
|  | 	    PRINTF(("Invalid number of columns (%d).\n", i1)) ; | ||
|  | 	    break ; | ||
|  | 
 | ||
|  | 	case COLAMD_ERROR_nnz_negative: | ||
|  | 
 | ||
|  | 	    PRINTF(("Invalid number of nonzero entries (%d).\n", i1)) ; | ||
|  | 	    break ; | ||
|  | 
 | ||
|  | 	case COLAMD_ERROR_p0_nonzero: | ||
|  | 
 | ||
|  | 	    PRINTF(("Invalid column pointer, p [0] = %d, must be zero.\n", i1)); | ||
|  | 	    break ; | ||
|  | 
 | ||
|  | 	case COLAMD_ERROR_A_too_small: | ||
|  | 
 | ||
|  | 	    PRINTF(("Array A too small.\n")) ; | ||
|  | 	    PRINTF(("        Need Alen >= %d, but given only Alen = %d.\n", | ||
|  | 	    i1, i2)) ; | ||
|  | 	    break ; | ||
|  | 
 | ||
|  | 	case COLAMD_ERROR_col_length_negative: | ||
|  | 
 | ||
|  | 	    PRINTF | ||
|  | 	    (("Column %d has a negative number of nonzero entries (%d).\n", | ||
|  | 	    INDEX (i1), i2)) ; | ||
|  | 	    break ; | ||
|  | 
 | ||
|  | 	case COLAMD_ERROR_row_index_out_of_bounds: | ||
|  | 
 | ||
|  | 	    PRINTF | ||
|  | 	    (("Row index (row %d) out of bounds (%d to %d) in column %d.\n", | ||
|  | 	    INDEX (i2), INDEX (0), INDEX (i3-1), INDEX (i1))) ; | ||
|  | 	    break ; | ||
|  | 
 | ||
|  | 	case COLAMD_ERROR_out_of_memory: | ||
|  | 
 | ||
|  | 	    PRINTF(("Out of memory.\n")) ; | ||
|  | 	    break ; | ||
|  | 
 | ||
|  | 	/* v2.4: internal-error case deleted */ | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === colamd debugging routines ============================================ */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /* When debugging is disabled, the remainder of this file is ignored. */ | ||
|  | 
 | ||
|  | #ifndef NDEBUG
 | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === debug_structures ===================================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |     At this point, all empty rows and columns are dead.  All live columns | ||
|  |     are "clean" (containing no dead rows) and simplicial (no supercolumns | ||
|  |     yet).  Rows may contain dead columns, but all live rows contain at | ||
|  |     least one live column. | ||
|  | */ | ||
|  | 
 | ||
|  | PRIVATE void debug_structures | ||
|  | ( | ||
|  |     /* === Parameters ======================================================= */ | ||
|  | 
 | ||
|  |     Int n_row, | ||
|  |     Int n_col, | ||
|  |     Colamd_Row Row [], | ||
|  |     Colamd_Col Col [], | ||
|  |     Int A [], | ||
|  |     Int n_col2 | ||
|  | ) | ||
|  | { | ||
|  |     /* === Local variables ================================================== */ | ||
|  | 
 | ||
|  |     Int i ; | ||
|  |     Int c ; | ||
|  |     Int *cp ; | ||
|  |     Int *cp_end ; | ||
|  |     Int len ; | ||
|  |     Int score ; | ||
|  |     Int r ; | ||
|  |     Int *rp ; | ||
|  |     Int *rp_end ; | ||
|  |     Int deg ; | ||
|  | 
 | ||
|  |     /* === Check A, Row, and Col ============================================ */ | ||
|  | 
 | ||
|  |     for (c = 0 ; c < n_col ; c++) | ||
|  |     { | ||
|  | 	if (COL_IS_ALIVE (c)) | ||
|  | 	{ | ||
|  | 	    len = Col [c].length ; | ||
|  | 	    score = Col [c].shared2.score ; | ||
|  | 	    DEBUG4 (("initial live col %5d %5d %5d\n", c, len, score)) ; | ||
|  | 	    ASSERT (len > 0) ; | ||
|  | 	    ASSERT (score >= 0) ; | ||
|  | 	    ASSERT (Col [c].shared1.thickness == 1) ; | ||
|  | 	    cp = &A [Col [c].start] ; | ||
|  | 	    cp_end = cp + len ; | ||
|  | 	    while (cp < cp_end) | ||
|  | 	    { | ||
|  | 		r = *cp++ ; | ||
|  | 		ASSERT (ROW_IS_ALIVE (r)) ; | ||
|  | 	    } | ||
|  | 	} | ||
|  | 	else | ||
|  | 	{ | ||
|  | 	    i = Col [c].shared2.order ; | ||
|  | 	    ASSERT (i >= n_col2 && i < n_col) ; | ||
|  | 	} | ||
|  |     } | ||
|  | 
 | ||
|  |     for (r = 0 ; r < n_row ; r++) | ||
|  |     { | ||
|  | 	if (ROW_IS_ALIVE (r)) | ||
|  | 	{ | ||
|  | 	    i = 0 ; | ||
|  | 	    len = Row [r].length ; | ||
|  | 	    deg = Row [r].shared1.degree ; | ||
|  | 	    ASSERT (len > 0) ; | ||
|  | 	    ASSERT (deg > 0) ; | ||
|  | 	    rp = &A [Row [r].start] ; | ||
|  | 	    rp_end = rp + len ; | ||
|  | 	    while (rp < rp_end) | ||
|  | 	    { | ||
|  | 		c = *rp++ ; | ||
|  | 		if (COL_IS_ALIVE (c)) | ||
|  | 		{ | ||
|  | 		    i++ ; | ||
|  | 		} | ||
|  | 	    } | ||
|  | 	    ASSERT (i > 0) ; | ||
|  | 	} | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === debug_deg_lists ====================================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |     Prints the contents of the degree lists.  Counts the number of columns | ||
|  |     in the degree list and compares it to the total it should have.  Also | ||
|  |     checks the row degrees. | ||
|  | */ | ||
|  | 
 | ||
|  | PRIVATE void debug_deg_lists | ||
|  | ( | ||
|  |     /* === Parameters ======================================================= */ | ||
|  | 
 | ||
|  |     Int n_row, | ||
|  |     Int n_col, | ||
|  |     Colamd_Row Row [], | ||
|  |     Colamd_Col Col [], | ||
|  |     Int head [], | ||
|  |     Int min_score, | ||
|  |     Int should, | ||
|  |     Int max_deg | ||
|  | ) | ||
|  | { | ||
|  |     /* === Local variables ================================================== */ | ||
|  | 
 | ||
|  |     Int deg ; | ||
|  |     Int col ; | ||
|  |     Int have ; | ||
|  |     Int row ; | ||
|  | 
 | ||
|  |     /* === Check the degree lists =========================================== */ | ||
|  | 
 | ||
|  |     if (n_col > 10000 && colamd_debug <= 0) | ||
|  |     { | ||
|  | 	return ; | ||
|  |     } | ||
|  |     have = 0 ; | ||
|  |     DEBUG4 (("Degree lists: %d\n", min_score)) ; | ||
|  |     for (deg = 0 ; deg <= n_col ; deg++) | ||
|  |     { | ||
|  | 	col = head [deg] ; | ||
|  | 	if (col == EMPTY) | ||
|  | 	{ | ||
|  | 	    continue ; | ||
|  | 	} | ||
|  | 	DEBUG4 (("%d:", deg)) ; | ||
|  | 	while (col != EMPTY) | ||
|  | 	{ | ||
|  | 	    DEBUG4 ((" %d", col)) ; | ||
|  | 	    have += Col [col].shared1.thickness ; | ||
|  | 	    ASSERT (COL_IS_ALIVE (col)) ; | ||
|  | 	    col = Col [col].shared4.degree_next ; | ||
|  | 	} | ||
|  | 	DEBUG4 (("\n")) ; | ||
|  |     } | ||
|  |     DEBUG4 (("should %d have %d\n", should, have)) ; | ||
|  |     ASSERT (should == have) ; | ||
|  | 
 | ||
|  |     /* === Check the row degrees ============================================ */ | ||
|  | 
 | ||
|  |     if (n_row > 10000 && colamd_debug <= 0) | ||
|  |     { | ||
|  | 	return ; | ||
|  |     } | ||
|  |     for (row = 0 ; row < n_row ; row++) | ||
|  |     { | ||
|  | 	if (ROW_IS_ALIVE (row)) | ||
|  | 	{ | ||
|  | 	    ASSERT (Row [row].shared1.degree <= max_deg) ; | ||
|  | 	} | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === debug_mark =========================================================== */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |     Ensures that the tag_mark is less that the maximum and also ensures that | ||
|  |     each entry in the mark array is less than the tag mark. | ||
|  | */ | ||
|  | 
 | ||
|  | PRIVATE void debug_mark | ||
|  | ( | ||
|  |     /* === Parameters ======================================================= */ | ||
|  | 
 | ||
|  |     Int n_row, | ||
|  |     Colamd_Row Row [], | ||
|  |     Int tag_mark, | ||
|  |     Int max_mark | ||
|  | ) | ||
|  | { | ||
|  |     /* === Local variables ================================================== */ | ||
|  | 
 | ||
|  |     Int r ; | ||
|  | 
 | ||
|  |     /* === Check the Row marks ============================================== */ | ||
|  | 
 | ||
|  |     ASSERT (tag_mark > 0 && tag_mark <= max_mark) ; | ||
|  |     if (n_row > 10000 && colamd_debug <= 0) | ||
|  |     { | ||
|  | 	return ; | ||
|  |     } | ||
|  |     for (r = 0 ; r < n_row ; r++) | ||
|  |     { | ||
|  | 	ASSERT (Row [r].shared2.mark < tag_mark) ; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ========================================================================== */ | ||
|  | /* === debug_matrix ========================================================= */ | ||
|  | /* ========================================================================== */ | ||
|  | 
 | ||
|  | /*
 | ||
|  |     Prints out the contents of the columns and the rows. | ||
|  | */ | ||
|  | 
 | ||
|  | PRIVATE void debug_matrix | ||
|  | ( | ||
|  |     /* === Parameters ======================================================= */ | ||
|  | 
 | ||
|  |     Int n_row, | ||
|  |     Int n_col, | ||
|  |     Colamd_Row Row [], | ||
|  |     Colamd_Col Col [], | ||
|  |     Int A [] | ||
|  | ) | ||
|  | { | ||
|  |     /* === Local variables ================================================== */ | ||
|  | 
 | ||
|  |     Int r ; | ||
|  |     Int c ; | ||
|  |     Int *rp ; | ||
|  |     Int *rp_end ; | ||
|  |     Int *cp ; | ||
|  |     Int *cp_end ; | ||
|  | 
 | ||
|  |     /* === Dump the rows and columns of the matrix ========================== */ | ||
|  | 
 | ||
|  |     if (colamd_debug < 3) | ||
|  |     { | ||
|  | 	return ; | ||
|  |     } | ||
|  |     DEBUG3 (("DUMP MATRIX:\n")) ; | ||
|  |     for (r = 0 ; r < n_row ; r++) | ||
|  |     { | ||
|  | 	DEBUG3 (("Row %d alive? %d\n", r, ROW_IS_ALIVE (r))) ; | ||
|  | 	if (ROW_IS_DEAD (r)) | ||
|  | 	{ | ||
|  | 	    continue ; | ||
|  | 	} | ||
|  | 	DEBUG3 (("start %d length %d degree %d\n", | ||
|  | 		Row [r].start, Row [r].length, Row [r].shared1.degree)) ; | ||
|  | 	rp = &A [Row [r].start] ; | ||
|  | 	rp_end = rp + Row [r].length ; | ||
|  | 	while (rp < rp_end) | ||
|  | 	{ | ||
|  | 	    c = *rp++ ; | ||
|  | 	    DEBUG4 (("	%d col %d\n", COL_IS_ALIVE (c), c)) ; | ||
|  | 	} | ||
|  |     } | ||
|  | 
 | ||
|  |     for (c = 0 ; c < n_col ; c++) | ||
|  |     { | ||
|  | 	DEBUG3 (("Col %d alive? %d\n", c, COL_IS_ALIVE (c))) ; | ||
|  | 	if (COL_IS_DEAD (c)) | ||
|  | 	{ | ||
|  | 	    continue ; | ||
|  | 	} | ||
|  | 	DEBUG3 (("start %d length %d shared1 %d shared2 %d\n", | ||
|  | 		Col [c].start, Col [c].length, | ||
|  | 		Col [c].shared1.thickness, Col [c].shared2.score)) ; | ||
|  | 	cp = &A [Col [c].start] ; | ||
|  | 	cp_end = cp + Col [c].length ; | ||
|  | 	while (cp < cp_end) | ||
|  | 	{ | ||
|  | 	    r = *cp++ ; | ||
|  | 	    DEBUG4 (("	%d row %d\n", ROW_IS_ALIVE (r), r)) ; | ||
|  | 	} | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | PRIVATE void colamd_get_debug | ||
|  | ( | ||
|  |     char *method | ||
|  | ) | ||
|  | { | ||
|  |     FILE *f ; | ||
|  |     colamd_debug = 0 ;		/* no debug printing */ | ||
|  |     f = fopen ("debug", "r") ; | ||
|  |     if (f == (FILE *) NULL) | ||
|  |     { | ||
|  | 	colamd_debug = 0 ; | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  | 	fscanf (f, "%d", &colamd_debug) ; | ||
|  | 	fclose (f) ; | ||
|  |     } | ||
|  |     DEBUG0 (("%s: debug version, D = %d (THIS WILL BE SLOW!)\n", | ||
|  |     	method, colamd_debug)) ; | ||
|  | } | ||
|  | 
 | ||
|  | #endif /* NDEBUG */
 |