diff liboctave/UMFPACK/UMFPACK/Source/umf_2by2.c @ 5164:57077d0ddc8e

[project @ 2005-02-25 19:55:24 by jwe]
author jwe
date Fri, 25 Feb 2005 19:55:28 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/liboctave/UMFPACK/UMFPACK/Source/umf_2by2.c	Fri Feb 25 19:55:28 2005 +0000
@@ -0,0 +1,859 @@
+/* ========================================================================== */
+/* === UMF_2by2 ============================================================= */
+/* ========================================================================== */
+
+/* -------------------------------------------------------------------------- */
+/* UMFPACK Version 4.4, Copyright (c) 2005 by Timothy A. Davis.  CISE Dept,   */
+/* Univ. of Florida.  All Rights Reserved.  See ../Doc/License for License.   */
+/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
+/* -------------------------------------------------------------------------- */
+
+/*  Not user-callable.  Computes a row permutation P so that A (P,:) has a
+ *  mostly zero-free diagonal, with large entries on the diagonal.  It does this
+ *  by swapping pairs of rows.  Once a row is swapped it is not swapped again.
+ *  This is a "cheap" assignment, not a complete max. transversal or
+ *  bi-partite matching.  It is only a partial matching.  For most matrices
+ *  for which this algorithm is used, however, the matching is complete (in
+ *  UMFPACK this algorithm is used for matrices with roughly symmetric pattern,
+ *  and these matrices typically have a mostly-zero-free diagonal to begin with.
+ *  This algorithm is not meant to be used on arbitrary unsymmetric matrices
+ *  (for those matrices, UMFPACK uses its unsymmetric strategy and does not
+ *  use this algorithm).
+ *
+ *  Even if incomplete, the matching is usually good enough for UMFPACK's
+ *  symmetric strategy, which can easily pivot off the diagonal during numerical
+ *  factorization if it finds a weak diagonal entry.
+ *
+ *  The algorithms works as follows.  First, row scaling factors are computed,
+ *  and weak diagonal entries are found.  A weak entry is a value A(k,k) whose
+ *  absolute value is < tol * max (abs (A (:,k))).  For each weak diagonal k in
+ *  increasing order of degree in A+A', the algorithm finds an index j such
+ *  that A (k,j) and A (j,k) are "large" (greater than or equal to tol times
+ *  the largest magnitude in their columns).  Row j must also not have already
+ *  been swapped.  Rows j and k are then swapped.  If we come to a diagonal k
+ *  that has already been swapped, then it is not modified.  This case occurs
+ *  for "oxo" pivots:
+ *
+ *    k j
+ *  k o x
+ *  j x o
+ *
+ *  which are swapped once to obtain
+ *
+ *    k j
+ *  j x o
+ *  k o x
+ *
+ *  These two rows are then not modified any further (A (j,j) was weak, but
+ *  after one swap the permuted the jth diagonal entry is strong.
+ *
+ *  This algorithm only works on square matrices (real, complex, or pattern-
+ *  only).  The numerical values are optional.  If not present, each entry is
+ *  treated as numerically acceptable (tol is ignored), and the algorithm
+ *  operates by just using the pattern, not the values.  Each column of the
+ *  input matrix A must be sorted, with no duplicate entries.  The matrix A
+ *  can be optionally scaled prior to the numerical test.  The matrix A (:,P)
+ *  has the same diagonal entries as A (:,P), except in different order.  So
+ *  the output permutation P can also be used to swap the columns of A.
+ */
+
+#include "umf_internal.h"
+
+#ifndef NDEBUG
+#include "umf_is_permutation.h"
+#endif
+
+/* x is "weak" if it is less than ctol.  If x or ctol are NaN, then define
+ * x as not "weak".  This is a rather arbitrary choice, made to simplify the
+ * computation.  On all but a PC with Microsoft C/C++, this test becomes
+ * ((x) - ctol < 0). */
+#define WEAK(x,ctol) (SCALAR_IS_LTZERO ((x)-(ctol)))
+
+/* For flag value in Next [col] */
+#define IS_WEAK -2
+
+/* ========================================================================== */
+/* === two_by_two =========================================================== */
+/* ========================================================================== */
+
+PRIVATE Int two_by_two	    /* returns # unmatched weak diagonals */
+(
+    /* input, not modified */
+    Int n2,		/* C is n2-by-n2 */
+    Int Cp [ ],		/* size n2+1, column pointers for C */
+    Int Ci [ ],		/* size snz = Cp [n2], row indices for C */
+    Int Degree [ ],	/* Degree [i] = degree of row i of C+C' */
+
+    /* input, not defined on output */
+    Int Next [ ],	/* Next [k] == IS_WEAK if k is a weak diagonal */
+    Int Ri [ ],		/* Ri [i] is the length of row i in C */
+
+    /* output, not defined on input */
+    Int P [ ],
+
+    /* workspace, not defined on input or output */
+    Int Rp [ ],
+    Int Head [ ]
+)
+{
+    Int deg, newcol, row, col, p, p2, unmatched, k, j, j2, j_best, best, jdiff,
+	jdiff_best, jdeg, jdeg_best, cp, cp1, cp2, rp, rp1, rp2, maxdeg,
+	mindeg ;
+
+    /* ---------------------------------------------------------------------- */
+    /* place weak diagonals in the degree lists */
+    /* ---------------------------------------------------------------------- */
+
+    for (deg = 0 ; deg < n2 ; deg++)
+    {
+	Head [deg] = EMPTY ;
+    }
+
+    maxdeg = 0 ;
+    mindeg = Int_MAX ;
+    for (newcol = n2-1 ; newcol >= 0 ; newcol--)
+    {
+	if (Next [newcol] == IS_WEAK)
+	{
+	    /* add this column to the list of weak nodes */
+	    DEBUGm1 (("    newcol "ID" has a weak diagonal deg "ID"\n",
+		newcol, deg)) ;
+	    deg = Degree [newcol] ;
+	    ASSERT (deg >= 0 && deg < n2) ;
+	    Next [newcol] = Head [deg] ;
+	    Head [deg] = newcol ;
+	    maxdeg = MAX (maxdeg, deg) ;
+	    mindeg = MIN (mindeg, deg) ;
+	}
+    }
+
+    /* ---------------------------------------------------------------------- */
+    /* construct R = C' (C = strong entries in pruned submatrix) */
+    /* ---------------------------------------------------------------------- */
+
+    /* Ri [0..n2-1] is the length of each row of R */
+    /* use P as temporary pointer into the row form of R [ */
+    Rp [0] = 0 ;
+    for (row = 0 ; row < n2 ; row++)
+    {
+	Rp [row+1] = Rp [row] + Ri [row] ;
+	P [row] = Rp [row] ;
+    }
+    /* Ri no longer needed for row counts */
+
+    /* all entries in C are strong */
+    for (col = 0 ; col < n2 ; col++)
+    {
+	p2 = Cp [col+1] ;
+	for (p = Cp [col] ; p < p2 ; p++)
+	{
+	    /* place the column index in row = Ci [p] */
+	    Ri [P [Ci [p]]++] = col ;
+	}
+    }
+
+    /* contents of P no longer needed ] */
+
+#ifndef NDEBUG
+    DEBUG0 (("==================R: row form of strong entries in A:\n")) ;
+    UMF_dump_col_matrix ((double *) NULL,
+#ifdef COMPLEX
+	    (double *) NULL,
+#endif
+	    Ri, Rp, n2, n2, Rp [n2]) ;
+#endif
+    ASSERT (AMD_valid (n2, n2, Rp, Ri)) ;
+
+    /* ---------------------------------------------------------------------- */
+    /* for each weak diagonal, find a pair of strong off-diagonal entries */
+    /* ---------------------------------------------------------------------- */
+
+    for (row = 0 ; row < n2 ; row++)
+    {
+	P [row] = EMPTY ;
+    }
+
+    unmatched = 0 ;
+    best = EMPTY ;
+    jdiff = EMPTY ;
+    jdeg = EMPTY ;
+
+    for (deg = mindeg ; deg <= maxdeg ; deg++)
+    {
+	/* find the next weak diagonal of lowest degree */
+	DEBUGm2 (("---------------------------------- Deg: "ID"\n", deg)) ;
+	for (k = Head [deg] ; k != EMPTY ; k = Next [k])
+	{
+	    DEBUGm2 (("k: "ID"\n", k)) ;
+	    if (P [k] == EMPTY)
+	    {
+		/* C (k,k) is a weak diagonal entry.  Find an index j != k such
+		 * that C (j,k) and C (k,j) are both strong, and also such
+		 * that Degree [j] is minimized.  In case of a tie, pick
+		 * the smallest index j.  C and R contain the pattern of
+		 * strong entries only.
+		 *
+		 * Note that row k of R and column k of C are both sorted. */
+
+		DEBUGm4 (("===== Weak diagonal k = "ID"\n", k)) ;
+		DEBUG1 (("Column k of C:\n")) ;
+		for (p = Cp [k] ; p < Cp [k+1] ; p++)
+		{
+		    DEBUG1 (("    "ID": deg "ID"\n", Ci [p], Degree [Ci [p]]));
+		}
+		DEBUG1 (("Row k of R (strong entries only):\n")) ;
+		for (p = Rp [k] ; p < Rp [k+1] ; p++)
+		{
+		    DEBUG1 (("    "ID": deg "ID"\n", Ri [p], Degree [Ri [p]]));
+		}
+
+		/* no (C (k,j), C (j,k)) pair exists yet */
+		j_best = EMPTY ;
+		jdiff_best = Int_MAX ;
+		jdeg_best = Int_MAX ;
+
+		/* pointers into column k (including values) */
+		cp1 = Cp [k] ;
+		cp2 = Cp [k+1] ;
+		cp = cp1 ;
+
+		/* pointers into row k (strong entries only, no values) */
+		rp1 = Rp [k] ;
+		rp2 = Rp [k+1] ;
+		rp = rp1 ;
+
+		/* while entries searched in column k and row k */
+		while (TRUE)
+		{
+
+		    if (cp >= cp2)
+		    {
+			/* no more entries in this column */
+			break ;
+		    }
+
+		    /* get C (j,k), which is strong */
+		    j = Ci [cp] ;
+
+		    if (rp >= rp2)
+		    {
+			/* no more entries in this column */
+			break ;
+		    }
+
+		    /* get R (k,j2), which is strong */
+		    j2 = Ri [rp] ;
+
+		    if (j < j2)
+		    {
+			/* C (j,k) is strong, but R (k,j) is not strong */
+			cp++ ;
+			continue ;
+		    }
+
+		    if (j2 < j)
+		    {
+			/* C (k,j2) is strong, but R (j2,k) is not strong */
+			rp++ ;
+			continue ;
+		    }
+
+		    /* j == j2: C (j,k) is strong and R (k,j) is strong */
+
+		    best = FALSE ;
+
+		    if (P [j] == EMPTY)
+		    {
+			/* j has not yet been matched */
+			jdeg = Degree [j] ;
+			jdiff = SCALAR_ABS (k-j) ;
+
+			DEBUG1 (("Try candidate j "ID" deg "ID" diff "ID
+				    "\n", j, jdeg, jdiff)) ;
+
+			if (j_best == EMPTY)
+			{
+			    /* this is the first candidate seen */
+			    DEBUG1 (("   first\n")) ;
+			    best = TRUE ;
+			}
+			else
+			{
+			    if (jdeg < jdeg_best)
+			    {
+				/* the degree of j is best seen so far. */
+				DEBUG1 (("   least degree\n")) ;
+				best = TRUE ;
+			    }
+			    else if (jdeg == jdeg_best)
+			    {
+				/* degree of j and j_best are the same */
+				/* tie break by nearest node number */
+				if (jdiff < jdiff_best)
+				{
+				    DEBUG1 (("   tie degree, closer\n")) ;
+				    best = TRUE ;
+				}
+				else if (jdiff == jdiff_best)
+				{
+				    /* |j-k| = |j_best-k|.  For any given k
+				     * and j_best there is only one other j
+				     * than can be just as close as j_best.
+				     * Tie break by picking the smaller of
+				     * j and j_best */
+				    DEBUG1 (("   tie degree, as close\n"));
+				    best = j < j_best ;
+				}
+			    }
+			    else
+			    {
+				/* j has higher degree than best so far */
+				best = FALSE ;
+			    }
+			}
+		    }
+
+		    if (best)
+		    {
+			/* j is best match for k */
+			/* found a strong pair, A (j,k) and A (k,j) */
+			DEBUG1 ((" --- Found pair k: "ID" j: " ID
+			    " jdeg: "ID" jdiff: "ID"\n",
+			    k, j, jdeg, jdiff)) ;
+			ASSERT (jdiff != EMPTY) ;
+			ASSERT (jdeg != EMPTY) ;
+			j_best = j ;
+			jdeg_best = jdeg ;
+			jdiff_best = jdiff ;
+		    }
+
+		    /* get the next entries in column k and row k */
+		    cp++ ;
+		    rp++ ;
+		}
+
+		/* save the pair (j,k), if we found one */
+		if (j_best != EMPTY)
+		{
+		    j = j_best ;
+		    DEBUGm4 ((" --- best pair j: "ID" for k: "ID"\n", j, k)) ;
+		    P [k] = j ;
+		    P [j] = k ;
+		}
+		else
+		{
+		    /* no match was found for k */
+		    unmatched++ ;
+		}
+	    }
+	}
+    }
+
+    /* ---------------------------------------------------------------------- */
+    /* finalize the row permutation, P */
+    /* ---------------------------------------------------------------------- */
+
+    for (k = 0 ; k < n2 ; k++)
+    {
+	if (P [k] == EMPTY)
+	{
+	    P [k] = k ;
+	}
+    }
+    ASSERT (UMF_is_permutation (P, Rp, n2, n2)) ;
+
+    return (unmatched) ;
+}
+
+
+/* ========================================================================== */
+/* === UMF_2by2 ============================================================= */
+/* ========================================================================== */
+
+GLOBAL void UMF_2by2
+(
+    /* input, not modified: */
+    Int n,		    /* A is n-by-n */
+    const Int Ap [ ],	    /* size n+1 */
+    const Int Ai [ ],	    /* size nz = Ap [n] */
+    const double Ax [ ],    /* size nz if present */
+#ifdef COMPLEX
+    const double Az [ ],    /* size nz if present */
+#endif
+    double tol,		/* tolerance for determining whether or not an
+			 * entry is numerically acceptable.  If tol <= 0
+			 * then all numerical values ignored. */
+    Int scale,		/* scaling to perform (none, sum, or max) */
+    Int Cperm1 [ ],	/* singleton permutations */
+#ifndef NDEBUG
+    Int Rperm1 [ ],	/* not needed, since Rperm1 = Cperm1 for submatrix S */
+#endif
+    Int InvRperm1 [ ],	/* inverse of Rperm1 */
+    Int n1,		/* number of singletons */
+    Int nempty,		/* number of empty rows/cols */
+
+    /* input, contents undefined on output: */
+    Int Degree [ ],	/* Degree [j] is the number of off-diagonal
+			 * entries in row/column j of S+S', where
+			 * where S = A (Cperm1 [n1..], Rperm1 [n1..]).
+			 * Note that S is not used, nor formed. */
+
+    /* output: */
+    Int P [ ],		/* P [k] = i means original row i is kth row in S(P,:)
+			 * where S = A (Cperm1 [n1..], Rperm1 [n1..]) */
+    Int *p_nweak,
+    Int *p_unmatched,
+
+    /* workspace (not defined on input or output): */
+    Int Ri [ ],		/* of size >= max (nz, n) */
+    Int Rp [ ],		/* of size n+1 */
+    double Rs [ ],	/* of size n if present.  Rs = sum (abs (A),2) or
+			 * max (abs (A),2), the sum or max of each row.  Unused
+			 * if scale is equal to UMFPACK_SCALE_NONE. */
+    Int Head [ ],	/* of size n.  Head pointers for bucket sort */
+    Int Next [ ],	/* of size n.  Next pointers for bucket sort */
+    Int Ci [ ],		/* size nz */
+    Int Cp [ ]		/* size n+1 */
+)
+{
+
+    /* ---------------------------------------------------------------------- */
+    /* local variables */
+    /* ---------------------------------------------------------------------- */
+
+    Entry aij ;
+    double cmax, value, rs, ctol, dvalue ;
+    Int k, p, row, col, do_values, do_sum, do_max, do_scale, nweak, weak,
+	p1, p2, dfound, unmatched, n2, oldrow, newrow, oldcol, newcol, pp ;
+#ifdef COMPLEX
+    Int split = SPLIT (Az) ;
+#endif
+#ifndef NRECIPROCAL
+    Int do_recip = FALSE ;
+#endif
+
+#ifndef NDEBUG
+    /* UMF_debug += 99 ; */
+    DEBUGm3 (("\n ==================================UMF_2by2: tol %g\n", tol)) ;
+    ASSERT (AMD_valid (n, n, Ap, Ai)) ;
+    for (k = n1 ; k < n - nempty ; k++)
+    {
+	ASSERT (Cperm1 [k] == Rperm1 [k]) ;
+    }
+#endif
+
+    /* ---------------------------------------------------------------------- */
+    /* determine scaling options */
+    /* ---------------------------------------------------------------------- */
+
+    /* use the values, but only if they are present */
+    /* ignore the values if tol <= 0 */
+    do_values = (tol > 0) && (Ax != (double *) NULL) ;
+    if (do_values && (Rs != (double *) NULL))
+    {
+	do_sum = (scale == UMFPACK_SCALE_SUM) ;
+	do_max = (scale == UMFPACK_SCALE_MAX) ;
+    }
+    else
+    {
+	/* no scaling */
+	do_sum = FALSE ;
+	do_max = FALSE ;
+    }
+    do_scale = do_max || do_sum ;
+    DEBUGm3 (("do_values "ID" do_sum "ID" do_max "ID" do_scale "ID"\n",
+	do_values, do_sum, do_max, do_scale)) ;
+
+    /* ---------------------------------------------------------------------- */
+    /* compute the row scaling, if requested */
+    /* ---------------------------------------------------------------------- */
+
+    /* see also umf_kernel_init */
+
+    if (do_scale)
+    {
+#ifndef NRECIPROCAL
+	double rsmin ;
+#endif
+	for (row = 0 ; row < n ; row++)
+	{
+	    Rs [row] = 0.0 ;
+	}
+	for (col = 0 ; col < n ; col++)
+	{
+	    p2 = Ap [col+1] ;
+	    for (p = Ap [col] ; p < p2 ; p++)
+	    {
+		row = Ai [p] ;
+		ASSIGN (aij, Ax, Az, p, split) ;
+		APPROX_ABS (value, aij) ;
+		rs = Rs [row] ;
+		if (!SCALAR_IS_NAN (rs))
+		{
+		    if (SCALAR_IS_NAN (value))
+		    {
+			/* if any entry in a row is NaN, then the scale factor
+			 * for the row is NaN.  It will be set to 1 later. */
+			Rs [row] = value ;
+		    }
+		    else if (do_max)
+		    {
+			Rs [row] = MAX (rs, value) ;
+		    }
+		    else
+		    {
+			Rs [row] += value ;
+		    }
+		}
+	    }
+	}
+#ifndef NRECIPROCAL
+	rsmin = Rs [0] ;
+	if (SCALAR_IS_ZERO (rsmin) || SCALAR_IS_NAN (rsmin))
+	{
+	    rsmin = 1.0 ;
+	}
+#endif
+	for (row = 0 ; row < n ; row++)
+	{
+	    /* do not scale an empty row, or a row with a NaN */
+	    rs = Rs [row] ;
+	    if (SCALAR_IS_ZERO (rs) || SCALAR_IS_NAN (rs))
+	    {
+		Rs [row] = 1.0 ;
+	    }
+#ifndef NRECIPROCAL
+	    rsmin = MIN (rsmin, Rs [row]) ;
+#endif
+	}
+
+#ifndef NRECIPROCAL
+	/* multiply by the reciprocal if Rs is not too small */
+	do_recip = (rsmin >= RECIPROCAL_TOLERANCE) ;
+	if (do_recip)
+	{
+	    /* invert the scale factors */
+	    for (row = 0 ; row < n ; row++)
+	    {
+		Rs [row] = 1.0 / Rs [row] ;
+	    }
+	}
+#endif
+    }
+
+    /* ---------------------------------------------------------------------- */
+    /* compute the max in each column and find diagonal */
+    /* ---------------------------------------------------------------------- */
+
+    nweak = 0 ;
+
+#ifndef NDEBUG
+    for (k = 0 ; k < n ; k++)
+    {
+	ASSERT (Rperm1 [k] >= 0 && Rperm1 [k] < n) ;
+	ASSERT (InvRperm1 [Rperm1 [k]] == k) ;
+    }
+#endif
+
+    n2 = n - n1 - nempty ;
+
+    /* use Ri to count the number of strong entries in each row */
+    for (row = 0 ; row < n2 ; row++)
+    {
+	Ri [row] = 0 ;
+    }
+
+    pp = 0 ;
+    ctol = 0 ;
+    dvalue = 1 ;
+
+    /* construct C = pruned submatrix, strong values only, column form */
+
+    for (k = n1 ; k < n - nempty ; k++)
+    {
+	oldcol = Cperm1 [k] ;
+	newcol = k - n1 ;
+	Next [newcol] = EMPTY ;
+	DEBUGm1 (("Column "ID" newcol "ID" oldcol "ID"\n", k, newcol, oldcol)) ;
+
+	Cp [newcol] = pp ;
+
+	dfound = FALSE ;
+	p1 = Ap [oldcol] ;
+	p2 = Ap [oldcol+1] ;
+	if (do_values)
+	{
+	    cmax = 0 ;
+	    dvalue = 0 ;
+
+	    if (!do_scale)
+	    {
+		/* no scaling */
+		for (p = p1 ; p < p2 ; p++)
+		{
+		    oldrow = Ai [p] ;
+		    ASSERT (oldrow >= 0 && oldrow < n) ;
+		    newrow = InvRperm1 [oldrow] - n1 ;
+		    ASSERT (newrow >= -n1 && newrow < n2) ;
+		    if (newrow < 0) continue ;
+		    ASSIGN (aij, Ax, Az, p, split) ;
+		    APPROX_ABS (value, aij) ;
+		    /* if either cmax or value is NaN, define cmax as NaN */
+		    if (!SCALAR_IS_NAN (cmax))
+		    {
+			if (SCALAR_IS_NAN (value))
+			{
+			    cmax = value ;
+			}
+			else
+			{
+			    cmax = MAX (cmax, value) ;
+			}
+		    }
+		    if (oldrow == oldcol)
+		    {
+			/* we found the diagonal entry in this column */
+			dvalue = value ;
+			dfound = TRUE ;
+			ASSERT (newrow == newcol) ;
+		    }
+		}
+	    }
+#ifndef NRECIPROCAL
+	    else if (do_recip)
+	    {
+		/* multiply by the reciprocal */
+		for (p = p1 ; p < p2 ; p++)
+		{
+		    oldrow = Ai [p] ;
+		    ASSERT (oldrow >= 0 && oldrow < n) ;
+		    newrow = InvRperm1 [oldrow] - n1 ;
+		    ASSERT (newrow >= -n1 && newrow < n2) ;
+		    if (newrow < 0) continue ;
+		    ASSIGN (aij, Ax, Az, p, split) ;
+		    APPROX_ABS (value, aij) ;
+		    value *= Rs [oldrow] ;
+		    /* if either cmax or value is NaN, define cmax as NaN */
+		    if (!SCALAR_IS_NAN (cmax))
+		    {
+			if (SCALAR_IS_NAN (value))
+			{
+			    cmax = value ;
+			}
+			else
+			{
+			    cmax = MAX (cmax, value) ;
+			}
+		    }
+		    if (oldrow == oldcol)
+		    {
+			/* we found the diagonal entry in this column */
+			dvalue = value ;
+			dfound = TRUE ;
+			ASSERT (newrow == newcol) ;
+		    }
+		}
+	    }
+#endif
+	    else
+	    {
+		/* divide instead */
+		for (p = p1 ; p < p2 ; p++)
+		{
+		    oldrow = Ai [p] ;
+		    ASSERT (oldrow >= 0 && oldrow < n) ;
+		    newrow = InvRperm1 [oldrow] - n1 ;
+		    ASSERT (newrow >= -n1 && newrow < n2) ;
+		    if (newrow < 0) continue ;
+		    ASSIGN (aij, Ax, Az, p, split) ;
+		    APPROX_ABS (value, aij) ;
+		    value /= Rs [oldrow] ;
+		    /* if either cmax or value is NaN, define cmax as NaN */
+		    if (!SCALAR_IS_NAN (cmax))
+		    {
+			if (SCALAR_IS_NAN (value))
+			{
+			    cmax = value ;
+			}
+			else
+			{
+			    cmax = MAX (cmax, value) ;
+			}
+		    }
+		    if (oldrow == oldcol)
+		    {
+			/* we found the diagonal entry in this column */
+			dvalue = value ;
+			dfound = TRUE ;
+			ASSERT (newrow == newcol) ;
+		    }
+		}
+	    }
+
+	    ctol = tol * cmax ;
+	    DEBUGm1 (("    cmax col "ID" %g  ctol %g\n", oldcol, cmax, ctol)) ;
+	}
+	else
+	{
+	    for (p = p1 ; p < p2 ; p++)
+	    {
+		oldrow = Ai [p] ;
+		ASSERT (oldrow >= 0 && oldrow < n) ;
+		newrow = InvRperm1 [oldrow] - n1 ;
+		ASSERT (newrow >= -n1 && newrow < n2) ;
+		if (newrow < 0) continue ;
+		Ci [pp++] = newrow ;
+		if (oldrow == oldcol)
+		{
+		    /* we found the diagonal entry in this column */
+		    ASSERT (newrow == newcol) ;
+		    dfound = TRUE ;
+		}
+		/* count the entries in each column */
+		Ri [newrow]++ ;
+	    }
+	}
+
+	/* ------------------------------------------------------------------ */
+	/* flag the weak diagonals */
+	/* ------------------------------------------------------------------ */
+
+	if (!dfound)
+	{
+	    /* no diagonal entry present */
+	    weak = TRUE ;
+	}
+	else
+	{
+	    /* diagonal entry is present, check its value */
+	    weak = (do_values) ?  WEAK (dvalue, ctol) : FALSE ;
+	}
+	if (weak)
+	{
+	    /* flag this column as weak */
+	    DEBUG0 (("Weak!\n")) ;
+	    Next [newcol] = IS_WEAK ;
+	    nweak++ ;
+	}
+
+	/* ------------------------------------------------------------------ */
+	/* count entries in each row that are not numerically weak */
+	/* ------------------------------------------------------------------ */
+
+	if (do_values)
+	{
+	    if (!do_scale)
+	    {
+		/* no scaling */
+		for (p = p1 ; p < p2 ; p++)
+		{
+		    oldrow = Ai [p] ;
+		    newrow = InvRperm1 [oldrow] - n1 ;
+		    if (newrow < 0) continue ;
+		    ASSIGN (aij, Ax, Az, p, split) ;
+		    APPROX_ABS (value, aij) ;
+		    weak = WEAK (value, ctol) ;
+		    if (!weak)
+		    {
+			DEBUG0 (("    strong: row "ID": %g\n", oldrow, value)) ;
+			Ci [pp++] = newrow ;
+			Ri [newrow]++ ;
+		    }
+		}
+	    }
+#ifndef NRECIPROCAL
+	    else if (do_recip)
+	    {
+		/* multiply by the reciprocal */
+		for (p = p1 ; p < p2 ; p++)
+		{
+		    oldrow = Ai [p] ;
+		    newrow = InvRperm1 [oldrow] - n1 ;
+		    if (newrow < 0) continue ;
+		    ASSIGN (aij, Ax, Az, p, split) ;
+		    APPROX_ABS (value, aij) ;
+		    value *= Rs [oldrow] ;
+		    weak = WEAK (value, ctol) ;
+		    if (!weak)
+		    {
+			DEBUG0 (("    strong: row "ID": %g\n", oldrow, value)) ;
+			Ci [pp++] = newrow ;
+			Ri [newrow]++ ;
+		    }
+		}
+	    }
+#endif
+	    else
+	    {
+		/* divide instead */
+		for (p = p1 ; p < p2 ; p++)
+		{
+		    oldrow = Ai [p] ;
+		    newrow = InvRperm1 [oldrow] - n1 ;
+		    if (newrow < 0) continue ;
+		    ASSIGN (aij, Ax, Az, p, split) ;
+		    APPROX_ABS (value, aij) ;
+		    value /= Rs [oldrow] ;
+		    weak = WEAK (value, ctol) ;
+		    if (!weak)
+		    {
+			DEBUG0 (("    strong: row "ID": %g\n", oldrow, value)) ;
+			Ci [pp++] = newrow ;
+			Ri [newrow]++ ;
+		    }
+		}
+	    }
+	}
+    }
+    Cp [n2] = pp ;
+    ASSERT (AMD_valid (n2, n2, Cp, Ci)) ;
+
+    if (nweak == 0)
+    {
+	/* nothing to do, quick return */
+	DEBUGm2 (("\n =============================UMF_2by2: quick return\n")) ;
+	for (k = 0 ; k < n ; k++)
+	{
+	    P [k] = k ;
+	}
+	*p_nweak = 0 ;
+	*p_unmatched = 0 ;
+	return ;
+    }
+
+#ifndef NDEBUG
+    for (k = 0 ; k < n2 ; k++)
+    {
+	P [k] = EMPTY ;
+    }
+    for (k = 0 ; k < n2 ; k++)
+    {
+	ASSERT (Degree [k] >= 0 && Degree [k] < n2) ;
+    }
+#endif
+
+    /* ---------------------------------------------------------------------- */
+    /* find the 2-by-2 permutation */
+    /* ---------------------------------------------------------------------- */
+
+    /* The matrix S is now mapped to the index range 0 to n2-1.  We have
+     * S = A (Rperm [n1 .. n-nempty-1], Cperm [n1 .. n-nempty-1]), and then
+     * C = pattern of strong entries in S.  A weak diagonal k in S is marked
+     * with Next [k] = IS_WEAK. */
+
+    unmatched = two_by_two (n2, Cp, Ci, Degree, Next, Ri, P, Rp, Head) ;
+
+    /* ---------------------------------------------------------------------- */
+
+    *p_nweak = nweak ;
+    *p_unmatched = unmatched ;
+
+#ifndef NDEBUG
+    DEBUGm4 (("UMF_2by2: weak "ID"  unmatched "ID"\n", nweak, unmatched)) ;
+    for (row = 0 ; row < n ; row++)
+    {
+	DEBUGm2 (("P ["ID"] = "ID"\n", row, P [row])) ;
+    }
+    DEBUGm2 (("\n =============================UMF_2by2: done\n\n")) ;
+#endif
+}