//------------------------------------------------------------------------------ // CHOLMOD/Utility/t_cholmod_change_factor: change format of a factor object //------------------------------------------------------------------------------ // CHOLMOD/Utility Module. Copyright (C) 2023, Timothy A. Davis, All Rights // Reserved. // SPDX-License-Identifier: LGPL-2.1+ //------------------------------------------------------------------------------ // The cholmod_factor object holds a sparse Cholesky factorization in one of // many formats. It can be numeric or symbolic, L*L' or L*D*L', simplicial // or supernodal, packed or unpacked, and its columns can appear in order // (monotonic) or not (non-monotonic). Not all combinations are possible. // The L->xtype field describes the type of the entries: // CHOLMOD_PATTERN: L->x and L->z are NULL, no values are stored. // CHOLMOD_REAL: L->x is float or double, and L->z is NULL. The numeric // value of the (p)th entry is L->x [p]. // CHOLMOD_COMPLEX: L->x is float or double, the matrix is complex, // where the value of (p)th entry is L->x [2*p] (the real part), // and L->x [2*p+1] (the imaginary part). L->z is NULL. // CHOLMOD_ZOMPLEX: L->x and L->z are float or double. The matrix is // "zomplex", which means the matrix is mathematically complex, but // the real and imaginary parts are held in two separate arrays. // The value of the (p)th entry is L->x [p] (the real part), and // L->z [p] (the imaginary part). Supernodal factors are never // zomplex. // This method can change the L->xtype of a factor object L, but only to // convert it to CHOLMOD_PATTERN (thus freeing all numeric values), or by // changing a factor of xtype CHOLMOD_PATTERN to any of the three other types // (thus allocating empty space, or placing a placeholder value of an // identitity matrix. // The L->dtype field, just like the CHOLMOD sparse matrix, triplet matrix, and // dense matrix formats, defines the underlying floating point type: single // (float) or double, as CHOLMOD_SINGLE or CHOLMOD_DOUBLE. Matrices of // different dtypes cannot be mixed. To convert the xtype and dtype of an // object, use the cholmod_*_xtype methods (named "_xtype" for backward // compatibility with CHOLMOD v4 and earlier). That method will preserve the // values of the factor L. // CHOLMOD has the following basic kinds of factor formats: // // (1) simplicial symbolic: this consists of just two arrays of size n. // L->Perm contains the fill-reducing ordering, and L->ColCount is an // array containing the # of entries in each column of L. All of the other // factor objects contain this information as well. The simplicial // symbolic format does not hold the pattern of the matrix L itself. // // (2) simplicial numeric: this can hold either an LL' or LDL' factorization. // // LDL': The numeric L matrix itself is unit-diagonal, and the diagonal // entries of L are not stored. The jth column appears in positions // L->p [j] to L->p [j] + L->nz [j], in the arrays L->i, L->x, and (if // zomplex), L->z. Thus, L->nz [j] is the # of entries in the jth column, // which includes the entry D(j,j) as the first entry held in that column. // The columns of L need not appear in order 0, 1, ... n-1 in L->(i,x,z). // Instead, a doubly-link list is used (with L->prev and L->next). // The value D(j,j) can be negative (that is, the matrix being factorized // can be symmetric indefinite but with all principal minor matrices being // full rank). // // LL': this is the same format as LL', except the first entry in each // column is L(j,j). The diagonal matrix D is not preset. // // (3) supernodal symbolic: this represents the nonzero pattern of the // supernodes for a supernodal factorization, with L->nsuper supernodes. // The kth supernode contains columns L->super [k] to L->super [k+1]-1. // Its row indices are held in L->s [L->pi [k] ... L->pi [k+1]-1]. // L->x is not allocated, and is NULL. // // (4) supernodal numeric: This is always an LL' factorization (not LDL'). // L->x holds then numeric values of each supernode. The values of // the kth supernode (for the real case) are held in // L->x [L->px [k] ... L->px [k+1]-1]. The factor can be complex but // not zomplex (L->z is never used for a supernodal numeric factor). // // Within each column, or which each supernode, the row indices in L->i // (simplicial) or L->s (supernodal) are kept sorted, from low to high. // // This function, cholmod_change_factor and cholmod_l_change_factor, converts // a factor object between these representations, with some limitations: // // (a) a simplicial numeric factor cannot be converted to supernodal. // // (b) only a symbolic factor (simplicial or supernodal) can be converted // into a supernodal numeric factor. // // (c) L->dtype is not changed (single or double precision). // // (d) L->xtype can be changed but the numeric contents of L are discarded. // // Some of these conversions are only meant for internal use by CHOLMOD itself, // and they allocate space whose contents are not defined: simplicial // symbolic to supernodal symbolic, and converting any factor to supernodal // numeric. CHOLMOD performs these conversions just before it does its // numeric factorization. #include "cholmod_internal.h" #define RETURN_IF_ERROR(result) \ { \ if (Common->status < CHOLMOD_OK) \ { \ return result ; \ } \ } //------------------------------------------------------------------------------ // grow_column: increase the space for a single column of L //------------------------------------------------------------------------------ static Int grow_column (Int len, double grow1, double grow2, Int maxlen) { double xlen = (double) len ; xlen = grow1 * xlen + grow2 ; xlen = MIN (xlen, maxlen) ; len = (Int) xlen ; len = MAX (1, len) ; len = MIN (len, maxlen) ; return (len) ; } //------------------------------------------------------------------------------ // grow_L: grow the space at the end of L //------------------------------------------------------------------------------ static Int grow_L (Int lnz, double grow0, Int n) { double xlnz = (double) lnz ; xlnz *= grow0 ; xlnz = MIN (xlnz, (double) Int_max) ; double d = (double) n ; d = (d*d + d) / 2 ; xlnz = MIN (xlnz, d) ; Int newlnz = MAX (lnz, (Int) xlnz) ; return (newlnz) ; } //------------------------------------------------------------------------------ // t_cholmod_change_factor_*_worker //------------------------------------------------------------------------------ #define DOUBLE #define REAL #include "t_cholmod_change_factor_1_worker.c" #include "t_cholmod_change_factor_2_worker.c" #include "t_cholmod_change_factor_3_worker.c" #undef REAL #define COMPLEX #include "t_cholmod_change_factor_1_worker.c" #include "t_cholmod_change_factor_2_worker.c" #include "t_cholmod_change_factor_3_worker.c" #undef COMPLEX #define ZOMPLEX #include "t_cholmod_change_factor_1_worker.c" #include "t_cholmod_change_factor_2_worker.c" #undef ZOMPLEX #undef DOUBLE #define SINGLE #define REAL #include "t_cholmod_change_factor_1_worker.c" #include "t_cholmod_change_factor_2_worker.c" #include "t_cholmod_change_factor_3_worker.c" #undef REAL #define COMPLEX #include "t_cholmod_change_factor_1_worker.c" #include "t_cholmod_change_factor_2_worker.c" #include "t_cholmod_change_factor_3_worker.c" #undef COMPLEX #define ZOMPLEX #include "t_cholmod_change_factor_1_worker.c" #include "t_cholmod_change_factor_2_worker.c" #undef ZOMPLEX //------------------------------------------------------------------------------ // natural list: create a doubly-link list of columns, in ordering 0 to n-1 //------------------------------------------------------------------------------ // The head of the link list is always n+1, and the tail is always n, where // n = L->n. The actual columns of L are in range 0 to L->n. static void natural_list (cholmod_factor *L) { // get inputs Int *Lnext = (Int *) L->next ; Int *Lprev = (Int *) L->prev ; ASSERT (Lprev != NULL && Lnext != NULL) ; Int n = L->n ; // create the head node Int head = n+1 ; Lnext [head] = 0 ; Lprev [head] = EMPTY ; // create the tail node Int tail = n ; Lnext [tail] = EMPTY ; Lprev [tail] = n-1 ; // link columns 0 to n-1 in increasing order: 0, 1, 2, ... n-1 for (Int j = 0 ; j < n ; j++) { Lnext [j] = j+1 ; Lprev [j] = j-1 ; } // the prev node of the first coumn 0 is n+1 Lprev [0] = head ; // the columns appear in order 0, 1, 2, ... n-1 in the link list L->is_monotonic = TRUE ; } //------------------------------------------------------------------------------ // L_is_packed: return true if no column of L has any extra space after it //------------------------------------------------------------------------------ // This method is used for debugging only. #ifndef NDEBUG static int L_is_packed (cholmod_factor *L, cholmod_common *Common) { Int *Lnz = (Int *) L->nz ; Int *Lp = (Int *) L->p ; Int n = L->n ; if (Lnz == NULL || Lp == NULL || L->xtype == CHOLMOD_PATTERN || L->is_super) { // nothing to check: L is intrinsically packed return (TRUE) ; } if (!L->is_monotonic) { // L is not packed, by definition return (FALSE) ; } for (Int j = 0 ; j < n ; j++) { PRINT3 (("j: "ID" Lnz "ID" Lp[j+1] "ID" Lp[j] "ID"\n", j, Lnz [j], Lp [j+1], Lp [j])) ; Int total_space_for_column_j = Lp [j+1] - Lp [j] ; Int entries_in_column_j = Lnz [j] ; if (entries_in_column_j != total_space_for_column_j) { // L(:,j) has extra slack space at the end of the column PRINT2 (("L is not packed\n")) ; return (FALSE) ; } } // L is packed return (TRUE) ; } #endif //------------------------------------------------------------------------------ // alloc_simplicial_num: allocate size-n arrays for simplicial numeric //------------------------------------------------------------------------------ // Does not allocate L->i, L->x, or L->z, which are larger. // See also cholmod_alloc_factor, which allocates L->Perm and L->ColCount. static int alloc_simplicial_num ( cholmod_factor *L, cholmod_common *Common ) { //-------------------------------------------------------------------------- // check inputs //-------------------------------------------------------------------------- ASSERT (L->xtype == CHOLMOD_PATTERN || L->is_super) ; ASSERT (L->p == NULL) ; ASSERT (L->nz == NULL) ; ASSERT (L->prev == NULL) ; ASSERT (L->next == NULL) ; size_t n = L->n ; //-------------------------------------------------------------------------- // allocate the four arrays //-------------------------------------------------------------------------- Int *Lp = CHOLMOD(malloc) (n+1, sizeof (Int), Common) ; Int *Lnz = CHOLMOD(malloc) (n, sizeof (Int), Common) ; Int *Lprev = CHOLMOD(malloc) (n+2, sizeof (Int), Common) ; Int *Lnext = CHOLMOD(malloc) (n+2, sizeof (Int), Common) ; //-------------------------------------------------------------------------- // check if out of memory //-------------------------------------------------------------------------- if (Common->status < CHOLMOD_OK) { // out of memory CHOLMOD(free) (n+1, sizeof (Int), Lp, Common) ; CHOLMOD(free) (n, sizeof (Int), Lnz, Common) ; CHOLMOD(free) (n+2, sizeof (Int), Lprev, Common) ; CHOLMOD(free) (n+2, sizeof (Int), Lnext, Common) ; return (FALSE) ; } //-------------------------------------------------------------------------- // place the arrays in L //-------------------------------------------------------------------------- L->p = Lp ; L->nz = Lnz ; L->prev = Lprev ; L->next = Lnext ; //-------------------------------------------------------------------------- // initialize the link list of the columns of L //-------------------------------------------------------------------------- natural_list (L) ; return (TRUE) ; } //------------------------------------------------------------------------------ // simplicial_sym_to_super_sym: converts simplicial symbolic to super //------------------------------------------------------------------------------ // converts a simplicial symbolic factor to supernodal symbolic. The space // is allocated but not initialized. static int simplicial_sym_to_super_sym ( cholmod_factor *L, cholmod_common *Common ) { //-------------------------------------------------------------------------- // check inputs //-------------------------------------------------------------------------- ASSERT (L->xtype == CHOLMOD_PATTERN && !(L->is_super)) ; //-------------------------------------------------------------------------- // allocate L->super, L->pi, L->px, and L->s //-------------------------------------------------------------------------- Int *Lsuper = CHOLMOD(malloc) (L->nsuper+1, sizeof (Int), Common) ; Int *Lpi = CHOLMOD(malloc) (L->nsuper+1, sizeof (Int), Common) ; Int *Lpx = CHOLMOD(malloc) (L->nsuper+1, sizeof (Int), Common) ; Int *Ls = CHOLMOD(malloc) (L->ssize, sizeof (Int), Common) ; //-------------------------------------------------------------------------- // check if out of memory //-------------------------------------------------------------------------- if (Common->status < CHOLMOD_OK) { // out of memory CHOLMOD(free) (L->nsuper+1, sizeof (Int), Lsuper, Common) ; CHOLMOD(free) (L->nsuper+1, sizeof (Int), Lpi, Common) ; CHOLMOD(free) (L->nsuper+1, sizeof (Int), Lpx, Common) ; CHOLMOD(free) (L->ssize, sizeof (Int), Ls, Common) ; return (FALSE) ; } //-------------------------------------------------------------------------- // place the arrays in L //-------------------------------------------------------------------------- L->super = Lsuper ; L->pi = Lpi ; L->px = Lpx ; L->s = Ls ; //-------------------------------------------------------------------------- // revise the header of L (L->dtype is not changed) //-------------------------------------------------------------------------- L->xtype = CHOLMOD_PATTERN ; // L is symbolic (no L->x, L->i, L->z) L->is_super = TRUE ; // L is now supernodal Ls [0] = EMPTY ; // contents of supernodal pattern undefined L->is_ll = TRUE ; // L is a supernodal LL' factorization L->maxcsize = 0 ; // size of largest update matrix L->maxesize = 0 ; // max rows in any supernode excl tri. part) L->minor = L->n ; // see cholmod.h for a description return (TRUE) ; } //------------------------------------------------------------------------------ // super_num_to_super_sym: convert numeric supernodal to symbolic //------------------------------------------------------------------------------ // This method converts a supernodal numeric factor L to supernodal symbolic, // by freeing the numeric values of all the supernodes. The supernodal // pattern (L->s) is kept. static void super_num_to_super_sym ( cholmod_factor *L, cholmod_common *Common ) { //-------------------------------------------------------------------------- // check inputs //-------------------------------------------------------------------------- ASSERT (L->xtype != CHOLMOD_PATTERN) ; ASSERT (L->xtype != CHOLMOD_ZOMPLEX) ; ASSERT (L->is_super) ; ASSERT (L->is_ll) ; DEBUG (CHOLMOD(dump_factor) (L, "supernum to supersym:L input", Common)) ; //-------------------------------------------------------------------------- // free L->x only //-------------------------------------------------------------------------- size_t e = (L->dtype == CHOLMOD_SINGLE) ? sizeof (float) : sizeof (double) ; size_t ex = e * ((L->xtype == CHOLMOD_PATTERN) ? 0 : ((L->xtype == CHOLMOD_COMPLEX) ? 2 : 1)) ; L->x = CHOLMOD(free) (L->xsize, ex, L->x, Common) ; //-------------------------------------------------------------------------- // change the header contents to reflect the supernodal symbolic status //-------------------------------------------------------------------------- L->xtype = CHOLMOD_PATTERN ; // L is symbolic L->minor = L->n ; // see cholmod.h L->is_ll = TRUE ; // supernodal factor is always LL', not LDL' DEBUG (CHOLMOD(dump_factor) (L, "supernum to supersym:L output", Common)) ; } //------------------------------------------------------------------------------ // simplicial_sym_to_simplicial_num: convert simplicial numeric to symbolic //------------------------------------------------------------------------------ // This methods allocates space and converts a simplicial symbolic L to // simplicial numeric. L is set to the identity matrix, except in one case // where the contents of L are not initialized (packed < 0 case). static void simplicial_sym_to_simplicial_num ( cholmod_factor *L, // factor to modify int to_ll, // if true, convert to LL. if false: to LDL' int packed, // if > 0: L is packed, if 0: L is unpacked, // if < 0: L is packed but contents are not initialized int to_xtype, // the L->xtype (real, complex, or zomplex) cholmod_common *Common ) { //-------------------------------------------------------------------------- // check inputs //-------------------------------------------------------------------------- ASSERT (L->xtype == CHOLMOD_PATTERN && !(L->is_super)) ; //-------------------------------------------------------------------------- // allocate space for the simplicial numeric factor (except L->(i,x,z)) //-------------------------------------------------------------------------- if (!alloc_simplicial_num (L, Common)) { // out of memory; error status is already in Common->status return ; } //-------------------------------------------------------------------------- // get inputs //-------------------------------------------------------------------------- Int n = L->n ; Int *Lp = L->p ; Int *Lnz = L->nz ; Int *ColCount = L->ColCount ; //-------------------------------------------------------------------------- // initialize L->p and L->nz //-------------------------------------------------------------------------- bool ok = true ; Int lnz = 0 ; if (packed < 0) { //---------------------------------------------------------------------- // do not initialize the space //---------------------------------------------------------------------- lnz = L->nzmax ; L->nzmax = 0 ; } else if (packed > 0) { //---------------------------------------------------------------------- // initialize the packed LL' or LDL' case (L is identity) //---------------------------------------------------------------------- for (Int j = 0 ; ok && (j < n) ; j++) { // ensure ColCount [j] is in the range 1 to n-j Int len = ColCount [j] ; len = MAX (1, len) ; len = MIN (len, n-j) ; lnz += len ; ok = (lnz >= 0) ; } // each column L(:,j) holds a single diagonal entry for (Int j = 0 ; j <= n ; j++) { Lp [j] = j ; } for (Int j = 0 ; j < n ; j++) { Lnz [j] = 1 ; } } else { //---------------------------------------------------------------------- // initialize the unpacked LL' or LDL' case (L is identity) //---------------------------------------------------------------------- // slack space will be added to L below double grow0 = Common->grow0 ; double grow1 = Common->grow1 ; double grow2 = (double) Common->grow2 ; grow0 = isnan (grow0) ? 1 : grow0 ; grow1 = isnan (grow1) ? 1 : grow1 ; Int grow = (grow0 >= 1.0) && (grow1 >= 1.0) && (grow2 > 0) ; for (Int j = 0 ; ok && (j < n) ; j++) { //------------------------------------------------------------------ // log the start of L(:,j), containing a single entry //------------------------------------------------------------------ Lp [j] = lnz ; Lnz [j] = 1 ; //------------------------------------------------------------------ // ensure ColCount [j] is in the range 1 to n-j //------------------------------------------------------------------ Int len = ColCount [j] ; len = MAX (1, len) ; len = MIN (len, n-j) ; //------------------------------------------------------------------ // add some slack space to L(:,j) //------------------------------------------------------------------ if (grow) { len = grow_column (len, grow1, grow2, n-j) ; } lnz += len ; ok = (lnz >= 0) ; } //---------------------------------------------------------------------- // add slack space at the end of L //---------------------------------------------------------------------- if (ok) { Lp [n] = lnz ; if (grow) { lnz = grow_L (lnz, grow0, n) ; } } } //-------------------------------------------------------------------------- // allocate L->i, L->x, and L->z with the new xtype and existing dtype //-------------------------------------------------------------------------- ASSERT (L->nzmax == 0) ; lnz = MAX (1, lnz) ; int nint = 1 ; Common->status = (ok) ? CHOLMOD_OK : CHOLMOD_TOO_LARGE ; if (!ok || !CHOLMOD(realloc_multiple) (lnz, nint, to_xtype + L->dtype, &(L->i), NULL, &(L->x), &(L->z), &(L->nzmax), Common)) { // out of memory: convert L back to simplicial symbolic CHOLMOD(to_simplicial_sym) (L, to_ll, Common) ; return ; } L->xtype = to_xtype ; L->minor = n ; //-------------------------------------------------------------------------- // set L to the identity matrix, if requested //-------------------------------------------------------------------------- if (packed >= 0) { switch ((L->xtype + L->dtype) % 8) { case CHOLMOD_REAL + CHOLMOD_SINGLE: rs_cholmod_change_factor_1_worker (L) ; break ; case CHOLMOD_COMPLEX + CHOLMOD_SINGLE: cs_cholmod_change_factor_1_worker (L) ; break ; case CHOLMOD_ZOMPLEX + CHOLMOD_SINGLE: zs_cholmod_change_factor_1_worker (L) ; break ; case CHOLMOD_REAL + CHOLMOD_DOUBLE: rd_cholmod_change_factor_1_worker (L) ; break ; case CHOLMOD_COMPLEX + CHOLMOD_DOUBLE: cd_cholmod_change_factor_1_worker (L) ; break ; case CHOLMOD_ZOMPLEX + CHOLMOD_DOUBLE: zd_cholmod_change_factor_1_worker (L) ; break ; } } L->is_ll = to_ll ; } //------------------------------------------------------------------------------ // change_simplicial_num: change LL' to LDL' or LDL' to LL' //------------------------------------------------------------------------------ // L must be simplicial numeric. // // to_ll: if true, L is converted to LL'; if false, to LDL' // to_packed: if true, L is converted to packed and monotonic // (to_monotonic is treated as true) // // to_monotonoic: if true but to_packed is false, L is converted to monotonic // but the columns are not packed. Slack space is left in // the columns of L. // // If both to_packed and to_monotonic are false: the columns of L are // converted in place, and neither packed nor made monotonic. // // To convert LDL' to LL', columns with D(j,j) <= 0 are left as-is, but L is // not numerically valid. The column L(:,j) is unchanged. If converted back // to LDL', the column is also left as-is, so that the LDL' can be recovered. // L->minor is set to the first j where D(j,j) <= 0. static void change_simplicial_num ( cholmod_factor *L, // factor to modify int to_ll, // if true: convert to LL'; else LDL' int to_packed, // if true: pack the columns of L int to_monotonic, // if true: make columns of L monotonic cholmod_common *Common ) { //-------------------------------------------------------------------------- // check inputs //-------------------------------------------------------------------------- ASSERT (Common != NULL) ; DEBUG (CHOLMOD(dump_factor) (L, "change simplnum:L input", Common)) ; ASSERT (L->xtype != CHOLMOD_PATTERN) ; ASSERT (!L->is_super) ; //-------------------------------------------------------------------------- // get inputs //-------------------------------------------------------------------------- bool out_of_place = ((to_packed || to_monotonic) && !(L->is_monotonic)) ; bool make_ll = (to_ll && !(L->is_ll)) ; bool make_ldl = (!to_ll && L->is_ll) ; Int n = L->n ; Int *Lp = (Int *) L->p ; Int *Li = (Int *) L->i ; Int *Lnz = (Int *) L->nz ; bool grow = false ; double grow0 = Common->grow0 ; double grow1 = Common->grow1 ; double grow2 = (double) Common->grow2 ; grow0 = isnan (grow0) ? 1 : grow0 ; grow1 = isnan (grow1) ? 1 : grow1 ; void *Li2 = NULL ; void *Lx2 = NULL ; void *Lz2 = NULL ; size_t ei = sizeof (Int) ; size_t e = (L->dtype == CHOLMOD_SINGLE) ? sizeof (float) : sizeof (double) ; size_t ex = e * ((L->xtype == CHOLMOD_COMPLEX) ? 2 : 1) ; size_t ez = e * ((L->xtype == CHOLMOD_ZOMPLEX) ? 1 : 0) ; //-------------------------------------------------------------------------- // resize if changing to monotonic and/or packed and not already monotonic //-------------------------------------------------------------------------- Int lnz = 0 ; if (out_of_place) { //---------------------------------------------------------------------- // out-of-place: construct L in new space (Li2, Lx2, and Lz2) //---------------------------------------------------------------------- // The columns of L are out of order (not monotonic), but L is being // changed to being either monotonic, or packed, or both. Thus, L // needs to be resized, in newly allocated space (Li2, Lx2, and Lz2). //---------------------------------------------------------------------- // determine if L should grow //---------------------------------------------------------------------- if (!to_packed) { grow = (grow0 >= 1.0) && (grow1 >= 1.0) && (grow2 > 0) ; } //---------------------------------------------------------------------- // compute the new space for each column of L //---------------------------------------------------------------------- bool ok = true ; for (Int j = 0 ; ok && (j < n) ; j++) { Int len = Lnz [j] ; ASSERT (len >= 1 && len <= n-j) ; if (grow) { len = grow_column (len, grow1, grow2, n-j) ; } ASSERT (len >= Lnz [j] && len <= n-j) ; lnz += len ; ok = (lnz >= 0) ; } Common->status = (ok) ? CHOLMOD_OK : CHOLMOD_TOO_LARGE ; if (ok) { //------------------------------------------------------------------ // add additional space at the end of L, if requested //------------------------------------------------------------------ if (grow) { lnz = grow_L (lnz, grow0, n) ; } //------------------------------------------------------------------ // allocate Li2, Lx2, and Lz2 (as newly allocated space) //------------------------------------------------------------------ lnz = MAX (1, lnz) ; int nint = 1 ; size_t nzmax0 = 0 ; CHOLMOD(realloc_multiple) (lnz, nint, L->xtype + L->dtype, &Li2, NULL, &Lx2, &Lz2, &nzmax0, Common) ; } //---------------------------------------------------------------------- // return if out of memory or problem too large //---------------------------------------------------------------------- RETURN_IF_ERROR () ; } //-------------------------------------------------------------------------- // convert the simplicial L //-------------------------------------------------------------------------- switch ((L->xtype + L->dtype) % 8) { case CHOLMOD_REAL + CHOLMOD_SINGLE: rs_cholmod_change_factor_2_worker (L, to_packed, Li2, Lx2, Lz2, lnz, grow, grow1, grow2, make_ll, out_of_place, make_ldl, Common) ; break ; case CHOLMOD_COMPLEX + CHOLMOD_SINGLE: cs_cholmod_change_factor_2_worker (L, to_packed, Li2, Lx2, Lz2, lnz, grow, grow1, grow2, make_ll, out_of_place, make_ldl, Common) ; break ; case CHOLMOD_ZOMPLEX + CHOLMOD_SINGLE: zs_cholmod_change_factor_2_worker (L, to_packed, Li2, Lx2, Lz2, lnz, grow, grow1, grow2, make_ll, out_of_place, make_ldl, Common) ; break ; case CHOLMOD_REAL + CHOLMOD_DOUBLE: rd_cholmod_change_factor_2_worker (L, to_packed, Li2, Lx2, Lz2, lnz, grow, grow1, grow2, make_ll, out_of_place, make_ldl, Common) ; break ; case CHOLMOD_COMPLEX + CHOLMOD_DOUBLE: cd_cholmod_change_factor_2_worker (L, to_packed, Li2, Lx2, Lz2, lnz, grow, grow1, grow2, make_ll, out_of_place, make_ldl, Common) ; break ; case CHOLMOD_ZOMPLEX + CHOLMOD_DOUBLE: zd_cholmod_change_factor_2_worker (L, to_packed, Li2, Lx2, Lz2, lnz, grow, grow1, grow2, make_ll, out_of_place, make_ldl, Common) ; break ; } //-------------------------------------------------------------------------- // finalize the result L //-------------------------------------------------------------------------- L->is_ll = to_ll ; if (out_of_place) { //---------------------------------------------------------------------- // free the old space and move the new space into L //---------------------------------------------------------------------- CHOLMOD(free) (L->nzmax, ei, L->i, Common) ; CHOLMOD(free) (L->nzmax, ex, L->x, Common) ; CHOLMOD(free) (L->nzmax, ez, L->z, Common) ; L->i = Li2 ; L->x = Lx2 ; L->z = Lz2 ; L->nzmax = lnz ; //---------------------------------------------------------------------- // revise the link list (columns 0 to n-1 now in natural order) //---------------------------------------------------------------------- natural_list (L) ; } DEBUG (CHOLMOD(dump_factor) (L, "change simplnum:L output", Common)) ; } //------------------------------------------------------------------------------ // super_num_to_simplicial_num: convert supernodal numeric to simplicial numeric //------------------------------------------------------------------------------ static void super_num_to_simplicial_num ( cholmod_factor *L, int to_packed, int to_ll, cholmod_common *Common ) { //-------------------------------------------------------------------------- // check inputs //-------------------------------------------------------------------------- ASSERT (L != NULL) ; ASSERT (Common != NULL) ; DEBUG (CHOLMOD(dump_factor) (L, "supernum to simplnum:L input", Common)) ; ASSERT (L->xtype != CHOLMOD_PATTERN) ; ASSERT (L->xtype != CHOLMOD_ZOMPLEX) ; ASSERT (L->is_ll) ; ASSERT (L->is_super) ; ASSERT (L->x != NULL) ; ASSERT (L->i == NULL) ; ASSERT (L->z == NULL) ; //-------------------------------------------------------------------------- // get inputs //-------------------------------------------------------------------------- Int n = L->n ; Int nsuper = L->nsuper ; Int *Lpi = (Int *) L->pi ; Int *Lpx = (Int *) L->px ; Int *Ls = (Int *) L->s ; Int *Super = (Int *) L->super ; size_t ei = sizeof (Int) ; size_t e = (L->dtype == CHOLMOD_SINGLE) ? sizeof (float) : sizeof (double) ; size_t ex = e * ((L->xtype == CHOLMOD_COMPLEX) ? 2 : 1) ; //-------------------------------------------------------------------------- // determine the size of L after conversion to simplicial numeric //-------------------------------------------------------------------------- Int lnz = 0 ; if (to_packed) { //---------------------------------------------------------------------- // count the # of nonzeros in all supernodes of L //---------------------------------------------------------------------- // Each supernode is lower trapezoidal, with a top part that is lower // triangular (with diagonal present) and a bottom part that is // rectangular. In this example below, nscol = 5, so the supernode // represents 5 columns of L, and nsrow = 8, which means that the // first column of the supernode has 8 entries, including the // diagonal. // // x . . . . // x x . . . // x x x . . // x x x x . // x x x x x // x x x x x // x x x x x // x x x x x // // The '.' entries above are in the data structure but not used, and // are not copied into the simplicial factor L. The 'x' entries are // used, but some might be exactly equal to zero. Some of these zeros // come from relaxed supernodal algamation, and some come from exact // numeric cancellation. These entries appear in the final // simplicial factor L. for (Int s = 0 ; s < nsuper ; s++) { //------------------------------------------------------------------ // get the supernode //------------------------------------------------------------------ Int k1 = Super [s] ; // L(:,k1) is 1st col in supernode s Int k2 = Super [s+1] ; // L(:,k2) is 1st col in supernode s+1 Int psi = Lpi [s] ; // start of pattern in Ls of supernode s Int psend = Lpi [s+1] ; // start of pattern in Ls of s+1 Int nsrow = psend - psi ; // # entries in 1st col of supernode s Int nscol = k2 - k1 ; // # of columns in supernode ASSERT (nsrow >= nscol) ; Int erows = nsrow - nscol ; // # of rows below triangular part //------------------------------------------------------------------ // count the entries in the supernode, including any exact zeros //------------------------------------------------------------------ // lower triangular part lnz += nscol * (nscol+1) / 2 ; // rectangular part lnz += nscol * erows ; } } else { //---------------------------------------------------------------------- // the supernodal L will not be packed //---------------------------------------------------------------------- // L->x holds all numeric values of the supernodes, and these entries // will remain in place. L->x will not be decreased in size, so Li // will have the same size as L->x. lnz = L->xsize ; } ASSERT (lnz >= 0 && lnz <= (Int) (L->xsize)) ; //-------------------------------------------------------------------------- // allocate Li for the pattern L->i of the simplicial factor of L //-------------------------------------------------------------------------- Int *Li = CHOLMOD(malloc) (lnz, ei, Common) ; RETURN_IF_ERROR () ; //-------------------------------------------------------------------------- // allocate the size-n arrays for L: L->p, L->nz, L->prev, and L->nex //-------------------------------------------------------------------------- if (!alloc_simplicial_num (L, Common)) { // out of memory CHOLMOD(free) (lnz, ei, Li, Common) ; return ; } //-------------------------------------------------------------------------- // convert the supernodal numeric L into a simplicial numeric L //-------------------------------------------------------------------------- L->i = Li ; L->nzmax = lnz ; switch ((L->xtype + L->dtype) % 8) { case CHOLMOD_SINGLE + CHOLMOD_REAL: rs_cholmod_change_factor_3_worker (L, to_packed, to_ll, Common) ; break ; case CHOLMOD_SINGLE + CHOLMOD_COMPLEX: cs_cholmod_change_factor_3_worker (L, to_packed, to_ll, Common) ; break ; case CHOLMOD_DOUBLE + CHOLMOD_REAL: rd_cholmod_change_factor_3_worker (L, to_packed, to_ll, Common) ; break ; case CHOLMOD_DOUBLE + CHOLMOD_COMPLEX: cd_cholmod_change_factor_3_worker (L, to_packed, to_ll, Common) ; break ; } //-------------------------------------------------------------------------- // reduce the size of L->x to match L->i (this cannot fail) //-------------------------------------------------------------------------- L->x = CHOLMOD(realloc) (lnz, ex, L->x, &(L->xsize), Common) ; Common->status = CHOLMOD_OK ; //-------------------------------------------------------------------------- // free the supernodal parts of L and return result //-------------------------------------------------------------------------- L->is_super = FALSE ; // L is now simplicial L->is_ll = to_ll ; // L is LL' or LDL', according to to_ll L->pi = CHOLMOD(free) (nsuper+1, ei, L->pi, Common) ; L->px = CHOLMOD(free) (nsuper+1, ei, L->px, Common) ; L->super = CHOLMOD(free) (nsuper+1, ei, L->super, Common) ; L->s = CHOLMOD(free) (L->ssize, ei, L->s, Common) ; L->ssize = 0 ; // L->s is not present L->xsize = 0 ; // L->x is not present L->nsuper = 0 ; // no supernodes L->maxesize = 0 ; // no rows in any supernodes L->maxcsize = 0 ; // largest update matrix is size zero DEBUG (CHOLMOD(dump_factor) (L, "supernum to simplnum:L output", Common)) ; } //------------------------------------------------------------------------------ // super_sym_to_super_num: convert supernodal symbolic to numeric //------------------------------------------------------------------------------ static int super_sym_to_super_num ( int to_xtype, cholmod_factor *L, cholmod_common *Common ) { //-------------------------------------------------------------------------- // check inputs //-------------------------------------------------------------------------- ASSERT (L != NULL) ; ASSERT (Common != NULL) ; ASSERT (L->xtype == CHOLMOD_PATTERN) ; ASSERT (L->is_super) ; ASSERT (L->x == NULL) ; ASSERT (to_xtype == CHOLMOD_REAL || to_xtype == CHOLMOD_COMPLEX) ; //-------------------------------------------------------------------------- // get the sizes of the entries //-------------------------------------------------------------------------- size_t ei = sizeof (Int) ; size_t e = (L->dtype == CHOLMOD_SINGLE) ? sizeof (float) : sizeof (double) ; size_t ex = e * ((to_xtype == CHOLMOD_COMPLEX) ? 2 : 1) ; size_t xs = L->xsize ; //-------------------------------------------------------------------------- // allocate the space //-------------------------------------------------------------------------- double *Lx = CHOLMOD(malloc) (xs, ex, Common) ; RETURN_IF_ERROR ((FALSE)) ; //-------------------------------------------------------------------------- // finalize L and return result //-------------------------------------------------------------------------- // clear the first few entries so valgrind is satisfied memset (Lx, 0, MIN (2 * sizeof (double), xs*ex)) ; L->x = Lx ; // new space for numeric values L->xtype = to_xtype ; // real or complex L->minor = L->n ; return (TRUE) ; } //============================================================================== // cholmod_change_factor //============================================================================== // Convert a factor L. Some conversions simply allocate uninitialized space // that is meant to be filled later. int CHOLMOD(change_factor) ( int to_xtype, // CHOLMOD_PATTERN, _REAL, _COMPLEX, or _ZOMPLEX int to_ll, // if true: convert to LL'; else to LDL' int to_super, // if true: convert to supernodal; else to simplicial int to_packed, // if true: pack simplicial columns; else: do not pack int to_monotonic, // if true, put simplicial columns in order cholmod_factor *L, // factor to change cholmod_common *Common ) { //-------------------------------------------------------------------------- // check inputs //-------------------------------------------------------------------------- RETURN_IF_NULL_COMMON (FALSE) ; RETURN_IF_NULL (L, FALSE) ; RETURN_IF_XTYPE_INVALID (L, CHOLMOD_PATTERN, CHOLMOD_ZOMPLEX, FALSE) ; Common->status = CHOLMOD_OK ; to_xtype = to_xtype & 3 ; to_ll = to_ll ? 1 : 0 ; if (to_super && (to_xtype == CHOLMOD_ZOMPLEX)) { ERROR (CHOLMOD_INVALID, "supernodal zomplex L not supported") ; return (FALSE) ; } PRINT1 (("-----convert from (%d,%d,%d,%d,%d) to (%d,%d,%d,%d,%d)\n", L->xtype, L->is_ll, L->is_super, L_is_packed (L, Common), L->is_monotonic, to_xtype, to_ll, to_super, to_packed, to_monotonic)) ; //-------------------------------------------------------------------------- // convert the factor L //-------------------------------------------------------------------------- if (to_xtype == CHOLMOD_PATTERN) { //---------------------------------------------------------------------- // convert to symbolic //---------------------------------------------------------------------- if (!to_super) { //------------------------------------------------------------------ // convert to simplicial symbolic factor (this cannot fail) //------------------------------------------------------------------ CHOLMOD(to_simplicial_sym) (L, to_ll, Common) ; } else { //------------------------------------------------------------------ // convert to supernodal symbolic factor //------------------------------------------------------------------ if (L->xtype != CHOLMOD_PATTERN && L->is_super) { // convert supernodal numeric to supernodal symbolic, // keeping the pattern but freeing the numeric values. super_num_to_super_sym (L, Common) ; } else if (L->xtype == CHOLMOD_PATTERN && !(L->is_super)) { // convert simplicial symbolic to supernodal symbolic, // only meant for use internally to CHOLMOD. simplicial_sym_to_super_sym (L, Common) ; } else { // can't convert simplicial numeric to supernodal symbolic ERROR (CHOLMOD_INVALID, "failed to change L") ; return (FALSE) ; } } } else { //---------------------------------------------------------------------- // convert to numeric //---------------------------------------------------------------------- if (to_super) { //------------------------------------------------------------------ // convert to supernodal numeric factor //------------------------------------------------------------------ if (L->xtype == CHOLMOD_PATTERN) { if (L->is_super) { // convert supernodal symbolic to supernodal numeric, // only meant for use internally to CHOLMOD. super_sym_to_super_num (to_xtype, L, Common) ; } else { // convert simplicial symbolic to supernodal numeric, // only meant for use internally to CHOLMOD. if (!simplicial_sym_to_super_sym (L, Common)) { // failure, convert back to simplicial symbolic CHOLMOD(to_simplicial_sym) (L, to_ll, Common) ; return (FALSE) ; } // convert supernodal symbolic to supernodal numeric super_sym_to_super_num (to_xtype, L, Common) ; } } else { // nothing to do if already supernodal numeric if (!(L->is_super)) { ERROR (CHOLMOD_INVALID, "failed to change L") ; return (FALSE) ; } } } else { //------------------------------------------------------------------ // convert any factor to simplicial numeric //------------------------------------------------------------------ if (L->xtype == CHOLMOD_PATTERN && !(L->is_super)) { // convert simplicial symbolic to simplicial numeric (L=D=I) simplicial_sym_to_simplicial_num (L, to_ll, to_packed, to_xtype, Common) ; } else if (L->xtype != CHOLMOD_PATTERN && L->is_super) { // convert a supernodal LL' to simplicial numeric super_num_to_simplicial_num (L, to_packed, to_ll, Common) ; } else if (L->xtype == CHOLMOD_PATTERN && L->is_super) { // convert a supernodal symbolic to simplicial numeric (L=D=I) CHOLMOD(to_simplicial_sym) (L, to_ll, Common) ; simplicial_sym_to_simplicial_num (L, to_ll, to_packed, to_xtype, Common) ; } else { // change a simplicial numeric factor: change LL' to LDL', LDL' // to LL', or leave as-is. pack the columns of L, or leave // as-is. Ensure the columns are monotonic, or leave as-is. change_simplicial_num (L, to_ll, to_packed, to_monotonic, Common) ; } } } //-------------------------------------------------------------------------- // return result //-------------------------------------------------------------------------- return (Common->status >= CHOLMOD_OK) ; }