Ejemplo n.º 1
0
void
cgstrf (superlu_options_t *options, SuperMatrix *A, float drop_tol,
        int relax, int panel_size, int *etree, void *work, int lwork,
        int *perm_c, int *perm_r, SuperMatrix *L, SuperMatrix *U,
        SuperLUStat_t *stat, int *info)
{
    /* Local working arrays */
    NCPformat *Astore;
    int       *iperm_r = NULL; /* inverse of perm_r; used when 
                                  options->Fact == SamePattern_SameRowPerm */
    int       *iperm_c; /* inverse of perm_c */
    int       *iwork;
    complex    *cwork;
    int	      *segrep, *repfnz, *parent, *xplore;
    int	      *panel_lsub; /* dense[]/panel_lsub[] pair forms a w-wide SPA */
    int	      *xprune;
    int	      *marker;
    complex    *dense, *tempv;
    int       *relax_end;
    complex    *a;
    int       *asub;
    int       *xa_begin, *xa_end;
    int       *xsup, *supno;
    int       *xlsub, *xlusup, *xusub;
    int       nzlumax;
    static GlobalLU_t Glu; /* persistent to facilitate multiple factors. */

    /* Local scalars */
    fact_t    fact = options->Fact;
    double    diag_pivot_thresh = options->DiagPivotThresh;
    int       pivrow;   /* pivotal row number in the original matrix A */
    int       nseg1;	/* no of segments in U-column above panel row jcol */
    int       nseg;	/* no of segments in each U-column */
    register int jcol;	
    register int kcol;	/* end column of a relaxed snode */
    register int icol;
    register int i, k, jj, new_next, iinfo;
    int       m, n, min_mn, jsupno, fsupc, nextlu, nextu;
    int       w_def;	/* upper bound on panel width */
    int       usepr, iperm_r_allocated = 0;
    int       nnzL, nnzU;
    int       *panel_histo = stat->panel_histo;
    flops_t   *ops = stat->ops;

    iinfo    = 0;
    m        = A->nrow;
    n        = A->ncol;
    min_mn   = SUPERLU_MIN(m, n);
    Astore   = A->Store;
    a        = Astore->nzval;
    asub     = Astore->rowind;
    xa_begin = Astore->colbeg;
    xa_end   = Astore->colend;

    /* Allocate storage common to the factor routines */
    *info = cLUMemInit(fact, work, lwork, m, n, Astore->nnz,
                       panel_size, L, U, &Glu, &iwork, &cwork);
    if ( *info ) return;
    
    xsup    = Glu.xsup;
    supno   = Glu.supno;
    xlsub   = Glu.xlsub;
    xlusup  = Glu.xlusup;
    xusub   = Glu.xusub;
    
    SetIWork(m, n, panel_size, iwork, &segrep, &parent, &xplore,
	     &repfnz, &panel_lsub, &xprune, &marker);
    cSetRWork(m, panel_size, cwork, &dense, &tempv);
    
    usepr = (fact == SamePattern_SameRowPerm);
    if ( usepr ) {
	/* Compute the inverse of perm_r */
	iperm_r = (int *) intMalloc(m);
	for (k = 0; k < m; ++k) iperm_r[perm_r[k]] = k;
	iperm_r_allocated = 1;
    }
    iperm_c = (int *) intMalloc(n);
    for (k = 0; k < n; ++k) iperm_c[perm_c[k]] = k;

    /* Identify relaxed snodes */
    relax_end = (int *) intMalloc(n);
    if ( options->SymmetricMode == YES ) {
        heap_relax_snode(n, etree, relax, marker, relax_end); 
    } else {
        relax_snode(n, etree, relax, marker, relax_end); 
    }
    
    ifill (perm_r, m, EMPTY);
    ifill (marker, m * NO_MARKER, EMPTY);
    supno[0] = -1;
    xsup[0]  = xlsub[0] = xusub[0] = xlusup[0] = 0;
    w_def    = panel_size;

    /* 
     * Work on one "panel" at a time. A panel is one of the following: 
     *	   (a) a relaxed supernode at the bottom of the etree, or
     *	   (b) panel_size contiguous columns, defined by the user
     */
    for (jcol = 0; jcol < min_mn; ) {

	if ( relax_end[jcol] != EMPTY ) { /* start of a relaxed snode */
   	    kcol = relax_end[jcol];	  /* end of the relaxed snode */
	    panel_histo[kcol-jcol+1]++;

	    /* --------------------------------------
	     * Factorize the relaxed supernode(jcol:kcol) 
	     * -------------------------------------- */
	    /* Determine the union of the row structure of the snode */
	    if ( (*info = csnode_dfs(jcol, kcol, asub, xa_begin, xa_end,
				    xprune, marker, &Glu)) != 0 )
		return;

            nextu    = xusub[jcol];
	    nextlu   = xlusup[jcol];
	    jsupno   = supno[jcol];
	    fsupc    = xsup[jsupno];
	    new_next = nextlu + (xlsub[fsupc+1]-xlsub[fsupc])*(kcol-jcol+1);
	    nzlumax = Glu.nzlumax;
	    while ( new_next > nzlumax ) {
		*info = cLUMemXpand(jcol, nextlu, LUSUP, &nzlumax, &Glu);
		if ( (*info) )
		    return;
	    }
    
	    for (icol = jcol; icol<= kcol; icol++) {
		xusub[icol+1] = nextu;
		
    		/* Scatter into SPA dense[*] */
    		for (k = xa_begin[icol]; k < xa_end[icol]; k++)
        	    dense[asub[k]] = a[k];

	       	/* Numeric update within the snode */
	        csnode_bmod(icol, jsupno, fsupc, dense, tempv, &Glu, stat);


		*info = cpivotL(icol, diag_pivot_thresh, &usepr, perm_r,iperm_r, iperm_c, &pivrow, &Glu, stat);
		if ( (*info) )
		    if ( iinfo == 0 ) iinfo = *info;
		
#ifdef DEBUG
		cprint_lu_col("[1]: ", icol, pivrow, xprune, &Glu);
#endif

	    }

	    jcol = icol;

	} else { /* Work on one panel of panel_size columns */
	    
	    /* Adjust panel_size so that a panel won't overlap with the next 
	     * relaxed snode.
	     */
	    panel_size = w_def;
	    for (k = jcol + 1; k < SUPERLU_MIN(jcol+panel_size, min_mn); k++) 
		if ( relax_end[k] != EMPTY ) {
		    panel_size = k - jcol;
		    break;
		}
	    if ( k == min_mn ) panel_size = min_mn - jcol;
	    panel_histo[panel_size]++;

	    /* symbolic factor on a panel of columns */
	    cpanel_dfs(m, panel_size, jcol, A, perm_r, &nseg1,
		      dense, panel_lsub, segrep, repfnz, xprune,
		      marker, parent, xplore, &Glu);
	    
	    /* numeric sup-panel updates in topological order */
	    cpanel_bmod(m, panel_size, jcol, nseg1, dense,
		        tempv, segrep, repfnz, &Glu, stat);
	    
	    /* Sparse LU within the panel, and below panel diagonal */
    	    for ( jj = jcol; jj < jcol + panel_size; jj++) {
 		k = (jj - jcol) * m; /* column index for w-wide arrays */

		nseg = nseg1;	/* Begin after all the panel segments */

	    	if ((*info = ccolumn_dfs(m, jj, perm_r, &nseg, &panel_lsub[k],
					segrep, &repfnz[k], xprune, marker,
					parent, xplore, &Glu)) != 0) return;

	      	/* Numeric updates */
	    	if ((*info = ccolumn_bmod(jj, (nseg - nseg1), &dense[k],
					 tempv, &segrep[nseg1], &repfnz[k],
					 jcol, &Glu, stat)) != 0) return;
		
	        /* Copy the U-segments to ucol[*] */
		if ((*info = ccopy_to_ucol(jj, nseg, segrep, &repfnz[k],
					  perm_r, &dense[k], &Glu)) != 0)
		    return;

			*info = cpivotL(jj, diag_pivot_thresh, &usepr, perm_r,iperm_r, iperm_c, &pivrow, &Glu, stat);
	    	if ( (*info) )
		    if ( iinfo == 0 ) iinfo = *info;

		/* Prune columns (0:jj-1) using column jj */
	    	cpruneL(jj, perm_r, pivrow, nseg, segrep,
                        &repfnz[k], xprune, &Glu);

		/* Reset repfnz[] for this column */
	    	resetrep_col (nseg, segrep, &repfnz[k]);
		
#ifdef DEBUG
		cprint_lu_col("[2]: ", jj, pivrow, xprune, &Glu);
#endif

	    }

   	    jcol += panel_size;	/* Move to the next panel */

	} /* else */

    } /* for */

    *info = iinfo;
    
    if ( m > n ) {
	k = 0;
        for (i = 0; i < m; ++i) 
            if ( perm_r[i] == EMPTY ) {
    		perm_r[i] = n + k;
		++k;
	    }
    }

    countnz(min_mn, xprune, &nnzL, &nnzU, &Glu);
    fixupL(min_mn, perm_r, &Glu);

    cLUWorkFree(iwork, cwork, &Glu); /* Free work space and compress storage */

    if ( fact == SamePattern_SameRowPerm ) {
        /* L and U structures may have changed due to possibly different
	   pivoting, even though the storage is available.
	   There could also be memory expansions, so the array locations
           may have changed, */
        ((SCformat *)L->Store)->nnz = nnzL;
	((SCformat *)L->Store)->nsuper = Glu.supno[n];
	((SCformat *)L->Store)->nzval = Glu.lusup;
	((SCformat *)L->Store)->nzval_colptr = Glu.xlusup;
	((SCformat *)L->Store)->rowind = Glu.lsub;
	((SCformat *)L->Store)->rowind_colptr = Glu.xlsub;
	((NCformat *)U->Store)->nnz = nnzU;
	((NCformat *)U->Store)->nzval = Glu.ucol;
	((NCformat *)U->Store)->rowind = Glu.usub;
	((NCformat *)U->Store)->colptr = Glu.xusub;
    } else {
        cCreate_SuperNode_Matrix(L, A->nrow, min_mn, nnzL, Glu.lusup, 
	                         Glu.xlusup, Glu.lsub, Glu.xlsub, Glu.supno,
			         Glu.xsup, SLU_SC, SLU_C, SLU_TRLU);
    	cCreate_CompCol_Matrix(U, min_mn, min_mn, nnzU, Glu.ucol, 
			       Glu.usub, Glu.xusub, SLU_NC, SLU_C, SLU_TRU);
    }
    
    ops[FACT] += ops[TRSV] + ops[GEMV];	
    
    if ( iperm_r_allocated ) SUPERLU_FREE (iperm_r);
    SUPERLU_FREE (iperm_c);
    SUPERLU_FREE (relax_end);

}
Ejemplo n.º 2
0
void
cgsitrf(superlu_options_t *options, SuperMatrix *A, int relax, int panel_size,
	int *etree, void *work, int lwork, int *perm_c, int *perm_r,
	SuperMatrix *L, SuperMatrix *U, SuperLUStat_t *stat, int *info)
{
    /* Local working arrays */
    NCPformat *Astore;
    int       *iperm_r = NULL; /* inverse of perm_r; used when
				  options->Fact == SamePattern_SameRowPerm */
    int       *iperm_c; /* inverse of perm_c */
    int       *swap, *iswap; /* swap is used to store the row permutation
				during the factorization. Initially, it is set
				to iperm_c (row indeces of Pc*A*Pc').
				iswap is the inverse of swap. After the
				factorization, it is equal to perm_r. */
    int       *iwork;
    complex   *cwork;
    int       *segrep, *repfnz, *parent, *xplore;
    int       *panel_lsub; /* dense[]/panel_lsub[] pair forms a w-wide SPA */
    int       *marker, *marker_relax;
    complex    *dense, *tempv;
    float *stempv;
    int       *relax_end, *relax_fsupc;
    complex    *a;
    int       *asub;
    int       *xa_begin, *xa_end;
    int       *xsup, *supno;
    int       *xlsub, *xlusup, *xusub;
    int       nzlumax;
    float    *amax; 
    complex    drop_sum;
    static GlobalLU_t Glu; /* persistent to facilitate multiple factors. */
    int       *iwork2;	   /* used by the second dropping rule */

    /* Local scalars */
    fact_t    fact = options->Fact;
    double    diag_pivot_thresh = options->DiagPivotThresh;
    double    drop_tol = options->ILU_DropTol; /* tau */
    double    fill_ini = options->ILU_FillTol; /* tau^hat */
    double    gamma = options->ILU_FillFactor;
    int       drop_rule = options->ILU_DropRule;
    milu_t    milu = options->ILU_MILU;
    double    fill_tol;
    int       pivrow;	/* pivotal row number in the original matrix A */
    int       nseg1;	/* no of segments in U-column above panel row jcol */
    int       nseg;	/* no of segments in each U-column */
    register int jcol;
    register int kcol;	/* end column of a relaxed snode */
    register int icol;
    register int i, k, jj, new_next, iinfo;
    int       m, n, min_mn, jsupno, fsupc, nextlu, nextu;
    int       w_def;	/* upper bound on panel width */
    int       usepr, iperm_r_allocated = 0;
    int       nnzL, nnzU;
    int       *panel_histo = stat->panel_histo;
    flops_t   *ops = stat->ops;

    int       last_drop;/* the last column which the dropping rules applied */
    int       quota;
    int       nnzAj;	/* number of nonzeros in A(:,1:j) */
    int       nnzLj, nnzUj;
    double    tol_L = drop_tol, tol_U = drop_tol;
    complex zero = {0.0, 0.0};

    /* Executable */	   
    iinfo    = 0;
    m	     = A->nrow;
    n	     = A->ncol;
    min_mn   = SUPERLU_MIN(m, n);
    Astore   = A->Store;
    a	     = Astore->nzval;
    asub     = Astore->rowind;
    xa_begin = Astore->colbeg;
    xa_end   = Astore->colend;

    /* Allocate storage common to the factor routines */
    *info = cLUMemInit(fact, work, lwork, m, n, Astore->nnz, panel_size,
		       gamma, L, U, &Glu, &iwork, &cwork);
    if ( *info ) return;

    xsup    = Glu.xsup;
    supno   = Glu.supno;
    xlsub   = Glu.xlsub;
    xlusup  = Glu.xlusup;
    xusub   = Glu.xusub;

    SetIWork(m, n, panel_size, iwork, &segrep, &parent, &xplore,
	     &repfnz, &panel_lsub, &marker_relax, &marker);
    cSetRWork(m, panel_size, cwork, &dense, &tempv);

    usepr = (fact == SamePattern_SameRowPerm);
    if ( usepr ) {
	/* Compute the inverse of perm_r */
	iperm_r = (int *) intMalloc(m);
	for (k = 0; k < m; ++k) iperm_r[perm_r[k]] = k;
	iperm_r_allocated = 1;
    }

    iperm_c = (int *) intMalloc(n);
    for (k = 0; k < n; ++k) iperm_c[perm_c[k]] = k;
    swap = (int *)intMalloc(n);
    for (k = 0; k < n; k++) swap[k] = iperm_c[k];
    iswap = (int *)intMalloc(n);
    for (k = 0; k < n; k++) iswap[k] = perm_c[k];
    amax = (float *) floatMalloc(panel_size);
    if (drop_rule & DROP_SECONDARY)
	iwork2 = (int *)intMalloc(n);
    else
	iwork2 = NULL;

    nnzAj = 0;
    nnzLj = 0;
    nnzUj = 0;
    last_drop = SUPERLU_MAX(min_mn - 2 * sp_ienv(3), (int)(min_mn * 0.95));

    /* Identify relaxed snodes */
    relax_end = (int *) intMalloc(n);
    relax_fsupc = (int *) intMalloc(n);
    if ( options->SymmetricMode == YES )
	ilu_heap_relax_snode(n, etree, relax, marker, relax_end, relax_fsupc);
    else
	ilu_relax_snode(n, etree, relax, marker, relax_end, relax_fsupc);

    ifill (perm_r, m, EMPTY);
    ifill (marker, m * NO_MARKER, EMPTY);
    supno[0] = -1;
    xsup[0]  = xlsub[0] = xusub[0] = xlusup[0] = 0;
    w_def    = panel_size;

    /* Mark the rows used by relaxed supernodes */
    ifill (marker_relax, m, EMPTY);
    i = mark_relax(m, relax_end, relax_fsupc, xa_begin, xa_end,
	         asub, marker_relax);
#if ( PRNTlevel >= 1)
    printf("%d relaxed supernodes.\n", i);
#endif

    /*
     * Work on one "panel" at a time. A panel is one of the following:
     *	   (a) a relaxed supernode at the bottom of the etree, or
     *	   (b) panel_size contiguous columns, defined by the user
     */
    for (jcol = 0; jcol < min_mn; ) {

	if ( relax_end[jcol] != EMPTY ) { /* start of a relaxed snode */
	    kcol = relax_end[jcol];	  /* end of the relaxed snode */
	    panel_histo[kcol-jcol+1]++;

	    /* Drop small rows in the previous supernode. */
	    if (jcol > 0 && jcol < last_drop) {
		int first = xsup[supno[jcol - 1]];
		int last = jcol - 1;
		int quota;

		/* Compute the quota */
		if (drop_rule & DROP_PROWS)
		    quota = gamma * Astore->nnz / m * (m - first) / m
			    * (last - first + 1);
		else if (drop_rule & DROP_COLUMN) {
		    int i;
		    quota = 0;
		    for (i = first; i <= last; i++)
			quota += xa_end[i] - xa_begin[i];
		    quota = gamma * quota * (m - first) / m;
		} else if (drop_rule & DROP_AREA)
		    quota = gamma * nnzAj * (1.0 - 0.5 * (last + 1.0) / m)
			    - nnzLj;
		else
		    quota = m * n;
		fill_tol = pow(fill_ini, 1.0 - 0.5 * (first + last) / min_mn);

		/* Drop small rows */
                stempv = (float *) tempv;
		i = ilu_cdrop_row(options, first, last, tol_L, quota, &nnzLj,
				  &fill_tol, &Glu, stempv, iwork2, 0);
		/* Reset the parameters */
		if (drop_rule & DROP_DYNAMIC) {
		    if (gamma * nnzAj * (1.0 - 0.5 * (last + 1.0) / m)
			     < nnzLj)
			tol_L = SUPERLU_MIN(1.0, tol_L * 2.0);
		    else
			tol_L = SUPERLU_MAX(drop_tol, tol_L * 0.5);
		}
		if (fill_tol < 0) iinfo -= (int)fill_tol;
#ifdef DEBUG
		num_drop_L += i * (last - first + 1);
#endif
	    }

	    /* --------------------------------------
	     * Factorize the relaxed supernode(jcol:kcol)
	     * -------------------------------------- */
	    /* Determine the union of the row structure of the snode */
	    if ( (*info = ilu_csnode_dfs(jcol, kcol, asub, xa_begin, xa_end,
					 marker, &Glu)) != 0 )
		return;

	    nextu    = xusub[jcol];
	    nextlu   = xlusup[jcol];
	    jsupno   = supno[jcol];
	    fsupc    = xsup[jsupno];
	    new_next = nextlu + (xlsub[fsupc+1]-xlsub[fsupc])*(kcol-jcol+1);
	    nzlumax = Glu.nzlumax;
	    while ( new_next > nzlumax ) {
		if ((*info = cLUMemXpand(jcol, nextlu, LUSUP, &nzlumax, &Glu)))
		    return;
	    }

	    for (icol = jcol; icol <= kcol; icol++) {
		xusub[icol+1] = nextu;

		amax[0] = 0.0;
		/* Scatter into SPA dense[*] */
		for (k = xa_begin[icol]; k < xa_end[icol]; k++) {
                    register float tmp = slu_c_abs1 (&a[k]);
		    if (tmp > amax[0]) amax[0] = tmp;
		    dense[asub[k]] = a[k];
		}
		nnzAj += xa_end[icol] - xa_begin[icol];
		if (amax[0] == 0.0) {
		    amax[0] = fill_ini;
#if ( PRNTlevel >= 1)
		    printf("Column %d is entirely zero!\n", icol);
		    fflush(stdout);
#endif
		}

		/* Numeric update within the snode */
		csnode_bmod(icol, jsupno, fsupc, dense, tempv, &Glu, stat);

		if (usepr) pivrow = iperm_r[icol];
		fill_tol = pow(fill_ini, 1.0 - (double)icol / (double)min_mn);
		if ( (*info = ilu_cpivotL(icol, diag_pivot_thresh, &usepr,
					  perm_r, iperm_c[icol], swap, iswap,
					  marker_relax, &pivrow,
                                          amax[0] * fill_tol, milu, zero,
                                          &Glu, stat)) ) {
		    iinfo++;
		    marker[pivrow] = kcol;
		}

	    }

	    jcol = kcol + 1;

	} else { /* Work on one panel of panel_size columns */

	    /* Adjust panel_size so that a panel won't overlap with the next
	     * relaxed snode.
	     */
	    panel_size = w_def;
	    for (k = jcol + 1; k < SUPERLU_MIN(jcol+panel_size, min_mn); k++)
		if ( relax_end[k] != EMPTY ) {
		    panel_size = k - jcol;
		    break;
		}
	    if ( k == min_mn ) panel_size = min_mn - jcol;
	    panel_histo[panel_size]++;

	    /* symbolic factor on a panel of columns */
	    ilu_cpanel_dfs(m, panel_size, jcol, A, perm_r, &nseg1,
                          dense, amax, panel_lsub, segrep, repfnz,
                          marker, parent, xplore, &Glu);

	    /* numeric sup-panel updates in topological order */
	    cpanel_bmod(m, panel_size, jcol, nseg1, dense,
			tempv, segrep, repfnz, &Glu, stat);

	    /* Sparse LU within the panel, and below panel diagonal */
	    for (jj = jcol; jj < jcol + panel_size; jj++) {

		k = (jj - jcol) * m; /* column index for w-wide arrays */

		nseg = nseg1;	/* Begin after all the panel segments */

		nnzAj += xa_end[jj] - xa_begin[jj];

		if ((*info = ilu_ccolumn_dfs(m, jj, perm_r, &nseg,
					     &panel_lsub[k], segrep, &repfnz[k],
					     marker, parent, xplore, &Glu)))
		    return;

		/* Numeric updates */
		if ((*info = ccolumn_bmod(jj, (nseg - nseg1), &dense[k],
					  tempv, &segrep[nseg1], &repfnz[k],
					  jcol, &Glu, stat)) != 0) return;

		/* Make a fill-in position if the column is entirely zero */
		if (xlsub[jj + 1] == xlsub[jj]) {
		    register int i, row;
		    int nextl;
		    int nzlmax = Glu.nzlmax;
		    int *lsub = Glu.lsub;
		    int *marker2 = marker + 2 * m;

		    /* Allocate memory */
		    nextl = xlsub[jj] + 1;
		    if (nextl >= nzlmax) {
			int error = cLUMemXpand(jj, nextl, LSUB, &nzlmax, &Glu);
			if (error) { *info = error; return; }
			lsub = Glu.lsub;
		    }
		    xlsub[jj + 1]++;
		    assert(xlusup[jj]==xlusup[jj+1]);
		    xlusup[jj + 1]++;
		    Glu.lusup[xlusup[jj]] = zero;

		    /* Choose a row index (pivrow) for fill-in */
		    for (i = jj; i < n; i++)
			if (marker_relax[swap[i]] <= jj) break;
		    row = swap[i];
		    marker2[row] = jj;
		    lsub[xlsub[jj]] = row;
#ifdef DEBUG
		    printf("Fill col %d.\n", jj);
		    fflush(stdout);
#endif
		}

		/* Computer the quota */
		if (drop_rule & DROP_PROWS)
		    quota = gamma * Astore->nnz / m * jj / m;
		else if (drop_rule & DROP_COLUMN)
		    quota = gamma * (xa_end[jj] - xa_begin[jj]) *
			    (jj + 1) / m;
		else if (drop_rule & DROP_AREA)
		    quota = gamma * 0.9 * nnzAj * 0.5 - nnzUj;
		else
		    quota = m;

		/* Copy the U-segments to ucol[*] and drop small entries */
		if ((*info = ilu_ccopy_to_ucol(jj, nseg, segrep, &repfnz[k],
					       perm_r, &dense[k], drop_rule,
					       milu, amax[jj - jcol] * tol_U,
					       quota, &drop_sum, &nnzUj, &Glu,
					       iwork2)) != 0)
		    return;

		/* Reset the dropping threshold if required */
		if (drop_rule & DROP_DYNAMIC) {
		    if (gamma * 0.9 * nnzAj * 0.5 < nnzLj)
			tol_U = SUPERLU_MIN(1.0, tol_U * 2.0);
		    else
			tol_U = SUPERLU_MAX(drop_tol, tol_U * 0.5);
		}

                cs_mult(&drop_sum, &drop_sum, MILU_ALPHA);
		if (usepr) pivrow = iperm_r[jj];
		fill_tol = pow(fill_ini, 1.0 - (double)jj / (double)min_mn);
		if ( (*info = ilu_cpivotL(jj, diag_pivot_thresh, &usepr, perm_r,
					  iperm_c[jj], swap, iswap,
					  marker_relax, &pivrow,
					  amax[jj - jcol] * fill_tol, milu,
					  drop_sum, &Glu, stat)) ) {
		    iinfo++;
		    marker[m + pivrow] = jj;
		    marker[2 * m + pivrow] = jj;
		}

		/* Reset repfnz[] for this column */
		resetrep_col (nseg, segrep, &repfnz[k]);

		/* Start a new supernode, drop the previous one */
		if (jj > 0 && supno[jj] > supno[jj - 1] && jj < last_drop) {
		    int first = xsup[supno[jj - 1]];
		    int last = jj - 1;
		    int quota;

		    /* Compute the quota */
		    if (drop_rule & DROP_PROWS)
			quota = gamma * Astore->nnz / m * (m - first) / m
				* (last - first + 1);
		    else if (drop_rule & DROP_COLUMN) {
			int i;
			quota = 0;
			for (i = first; i <= last; i++)
			    quota += xa_end[i] - xa_begin[i];
			quota = gamma * quota * (m - first) / m;
		    } else if (drop_rule & DROP_AREA)
			quota = gamma * nnzAj * (1.0 - 0.5 * (last + 1.0)
				/ m) - nnzLj;
		    else
			quota = m * n;
		    fill_tol = pow(fill_ini, 1.0 - 0.5 * (first + last) /
			    (double)min_mn);

		    /* Drop small rows */
                    stempv = (float *) tempv;
		    i = ilu_cdrop_row(options, first, last, tol_L, quota,
				      &nnzLj, &fill_tol, &Glu, stempv, iwork2,
				      1);

		    /* Reset the parameters */
		    if (drop_rule & DROP_DYNAMIC) {
			if (gamma * nnzAj * (1.0 - 0.5 * (last + 1.0) / m)
				< nnzLj)
			    tol_L = SUPERLU_MIN(1.0, tol_L * 2.0);
			else
			    tol_L = SUPERLU_MAX(drop_tol, tol_L * 0.5);
		    }
		    if (fill_tol < 0) iinfo -= (int)fill_tol;
#ifdef DEBUG
		    num_drop_L += i * (last - first + 1);
#endif
		} /* if start a new supernode */

	    } /* for */

	    jcol += panel_size; /* Move to the next panel */

	} /* else */

    } /* for */

    *info = iinfo;

    if ( m > n ) {
	k = 0;
	for (i = 0; i < m; ++i)
	    if ( perm_r[i] == EMPTY ) {
		perm_r[i] = n + k;
		++k;
	    }
    }

    ilu_countnz(min_mn, &nnzL, &nnzU, &Glu);
    fixupL(min_mn, perm_r, &Glu);

    cLUWorkFree(iwork, cwork, &Glu); /* Free work space and compress storage */

    if ( fact == SamePattern_SameRowPerm ) {
	/* L and U structures may have changed due to possibly different
	   pivoting, even though the storage is available.
	   There could also be memory expansions, so the array locations
	   may have changed, */
	((SCformat *)L->Store)->nnz = nnzL;
	((SCformat *)L->Store)->nsuper = Glu.supno[n];
	((SCformat *)L->Store)->nzval = Glu.lusup;
	((SCformat *)L->Store)->nzval_colptr = Glu.xlusup;
	((SCformat *)L->Store)->rowind = Glu.lsub;
	((SCformat *)L->Store)->rowind_colptr = Glu.xlsub;
	((NCformat *)U->Store)->nnz = nnzU;
	((NCformat *)U->Store)->nzval = Glu.ucol;
	((NCformat *)U->Store)->rowind = Glu.usub;
	((NCformat *)U->Store)->colptr = Glu.xusub;
    } else {
	cCreate_SuperNode_Matrix(L, A->nrow, min_mn, nnzL, Glu.lusup,
				 Glu.xlusup, Glu.lsub, Glu.xlsub, Glu.supno,
				 Glu.xsup, SLU_SC, SLU_C, SLU_TRLU);
	cCreate_CompCol_Matrix(U, min_mn, min_mn, nnzU, Glu.ucol,
			       Glu.usub, Glu.xusub, SLU_NC, SLU_C, SLU_TRU);
    }

    ops[FACT] += ops[TRSV] + ops[GEMV];

    if ( iperm_r_allocated ) SUPERLU_FREE (iperm_r);
    SUPERLU_FREE (iperm_c);
    SUPERLU_FREE (relax_end);
    SUPERLU_FREE (swap);
    SUPERLU_FREE (iswap);
    SUPERLU_FREE (relax_fsupc);
    SUPERLU_FREE (amax);
    if ( iwork2 ) SUPERLU_FREE (iwork2);

}