Rev 1820 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/* ========================================================================== *//* === UMF_row_search ======================================================= *//* ========================================================================== *//* -------------------------------------------------------------------------- *//* UMFPACK Version 4.5, 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 *//* -------------------------------------------------------------------------- *//*Find two candidate pivot rows in a column: the best one in the front,and the best one not in the front. Return the two pivot row patterns andtheir exact degrees. Called by UMF_local_search.Returns UMFPACK_OK if successful, or UMFPACK_WARNING_singular_matrix orUMFPACK_ERROR_different_pattern if not.*/#include "umf_internal.h"#include "umf_row_search.h"GLOBAL Int UMF_row_search(NumericType *Numeric,WorkType *Work,SymbolicType *Symbolic,Int cdeg0, /* length of column in Front */Int cdeg1, /* length of column outside Front */const Int Pattern [ ], /* pattern of column, Pattern [0..cdeg1 -1] */const Int Pos [ ], /* Pos [Pattern [0..cdeg1 -1]] = 0..cdeg1 -1 */Int pivrow [2], /* pivrow [IN] and pivrow [OUT] */Int rdeg [2], /* rdeg [IN] and rdeg [OUT] */Int W_i [ ], /* pattern of pivrow [IN], *//* either Fcols or Woi */Int W_o [ ], /* pattern of pivrow [OUT], *//* either Wio or Woo */Int prior_pivrow [2], /* the two other rows just scanned, if any */const Entry Wxy [ ], /* numerical values Wxy [0..cdeg1-1],either Wx or Wy */Int pivcol, /* the candidate column being searched */Int freebie [ ]){/* ---------------------------------------------------------------------- *//* local variables *//* ---------------------------------------------------------------------- */double maxval, toler, toler2, value, pivot [2] ;Int i, row, deg, col, *Frpos, fnrows, *E, j, ncols, *Cols, *Rows,e, f, Wrpflag, *Fcpos, fncols, tpi, max_rdeg, nans_in_col, was_offdiag,diag_row, prefer_diagonal, *Wrp, found, *Diagonal_map ;Tuple *tp, *tpend, *tp1, *tp2 ;Unit *Memory, *p ;Element *ep ;Int *Row_tuples, *Row_degree, *Row_tlen ;#ifndef NDEBUGInt *Col_degree ;DEBUG2 (("Row_search:\n")) ;for (i = 0 ; i < cdeg1 ; i++){row = Pattern [i] ;DEBUG4 ((" row: "ID"\n", row)) ;ASSERT (row >= 0 && row < Numeric->n_row) ;ASSERT (i == Pos [row]) ;}/* If row is not in Pattern [0..cdeg1-1], then Pos [row] == EMPTY */if (UMF_debug > 0 || Numeric->n_row < 1000){Int cnt = cdeg1 ;DEBUG4 (("Scan all rows:\n")) ;for (row = 0 ; row < Numeric->n_row ; row++){if (Pos [row] < 0){cnt++ ;}else{DEBUG4 ((" row: "ID" pos "ID"\n", row, Pos [row])) ;}}ASSERT (cnt == Numeric->n_row) ;}Col_degree = Numeric->Cperm ; /* for NON_PIVOTAL_COL macro only */ASSERT (pivcol >= 0 && pivcol < Work->n_col) ;ASSERT (NON_PIVOTAL_COL (pivcol)) ;#endifpivot [IN] = 0. ;pivot [OUT] = 0. ;/* ---------------------------------------------------------------------- *//* get parameters *//* ---------------------------------------------------------------------- */Row_degree = Numeric->Rperm ;Row_tuples = Numeric->Uip ;Row_tlen = Numeric->Uilen ;Wrp = Work->Wrp ;Frpos = Work->Frpos ;E = Work->E ;Memory = Numeric->Memory ;fnrows = Work->fnrows ;prefer_diagonal = Symbolic->prefer_diagonal ;Diagonal_map = Work->Diagonal_map ;if (Diagonal_map){diag_row = Diagonal_map [pivcol] ;was_offdiag = diag_row < 0 ;if (was_offdiag){/* the "diagonal" entry in this column was permuted here by an* earlier pivot choice. The tighter off-diagonal tolerance will* be used instead of the symmetric tolerance. */diag_row = FLIP (diag_row) ;}ASSERT (diag_row >= 0 && diag_row < Numeric->n_row) ;}else{diag_row = EMPTY ; /* unused */was_offdiag = EMPTY ; /* unused */}/* pivot row degree cannot exceed max_rdeg */max_rdeg = Work->fncols_max ;/* ---------------------------------------------------------------------- *//* scan pivot column for candidate rows *//* ---------------------------------------------------------------------- */maxval = 0.0 ;nans_in_col = FALSE ;for (i = 0 ; i < cdeg1 ; i++){APPROX_ABS (value, Wxy [i]) ;if (SCALAR_IS_NAN (value)){nans_in_col = TRUE ;maxval = value ;break ;}/* This test can now ignore the NaN case: */maxval = MAX (maxval, value) ;}/* if maxval is zero, the matrix is numerically singular */toler = Numeric->relpt * maxval ;toler2 = Numeric->relpt2 * maxval ;toler2 = was_offdiag ? toler : toler2 ;DEBUG5 (("Row_search begins [ maxval %g toler %g %g\n",maxval, toler, toler2)) ;if (SCALAR_IS_NAN (toler) || SCALAR_IS_NAN (toler2)){nans_in_col = TRUE ;}if (!nans_in_col){/* look for the diagonal entry, if it exists */found = FALSE ;ASSERT (!SCALAR_IS_NAN (toler)) ;if (prefer_diagonal){ASSERT (diag_row != EMPTY) ;i = Pos [diag_row] ;if (i >= 0){double a ;ASSERT (i < cdeg1) ;ASSERT (diag_row == Pattern [i]) ;APPROX_ABS (a, Wxy [i]) ;ASSERT (!SCALAR_IS_NAN (a)) ;ASSERT (!SCALAR_IS_NAN (toler2)) ;if (SCALAR_IS_NONZERO (a) && a >= toler2){/* found it! */DEBUG3 (("Symmetric pivot: "ID" "ID"\n", pivcol, diag_row));found = TRUE ;if (Frpos [diag_row] >= 0 && Frpos [diag_row] < fnrows){pivrow [IN] = diag_row ;pivrow [OUT] = EMPTY ;}else{pivrow [IN] = EMPTY ;pivrow [OUT] = diag_row ;}}}}/* either no diagonal found, or we didn't look for it */if (!found){if (cdeg0 > 0){/* this is a column in the front */for (i = 0 ; i < cdeg0 ; i++){double a ;APPROX_ABS (a, Wxy [i]) ;ASSERT (!SCALAR_IS_NAN (a)) ;ASSERT (!SCALAR_IS_NAN (toler)) ;if (SCALAR_IS_NONZERO (a) && a >= toler){row = Pattern [i] ;deg = Row_degree [row] ;#ifndef NDEBUGDEBUG6 ((ID" candidate row "ID" deg "ID" absval %g\n",i, row, deg, a)) ;UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ;#endifASSERT (Frpos [row] >= 0 && Frpos [row] < fnrows) ;ASSERT (Frpos [row] == i) ;/* row is in the current front */DEBUG4 ((" in front\n")) ;if (deg < rdeg [IN]/* break ties by picking the largest entry: */|| (deg == rdeg [IN] && a > pivot [IN])/* break ties by picking the diagonal entry: *//* || (deg == rdeg [IN] && row == diag_row) */){/* best row in front, so far */pivrow [IN] = row ;rdeg [IN] = deg ;pivot [IN] = a ;}}}for ( ; i < cdeg1 ; i++){double a ;APPROX_ABS (a, Wxy [i]) ;ASSERT (!SCALAR_IS_NAN (a)) ;ASSERT (!SCALAR_IS_NAN (toler)) ;if (SCALAR_IS_NONZERO (a) && a >= toler){row = Pattern [i] ;deg = Row_degree [row] ;#ifndef NDEBUGDEBUG6 ((ID" candidate row "ID" deg "ID" absval %g\n",i, row, deg, a)) ;UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ;#endifASSERT (Frpos [row] == i) ;/* row is not in the current front */DEBUG4 ((" NOT in front\n")) ;if (deg < rdeg [OUT]/* break ties by picking the largest entry: */|| (deg == rdeg [OUT] && a > pivot [OUT])/* break ties by picking the diagonal entry: *//* || (deg == rdeg [OUT] && row == diag_row) */){/* best row not in front, so far */pivrow [OUT] = row ;rdeg [OUT] = deg ;pivot [OUT] = a ;}}}}else{/* this column is not in the front */for (i = 0 ; i < cdeg1 ; i++){double a ;APPROX_ABS (a, Wxy [i]) ;ASSERT (!SCALAR_IS_NAN (a)) ;ASSERT (!SCALAR_IS_NAN (toler)) ;if (SCALAR_IS_NONZERO (a) && a >= toler){row = Pattern [i] ;deg = Row_degree [row] ;#ifndef NDEBUGDEBUG6 ((ID" candidate row "ID" deg "ID" absval %g\n",i, row, deg, a)) ;UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ;#endifif (Frpos [row] >= 0 && Frpos [row] < fnrows){/* row is in the current front */DEBUG4 ((" in front\n")) ;if (deg < rdeg [IN]/* break ties by picking the largest entry: */|| (deg == rdeg [IN] && a > pivot [IN])/* break ties by picking the diagonal entry: *//* || (deg == rdeg [IN] && row == diag_row) */){/* best row in front, so far */pivrow [IN] = row ;rdeg [IN] = deg ;pivot [IN] = a ;}}else{/* row is not in the current front */DEBUG4 ((" NOT in front\n")) ;if (deg < rdeg [OUT]/* break ties by picking the largest entry: */|| (deg == rdeg[OUT] && a > pivot [OUT])/* break ties by picking the diagonal entry: *//* || (deg == rdeg[OUT] && row == diag_row) */){/* best row not in front, so far */pivrow [OUT] = row ;rdeg [OUT] = deg ;pivot [OUT] = a ;}}}}}}}/* ---------------------------------------------------------------------- *//* NaN handling *//* ---------------------------------------------------------------------- *//* if cdeg1 > 0 then we must have found a pivot row ... unless NaN's *//* exist. Try with no numerical tests if no pivot found. */if (cdeg1 > 0 && pivrow [IN] == EMPTY && pivrow [OUT] == EMPTY){/* cleanup for the NaN case */DEBUG0 (("Found a NaN in pivot column!\n")) ;/* grab the first entry in the pivot column, ignoring degree, *//* numerical stability, and symmetric preference */row = Pattern [0] ;deg = Row_degree [row] ;if (Frpos [row] >= 0 && Frpos [row] < fnrows){/* row is in the current front */DEBUG4 ((" in front\n")) ;pivrow [IN] = row ;rdeg [IN] = deg ;}else{/* row is not in the current front */DEBUG4 ((" NOT in front\n")) ;pivrow [OUT] = row ;rdeg [OUT] = deg ;}/* We are now guaranteed to have a pivot, no matter how broken *//* (non-IEEE compliant) the underlying numerical operators are. *//* This is particularly a problem for Microsoft compilers (they do *//* not handle NaN's properly). Now try to find a sparser pivot, if *//* possible. */for (i = 1 ; i < cdeg1 ; i++){row = Pattern [i] ;deg = Row_degree [row] ;if (Frpos [row] >= 0 && Frpos [row] < fnrows){/* row is in the current front */DEBUG4 ((" in front\n")) ;if (deg < rdeg [IN] || (deg == rdeg [IN] && row == diag_row)){/* best row in front, so far */pivrow [IN] = row ;rdeg [IN] = deg ;}}else{/* row is not in the current front */DEBUG4 ((" NOT in front\n")) ;if (deg < rdeg [OUT] || (deg == rdeg [OUT] && row == diag_row)){/* best row not in front, so far */pivrow [OUT] = row ;rdeg [OUT] = deg ;}}}}/* We found a pivot if there are entries (even zero ones) in pivot col */ASSERT (IMPLIES (cdeg1 > 0, pivrow[IN] != EMPTY || pivrow[OUT] != EMPTY)) ;/* If there are no entries in the pivot column, then no pivot is found */ASSERT (IMPLIES (cdeg1 == 0, pivrow[IN] == EMPTY && pivrow[OUT] == EMPTY)) ;/* ---------------------------------------------------------------------- *//* check for singular matrix *//* ---------------------------------------------------------------------- */if (cdeg1 == 0){if (fnrows > 0){/*Get the pivrow [OUT][IN] from the current front.The frontal matrix looks like this:pivcol[OUT]|vx x x x 0 <- so grab this row as the pivrow [OUT][IN].x x x x 0x x x x 00 0 0 0 0The current frontal matrix has some rows in it. The degreeof the pivcol[OUT] is zero. The column is empty, and thecurrent front does not contribute to it.*/pivrow [IN] = Work->Frows [0] ;DEBUGm4 (("Got zero pivrow[OUT][IN] "ID" from current front\n",pivrow [IN])) ;}else{/*Get a pivot row from the row-merge tree, use aspivrow [OUT][OUT]. pivrow [IN] remains EMPTY.This can only happen if the current front is 0-by-0.*/Int *Front_leftmostdesc, *Front_1strow, *Front_new1strow, row1,row2, fleftmost, nfr, n_row, frontid ;ASSERT (Work->fncols == 0) ;Front_leftmostdesc = Symbolic->Front_leftmostdesc ;Front_1strow = Symbolic->Front_1strow ;Front_new1strow = Work->Front_new1strow ;nfr = Symbolic->nfr ;n_row = Numeric->n_row ;frontid = Work->frontid ;DEBUGm4 (("Note: pivcol: "ID" is empty front "ID"\n",pivcol, frontid)) ;#ifndef NDEBUGDEBUG1 (("Calling dump rowmerge\n")) ;UMF_dump_rowmerge (Numeric, Symbolic, Work) ;#endif/* Row-merge set is the non-pivotal rows in the range *//* Front_new1strow [Front_leftmostdesc [frontid]] to *//* Front_1strow [frontid+1] - 1. *//* If this is empty, then use the empty rows, in the range *//* Front_new1strow [nfr] to n_row-1. *//* If this too is empty, then pivrow [OUT] will be empty. *//* In both cases, update Front_new1strow [...]. */fleftmost = Front_leftmostdesc [frontid] ;row1 = Front_new1strow [fleftmost] ;row2 = Front_1strow [frontid+1] - 1 ;DEBUG1 (("Leftmost: "ID" Rows ["ID" to "ID"] srch ["ID" to "ID"]\n",fleftmost, Front_1strow [frontid], row2, row1, row2)) ;/* look in the range row1 ... row2 */for (row = row1 ; row <= row2 ; row++){DEBUG3 ((" Row: "ID"\n", row)) ;if (NON_PIVOTAL_ROW (row)){/* found it */DEBUG3 ((" Row: "ID" found\n", row)) ;ASSERT (Frpos [row] == EMPTY) ;pivrow [OUT] = row ;DEBUGm4 (("got row merge pivrow %d\n", pivrow [OUT])) ;break ;}}Front_new1strow [fleftmost] = row ;if (pivrow [OUT] == EMPTY){/* not found, look in empty row set in "dummy" front */row1 = Front_new1strow [nfr] ;row2 = n_row-1 ;DEBUG3 (("Empty: "ID" Rows ["ID" to "ID"] srch["ID" to "ID"]\n",nfr, Front_1strow [nfr], row2, row1, row2)) ;/* look in the range row1 ... row2 */for (row = row1 ; row <= row2 ; row++){DEBUG3 ((" Empty Row: "ID"\n", row)) ;if (NON_PIVOTAL_ROW (row)){/* found it */DEBUG3 ((" Empty Row: "ID" found\n", row)) ;ASSERT (Frpos [row] == EMPTY) ;pivrow [OUT] = row ;DEBUGm4 (("got dummy row pivrow %d\n", pivrow [OUT])) ;break ;}}Front_new1strow [nfr] = row ;}if (pivrow [OUT] == EMPTY){/* Row-merge set is empty. We can just discard *//* the candidate pivot column. */DEBUG0 (("Note: row-merge set empty\n")) ;DEBUGm4 (("got no pivrow \n")) ;return (UMFPACK_WARNING_singular_matrix) ;}}}/* ---------------------------------------------------------------------- *//* construct the candidate row in the front, if any *//* ---------------------------------------------------------------------- */#ifndef NDEBUG/* check Wrp */ASSERT (Work->Wrpflag > 0) ;if (UMF_debug > 0 || Work->n_col < 1000){for (i = 0 ; i < Work->n_col ; i++){ASSERT (Wrp [i] < Work->Wrpflag) ;}}#endif#ifndef NDEBUGDEBUG4 (("pivrow [IN]: "ID"\n", pivrow [IN])) ;UMF_dump_rowcol (0, Numeric, Work, pivrow [IN], TRUE) ;#endifif (pivrow [IN] != EMPTY){/* the row merge candidate row is not pivrow [IN] */freebie [IN] = (pivrow [IN] == prior_pivrow [IN]) && (cdeg1 > 0) ;ASSERT (cdeg1 >= 0) ;if (!freebie [IN]){/* include current front in the degree of this row */Fcpos = Work->Fcpos ;fncols = Work->fncols ;Wrpflag = Work->Wrpflag ;/* -------------------------------------------------------------- *//* construct the pattern of the IN row *//* -------------------------------------------------------------- */#ifndef NDEBUG/* check Fcols */DEBUG5 (("ROW ASSEMBLE: rdeg "ID"\nREDUCE ROW "ID"\n",fncols, pivrow [IN])) ;for (j = 0 ; j < fncols ; j++){col = Work->Fcols [j] ;ASSERT (col >= 0 && col < Work->n_col) ;ASSERT (Fcpos [col] >= 0) ;}if (UMF_debug > 0 || Work->n_col < 1000){Int cnt = fncols ;for (col = 0 ; col < Work->n_col ; col++){if (Fcpos [col] < 0) cnt++ ;}ASSERT (cnt == Work->n_col) ;}#endifrdeg [IN] = fncols ;ASSERT (pivrow [IN] >= 0 && pivrow [IN] < Work->n_row) ;ASSERT (NON_PIVOTAL_ROW (pivrow [IN])) ;/* add the pivot column itself */ASSERT (Wrp [pivcol] != Wrpflag) ;if (Fcpos [pivcol] < 0){DEBUG3 (("Adding pivot col to pivrow [IN] pattern\n")) ;if (rdeg [IN] >= max_rdeg){/* :: pattern change (in) :: */return (UMFPACK_ERROR_different_pattern) ;}Wrp [pivcol] = Wrpflag ;W_i [rdeg [IN]++] = pivcol ;}tpi = Row_tuples [pivrow [IN]] ;if (tpi){tp = (Tuple *) (Memory + tpi) ;tp1 = tp ;tp2 = tp ;tpend = tp + Row_tlen [pivrow [IN]] ;for ( ; tp < tpend ; tp++){e = tp->e ;ASSERT (e > 0 && e <= Work->nel) ;if (!E [e]){continue ; /* element already deallocated */}f = tp->f ;p = Memory + E [e] ;ep = (Element *) p ;p += UNITS (Element, 1) ;Cols = (Int *) p ;ncols = ep->ncols ;Rows = Cols + ncols ;if (Rows [f] == EMPTY){continue ; /* row already assembled */}ASSERT (pivrow [IN] == Rows [f]) ;for (j = 0 ; j < ncols ; j++){col = Cols [j] ;ASSERT (col >= EMPTY && col < Work->n_col) ;if ((col >= 0) && (Wrp [col] != Wrpflag)&& Fcpos [col] <0){ASSERT (NON_PIVOTAL_COL (col)) ;if (rdeg [IN] >= max_rdeg){/* :: pattern change (rdeg in failure) :: */DEBUGm4 (("rdeg [IN] >= max_rdeg failure\n")) ;return (UMFPACK_ERROR_different_pattern) ;}Wrp [col] = Wrpflag ;W_i [rdeg [IN]++] = col ;}}*tp2++ = *tp ; /* leave the tuple in the list */}Row_tlen [pivrow [IN]] = tp2 - tp1 ;}#ifndef NDEBUGDEBUG4 (("Reduced IN row:\n")) ;for (j = 0 ; j < fncols ; j++){DEBUG6 ((" "ID" "ID" "ID"\n",j, Work->Fcols [j], Fcpos [Work->Fcols [j]])) ;ASSERT (Fcpos [Work->Fcols [j]] >= 0) ;}for (j = fncols ; j < rdeg [IN] ; j++){DEBUG6 ((" "ID" "ID" "ID"\n", j, W_i [j], Wrp [W_i [j]]));ASSERT (W_i [j] >= 0 && W_i [j] < Work->n_col) ;ASSERT (Wrp [W_i [j]] == Wrpflag) ;}/* mark the end of the pattern in case we scan it by mistake *//* Note that this means W_i must be of size >= fncols_max + 1 */W_i [rdeg [IN]] = EMPTY ;#endif/* rdeg [IN] is now the exact degree of the IN row *//* clear Work->Wrp. */Work->Wrpflag++ ;/* All Wrp [0..n_col] is now < Wrpflag */}}#ifndef NDEBUG/* check Wrp */if (UMF_debug > 0 || Work->n_col < 1000){for (i = 0 ; i < Work->n_col ; i++){ASSERT (Wrp [i] < Work->Wrpflag) ;}}#endif/* ---------------------------------------------------------------------- *//* construct the candidate row not in the front, if any *//* ---------------------------------------------------------------------- */#ifndef NDEBUGDEBUG4 (("pivrow [OUT]: "ID"\n", pivrow [OUT])) ;UMF_dump_rowcol (0, Numeric, Work, pivrow [OUT], TRUE) ;#endif/* If this is a candidate row from the row merge set, force it to be *//* scanned (ignore prior_pivrow [OUT]). */if (pivrow [OUT] != EMPTY){freebie [OUT] = (pivrow [OUT] == prior_pivrow [OUT]) && cdeg1 > 0 ;ASSERT (cdeg1 >= 0) ;if (!freebie [OUT]){Wrpflag = Work->Wrpflag ;/* -------------------------------------------------------------- *//* construct the pattern of the row *//* -------------------------------------------------------------- */rdeg [OUT] = 0 ;ASSERT (pivrow [OUT] >= 0 && pivrow [OUT] < Work->n_row) ;ASSERT (NON_PIVOTAL_ROW (pivrow [OUT])) ;/* add the pivot column itself */ASSERT (Wrp [pivcol] != Wrpflag) ;DEBUG3 (("Adding pivot col to pivrow [OUT] pattern\n")) ;if (rdeg [OUT] >= max_rdeg){/* :: pattern change (out) :: */return (UMFPACK_ERROR_different_pattern) ;}Wrp [pivcol] = Wrpflag ;W_o [rdeg [OUT]++] = pivcol ;tpi = Row_tuples [pivrow [OUT]] ;if (tpi){tp = (Tuple *) (Memory + tpi) ;tp1 = tp ;tp2 = tp ;tpend = tp + Row_tlen [pivrow [OUT]] ;for ( ; tp < tpend ; tp++){e = tp->e ;ASSERT (e > 0 && e <= Work->nel) ;if (!E [e]){continue ; /* element already deallocated */}f = tp->f ;p = Memory + E [e] ;ep = (Element *) p ;p += UNITS (Element, 1) ;Cols = (Int *) p ;ncols = ep->ncols ;Rows = Cols + ncols ;if (Rows [f] == EMPTY){continue ; /* row already assembled */}ASSERT (pivrow [OUT] == Rows [f]) ;for (j = 0 ; j < ncols ; j++){col = Cols [j] ;ASSERT (col >= EMPTY && col < Work->n_col) ;if ((col >= 0) && (Wrp [col] != Wrpflag)){ASSERT (NON_PIVOTAL_COL (col)) ;if (rdeg [OUT] >= max_rdeg){/* :: pattern change (rdeg out failure) :: */DEBUGm4 (("rdeg [OUT] failure\n")) ;return (UMFPACK_ERROR_different_pattern) ;}Wrp [col] = Wrpflag ;W_o [rdeg [OUT]++] = col ;}}*tp2++ = *tp ; /* leave the tuple in the list */}Row_tlen [pivrow [OUT]] = tp2 - tp1 ;}#ifndef NDEBUGDEBUG4 (("Reduced row OUT:\n")) ;for (j = 0 ; j < rdeg [OUT] ; j++){DEBUG6 ((" "ID" "ID" "ID"\n", j, W_o [j], Wrp [W_o [j]])) ;ASSERT (W_o [j] >= 0 && W_o [j] < Work->n_col) ;ASSERT (Wrp [W_o [j]] == Wrpflag) ;}/* mark the end of the pattern in case we scan it by mistake *//* Note that this means W_o must be of size >= fncols_max + 1 */W_o [rdeg [OUT]] = EMPTY ;#endif/* rdeg [OUT] is now the exact degree of the row *//* clear Work->Wrp. */Work->Wrpflag++ ;/* All Wrp [0..n] is now < Wrpflag */}}DEBUG5 (("Row_search end ] \n")) ;#ifndef NDEBUG/* check Wrp */if (UMF_debug > 0 || Work->n_col < 1000){for (i = 0 ; i < Work->n_col ; i++){ASSERT (Wrp [i] < Work->Wrpflag) ;}}#endifreturn (UMFPACK_OK) ;}