GLOBAL Int AMD_order ( Int n, const Int Ap [ ], const Int Ai [ ], Int P [ ], double Control [ ], double Info [ ] ) { Int slen, *Len, *S, nz, nzaat, i, *Pinv, info ; #ifndef NDEBUG AMD_debug_init ("amd") ; #endif /* clear the Info array, if it exists */ info = Info != (double *) NULL ; if (info) { for (i = 0 ; i < AMD_INFO ; i++) { Info [i] = EMPTY ; } Info [AMD_N] = n ; Info [AMD_STATUS] = AMD_OK ; } /* make sure inputs exist and n is >= 0 */ if (Ai == (Int *) NULL || Ap == (Int *) NULL || P == (Int *) NULL || n < 0) { if (info) Info [AMD_STATUS] = AMD_INVALID ; return (AMD_INVALID) ; /* arguments are invalid */ } if (n == 0) { return (AMD_OK) ; /* n is 0 so there's nothing to do */ } nz = Ap [n] ; if (info) { Info [AMD_NZ] = nz ; } if (nz < 0) { if (info) Info [AMD_STATUS] = AMD_INVALID ; return (AMD_INVALID) ; } /* Avoid integer overflow in memory size calculations. The space required * by AMD is at most 2.4nz + 8n for S, and n for Len. * Note nz - n <= nzaat <= 2*nz, below. */ if ((2.4 * (double) nz + 8 * (double) n) > (double) Int_MAX / sizeof (Int)) { /* :: int overflow :: */ if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; return (AMD_OUT_OF_MEMORY) ; } if (!AMD_valid (n, n, Ap, Ai)) { if (info) Info [AMD_STATUS] = AMD_INVALID ; return (AMD_INVALID) ; /* matrix is invalid */ } /* --------------------------------------------------------------------- */ /* determine the symmetry and count off-diagonal nonzeros in A+A' */ /* --------------------------------------------------------------------- */ /* allocate size-n integer workspace */ Len = (Int *) amd_malloc (n * sizeof (Int)) ; if (!Len) { /* :: out of memory :: */ if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; return (AMD_OUT_OF_MEMORY) ; } nzaat = AMD_aat (n, Ap, Ai, Len, P, Info) ; AMD_DEBUG1 (("nzaat: "ID"\n", nzaat)) ; ASSERT (nz-n <= nzaat && nzaat <= 2*nz) ; /* --------------------------------------------------------------------- */ /* allocate workspace for matrix, elbow room, and 7 size-n vectors */ /* --------------------------------------------------------------------- */ slen = (nzaat + nzaat/5 + n) + 7*n ; if (info) { /* memory usage (Len and S), in bytes. */ Info [AMD_MEMORY] = ((double) slen + n) * sizeof (Int) ; } S = (Int *) amd_malloc (slen * sizeof (Int)) ; AMD_DEBUG1 ((" S "ID" Len "ID" n "ID" nzaat "ID" slen "ID"\n", (Int) S, (Int) Len, n, nzaat, slen)) ; if (S == (Int *) NULL) { /* :: out of memory :: */ amd_free (Len) ; if (Info != (double *) NULL) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; return (AMD_OUT_OF_MEMORY) ; } /* allocate space from S for Pinv */ Pinv = S + slen - n ; slen -= n ; /* --------------------------------------------------------------------- */ /* order the matrix */ /* --------------------------------------------------------------------- */ AMD_1 (n, Ap, Ai, P, Pinv, Len, slen, S, Control, Info) ; /* --------------------------------------------------------------------- */ /* free the workspace */ /* --------------------------------------------------------------------- */ amd_free (Len) ; amd_free (S) ; return (AMD_OK) ; /* successful ordering */ }
GLOBAL void AMD_preprocess ( Int n, /* input matrix: A is n-by-n */ const Int Ap [ ], /* size n+1 */ const Int Ai [ ], /* size nz = Ap [n] */ /* output matrix R: */ Int Rp [ ], /* size n+1 */ Int Ri [ ], /* size nz (or less, if duplicates present) */ Int W [ ], /* workspace of size n */ Int Flag [ ] /* workspace of size n */ ) { /* --------------------------------------------------------------------- */ /* local variables */ /* --------------------------------------------------------------------- */ Int i, j, p, p2 ; ASSERT (AMD_valid (n, n, Ap, Ai) != AMD_INVALID) ; /* --------------------------------------------------------------------- */ /* count the entries in each row of A (excluding duplicates) */ /* --------------------------------------------------------------------- */ for (i = 0 ; i < n ; i++) { W [i] = 0 ; /* # of nonzeros in row i (excl duplicates) */ Flag [i] = EMPTY ; /* Flag [i] = j if i appears in column j */ } for (j = 0 ; j < n ; j++) { p2 = Ap [j+1] ; for (p = Ap [j] ; p < p2 ; p++) { i = Ai [p] ; if (Flag [i] != j) { /* row index i has not yet appeared in column j */ W [i]++ ; /* one more entry in row i */ Flag [i] = j ; /* flag row index i as appearing in col j*/ } } } /* --------------------------------------------------------------------- */ /* compute the row pointers for R */ /* --------------------------------------------------------------------- */ Rp [0] = 0 ; for (i = 0 ; i < n ; i++) { Rp [i+1] = Rp [i] + W [i] ; } for (i = 0 ; i < n ; i++) { W [i] = Rp [i] ; Flag [i] = EMPTY ; } /* --------------------------------------------------------------------- */ /* construct the row form matrix R */ /* --------------------------------------------------------------------- */ /* R = row form of pattern of A */ for (j = 0 ; j < n ; j++) { p2 = Ap [j+1] ; for (p = Ap [j] ; p < p2 ; p++) { i = Ai [p] ; if (Flag [i] != j) { /* row index i has not yet appeared in column j */ Ri [W [i]++] = j ; /* put col j in row i */ Flag [i] = j ; /* flag row index i as appearing in col j*/ } } } #ifndef NDEBUG ASSERT (AMD_valid (n, n, Rp, Ri) == AMD_OK) ; for (j = 0 ; j < n ; j++) { ASSERT (W [j] == Rp [j+1]) ; } #endif }
PRIVATE Int two_by_two /* returns # unmatched weak diagonals */ ( /* input, not modified */ Int n2, /* C is n2-by-n2 */ Int Cp [ ], /* size n2+1, column pointers for C */ Int Ci [ ], /* size snz = Cp [n2], row indices for C */ Int Degree [ ], /* Degree [i] = degree of row i of C+C' */ /* input, not defined on output */ Int Next [ ], /* Next [k] == IS_WEAK if k is a weak diagonal */ Int Ri [ ], /* Ri [i] is the length of row i in C */ /* output, not defined on input */ Int P [ ], /* workspace, not defined on input or output */ Int Rp [ ], Int Head [ ] ) { Int deg, newcol, row, col, p, p2, unmatched, k, j, j2, j_best, best, jdiff, jdiff_best, jdeg, jdeg_best, cp, cp1, cp2, rp, rp1, rp2, maxdeg, mindeg ; /* ---------------------------------------------------------------------- */ /* place weak diagonals in the degree lists */ /* ---------------------------------------------------------------------- */ for (deg = 0 ; deg < n2 ; deg++) { Head [deg] = EMPTY ; } maxdeg = 0 ; mindeg = Int_MAX ; for (newcol = n2-1 ; newcol >= 0 ; newcol--) { if (Next [newcol] == IS_WEAK) { /* add this column to the list of weak nodes */ DEBUGm1 ((" newcol "ID" has a weak diagonal deg "ID"\n", newcol, deg)) ; deg = Degree [newcol] ; ASSERT (deg >= 0 && deg < n2) ; Next [newcol] = Head [deg] ; Head [deg] = newcol ; maxdeg = MAX (maxdeg, deg) ; mindeg = MIN (mindeg, deg) ; } } /* ---------------------------------------------------------------------- */ /* construct R = C' (C = strong entries in pruned submatrix) */ /* ---------------------------------------------------------------------- */ /* Ri [0..n2-1] is the length of each row of R */ /* use P as temporary pointer into the row form of R [ */ Rp [0] = 0 ; for (row = 0 ; row < n2 ; row++) { Rp [row+1] = Rp [row] + Ri [row] ; P [row] = Rp [row] ; } /* Ri no longer needed for row counts */ /* all entries in C are strong */ for (col = 0 ; col < n2 ; col++) { p2 = Cp [col+1] ; for (p = Cp [col] ; p < p2 ; p++) { /* place the column index in row = Ci [p] */ Ri [P [Ci [p]]++] = col ; } } /* contents of P no longer needed ] */ #ifndef NDEBUG DEBUG0 (("==================R: row form of strong entries in A:\n")) ; UMF_dump_col_matrix ((double *) NULL, #ifdef COMPLEX (double *) NULL, #endif Ri, Rp, n2, n2, Rp [n2]) ; #endif ASSERT (AMD_valid (n2, n2, Rp, Ri) == AMD_OK) ; /* ---------------------------------------------------------------------- */ /* for each weak diagonal, find a pair of strong off-diagonal entries */ /* ---------------------------------------------------------------------- */ for (row = 0 ; row < n2 ; row++) { P [row] = EMPTY ; } unmatched = 0 ; best = EMPTY ; jdiff = EMPTY ; jdeg = EMPTY ; for (deg = mindeg ; deg <= maxdeg ; deg++) { /* find the next weak diagonal of lowest degree */ DEBUGm2 (("---------------------------------- Deg: "ID"\n", deg)) ; for (k = Head [deg] ; k != EMPTY ; k = Next [k]) { DEBUGm2 (("k: "ID"\n", k)) ; if (P [k] == EMPTY) { /* C (k,k) is a weak diagonal entry. Find an index j != k such * that C (j,k) and C (k,j) are both strong, and also such * that Degree [j] is minimized. In case of a tie, pick * the smallest index j. C and R contain the pattern of * strong entries only. * * Note that row k of R and column k of C are both sorted. */ DEBUGm4 (("===== Weak diagonal k = "ID"\n", k)) ; DEBUG1 (("Column k of C:\n")) ; for (p = Cp [k] ; p < Cp [k+1] ; p++) { DEBUG1 ((" "ID": deg "ID"\n", Ci [p], Degree [Ci [p]])); } DEBUG1 (("Row k of R (strong entries only):\n")) ; for (p = Rp [k] ; p < Rp [k+1] ; p++) { DEBUG1 ((" "ID": deg "ID"\n", Ri [p], Degree [Ri [p]])); } /* no (C (k,j), C (j,k)) pair exists yet */ j_best = EMPTY ; jdiff_best = Int_MAX ; jdeg_best = Int_MAX ; /* pointers into column k (including values) */ cp1 = Cp [k] ; cp2 = Cp [k+1] ; cp = cp1 ; /* pointers into row k (strong entries only, no values) */ rp1 = Rp [k] ; rp2 = Rp [k+1] ; rp = rp1 ; /* while entries searched in column k and row k */ while (TRUE) { if (cp >= cp2) { /* no more entries in this column */ break ; } /* get C (j,k), which is strong */ j = Ci [cp] ; if (rp >= rp2) { /* no more entries in this column */ break ; } /* get R (k,j2), which is strong */ j2 = Ri [rp] ; if (j < j2) { /* C (j,k) is strong, but R (k,j) is not strong */ cp++ ; continue ; } if (j2 < j) { /* C (k,j2) is strong, but R (j2,k) is not strong */ rp++ ; continue ; } /* j == j2: C (j,k) is strong and R (k,j) is strong */ best = FALSE ; if (P [j] == EMPTY) { /* j has not yet been matched */ jdeg = Degree [j] ; jdiff = SCALAR_ABS (k-j) ; DEBUG1 (("Try candidate j "ID" deg "ID" diff "ID "\n", j, jdeg, jdiff)) ; if (j_best == EMPTY) { /* this is the first candidate seen */ DEBUG1 ((" first\n")) ; best = TRUE ; } else { if (jdeg < jdeg_best) { /* the degree of j is best seen so far. */ DEBUG1 ((" least degree\n")) ; best = TRUE ; } else if (jdeg == jdeg_best) { /* degree of j and j_best are the same */ /* tie break by nearest node number */ if (jdiff < jdiff_best) { DEBUG1 ((" tie degree, closer\n")) ; best = TRUE ; } else if (jdiff == jdiff_best) { /* |j-k| = |j_best-k|. For any given k * and j_best there is only one other j * than can be just as close as j_best. * Tie break by picking the smaller of * j and j_best */ DEBUG1 ((" tie degree, as close\n")); best = j < j_best ; } } else { /* j has higher degree than best so far */ best = FALSE ; } } } if (best) { /* j is best match for k */ /* found a strong pair, A (j,k) and A (k,j) */ DEBUG1 ((" --- Found pair k: "ID" j: " ID " jdeg: "ID" jdiff: "ID"\n", k, j, jdeg, jdiff)) ; ASSERT (jdiff != EMPTY) ; ASSERT (jdeg != EMPTY) ; j_best = j ; jdeg_best = jdeg ; jdiff_best = jdiff ; } /* get the next entries in column k and row k */ cp++ ; rp++ ; } /* save the pair (j,k), if we found one */ if (j_best != EMPTY) { j = j_best ; DEBUGm4 ((" --- best pair j: "ID" for k: "ID"\n", j, k)) ; P [k] = j ; P [j] = k ; } else { /* no match was found for k */ unmatched++ ; } } } } /* ---------------------------------------------------------------------- */ /* finalize the row permutation, P */ /* ---------------------------------------------------------------------- */ for (k = 0 ; k < n2 ; k++) { if (P [k] == EMPTY) { P [k] = k ; } } ASSERT (UMF_is_permutation (P, Rp, n2, n2)) ; return (unmatched) ; }
void amdtest (cholmod_sparse *A) { double Control [AMD_CONTROL], Info [AMD_INFO], alpha ; Int *P, *Cp, *Ci, *Sp, *Si, *Bp, *Bi, *Ep, *Ei, *Fp, *Fi, *Len, *Nv, *Next, *Head, *Elen, *Deg, *Wi, *W, *Flag ; cholmod_sparse *C, *B, *S, *E, *F ; Int i, j, n, nrow, ncol, ok, cnz, bnz, p, trial, sorted ; /* ---------------------------------------------------------------------- */ /* get inputs */ /* ---------------------------------------------------------------------- */ printf ("\nAMD test\n") ; if (A == NULL) { return ; } if (A->stype) { B = CHOLMOD(copy) (A, 0, 0, cm) ; } else { B = CHOLMOD(aat) (A, NULL, 0, 0, cm) ; } if (A->nrow != A->ncol) { F = CHOLMOD(copy_sparse) (B, cm) ; OK (F->nrow == F->ncol) ; CHOLMOD(sort) (F, cm) ; } else { /* A is square and unsymmetric, and may have entries in A+A' that * are not in A */ F = CHOLMOD(copy_sparse) (A, cm) ; CHOLMOD(sort) (F, cm) ; } C = CHOLMOD(copy_sparse) (B, cm) ; nrow = C->nrow ; ncol = C->ncol ; n = nrow ; OK (nrow == ncol) ; Cp = C->p ; Ci = C->i ; Bp = B->p ; Bi = B->i ; /* ---------------------------------------------------------------------- */ /* S = sorted form of B, using AMD_preprocess */ /* ---------------------------------------------------------------------- */ cnz = CHOLMOD(nnz) (C, cm) ; S = CHOLMOD(allocate_sparse) (n, n, cnz, TRUE, TRUE, 0, CHOLMOD_PATTERN, cm); Sp = S->p ; Si = S->i ; W = CHOLMOD(malloc) (n, sizeof (Int), cm) ; Flag = CHOLMOD(malloc) (n, sizeof (Int), cm) ; AMD_preprocess (n, Bp, Bi, Sp, Si, W, Flag) ; /* ---------------------------------------------------------------------- */ /* allocate workspace for amd */ /* ---------------------------------------------------------------------- */ P = CHOLMOD(malloc) (n+1, sizeof (Int), cm) ; Len = CHOLMOD(malloc) (n, sizeof (Int), cm) ; Nv = CHOLMOD(malloc) (n, sizeof (Int), cm) ; Next = CHOLMOD(malloc) (n, sizeof (Int), cm) ; Head = CHOLMOD(malloc) (n+1, sizeof (Int), cm) ; Elen = CHOLMOD(malloc) (n, sizeof (Int), cm) ; Deg = CHOLMOD(malloc) (n, sizeof (Int), cm) ; Wi = CHOLMOD(malloc) (n, sizeof (Int), cm) ; /* ---------------------------------------------------------------------- */ for (sorted = 0 ; sorted <= 1 ; sorted++) { if (sorted) CHOLMOD(sort) (C, cm) ; Cp = C->p ; Ci = C->i ; /* ------------------------------------------------------------------ */ /* order C with AMD_order */ /* ------------------------------------------------------------------ */ AMD_defaults (Control) ; AMD_defaults (NULL) ; AMD_control (Control) ; AMD_control (NULL) ; AMD_info (NULL) ; ok = AMD_order (n, Cp, Ci, P, Control, Info) ; printf ("amd return value: "ID"\n", ok) ; AMD_info (Info) ; OK (sorted ? (ok == AMD_OK) : (ok >= AMD_OK)) ; OK (CHOLMOD(print_perm) (P, n, n, "AMD permutation", cm)) ; /* no dense rows/cols */ alpha = Control [AMD_DENSE] ; Control [AMD_DENSE] = -1 ; AMD_control (Control) ; ok = AMD_order (n, Cp, Ci, P, Control, Info) ; printf ("amd return value: "ID"\n", ok) ; AMD_info (Info) ; OK (sorted ? (ok == AMD_OK) : (ok >= AMD_OK)) ; OK (CHOLMOD(print_perm) (P, n, n, "AMD permutation (alpha=-1)", cm)) ; /* many dense rows/cols */ Control [AMD_DENSE] = 0 ; AMD_control (Control) ; ok = AMD_order (n, Cp, Ci, P, Control, Info) ; printf ("amd return value: "ID"\n", ok) ; AMD_info (Info) ; OK (sorted ? (ok == AMD_OK) : (ok >= AMD_OK)) ; OK (CHOLMOD(print_perm) (P, n, n, "AMD permutation (alpha=0)", cm)) ; Control [AMD_DENSE] = alpha ; /* no aggressive absorption */ Control [AMD_AGGRESSIVE] = FALSE ; AMD_control (Control) ; ok = AMD_order (n, Cp, Ci, P, Control, Info) ; printf ("amd return value: "ID"\n", ok) ; AMD_info (Info) ; OK (sorted ? (ok == AMD_OK) : (ok >= AMD_OK)) ; OK (CHOLMOD(print_perm) (P, n, n, "AMD permutation (no agg) ", cm)) ; Control [AMD_AGGRESSIVE] = TRUE ; /* ------------------------------------------------------------------ */ /* order F with AMD_order */ /* ------------------------------------------------------------------ */ Fp = F->p ; Fi = F->i ; ok = AMD_order (n, Fp, Fi, P, Control, Info) ; printf ("amd return value: "ID"\n", ok) ; AMD_info (Info) ; OK (sorted ? (ok == AMD_OK) : (ok >= AMD_OK)) ; OK (CHOLMOD(print_perm) (P, n, n, "F: AMD permutation", cm)) ; /* ------------------------------------------------------------------ */ /* order S with AMD_order */ /* ------------------------------------------------------------------ */ ok = AMD_order (n, Sp, Si, P, Control, Info) ; printf ("amd return value: "ID"\n", ok) ; AMD_info (Info) ; OK (sorted ? (ok == AMD_OK) : (ok >= AMD_OK)) ; OK (CHOLMOD(print_perm) (P, n, n, "AMD permutation", cm)) ; /* ------------------------------------------------------------------ */ /* order E with AMD_2, which destroys its contents */ /* ------------------------------------------------------------------ */ E = CHOLMOD(copy) (B, 0, -1, cm) ; /* remove diagonal entries */ bnz = CHOLMOD(nnz) (E, cm) ; /* add the bare minimum extra space to E */ ok = CHOLMOD(reallocate_sparse) (bnz + n, E, cm) ; OK (ok) ; Ep = E->p ; Ei = E->i ; for (j = 0 ; j < n ; j++) { Len [j] = Ep [j+1] - Ep [j] ; } printf ("calling AMD_2:\n") ; if (n > 0) { AMD_2 (n, Ep, Ei, Len, E->nzmax, Ep [n], Nv, Next, P, Head, Elen, Deg, Wi, Control, Info) ; AMD_info (Info) ; OK (CHOLMOD(print_perm) (P, n, n, "AMD2 permutation", cm)) ; } /* ------------------------------------------------------------------ */ /* error tests */ /* ------------------------------------------------------------------ */ ok = AMD_order (n, Cp, Ci, P, Control, Info) ; OK (sorted ? (ok == AMD_OK) : (ok >= AMD_OK)) ; ok = AMD_order (-1, Cp, Ci, P, Control, Info) ; OK (ok == AMD_INVALID); ok = AMD_order (0, Cp, Ci, P, Control, Info) ; OK (sorted ? (ok == AMD_OK) : (ok >= AMD_OK)) ; ok = AMD_order (n, NULL, Ci, P, Control, Info) ; OK (ok == AMD_INVALID); ok = AMD_order (n, Cp, NULL, P, Control, Info) ; OK (ok == AMD_INVALID); ok = AMD_order (n, Cp, Ci, NULL, Control, Info) ; OK (ok == AMD_INVALID); if (n > 0) { printf ("AMD error tests:\n") ; p = Cp [n] ; Cp [n] = -1 ; ok = AMD_order (n, Cp, Ci, P, Control, Info) ; OK (ok == AMD_INVALID) ; if (Size_max/2 == Int_max) { Cp [n] = Int_max ; ok = AMD_order (n, Cp, Ci, P, Control, Info) ; printf ("AMD status is "ID"\n", ok) ; OK (ok == AMD_OUT_OF_MEMORY) ; } Cp [n] = p ; ok = AMD_order (n, Cp, Ci, P, Control, Info) ; OK (sorted ? (ok == AMD_OK) : (ok >= AMD_OK)) ; if (Cp [n] > 0) { printf ("Mangle column zero:\n") ; i = Ci [0] ; Ci [0] = -1 ; ok = AMD_order (n, Cp, Ci, P, Control, Info) ; AMD_info (Info) ; OK (ok == AMD_INVALID) ; Ci [0] = i ; } } ok = AMD_valid (n, n, Sp, Si) ; OK (sorted ? (ok == AMD_OK) : (ok >= AMD_OK)) ; ok = AMD_valid (-1, n, Sp, Si) ; OK (ok == AMD_INVALID) ; ok = AMD_valid (n, -1, Sp, Si) ; OK (ok == AMD_INVALID) ; ok = AMD_valid (n, n, NULL, Si) ; OK (ok == AMD_INVALID) ; ok = AMD_valid (n, n, Sp, NULL) ; OK (ok == AMD_INVALID) ; if (n > 0 && Sp [n] > 0) { p = Sp [n] ; Sp [n] = -1 ; ok = AMD_valid (n, n, Sp, Si) ; OK (ok == AMD_INVALID) ; Sp [n] = p ; p = Sp [0] ; Sp [0] = -1 ; ok = AMD_valid (n, n, Sp, Si) ; OK (ok == AMD_INVALID) ; Sp [0] = p ; p = Sp [1] ; Sp [1] = -1 ; ok = AMD_valid (n, n, Sp, Si) ; OK (ok == AMD_INVALID) ; Sp [1] = p ; i = Si [0] ; Si [0] = -1 ; ok = AMD_valid (n, n, Sp, Si) ; OK (ok == AMD_INVALID) ; Si [0] = i ; } ok = AMD_valid (n, n, Sp, Si) ; OK (sorted ? (ok == AMD_OK) : (ok >= AMD_OK)) ; AMD_preprocess (n, Bp, Bi, Sp, Si, W, Flag) ; ok = AMD_valid (n, n, Sp, Si) ; OK (ok == AMD_OK) ; if (n > 0 && Bp [n] > 0) { p = Bp [n] ; Bp [n] = -1 ; ok = AMD_valid (n, n, Bp, Bi) ; OK (ok == AMD_INVALID) ; Bp [n] = p ; p = Bp [1] ; Bp [1] = -1 ; ok = AMD_valid (n, n, Bp, Bi) ; OK (ok == AMD_INVALID) ; Bp [1] = p ; i = Bi [0] ; Bi [0] = -1 ; ok = AMD_valid (n, n, Bp, Bi) ; OK (ok == AMD_INVALID) ; Bi [0] = i ; } AMD_preprocess (n, Bp, Bi, Sp, Si, W, Flag) ; Info [AMD_STATUS] = 777 ; AMD_info (Info) ; /* ------------------------------------------------------------------ */ /* memory tests */ /* ------------------------------------------------------------------ */ if (n > 0) { amd_malloc = cm->malloc_memory ; amd_free = cm->free_memory ; ok = AMD_order (n, Cp, Ci, P, Control, Info) ; OK (sorted ? (ok == AMD_OK) : (ok >= AMD_OK)) ; test_memory_handler ( ) ; amd_malloc = cm->malloc_memory ; amd_free = cm->free_memory ; for (trial = 0 ; trial < 6 ; trial++) { my_tries = trial ; printf ("AMD memory trial "ID"\n", trial) ; ok = AMD_order (n, Cp, Ci, P, Control, Info) ; AMD_info (Info) ; OK (ok == AMD_OUT_OF_MEMORY || (sorted ? (ok == AMD_OK) : (ok >= AMD_OK))) ; } normal_memory_handler ( ) ; OK (CHOLMOD(print_perm) (P, n, n, "AMD2 permutation", cm)) ; amd_malloc = cm->malloc_memory ; amd_free = cm->free_memory ; } CHOLMOD(free_sparse) (&E, cm) ; } /* ---------------------------------------------------------------------- */ /* free everything */ /* ---------------------------------------------------------------------- */ CHOLMOD(free) (n, sizeof (Int), Len, cm) ; CHOLMOD(free) (n, sizeof (Int), Nv, cm) ; CHOLMOD(free) (n, sizeof (Int), Next, cm) ; CHOLMOD(free) (n+1, sizeof (Int), Head, cm) ; CHOLMOD(free) (n, sizeof (Int), Elen, cm) ; CHOLMOD(free) (n, sizeof (Int), Deg, cm) ; CHOLMOD(free) (n, sizeof (Int), Wi, cm) ; CHOLMOD(free) (n+1, sizeof (Int), P, cm) ; CHOLMOD(free) (n, sizeof (Int), W, cm) ; CHOLMOD(free) (n, sizeof (Int), Flag, cm) ; CHOLMOD(free_sparse) (&S, cm) ; CHOLMOD(free_sparse) (&B, cm) ; CHOLMOD(free_sparse) (&C, cm) ; CHOLMOD(free_sparse) (&F, cm) ; }
GLOBAL void UMF_2by2 ( /* input, not modified: */ Int n, /* A is n-by-n */ const Int Ap [ ], /* size n+1 */ const Int Ai [ ], /* size nz = Ap [n] */ const double Ax [ ], /* size nz if present */ #ifdef COMPLEX const double Az [ ], /* size nz if present */ #endif double tol, /* tolerance for determining whether or not an * entry is numerically acceptable. If tol <= 0 * then all numerical values ignored. */ Int scale, /* scaling to perform (none, sum, or max) */ Int Cperm1 [ ], /* singleton permutations */ #ifndef NDEBUG Int Rperm1 [ ], /* not needed, since Rperm1 = Cperm1 for submatrix S */ #endif Int InvRperm1 [ ], /* inverse of Rperm1 */ Int n1, /* number of singletons */ Int nempty, /* number of empty rows/cols */ /* input, contents undefined on output: */ Int Degree [ ], /* Degree [j] is the number of off-diagonal * entries in row/column j of S+S', where * where S = A (Cperm1 [n1..], Rperm1 [n1..]). * Note that S is not used, nor formed. */ /* output: */ Int P [ ], /* P [k] = i means original row i is kth row in S(P,:) * where S = A (Cperm1 [n1..], Rperm1 [n1..]) */ Int *p_nweak, Int *p_unmatched, /* workspace (not defined on input or output): */ Int Ri [ ], /* of size >= max (nz, n) */ Int Rp [ ], /* of size n+1 */ double Rs [ ], /* of size n if present. Rs = sum (abs (A),2) or * max (abs (A),2), the sum or max of each row. Unused * if scale is equal to UMFPACK_SCALE_NONE. */ Int Head [ ], /* of size n. Head pointers for bucket sort */ Int Next [ ], /* of size n. Next pointers for bucket sort */ Int Ci [ ], /* size nz */ Int Cp [ ] /* size n+1 */ ) { /* ---------------------------------------------------------------------- */ /* local variables */ /* ---------------------------------------------------------------------- */ Entry aij ; double cmax, value, rs, ctol, dvalue ; Int k, p, row, col, do_values, do_sum, do_max, do_scale, nweak, weak, p1, p2, dfound, unmatched, n2, oldrow, newrow, oldcol, newcol, pp ; #ifdef COMPLEX Int split = SPLIT (Az) ; #endif #ifndef NRECIPROCAL Int do_recip = FALSE ; #endif #ifndef NDEBUG /* UMF_debug += 99 ; */ DEBUGm3 (("\n ==================================UMF_2by2: tol %g\n", tol)) ; ASSERT (AMD_valid (n, n, Ap, Ai) == AMD_OK) ; for (k = n1 ; k < n - nempty ; k++) { ASSERT (Cperm1 [k] == Rperm1 [k]) ; } #endif /* ---------------------------------------------------------------------- */ /* determine scaling options */ /* ---------------------------------------------------------------------- */ /* use the values, but only if they are present */ /* ignore the values if tol <= 0 */ do_values = (tol > 0) && (Ax != (double *) NULL) ; if (do_values && (Rs != (double *) NULL)) { do_sum = (scale == UMFPACK_SCALE_SUM) ; do_max = (scale == UMFPACK_SCALE_MAX) ; } else { /* no scaling */ do_sum = FALSE ; do_max = FALSE ; } do_scale = do_max || do_sum ; DEBUGm3 (("do_values "ID" do_sum "ID" do_max "ID" do_scale "ID"\n", do_values, do_sum, do_max, do_scale)) ; /* ---------------------------------------------------------------------- */ /* compute the row scaling, if requested */ /* ---------------------------------------------------------------------- */ /* see also umf_kernel_init */ if (do_scale) { #ifndef NRECIPROCAL double rsmin ; #endif for (row = 0 ; row < n ; row++) { Rs [row] = 0.0 ; } for (col = 0 ; col < n ; col++) { p2 = Ap [col+1] ; for (p = Ap [col] ; p < p2 ; p++) { row = Ai [p] ; ASSIGN (aij, Ax, Az, p, split) ; APPROX_ABS (value, aij) ; rs = Rs [row] ; if (!SCALAR_IS_NAN (rs)) { if (SCALAR_IS_NAN (value)) { /* if any entry in a row is NaN, then the scale factor * for the row is NaN. It will be set to 1 later. */ Rs [row] = value ; } else if (do_max) { Rs [row] = MAX (rs, value) ; } else { Rs [row] += value ; } } } } #ifndef NRECIPROCAL rsmin = Rs [0] ; if (SCALAR_IS_ZERO (rsmin) || SCALAR_IS_NAN (rsmin)) { rsmin = 1.0 ; } #endif for (row = 0 ; row < n ; row++) { /* do not scale an empty row, or a row with a NaN */ rs = Rs [row] ; if (SCALAR_IS_ZERO (rs) || SCALAR_IS_NAN (rs)) { Rs [row] = 1.0 ; } #ifndef NRECIPROCAL rsmin = MIN (rsmin, Rs [row]) ; #endif } #ifndef NRECIPROCAL /* multiply by the reciprocal if Rs is not too small */ do_recip = (rsmin >= RECIPROCAL_TOLERANCE) ; if (do_recip) { /* invert the scale factors */ for (row = 0 ; row < n ; row++) { Rs [row] = 1.0 / Rs [row] ; } } #endif } /* ---------------------------------------------------------------------- */ /* compute the max in each column and find diagonal */ /* ---------------------------------------------------------------------- */ nweak = 0 ; #ifndef NDEBUG for (k = 0 ; k < n ; k++) { ASSERT (Rperm1 [k] >= 0 && Rperm1 [k] < n) ; ASSERT (InvRperm1 [Rperm1 [k]] == k) ; } #endif n2 = n - n1 - nempty ; /* use Ri to count the number of strong entries in each row */ for (row = 0 ; row < n2 ; row++) { Ri [row] = 0 ; } pp = 0 ; ctol = 0 ; dvalue = 1 ; /* construct C = pruned submatrix, strong values only, column form */ for (k = n1 ; k < n - nempty ; k++) { oldcol = Cperm1 [k] ; newcol = k - n1 ; Next [newcol] = EMPTY ; DEBUGm1 (("Column "ID" newcol "ID" oldcol "ID"\n", k, newcol, oldcol)) ; Cp [newcol] = pp ; dfound = FALSE ; p1 = Ap [oldcol] ; p2 = Ap [oldcol+1] ; if (do_values) { cmax = 0 ; dvalue = 0 ; if (!do_scale) { /* no scaling */ for (p = p1 ; p < p2 ; p++) { oldrow = Ai [p] ; ASSERT (oldrow >= 0 && oldrow < n) ; newrow = InvRperm1 [oldrow] - n1 ; ASSERT (newrow >= -n1 && newrow < n2) ; if (newrow < 0) continue ; ASSIGN (aij, Ax, Az, p, split) ; APPROX_ABS (value, aij) ; /* if either cmax or value is NaN, define cmax as NaN */ if (!SCALAR_IS_NAN (cmax)) { if (SCALAR_IS_NAN (value)) { cmax = value ; } else { cmax = MAX (cmax, value) ; } } if (oldrow == oldcol) { /* we found the diagonal entry in this column */ dvalue = value ; dfound = TRUE ; ASSERT (newrow == newcol) ; } } } #ifndef NRECIPROCAL else if (do_recip) { /* multiply by the reciprocal */ for (p = p1 ; p < p2 ; p++) { oldrow = Ai [p] ; ASSERT (oldrow >= 0 && oldrow < n) ; newrow = InvRperm1 [oldrow] - n1 ; ASSERT (newrow >= -n1 && newrow < n2) ; if (newrow < 0) continue ; ASSIGN (aij, Ax, Az, p, split) ; APPROX_ABS (value, aij) ; value *= Rs [oldrow] ; /* if either cmax or value is NaN, define cmax as NaN */ if (!SCALAR_IS_NAN (cmax)) { if (SCALAR_IS_NAN (value)) { cmax = value ; } else { cmax = MAX (cmax, value) ; } } if (oldrow == oldcol) { /* we found the diagonal entry in this column */ dvalue = value ; dfound = TRUE ; ASSERT (newrow == newcol) ; } } } #endif else { /* divide instead */ for (p = p1 ; p < p2 ; p++) { oldrow = Ai [p] ; ASSERT (oldrow >= 0 && oldrow < n) ; newrow = InvRperm1 [oldrow] - n1 ; ASSERT (newrow >= -n1 && newrow < n2) ; if (newrow < 0) continue ; ASSIGN (aij, Ax, Az, p, split) ; APPROX_ABS (value, aij) ; value /= Rs [oldrow] ; /* if either cmax or value is NaN, define cmax as NaN */ if (!SCALAR_IS_NAN (cmax)) { if (SCALAR_IS_NAN (value)) { cmax = value ; } else { cmax = MAX (cmax, value) ; } } if (oldrow == oldcol) { /* we found the diagonal entry in this column */ dvalue = value ; dfound = TRUE ; ASSERT (newrow == newcol) ; } } } ctol = tol * cmax ; DEBUGm1 ((" cmax col "ID" %g ctol %g\n", oldcol, cmax, ctol)) ; } else { for (p = p1 ; p < p2 ; p++) { oldrow = Ai [p] ; ASSERT (oldrow >= 0 && oldrow < n) ; newrow = InvRperm1 [oldrow] - n1 ; ASSERT (newrow >= -n1 && newrow < n2) ; if (newrow < 0) continue ; Ci [pp++] = newrow ; if (oldrow == oldcol) { /* we found the diagonal entry in this column */ ASSERT (newrow == newcol) ; dfound = TRUE ; } /* count the entries in each column */ Ri [newrow]++ ; } } /* ------------------------------------------------------------------ */ /* flag the weak diagonals */ /* ------------------------------------------------------------------ */ if (!dfound) { /* no diagonal entry present */ weak = TRUE ; } else { /* diagonal entry is present, check its value */ weak = (do_values) ? WEAK (dvalue, ctol) : FALSE ; } if (weak) { /* flag this column as weak */ DEBUG0 (("Weak!\n")) ; Next [newcol] = IS_WEAK ; nweak++ ; } /* ------------------------------------------------------------------ */ /* count entries in each row that are not numerically weak */ /* ------------------------------------------------------------------ */ if (do_values) { if (!do_scale) { /* no scaling */ for (p = p1 ; p < p2 ; p++) { oldrow = Ai [p] ; newrow = InvRperm1 [oldrow] - n1 ; if (newrow < 0) continue ; ASSIGN (aij, Ax, Az, p, split) ; APPROX_ABS (value, aij) ; weak = WEAK (value, ctol) ; if (!weak) { DEBUG0 ((" strong: row "ID": %g\n", oldrow, value)) ; Ci [pp++] = newrow ; Ri [newrow]++ ; } } } #ifndef NRECIPROCAL else if (do_recip) { /* multiply by the reciprocal */ for (p = p1 ; p < p2 ; p++) { oldrow = Ai [p] ; newrow = InvRperm1 [oldrow] - n1 ; if (newrow < 0) continue ; ASSIGN (aij, Ax, Az, p, split) ; APPROX_ABS (value, aij) ; value *= Rs [oldrow] ; weak = WEAK (value, ctol) ; if (!weak) { DEBUG0 ((" strong: row "ID": %g\n", oldrow, value)) ; Ci [pp++] = newrow ; Ri [newrow]++ ; } } } #endif else { /* divide instead */ for (p = p1 ; p < p2 ; p++) { oldrow = Ai [p] ; newrow = InvRperm1 [oldrow] - n1 ; if (newrow < 0) continue ; ASSIGN (aij, Ax, Az, p, split) ; APPROX_ABS (value, aij) ; value /= Rs [oldrow] ; weak = WEAK (value, ctol) ; if (!weak) { DEBUG0 ((" strong: row "ID": %g\n", oldrow, value)) ; Ci [pp++] = newrow ; Ri [newrow]++ ; } } } } } Cp [n2] = pp ; ASSERT (AMD_valid (n2, n2, Cp, Ci) == AMD_OK) ; if (nweak == 0) { /* nothing to do, quick return */ DEBUGm2 (("\n =============================UMF_2by2: quick return\n")) ; for (k = 0 ; k < n ; k++) { P [k] = k ; } *p_nweak = 0 ; *p_unmatched = 0 ; return ; } #ifndef NDEBUG for (k = 0 ; k < n2 ; k++) { P [k] = EMPTY ; } for (k = 0 ; k < n2 ; k++) { ASSERT (Degree [k] >= 0 && Degree [k] < n2) ; } #endif /* ---------------------------------------------------------------------- */ /* find the 2-by-2 permutation */ /* ---------------------------------------------------------------------- */ /* The matrix S is now mapped to the index range 0 to n2-1. We have * S = A (Rperm [n1 .. n-nempty-1], Cperm [n1 .. n-nempty-1]), and then * C = pattern of strong entries in S. A weak diagonal k in S is marked * with Next [k] = IS_WEAK. */ unmatched = two_by_two (n2, Cp, Ci, Degree, Next, Ri, P, Rp, Head) ; /* ---------------------------------------------------------------------- */ *p_nweak = nweak ; *p_unmatched = unmatched ; #ifndef NDEBUG DEBUGm4 (("UMF_2by2: weak "ID" unmatched "ID"\n", nweak, unmatched)) ; for (row = 0 ; row < n ; row++) { DEBUGm2 (("P ["ID"] = "ID"\n", row, P [row])) ; } DEBUGm2 (("\n =============================UMF_2by2: done\n\n")) ; #endif }
GLOBAL void AMD_1 ( Int n, /* n > 0 */ const Int Ap [ ], /* input of size n+1, not modified */ const Int Ai [ ], /* input of size nz = Ap [n], not modified */ Int P [ ], /* size n output permutation */ Int Pinv [ ], /* size n output inverse permutation */ Int Len [ ], /* size n input, undefined on output */ Int slen, /* slen >= sum (Len [0..n-1]) + 7n, * ideally slen = 1.2 * sum (Len) + 8n */ Int S [ ], /* size slen workspace */ double Control [ ], /* input array of size AMD_CONTROL */ double Info [ ] /* output array of size AMD_INFO */ ) { Int i, j, k, p, pfree, iwlen, pj, p1, p2, pj2, *Iw, *Pe, *Nv, *Head, *Elen, *Degree, *s, *W, *Sp, *Tp ; /* --------------------------------------------------------------------- */ /* construct the matrix for AMD_2 */ /* --------------------------------------------------------------------- */ ASSERT (n > 0) ; iwlen = slen - 6*n ; s = S ; Pe = s ; s += n ; Nv = s ; s += n ; Head = s ; s += n ; Elen = s ; s += n ; Degree = s ; s += n ; W = s ; s += n ; Iw = s ; s += iwlen ; ASSERT (AMD_valid (n, n, Ap, Ai)) ; /* construct the pointers for A+A' */ Sp = Nv ; /* use Nv and W as workspace for Sp and Tp [ */ Tp = W ; pfree = 0 ; for (j = 0 ; j < n ; j++) { Pe [j] = pfree ; Sp [j] = pfree ; pfree += Len [j] ; } /* Note that this restriction on iwlen is slightly more restrictive than * what is strictly required in AMD_2. AMD_2 can operate with no elbow * room at all, but it will be very slow. For better performance, at * least size-n elbow room is enforced. */ ASSERT (iwlen >= pfree + n) ; #ifndef NDEBUG for (p = 0 ; p < iwlen ; p++) Iw [p] = EMPTY ; #endif for (k = 0 ; k < n ; k++) { AMD_DEBUG1 (("Construct row/column k= "ID" of A+A'\n", k)) ; p1 = Ap [k] ; p2 = Ap [k+1] ; /* construct A+A' */ for (p = p1 ; p < p2 ; ) { /* scan the upper triangular part of A */ j = Ai [p] ; ASSERT (j >= 0 && j < n) ; if (j < k) { /* entry A (j,k) in the strictly upper triangular part */ ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ; ASSERT (Sp [k] < (k == n-1 ? pfree : Pe [k+1])) ; Iw [Sp [j]++] = k ; Iw [Sp [k]++] = j ; p++ ; } else if (j == k) { /* skip the diagonal */ p++ ; break ; } else /* j > k */ { /* first entry below the diagonal */ break ; } /* scan lower triangular part of A, in column j until reaching * row k. Start where last scan left off. */ ASSERT (Ap [j] <= Tp [j] && Tp [j] <= Ap [j+1]) ; pj2 = Ap [j+1] ; for (pj = Tp [j] ; pj < pj2 ; ) { i = Ai [pj] ; ASSERT (i >= 0 && i < n) ; if (i < k) { /* A (i,j) is only in the lower part, not in upper */ ASSERT (Sp [i] < (i == n-1 ? pfree : Pe [i+1])) ; ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ; Iw [Sp [i]++] = j ; Iw [Sp [j]++] = i ; pj++ ; } else if (i == k) { /* entry A (k,j) in lower part and A (j,k) in upper */ pj++ ; break ; } else /* i > k */ { /* consider this entry later, when k advances to i */ break ; } } Tp [j] = pj ; } Tp [k] = p ; } /* clean up, for remaining mismatched entries */ for (j = 0 ; j < n ; j++) { for (pj = Tp [j] ; pj < Ap [j+1] ; pj++) { i = Ai [pj] ; ASSERT (i >= 0 && i < n) ; /* A (i,j) is only in the lower part, not in upper */ ASSERT (Sp [i] < (i == n-1 ? pfree : Pe [i+1])) ; ASSERT (Sp [j] < (j == n-1 ? pfree : Pe [j+1])) ; Iw [Sp [i]++] = j ; Iw [Sp [j]++] = i ; } } #ifndef NDEBUG for (j = 0 ; j < n-1 ; j++) ASSERT (Sp [j] == Pe [j+1]) ; ASSERT (Sp [n-1] == pfree) ; #endif /* Tp and Sp no longer needed ] */ /* --------------------------------------------------------------------- */ /* order the matrix */ /* --------------------------------------------------------------------- */ AMD_2 (n, Pe, Iw, Len, iwlen, pfree, Nv, Pinv, P, Head, Elen, Degree, W, Control, Info) ; }
GLOBAL Int AMD_order ( Int n, const Int Ap [ ], const Int Ai [ ], Int P [ ], double Control [ ], double Info [ ] ) { Int *Len, *S, nz, i, *Pinv, info, status, *Rp, *Ri, *Cp, *Ci, ok ; size_t nzaat, slen ; double mem = 0 ; #ifndef NDEBUG AMD_debug_init ("amd") ; #endif /* clear the Info array, if it exists */ info = Info != (double *) NULL ; if (info) { for (i = 0 ; i < AMD_INFO ; i++) { Info [i] = EMPTY ; } Info [AMD_N] = n ; Info [AMD_STATUS] = AMD_OK ; } /* make sure inputs exist and n is >= 0 */ if (Ai == (Int *) NULL || Ap == (Int *) NULL || P == (Int *) NULL || n < 0) { if (info) Info [AMD_STATUS] = AMD_INVALID ; return (AMD_INVALID) ; /* arguments are invalid */ } if (n == 0) { return (AMD_OK) ; /* n is 0 so there's nothing to do */ } nz = Ap [n] ; if (info) { Info [AMD_NZ] = nz ; } if (nz < 0) { if (info) Info [AMD_STATUS] = AMD_INVALID ; return (AMD_INVALID) ; } /* check if n or nz will cause size_t overflow */ if (((size_t) n) >= SIZE_T_MAX / sizeof (Int) || ((size_t) nz) >= SIZE_T_MAX / sizeof (Int)) { if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; return (AMD_OUT_OF_MEMORY) ; /* problem too large */ } /* check the input matrix: AMD_OK, AMD_INVALID, or AMD_OK_BUT_JUMBLED */ status = AMD_valid (n, n, Ap, Ai) ; if (status == AMD_INVALID) { if (info) Info [AMD_STATUS] = AMD_INVALID ; return (AMD_INVALID) ; /* matrix is invalid */ } /* allocate two size-n integer workspaces */ Len = (Int*)amd_malloc (n * sizeof (Int)) ; Pinv = (Int*)amd_malloc (n * sizeof (Int)) ; mem += n ; mem += n ; if (!Len || !Pinv) { /* :: out of memory :: */ amd_free (Len) ; amd_free (Pinv) ; if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; return (AMD_OUT_OF_MEMORY) ; } if (status == AMD_OK_BUT_JUMBLED) { /* sort the input matrix and remove duplicate entries */ AMD_DEBUG1 (("Matrix is jumbled\n")) ; Rp = (Int*)amd_malloc ((n+1) * sizeof (Int)) ; Ri = (Int*)amd_malloc (MAX (nz,1) * sizeof (Int)) ; mem += (n+1) ; mem += MAX (nz,1) ; if (!Rp || !Ri) { /* :: out of memory :: */ amd_free (Rp) ; amd_free (Ri) ; amd_free (Len) ; amd_free (Pinv) ; if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; return (AMD_OUT_OF_MEMORY) ; } /* use Len and Pinv as workspace to create R = A' */ AMD_preprocess (n, Ap, Ai, Rp, Ri, Len, Pinv) ; Cp = Rp ; Ci = Ri ; } else { /* order the input matrix as-is. No need to compute R = A' first */ Rp = NULL ; Ri = NULL ; Cp = (Int *) Ap ; Ci = (Int *) Ai ; } /* --------------------------------------------------------------------- */ /* determine the symmetry and count off-diagonal nonzeros in A+A' */ /* --------------------------------------------------------------------- */ nzaat = AMD_aat (n, Cp, Ci, Len, P, Info) ; AMD_DEBUG1 (("nzaat: %g\n", (double) nzaat)) ; ASSERT ((MAX (nz-n, 0) <= nzaat) && (nzaat <= 2 * (size_t) nz)) ; /* --------------------------------------------------------------------- */ /* allocate workspace for matrix, elbow room, and 6 size-n vectors */ /* --------------------------------------------------------------------- */ S = NULL ; slen = nzaat ; /* space for matrix */ ok = ((slen + nzaat/5) >= slen) ; /* check for size_t overflow */ slen += nzaat/5 ; /* add elbow room */ for (i = 0 ; ok && i < 7 ; i++) { ok = ((slen + n) > slen) ; /* check for size_t overflow */ slen += n ; /* size-n elbow room, 6 size-n work */ } mem += slen ; ok = ok && (slen < SIZE_T_MAX / sizeof (Int)) ; /* check for overflow */ ok = ok && (slen < Int_MAX) ; /* S[i] for Int i must be OK */ if (ok) { S = (Int*)amd_malloc (slen * sizeof (Int)) ; } AMD_DEBUG1 (("slen %g\n", (double) slen)) ; if (!S) { /* :: out of memory :: (or problem too large) */ amd_free (Rp) ; amd_free (Ri) ; amd_free (Len) ; amd_free (Pinv) ; if (info) Info [AMD_STATUS] = AMD_OUT_OF_MEMORY ; return (AMD_OUT_OF_MEMORY) ; } if (info) { /* memory usage, in bytes. */ Info [AMD_MEMORY] = mem * sizeof (Int) ; } /* --------------------------------------------------------------------- */ /* order the matrix */ /* --------------------------------------------------------------------- */ AMD_1 (n, Cp, Ci, P, Pinv, Len, slen, S, Control, Info) ; /* --------------------------------------------------------------------- */ /* free the workspace */ /* --------------------------------------------------------------------- */ amd_free (Rp) ; amd_free (Ri) ; amd_free (Len) ; amd_free (Pinv) ; amd_free (S) ; if (info) Info [AMD_STATUS] = status ; return (status) ; /* successful ordering */ }
GLOBAL size_t AMD_aat /* returns nz in A+A' */ ( Int n, const Int Ap [ ], const Int Ai [ ], Int Len [ ], /* Len [j]: length of column j of A+A', excl diagonal*/ Int Tp [ ], /* workspace of size n */ double Info [ ] ) { Int p1, p2, p, i, j, pj, pj2, k, nzdiag, nzboth, nz ; double sym ; size_t nzaat ; #ifndef NDEBUG AMD_debug_init ("AMD AAT") ; for (k = 0 ; k < n ; k++) Tp [k] = EMPTY ; ASSERT (AMD_valid (n, n, Ap, Ai) == AMD_OK) ; #endif if (Info != (double *) NULL) { /* clear the Info array, if it exists */ for (i = 0 ; i < AMD_INFO ; i++) { Info [i] = EMPTY ; } Info [AMD_STATUS] = AMD_OK ; } for (k = 0 ; k < n ; k++) { Len [k] = 0 ; } nzdiag = 0 ; nzboth = 0 ; nz = Ap [n] ; for (k = 0 ; k < n ; k++) { p1 = Ap [k] ; p2 = Ap [k+1] ; AMD_DEBUG2 (("\nAAT Column: "ID" p1: "ID" p2: "ID"\n", k, p1, p2)) ; /* construct A+A' */ for (p = p1 ; p < p2 ; ) { /* scan the upper triangular part of A */ j = Ai [p] ; if (j < k) { /* entry A (j,k) is in the strictly upper triangular part, * add both A (j,k) and A (k,j) to the matrix A+A' */ Len [j]++ ; Len [k]++ ; AMD_DEBUG3 ((" upper ("ID","ID") ("ID","ID")\n", j,k, k,j)); p++ ; } else if (j == k) { /* skip the diagonal */ p++ ; nzdiag++ ; break ; } else /* j > k */ { /* first entry below the diagonal */ break ; } /* scan lower triangular part of A, in column j until reaching * row k. Start where last scan left off. */ ASSERT (Tp [j] != EMPTY) ; ASSERT (Ap [j] <= Tp [j] && Tp [j] <= Ap [j+1]) ; pj2 = Ap [j+1] ; for (pj = Tp [j] ; pj < pj2 ; ) { i = Ai [pj] ; if (i < k) { /* A (i,j) is only in the lower part, not in upper. * add both A (i,j) and A (j,i) to the matrix A+A' */ Len [i]++ ; Len [j]++ ; AMD_DEBUG3 ((" lower ("ID","ID") ("ID","ID")\n", i,j, j,i)) ; pj++ ; } else if (i == k) { /* entry A (k,j) in lower part and A (j,k) in upper */ pj++ ; nzboth++ ; break ; } else /* i > k */ { /* consider this entry later, when k advances to i */ break ; } } Tp [j] = pj ; } /* Tp [k] points to the entry just below the diagonal in column k */ Tp [k] = p ; } /* clean up, for remaining mismatched entries */ for (j = 0 ; j < n ; j++) { for (pj = Tp [j] ; pj < Ap [j+1] ; pj++) { i = Ai [pj] ; /* A (i,j) is only in the lower part, not in upper. * add both A (i,j) and A (j,i) to the matrix A+A' */ Len [i]++ ; Len [j]++ ; AMD_DEBUG3 ((" lower cleanup ("ID","ID") ("ID","ID")\n", i,j, j,i)) ; } } /* --------------------------------------------------------------------- */ /* compute the symmetry of the nonzero pattern of A */ /* --------------------------------------------------------------------- */ /* Given a matrix A, the symmetry of A is: * B = tril (spones (A), -1) + triu (spones (A), 1) ; * sym = nnz (B & B') / nnz (B) ; * or 1 if nnz (B) is zero. */ if (nz == nzdiag) { sym = 1 ; } else { sym = (2 * (double) nzboth) / ((double) (nz - nzdiag)) ; } nzaat = 0 ; for (k = 0 ; k < n ; k++) { nzaat += Len [k] ; } AMD_DEBUG1 (("AMD nz in A+A', excluding diagonal (nzaat) = %g\n", (double) nzaat)) ; AMD_DEBUG1 ((" nzboth: "ID" nz: "ID" nzdiag: "ID" symmetry: %g\n", nzboth, nz, nzdiag, sym)) ; if (Info != (double *) NULL) { Info [AMD_STATUS] = AMD_OK ; Info [AMD_N] = n ; Info [AMD_NZ] = nz ; Info [AMD_SYMMETRY] = sym ; /* symmetry of pattern of A */ Info [AMD_NZDIAG] = nzdiag ; /* nonzeros on diagonal of A */ Info [AMD_NZ_A_PLUS_AT] = nzaat ; /* nonzeros in A+A' */ } return (nzaat) ; }
GLOBAL Int UMF_transpose ( Int n_row, /* A is n_row-by-n_col */ Int n_col, const Int Ap [ ], /* size n_col+1 */ const Int Ai [ ], /* size nz = Ap [n_col] */ const double Ax [ ], /* size nz if present */ const Int P [ ], /* P [k] = i means original row i is kth row in A(P,Q)*/ /* P is identity if not present */ /* size n_row, if present */ const Int Q [ ], /* Q [k] = j means original col j is kth col in A(P,Q)*/ /* Q is identity if not present */ /* size nq, if present */ Int nq, /* size of Q, ignored if Q is (Int *) NULL */ /* output matrix: Rp, Ri, Rx, and Rz: */ Int Rp [ ], /* size n_row+1 */ Int Ri [ ], /* size nz */ double Rx [ ], /* size nz, if present */ Int W [ ], /* size max (n_row,n_col) workspace */ Int check /* if true, then check inputs */ #ifdef COMPLEX , const double Az [ ] /* size nz */ , double Rz [ ] /* size nz */ , Int do_conjugate /* if true, then do conjugate transpose */ /* otherwise, do array transpose */ #endif ) { /* ---------------------------------------------------------------------- */ /* local variables */ /* ---------------------------------------------------------------------- */ Int i, j, k, p, bp, newj, do_values ; #ifdef COMPLEX Int split ; #endif /* ---------------------------------------------------------------------- */ /* check inputs */ /* ---------------------------------------------------------------------- */ #ifndef NDEBUG Int nz ; ASSERT (n_col >= 0) ; nz = (Ap != (Int *) NULL) ? Ap [n_col] : 0 ; DEBUG2 (("UMF_transpose: "ID"-by-"ID" nz "ID"\n", n_row, n_col, nz)) ; #endif if (check) { /* UMFPACK_symbolic skips this check */ /* UMFPACK_transpose always does this check */ if (!Ai || !Ap || !Ri || !Rp || !W) { return (UMFPACK_ERROR_argument_missing) ; } if (n_row <= 0 || n_col <= 0) /* n_row,n_col must be > 0 */ { return (UMFPACK_ERROR_n_nonpositive) ; } if (!UMF_is_permutation (P, W, n_row, n_row) || !UMF_is_permutation (Q, W, nq, nq)) { return (UMFPACK_ERROR_invalid_permutation) ; } if (!AMD_valid (n_row, n_col, Ap, Ai)) { return (UMFPACK_ERROR_invalid_matrix) ; } } #ifndef NDEBUG DEBUG2 (("UMF_transpose, input matrix:\n")) ; UMF_dump_col_matrix (Ax, #ifdef COMPLEX Az, #endif Ai, Ap, n_row, n_col, nz) ; #endif /* ---------------------------------------------------------------------- */ /* count the entries in each row of A */ /* ---------------------------------------------------------------------- */ /* use W as workspace for RowCount */ for (i = 0 ; i < n_row ; i++) { W [i] = 0 ; Rp [i] = 0 ; } if (Q != (Int *) NULL) { for (newj = 0 ; newj < nq ; newj++) { j = Q [newj] ; ASSERT (j >= 0 && j < n_col) ; for (p = Ap [j] ; p < Ap [j+1] ; p++) { i = Ai [p] ; ASSERT (i >= 0 && i < n_row) ; W [i]++ ; } } } else { for (j = 0 ; j < n_col ; j++) { for (p = Ap [j] ; p < Ap [j+1] ; p++) { i = Ai [p] ; ASSERT (i >= 0 && i < n_row) ; W [i]++ ; } } } /* ---------------------------------------------------------------------- */ /* compute the row pointers for R = A (P,Q) */ /* ---------------------------------------------------------------------- */ if (P != (Int *) NULL) { Rp [0] = 0 ; for (k = 0 ; k < n_row ; k++) { i = P [k] ; ASSERT (i >= 0 && i < n_row) ; Rp [k+1] = Rp [k] + W [i] ; } for (k = 0 ; k < n_row ; k++) { i = P [k] ; ASSERT (i >= 0 && i < n_row) ; W [i] = Rp [k] ; } } else { Rp [0] = 0 ; for (i = 0 ; i < n_row ; i++) { Rp [i+1] = Rp [i] + W [i] ; } for (i = 0 ; i < n_row ; i++) { W [i] = Rp [i] ; } } ASSERT (Rp [n_row] <= Ap [n_col]) ; /* at this point, W holds the permuted row pointers */ /* ---------------------------------------------------------------------- */ /* construct the row form of B */ /* ---------------------------------------------------------------------- */ do_values = Ax && Rx ; #ifdef COMPLEX split = SPLIT (Az) && SPLIT (Rz) ; if (do_conjugate && do_values) { if (Q != (Int *) NULL) { if (split) { /* R = A (P,Q)' */ for (newj = 0 ; newj < nq ; newj++) { j = Q [newj] ; ASSERT (j >= 0 && j < n_col) ; for (p = Ap [j] ; p < Ap [j+1] ; p++) { bp = W [Ai [p]]++ ; Ri [bp] = newj ; Rx [bp] = Ax [p] ; Rz [bp] = -Az [p] ; } } } else { /* R = A (P,Q)' (merged complex values) */ for (newj = 0 ; newj < nq ; newj++) { j = Q [newj] ; ASSERT (j >= 0 && j < n_col) ; for (p = Ap [j] ; p < Ap [j+1] ; p++) { bp = W [Ai [p]]++ ; Ri [bp] = newj ; Rx [2*bp] = Ax [2*p] ; Rx [2*bp+1] = -Ax [2*p+1] ; } } } } else { if (split) { /* R = A (P,:)' */ for (j = 0 ; j < n_col ; j++) { for (p = Ap [j] ; p < Ap [j+1] ; p++) { bp = W [Ai [p]]++ ; Ri [bp] = j ; Rx [bp] = Ax [p] ; Rz [bp] = -Az [p] ; } } } else { /* R = A (P,:)' (merged complex values) */ for (j = 0 ; j < n_col ; j++) { for (p = Ap [j] ; p < Ap [j+1] ; p++) { bp = W [Ai [p]]++ ; Ri [bp] = j ; Rx [2*bp] = Ax [2*p] ; Rx [2*bp+1] = -Ax [2*p+1] ; } } } } } else #endif { if (Q != (Int *) NULL) { if (do_values) { #ifdef COMPLEX if (split) #endif { /* R = A (P,Q).' */ for (newj = 0 ; newj < nq ; newj++) { j = Q [newj] ; ASSERT (j >= 0 && j < n_col) ; for (p = Ap [j] ; p < Ap [j+1] ; p++) { bp = W [Ai [p]]++ ; Ri [bp] = newj ; Rx [bp] = Ax [p] ; #ifdef COMPLEX Rz [bp] = Az [p] ; #endif } } } #ifdef COMPLEX else { /* R = A (P,Q).' (merged complex values) */ for (newj = 0 ; newj < nq ; newj++) { j = Q [newj] ; ASSERT (j >= 0 && j < n_col) ; for (p = Ap [j] ; p < Ap [j+1] ; p++) { bp = W [Ai [p]]++ ; Ri [bp] = newj ; Rx [2*bp] = Ax [2*p] ; Rx [2*bp+1] = Ax [2*p+1] ; } } } #endif } else { /* R = pattern of A (P,Q).' */ for (newj = 0 ; newj < nq ; newj++) { j = Q [newj] ; ASSERT (j >= 0 && j < n_col) ; for (p = Ap [j] ; p < Ap [j+1] ; p++) { Ri [W [Ai [p]]++] = newj ; } } } } else { if (do_values) { #ifdef COMPLEX if (split) #endif { /* R = A (P,:).' */ for (j = 0 ; j < n_col ; j++) { for (p = Ap [j] ; p < Ap [j+1] ; p++) { bp = W [Ai [p]]++ ; Ri [bp] = j ; Rx [bp] = Ax [p] ; #ifdef COMPLEX Rz [bp] = Az [p] ; #endif } } } #ifdef COMPLEX else { /* R = A (P,:).' (merged complex values) */ for (j = 0 ; j < n_col ; j++) { for (p = Ap [j] ; p < Ap [j+1] ; p++) { bp = W [Ai [p]]++ ; Ri [bp] = j ; Rx [2*bp] = Ax [2*p] ; Rx [2*bp+1] = Ax [2*p+1] ; } } } #endif } else { /* R = pattern of A (P,:).' */ for (j = 0 ; j < n_col ; j++) { for (p = Ap [j] ; p < Ap [j+1] ; p++) { Ri [W [Ai [p]]++] = j ; } } } } } #ifndef NDEBUG for (k = 0 ; k < n_row ; k++) { if (P != (Int *) NULL) { i = P [k] ; } else { i = k ; } DEBUG3 ((ID": W[i] "ID" Rp[k+1] "ID"\n", i, W [i], Rp [k+1])) ; ASSERT (W [i] == Rp [k+1]) ; } DEBUG2 (("UMF_transpose, output matrix:\n")) ; UMF_dump_col_matrix (Rx, #ifdef COMPLEX Rz, #endif Ri, Rp, n_col, n_row, Rp [n_row]) ; ASSERT (AMD_valid (n_col, n_row, Rp, Ri)) ; #endif return (UMFPACK_OK) ; }