/* ========================================================================== */ /* === Core/cholmod_common ================================================== */ /* ========================================================================== */ /* ----------------------------------------------------------------------------- * CHOLMOD/Core Module. Copyright (C) 2005-2006, * Univ. of Florida. Author: Timothy A. Davis * The CHOLMOD/Core Module is licensed under Version 2.1 of the GNU * Lesser General Public License. See lesser.txt for a text of the license. * CHOLMOD is also available under other licenses; contact authors for details. * http://www.cise.ufl.edu/research/sparse * -------------------------------------------------------------------------- */ /* Core utility routines for the cholmod_common object: * * Primary routines: * ----------------- * cholmod_start the first call to CHOLMOD * cholmod_finish the last call to CHOLMOD * * Secondary routines: * ------------------- * cholmod_defaults restore (most) default control parameters * cholmod_allocate_work allocate (or reallocate) workspace in Common * cholmod_free_work free workspace in Common * cholmod_clear_flag clear Common->Flag in workspace * cholmod_maxrank column dimension of Common->Xwork workspace * * The Common object is unique. It cannot be allocated or deallocated by * CHOLMOD, since it contains the definition of the memory management routines * used (pointers to malloc, free, realloc, and calloc, or their equivalent). * The Common object contains workspace that is used between calls to * CHOLMOD routines. This workspace allocated by CHOLMOD as needed, by * cholmod_allocate_work and cholmod_free_work. */ #include "cholmod_internal.h" #include "cholmod_core.h" /* ========================================================================== */ /* === cholmod_start ======================================================== */ /* ========================================================================== */ /* Initialize Common default parameters and statistics. Sets workspace * pointers to NULL. * * This routine must be called just once, prior to calling any other CHOLMOD * routine. Do not call this routine after any other CHOLMOD routine (except * cholmod_finish, to start a new CHOLMOD session), or a memory leak will * occur. * * workspace: none */ int CHOLMOD(start) ( cholmod_common *Common ) { int k ; if (Common == NULL) { return (FALSE) ; } /* ---------------------------------------------------------------------- */ /* user error handling routine */ /* ---------------------------------------------------------------------- */ Common->error_handler = NULL ; /* ---------------------------------------------------------------------- */ /* integer and numerical types */ /* ---------------------------------------------------------------------- */ Common->itype = ITYPE ; Common->dtype = DTYPE ; /* ---------------------------------------------------------------------- */ /* default control parameters */ /* ---------------------------------------------------------------------- */ cholmod_l_defaults(Common) ; Common->try_catch = FALSE ; /* ---------------------------------------------------------------------- */ /* memory management routines */ /* ---------------------------------------------------------------------- */ /* The user can replace cholmod's memory management routines by redefining * these function pointers. */ #ifndef NMALLOC /* stand-alone ANSI C program */ Common->malloc_memory = malloc ; Common->free_memory = free ; Common->realloc_memory = realloc ; Common->calloc_memory = calloc ; #else /* no memory manager defined at compile-time; MUST define one at run-time */ Common->malloc_memory = NULL ; Common->free_memory = NULL ; Common->realloc_memory = NULL ; Common->calloc_memory = NULL ; #endif /* ---------------------------------------------------------------------- */ /* complex arithmetic routines */ /* ---------------------------------------------------------------------- */ // Common->complex_divide = CHOLMOD(divcomplex) ; // Common->hypotenuse = CHOLMOD(hypot) ; /* ---------------------------------------------------------------------- */ /* print routine */ /* ---------------------------------------------------------------------- */ #ifndef NPRINT /* stand-alone ANSI C program */ Common->print_function = printf ; #else /* printing disabled */ Common->print_function = NULL ; #endif /* ---------------------------------------------------------------------- */ /* workspace */ /* ---------------------------------------------------------------------- */ /* This code assumes the workspace held in Common is not initialized. If * it is, then a memory leak will occur because the pointers are * overwritten with NULL. */ Common->nrow = 0 ; Common->mark = EMPTY ; Common->xworksize = 0 ; Common->iworksize = 0 ; Common->Flag = NULL ; Common->Head = NULL ; Common->Iwork = NULL ; Common->Xwork = NULL ; Common->no_workspace_reallocate = FALSE ; /* ---------------------------------------------------------------------- */ /* statistics */ /* ---------------------------------------------------------------------- */ /* fl and lnz are computed in cholmod_analyze and cholmod_rowcolcounts */ Common->fl = EMPTY ; Common->lnz = EMPTY ; /* modfl is computed in cholmod_updown, cholmod_rowadd, and cholmod_rowdel*/ Common->modfl = EMPTY ; /* all routines use status as their error-report code */ Common->status = CHOLMOD_OK ; Common->malloc_count = 0 ; /* # calls to malloc minus # calls to free */ Common->memory_usage = 0 ; /* peak memory usage (in bytes) */ Common->memory_inuse = 0 ; /* current memory in use (in bytes) */ Common->nrealloc_col = 0 ; Common->nrealloc_factor = 0 ; Common->ndbounds_hit = 0 ; Common->rowfacfl = 0 ; Common->aatfl = EMPTY ; /* Common->called_nd is TRUE if cholmod_analyze called or NESDIS */ Common->called_nd = FALSE ; Common->blas_ok = TRUE ; /* false if BLAS int overflow occurs */ /* ---------------------------------------------------------------------- */ /* default SuiteSparseQR knobs and statististics */ /* ---------------------------------------------------------------------- */ for (k = 0 ; k < 4 ; k++) Common->SPQR_xstat [k] = 0 ; for (k = 0 ; k < 10 ; k++) Common->SPQR_istat [k] = 0 ; Common->SPQR_grain = 1 ; /* no Intel TBB multitasking, by default */ Common->SPQR_small = 1e6 ; /* target min task size for TBB */ Common->SPQR_shrink = 1 ; /* controls SPQR shrink realloc */ Common->SPQR_nthreads = 0 ; /* 0: let TBB decide how many threads to use */ DEBUG_INIT ("cholmod start", Common) ; return (TRUE) ; } /* ========================================================================== */ /* === cholmod_defaults ===================================================== */ /* ========================================================================== */ /* Set Common default parameters, except for the function pointers. * * workspace: none */ int CHOLMOD(defaults) ( cholmod_common *Common ) { Int i ; RETURN_IF_NULL_COMMON (FALSE) ; /* ---------------------------------------------------------------------- */ /* default control parameters */ /* ---------------------------------------------------------------------- */ Common->dbound = 0.0 ; Common->grow0 = 1.2 ; Common->grow1 = 1.2 ; Common->grow2 = 5 ; Common->maxrank = 8 ; Common->final_asis = TRUE ; Common->final_super = TRUE ; Common->final_ll = FALSE ; Common->final_pack = TRUE ; Common->final_monotonic = TRUE ; Common->final_resymbol = FALSE ; /* use simplicial factorization if flop/nnz(L) < 40, supernodal otherwise */ Common->supernodal = CHOLMOD_AUTO ; Common->supernodal_switch = 40 ; Common->nrelax [0] = 4 ; Common->nrelax [1] = 16 ; Common->nrelax [2] = 48 ; Common->zrelax [0] = 0.8 ; Common->zrelax [1] = 0.1 ; Common->zrelax [2] = 0.05 ; Common->prefer_zomplex = FALSE ; Common->prefer_upper = TRUE ; Common->prefer_binary = FALSE ; Common->quick_return_if_not_posdef = FALSE ; /* METIS workarounds */ Common->metis_memory = 0.0 ; /* > 0 for memory guard (2 is reasonable) */ Common->metis_nswitch = 3000 ; Common->metis_dswitch = 0.66 ; Common->print = 3 ; Common->precise = FALSE ; /* ---------------------------------------------------------------------- */ /* default ordering methods */ /* ---------------------------------------------------------------------- */ /* Note that if the Partition module is not installed, the CHOLMOD_METIS * and CHOLMOD_NESDIS methods will not be available. cholmod_analyze will * report the CHOLMOD_NOT_INSTALLED error, and safely skip over them. */ #if (CHOLMOD_MAXMETHODS < 9) #error "CHOLMOD_MAXMETHODS must be 9 or more (defined in cholmod_core.h)." #endif /* default strategy: try given, AMD, and then METIS if AMD reports high * fill-in. NESDIS can be used instead, if Common->default_nesdis is TRUE. */ Common->nmethods = 0 ; /* use default strategy */ Common->default_nesdis = FALSE ; /* use METIS in default strategy */ Common->current = 0 ; /* current method being tried */ Common->selected = 0 ; /* the best method selected */ /* first, fill each method with default parameters */ for (i = 0 ; i <= CHOLMOD_MAXMETHODS ; i++) { /* CHOLMOD's default method is AMD for A or AA' */ Common->method [i].ordering = CHOLMOD_AMD ; /* CHOLMOD nested dissection and minimum degree parameter */ Common->method [i].prune_dense = 10.0 ; /* dense row/col control */ /* min degree parameters (AMD, COLAMD, SYMAMD, CAMD, CCOLAMD, CSYMAMD)*/ Common->method [i].prune_dense2 = -1 ; /* COLAMD dense row control */ Common->method [i].aggressive = TRUE ; /* aggressive absorption */ Common->method [i].order_for_lu = FALSE ;/* order for Cholesky not LU */ /* CHOLMOD's nested dissection (METIS + constrained AMD) */ Common->method [i].nd_small = 200 ; /* small graphs aren't cut */ Common->method [i].nd_compress = TRUE ; /* compress graph & subgraphs */ Common->method [i].nd_camd = 1 ; /* use CAMD */ Common->method [i].nd_components = FALSE ; /* lump connected comp. */ Common->method [i].nd_oksep = 1.0 ; /* sep ok if < oksep*n */ /* statistics for each method are not yet computed */ Common->method [i].fl = EMPTY ; Common->method [i].lnz = EMPTY ; } Common->postorder = TRUE ; /* follow ordering with weighted postorder */ /* Next, define some methods. The first five use default parameters. */ Common->method [0].ordering = CHOLMOD_GIVEN ; /* skip if UserPerm NULL */ Common->method [1].ordering = CHOLMOD_AMD ; Common->method [2].ordering = CHOLMOD_METIS ; Common->method [3].ordering = CHOLMOD_NESDIS ; Common->method [4].ordering = CHOLMOD_NATURAL ; /* CHOLMOD's nested dissection with large leaves of separator tree */ Common->method [5].ordering = CHOLMOD_NESDIS ; Common->method [5].nd_small = 20000 ; /* CHOLMOD's nested dissection with tiny leaves, and no AMD ordering */ Common->method [6].ordering = CHOLMOD_NESDIS ; Common->method [6].nd_small = 4 ; Common->method [6].nd_camd = 0 ; /* no CSYMAMD or CAMD */ /* CHOLMOD's nested dissection with no dense node removal */ Common->method [7].ordering = CHOLMOD_NESDIS ; Common->method [7].prune_dense = -1. ; /* COLAMD for A*A', AMD for A */ Common->method [8].ordering = CHOLMOD_COLAMD ; return (TRUE) ; } /* ========================================================================== */ /* === cholmod_finish ======================================================= */ /* ========================================================================== */ /* The last call to CHOLMOD must be cholmod_finish. You may call this routine * more than once, and can safely call any other CHOLMOD routine after calling * it (including cholmod_start). * * The statistics and parameter settings in Common are preserved. The * workspace in Common is freed. This routine is just another name for * cholmod_free_work. */ int CHOLMOD(finish) ( cholmod_common *Common ) { return (CHOLMOD(free_work) (Common)) ; } /* ========================================================================== */ /* === cholmod_allocate_work ================================================ */ /* ========================================================================== */ /* Allocate and initialize workspace for CHOLMOD routines, or increase the size * of already-allocated workspace. If enough workspace is already allocated, * then nothing happens. * * workspace: Flag (nrow), Head (nrow+1), Iwork (iworksize), Xwork (xworksize) */ int CHOLMOD(allocate_work) ( /* ---- input ---- */ size_t nrow, /* # of rows in the matrix A */ size_t iworksize, /* size of Iwork */ size_t xworksize, /* size of Xwork */ /* --------------- */ cholmod_common *Common ) { double *W ; Int *Head ; Int i ; size_t nrow1 ; int ok = TRUE ; /* ---------------------------------------------------------------------- */ /* get inputs */ /* ---------------------------------------------------------------------- */ RETURN_IF_NULL_COMMON (FALSE) ; Common->status = CHOLMOD_OK ; /* ---------------------------------------------------------------------- */ /* Allocate Flag (nrow) and Head (nrow+1) */ /* ---------------------------------------------------------------------- */ nrow = MAX (1, nrow) ; /* nrow1 = nrow + 1 */ nrow1 = CHOLMOD(add_size_t) (nrow, 1, &ok) ; if (!ok) { /* nrow+1 causes size_t overflow ; problem is too large */ Common->status = CHOLMOD_TOO_LARGE ; CHOLMOD(free_work) (Common) ; return (FALSE) ; } if (nrow > Common->nrow) { if (Common->no_workspace_reallocate) { /* CHOLMOD is not allowed to change the workspace here */ Common->status = CHOLMOD_INVALID ; return (FALSE) ; } /* free the old workspace (if any) and allocate new space */ Common->Flag = CHOLMOD(free) (Common->nrow, sizeof (Int), Common->Flag, Common) ; Common->Head = CHOLMOD(free) (Common->nrow+1,sizeof (Int), Common->Head, Common) ; Common->Flag = CHOLMOD(malloc) (nrow, sizeof (Int), Common) ; Common->Head = CHOLMOD(malloc) (nrow1, sizeof (Int), Common) ; /* record the new size of Flag and Head */ Common->nrow = nrow ; if (Common->status < CHOLMOD_OK) { CHOLMOD(free_work) (Common) ; return (FALSE) ; } /* initialize Flag and Head */ Common->mark = EMPTY ; CHOLMOD(clear_flag) (Common) ; Head = Common->Head ; for (i = 0 ; i <= (Int) (nrow) ; i++) { Head [i] = EMPTY ; } } /* ---------------------------------------------------------------------- */ /* Allocate Iwork (iworksize) */ /* ---------------------------------------------------------------------- */ iworksize = MAX (1, iworksize) ; if (iworksize > Common->iworksize) { if (Common->no_workspace_reallocate) { /* CHOLMOD is not allowed to change the workspace here */ Common->status = CHOLMOD_INVALID ; return (FALSE) ; } /* free the old workspace (if any) and allocate new space. * integer overflow safely detected in cholmod_malloc */ CHOLMOD(free) (Common->iworksize, sizeof (Int), Common->Iwork, Common) ; Common->Iwork = CHOLMOD(malloc) (iworksize, sizeof (Int), Common) ; /* record the new size of Iwork */ Common->iworksize = iworksize ; if (Common->status < CHOLMOD_OK) { CHOLMOD(free_work) (Common) ; return (FALSE) ; } /* note that Iwork does not need to be initialized */ } /* ---------------------------------------------------------------------- */ /* Allocate Xwork (xworksize) and set it to ((double) 0.) */ /* ---------------------------------------------------------------------- */ /* make sure xworksize is >= 1 */ xworksize = MAX (1, xworksize) ; if (xworksize > Common->xworksize) { if (Common->no_workspace_reallocate) { /* CHOLMOD is not allowed to change the workspace here */ Common->status = CHOLMOD_INVALID ; return (FALSE) ; } /* free the old workspace (if any) and allocate new space */ CHOLMOD(free) (Common->xworksize, sizeof (double), Common->Xwork, Common) ; Common->Xwork = CHOLMOD(malloc) (xworksize, sizeof (double), Common) ; /* record the new size of Xwork */ Common->xworksize = xworksize ; if (Common->status < CHOLMOD_OK) { CHOLMOD(free_work) (Common) ; return (FALSE) ; } /* initialize Xwork */ W = Common->Xwork ; for (i = 0 ; i < (Int) xworksize ; i++) { W [i] = 0. ; } } return (TRUE) ; } /* ========================================================================== */ /* === cholmod_free_work ==================================================== */ /* ========================================================================== */ /* Deallocate the CHOLMOD workspace. * * workspace: deallocates all workspace in Common */ int CHOLMOD(free_work) ( cholmod_common *Common ) { RETURN_IF_NULL_COMMON (FALSE) ; Common->Flag = CHOLMOD(free) (Common->nrow, sizeof (Int), Common->Flag, Common) ; Common->Head = CHOLMOD(free) (Common->nrow+1, sizeof (Int), Common->Head, Common) ; Common->Iwork = CHOLMOD(free) (Common->iworksize, sizeof (Int), Common->Iwork, Common) ; Common->Xwork = CHOLMOD(free) (Common->xworksize, sizeof (double), Common->Xwork, Common) ; Common->nrow = 0 ; Common->iworksize = 0 ; Common->xworksize = 0 ; return (TRUE) ; } /* ========================================================================== */ /* === cholmod_clear_flag =================================================== */ /* ========================================================================== */ /* Increment mark to ensure Flag [0..nrow-1] < mark. If integer overflow * occurs, or mark was initially negative, reset the entire array. This is * not an error condition, but an intended function of the Flag workspace. * * workspace: Flag (nrow). Does not modify Flag if nrow is zero. */ UF_long cholmod_l_clear_flag ( cholmod_common *Common ) { Int i, nrow, *Flag ; RETURN_IF_NULL_COMMON (-1) ; Common->mark++ ; if (Common->mark <= 0) { nrow = Common->nrow ; Flag = Common->Flag ; PRINT2 (("reset Flag: nrow "ID"\n", nrow)) ; PRINT2 (("reset Flag: mark %ld\n", Common->mark)) ; for (i = 0 ; i < nrow ; i++) { Flag [i] = EMPTY ; } Common->mark = 0 ; } return (Common->mark) ; } /* ========================================================================== */ /* ==== cholmod_maxrank ===================================================== */ /* ========================================================================== */ /* Find a valid value of Common->maxrank. Returns 0 if error, or 2, 4, or 8 * if successful. */ size_t cholmod_l_maxrank /* returns validated value of Common->maxrank */ ( /* ---- input ---- */ size_t n, /* A and L will have n rows */ /* --------------- */ cholmod_common *Common ) { size_t maxrank ; RETURN_IF_NULL_COMMON (0) ; maxrank = Common->maxrank ; if (n > 0) { /* Ensure maxrank*n*sizeof(double) does not result in integer overflow. * If n is so large that 2*n*sizeof(double) results in integer overflow * (n = 268,435,455 if an Int is 32 bits), then maxrank will be 0 or 1, * but maxrank will be set to 2 below. 2*n will not result in integer * overflow, and CHOLMOD will run out of memory or safely detect integer * overflow elsewhere. */ maxrank = MIN (maxrank, Size_max / (n * sizeof (double))) ; } if (maxrank <= 2) { maxrank = 2 ; } else if (maxrank <= 4) { maxrank = 4 ; } else { maxrank = 8 ; } return (maxrank) ; } /* ========================================================================== */ /* === cholmod_dbound ======================================================= */ /* ========================================================================== */ /* Ensure the absolute value of a diagonal entry, D (j,j), is greater than * Common->dbound. This routine is not meant for the user to call. It is used * by the various LDL' factorization and update/downdate routines. The * default value of Common->dbound is zero, and in that case this routine is not * called at all. No change is made if D (j,j) is NaN. CHOLMOD does not call * this routine if Common->dbound is NaN. */ double cholmod_l_dbound /* returns modified diagonal entry of D */ ( /* ---- input ---- */ double dj, /* diagonal entry of D, for LDL' factorization */ /* --------------- */ cholmod_common *Common ) { double dbound ; RETURN_IF_NULL_COMMON (0) ; if (!IS_NAN (dj)) { dbound = Common->dbound ; if (dj < 0) { if (dj > -dbound) { dj = -dbound ; Common->ndbounds_hit++ ; if (Common->status == CHOLMOD_OK) { ERROR (CHOLMOD_DSMALL, "diagonal below threshold") ; } } } else { if (dj < dbound) { dj = dbound ; Common->ndbounds_hit++ ; if (Common->status == CHOLMOD_OK) { ERROR (CHOLMOD_DSMALL, "diagonal below threshold") ; } } } } return (dj) ; }