diff options
author | jthorn <jthorn@f88db872-0e4f-0410-b76b-b9085cfa78c5> | 2003-06-04 11:49:29 +0000 |
---|---|---|
committer | jthorn <jthorn@f88db872-0e4f-0410-b76b-b9085cfa78c5> | 2003-06-04 11:49:29 +0000 |
commit | 2e72b0e19eb45f00fb6fc7411d1bb9e3be0b3d8f (patch) | |
tree | f93e703ce65b39a221db4864731321067396846e | |
parent | c37b4c96f3426ed7019be2dd9acd416853c16cf2 (diff) |
import the rest (of that subset that I need) of UMFPACK into CVS
the make.code.* and wrap* routines are mine,
everything else here is from UMFPACK v4.0
git-svn-id: http://svn.einsteintoolkit.org/cactus/EinsteinAnalysis/AHFinderDirect/trunk@1092 f88db872-0e4f-0410-b76b-b9085cfa78c5
59 files changed, 19658 insertions, 1 deletions
diff --git a/src/sparse-matrix/umfpack/README b/src/sparse-matrix/umfpack/README index b19cca2..776817e 100644 --- a/src/sparse-matrix/umfpack/README +++ b/src/sparse-matrix/umfpack/README @@ -1,9 +1,13 @@ This directory contains a subset of the files from the UMFPACK Version 4.0 (Apr 11, 2002) sparse matrix package. UMFPACK is copyright (c) 2002 by Timothy A. Davis. + See License for the UMFPACK license. See README.UMFPACK for the original UMFPACK README file, including the url where the full package (including documentation) may be optained. -make.code.defn controls compilation of this directory in Cactus. +make.code.defn controls the Cactus compilation of this directory. + +umfpack_wsolve.c is a copy of umfpack_solve.c to work around +limitations in the Cactus make.code.defn system. diff --git a/src/sparse-matrix/umfpack/make.code.defn b/src/sparse-matrix/umfpack/make.code.defn new file mode 100644 index 0000000..4913278 --- /dev/null +++ b/src/sparse-matrix/umfpack/make.code.defn @@ -0,0 +1,87 @@ +# Cactus specification of things to be compiled in this directory +# $Header$ + +# +# UMFPACK comes with a Makefile which compiles many of the source files +# multiple times with different -D compiler options, both to generate the +# 4 UMFPACK variants (real/complex, int/long), and for other purposes. +# Alas, Cactus doesn't seem to provide any way to use a Makefile for +# this subdirectory, and still use make.code.defn building for the +# rest of this thorn. :( :( +# +# Instead, we [have to] fake things with this make.code.defn and the +# accompanying make.code.deps . Fortunately, we only need one UMFPACK +# variant: We know it's real, but we actually want the integer datatype +# to match Fortran's (since ../../elliptic/row_sparse_matrix.{cc,hh} +# potentially use the same data structures for both UMFPACK and ILUCG +# matrices, and ILUCG is Fortran). This ought to be determined by a +# configure script, but for now (FIXME) we just hard-wire it to long +# with -D compile flags set in make.code.deps . +# +# For the other multiple-compilation and special-compilation-flags cases, +# we use wrapper files which #define the appropriate symbols, then #include +# the main UMFPACK source files. +# + +# Source files in this directory +SRCS = \ + umf_analyze.c \ + umf_apply_order.c \ + umf_assemble.c \ + umf_blas3_update.c \ + umf_build_tuples.c \ + umf_build_tuples_usage.c \ + umf_colamd.c \ + umf_create_element.c \ + umf_dump.c \ + umf_extend_front.c \ + umf_free.c \ + umf_garbage_collection.c \ + umf_get_memory.c \ + umf_init_front.c \ + umf_is_permutation.c \ + umf_kernel.c \ + umf_kernel_init.c \ + umf_kernel_init_usage.c \ + umf_kernel_wrapup.c \ + umf_local_search.c \ + umf_lsolve.c \ + umf_ltsolve.c \ + umf_malloc.c \ + umf_mem_alloc_element.c \ + umf_mem_alloc_head_block.c \ + umf_mem_alloc_tail_block.c \ + umf_mem_free_tail_block.c \ + umf_mem_init_memoryspace.c \ + umf_order_front_tree.c \ + umf_realloc.c \ + umf_row_search.c \ + umf_scale_column.c \ + umf_set_stats.c \ + umf_solve.c \ + umf_symbolic_usage.c \ + umf_transpose.c \ + umf_tuple_lengths.c \ + umf_usolve.c \ + umf_utsolve.c \ + umf_valid_numeric.c \ + umf_valid_symbolic.c \ + umfpack_defaults.c \ + umfpack_free_numeric.c \ + umfpack_free_symbolic.c \ + umfpack_get_lunz.c \ + umfpack_get_numeric.c \ + umfpack_get_symbolic.c \ + umfpack_numeric.c \ + umfpack_qsymbolic.c \ + umfpack_solve.c \ + umfpack_symbolic.c \ + umfpack_timer.c \ + umfpack_transpose.c \ + \ + wrap_umf_ltsolve.c \ + wrap_umf_utsolve.c \ + wrap_umfpack_wsolve.c + +# Subdirectories containing source files +SUBDIRS = diff --git a/src/sparse-matrix/umfpack/make.code.deps b/src/sparse-matrix/umfpack/make.code.deps new file mode 100644 index 0000000..e0bea85 --- /dev/null +++ b/src/sparse-matrix/umfpack/make.code.deps @@ -0,0 +1,8 @@ +# extra Make rules for Cactus compilation of things in this directory +# $Header$ + +# +# See the header comments in make.code.defn for general comments. +# +CFLAGS += -DNBLAS -DNDEBUG -DLONG +CPPFLAGS += -DNBLAS -DNDEBUG -DDLONG diff --git a/src/sparse-matrix/umfpack/umf_analyze.c b/src/sparse-matrix/umfpack/umf_analyze.c new file mode 100644 index 0000000..0490ece --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_analyze.c @@ -0,0 +1,883 @@ +/* ========================================================================== */ +/* === UMF_analyze ========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Symbolic LL' factorization of A'*A, to get upper bounds on the size of + L and U for LU = PAQ, and to determine the frontal matrices and + (supernodal) column elimination tree. No fill-reducing column pre-ordering + is used. + + Returns TRUE if successful, FALSE if out of memory. UMF_analyze can only + run out of memory if anzmax (which is Ap [n_row]) is too small. + + Uses workspace of size O(nonzeros in A). On input, the matrix A is + stored in row-form at the tail end of Ai. It is destroyed on output. + The rows of A must be sorted by increasing first column index. + The matrix is assumed to be valid. + + Empty rows and columns have already been removed. + +*/ + +#include "umf_internal.h" +#include "umf_order_front_tree.h" +#include "umf_apply_order.h" + +/* ========================================================================== */ + +GLOBAL Int UMF_analyze +( + Int n_row, /* A is n_row-by-n_col */ + Int n_col, + Int Ai [ ], /* Ai [Ap [0]..Ap[n_row]-1]: column indices */ + /* destroyed on output. Note that this is NOT the */ + /* user's Ai that was passed to UMFPACK_*symbolic */ + /* size of Ai, Ap [n_row] = anzmax >= anz + n_col */ + /* Ap [0] must be => n_col. The space to the */ + /* front of Ai is used as workspace. */ + + Int Ap [ ], /* of size MAX (n_row, n_col) + 1 */ + /* Ap [0..n_row]: row pointers */ + /* Row i is in Ai [Ap [i] ... Ap [i+1]-1] */ + + /* rows must have smallest col index first, or be */ + /* in sorted form. Used as workspace of size n_col */ + /* and destroyed. */ + + /* Note that this is NOT the */ + /* user's Ap that was passed to UMFPACK_*symbolic */ + + Int Up [ ], /* workspace of size n_col, and output column perm. */ + + /* temporary workspaces: */ + Int W [ ], /* W [0..n_col-1] */ + Int Link [ ], /* Link [0..n_col-1] */ + + /* output: information about each frontal matrix: */ + Int Front_ncols [ ], /* size n_col */ + Int Front_nrows [ ], /* of size n_col */ + Int Front_npivcol [ ], /* of size n_col */ + Int Front_parent [ ], /* of size n_col */ + Int *nfr_out, + + Int *p_ncompactions /* number of compactions in UMF_analyze */ +) +{ + /* ====================================================================== */ + /* ==== local variables ================================================= */ + /* ====================================================================== */ + + Int j, j3, col, k, row, parent, j2, pdest, p, p2, thickness, npivots, nfr, + frsize, i, *Winv, kk, npiv, jnext, krow, knext, pfirst, + jlast, ncompactions, *Stack, *Front_maxfr, *Front_order, *Front_child, + *Front_sibling, fprev, maxfrsize, bigf, fnext, bigfprev, f, Wflag, + npivcol, fallrows, fallcols, fpiv, frows, fcols ; + +#ifndef NDEBUG + Int nfr2, nchild ; +#endif + + nfr = 0 ; + DEBUG0 (("UMF_analyze: anzmax "ID" anrow "ID" ancol "ID"\n", + Ap [n_row], n_row, n_col)) ; + + /* ====================================================================== */ + /* ==== initializations ================================================= */ + /* ====================================================================== */ + + for (j = 0 ; j < n_col ; j++) + { + Link [j] = EMPTY ; + W [j] = EMPTY ; + Up [j] = EMPTY ; + + /* Frontal matrix data structure: */ + Front_npivcol [j] = 0 ; /* number of pivot columns */ + Front_nrows [j] = 0 ; /* number of rows, incl. pivot rows */ + Front_ncols [j] = 0 ; /* number of cols, incl. pivot cols */ + Front_parent [j] = EMPTY ; /* parent front */ + /* Note that only non-pivotal columns are stored in a front (a "row" */ + /* of U) during elimination. */ + } + + /* the rows must be sorted by increasing min col */ + krow = 0 ; + pfirst = Ap [0] ; + jlast = EMPTY ; + jnext = EMPTY ; + Wflag = 0 ; + + ASSERT (pfirst >= n_col) ; /* Ai must be large enough */ + + /* pdest points to the first free space in Ai */ + pdest = 0 ; + ncompactions = 0 ; + + /* ====================================================================== */ + /* === compute symbolic LL' factorization (unsorted) ==================== */ + /* ====================================================================== */ + + for (j = 0 ; j < n_col ; j = jnext) + { + DEBUG1 (("\n\n============Front "ID" starting. nfr = "ID"\n", j, nfr)) ; + + /* ================================================================== */ + /* === garbage collection =========================================== */ + /* ================================================================== */ + + if (pdest + (n_col-j) > pfirst) + { + /* we might run out ... compact the rows of U */ + +#ifndef NDEBUG + DEBUG0 (("UMF_analyze COMPACTION, j="ID" pfirst="ID"\n", + j, pfirst)) ; + for (row = 0 ; row < j ; row++) + { + if (Up [row] != EMPTY) + { + /* this is a live row of U */ + DEBUG1 (("Live row: "ID" cols: ", row)) ; + p = Up [row] ; + ASSERT (Front_ncols [row] > Front_npivcol [row]) ; + p2 = p + (Front_ncols [row] - Front_npivcol [row]) ; + for ( ; p < p2 ; p++) + { + DEBUG1 ((ID, Ai [p])) ; + ASSERT (p < pfirst) ; + ASSERT (Ai [p] > row && Ai [p] < n_col) ; + } + DEBUG1 (("\n")) ; + } + } + DEBUG1 (("\nStarting to compact:\n")) ; +#endif + + pdest = 0 ; + ncompactions++ ; + for (row = 0 ; row < j ; row++) + { + if (Up [row] != EMPTY) + { + /* this is a live row of U */ + DEBUG1 (("Live row: "ID" cols: ", row)) ; + ASSERT (row < n_col) ; + p = Up [row] ; + ASSERT (Front_ncols [row] > Front_npivcol [row]) ; + p2 = p + (Front_ncols [row] - Front_npivcol [row]) ; + Up [row] = pdest ; + for ( ; p < p2 ; p++) + { + DEBUG1 ((ID, Ai [p])) ; + ASSERT (p < pfirst) ; + ASSERT (Ai [p] > row && Ai [p] < n_col) ; + Ai [pdest++] = Ai [p] ; + ASSERT (pdest <= pfirst) ; + } + DEBUG1 (("\n")) ; + } + } + +#ifndef NDEBUG + DEBUG1 (("\nAFTER COMPACTION, j="ID" pfirst="ID"\n", j, pfirst)) ; + for (row = 0 ; row < j ; row++) + { + if (Up [row] != EMPTY) + { + /* this is a live row of U */ + DEBUG1 (("Live row: "ID" cols: ", row)) ; + p = Up [row] ; + ASSERT (Front_ncols [row] > Front_npivcol [row]) ; + p2 = p + (Front_ncols [row] - Front_npivcol [row]) ; + for ( ; p < p2 ; p++) + { + DEBUG1 ((ID, Ai [p])) ; + ASSERT (p < pfirst) ; + ASSERT (Ai [p] > row && Ai [p] < n_col) ; + } + DEBUG1 (("\n")) ; + } + } +#endif + + } + + if (pdest + (n_col-j) > pfirst) + { + /* Out of memory! This is not supposed to happen ... */ + /* it can't, if pfirst >= n_col */ + return (FALSE) ; /* internal error! */ + } + + /* ------------------------------------------------------------------ */ + /* is the last front a child of this one? */ + /* ------------------------------------------------------------------ */ + + if (jlast != EMPTY && Link [j] == jlast) + { + /* yes - create row j by appending to jlast */ + DEBUG1 (("GOT:last front is child of this one: j "ID" jlast "ID"\n", + j, jlast)) ; + ASSERT (jlast >= 0 && jlast < j) ; + + Up [j] = Up [jlast] ; + Up [jlast] = EMPTY ; + + /* find the parent, delete column j, and update W */ + parent = n_col ; + for (p = Up [j] ; p < pdest ; ) + { + j3 = Ai [p] ; + DEBUG1 (("Initial row of U: col "ID" ", j3)) ; + ASSERT (j3 >= 0 && j3 < n_col) ; + DEBUG1 (("W: "ID" \n", W [j3])) ; + ASSERT (W [j3] == Wflag) ; + if (j == j3) + { + DEBUG1 (("Found column j at p = "ID"\n", p)) ; + Ai [p] = Ai [--pdest] ; + } + else + { + if (j3 < parent) + { + parent = j3 ; + } + p++ ; + } + } + + /* delete jlast from the link list of j */ + Link [j] = Link [jlast] ; + + ASSERT (Front_nrows [jlast] > Front_npivcol [jlast]) ; + thickness = (Front_nrows [jlast] - Front_npivcol [jlast]) ; + + } + else + { + Up [j] = pdest ; + parent = n_col ; + /* thickness: number of (nonpivotal) rows in frontal matrix j */ + thickness = 0 ; + Wflag = j ; + } + + /* ================================================================== */ + /* === compute row j of A*A' ======================================== */ + /* ================================================================== */ + + /* ------------------------------------------------------------------ */ + /* flag the diagonal entry in row U, but do not add to pattern */ + /* ------------------------------------------------------------------ */ + + ASSERT (pdest <= pfirst) ; + W [j] = Wflag ; + + DEBUG1 (("\nComputing row "ID" of A'*A\n", j)) ; + DEBUG2 ((" col: "ID" (diagonal)\n", j)) ; + + /* ------------------------------------------------------------------ */ + /* find the rows the contribute to this column j */ + /* ------------------------------------------------------------------ */ + + jnext = n_col ; + for (knext = krow ; knext < n_row ; knext++) + { + ASSERT (Ap [knext] < Ap [knext+1]) ; + + ASSERT (Ap [knext] >= pfirst && Ap [knext] <= Ap [n_row]) ; + jnext = Ai [Ap [knext]] ; + ASSERT (jnext >= j) ; + if (jnext != j) + { + break ; + } + } + + /* rows krow ... knext-1 all have first column index of j */ + /* (or are empty) */ + + /* row knext has first column index of jnext */ + /* if knext = n_row, then jnext is n_col */ + if (knext == n_row) + { + jnext = n_col ; + } + + ASSERT (jnext > j) ; + ASSERT (jnext <= n_col) ; + + /* ------------------------------------------------------------------ */ + /* for each nonzero A (k,j) in column j of A do: */ + /* ------------------------------------------------------------------ */ + + for (k = krow ; k < knext ; k++) + { + p = Ap [k] ; + p2 = Ap [k+1] ; + ASSERT (p < p2) ; + + /* merge row k of A into W */ + DEBUG2 ((" ---- A row "ID" ", k)) ; + ASSERT (k >= 0 && k < n_row) ; + ASSERT (Ai [p] == j) ; + DEBUG2 ((" p "ID" p2 "ID"\n cols:", p, p2)) ; + ASSERT (p >= pfirst && p < Ap [n_row]) ; + ASSERT (p2 > pfirst && p2 <= Ap [n_row]) ; + for ( ; p < p2 ; p++) + { + /* add to pattern if seen for the first time */ + col = Ai [p] ; + ASSERT (col >= j && col < n_col) ; + DEBUG3 ((" "ID, col)) ; + if (W [col] != Wflag) + { + Ai [pdest++] = col ; + ASSERT (pdest <= pfirst) ; + /* flag this column has having been seen for row j */ + W [col] = Wflag ; + if (col < parent) + { + parent = col ; + } + } + } + DEBUG2 (("\n")) ; + thickness++ ; + + } + +#ifndef NDEBUG + DEBUG3 (("\nRow "ID" of A'A:\n", j)) ; + for (p = Up [j] ; p < pdest ; p++) + { + DEBUG3 ((" "ID, Ai [p])) ; + } + DEBUG3 (("\n")) ; +#endif + + /* ------------------------------------------------------------------ */ + /* delete rows up to but not including knext */ + /* ------------------------------------------------------------------ */ + + krow = knext ; + pfirst = Ap [knext] ; + + /* we can now use Ai [0..pfirst-1] as workspace for rows of U */ + + /* ================================================================== */ + /* === compute jth row of U ========================================= */ + /* ================================================================== */ + + /* for each nonzero U (k,j) in column j of U (1:j-1,:) do */ + for (k = Link [j] ; k != EMPTY ; k = Link [k]) + { + /* merge row k of U into W */ + DEBUG2 ((" ---- U row "ID, k)) ; + ASSERT (k >= 0 && k < n_col) ; + ASSERT (Up [k] != EMPTY) ; + p = Up [k] ; + ASSERT (Front_ncols [k] > Front_npivcol [k]) ; + p2 = p + (Front_ncols [k] - Front_npivcol [k]) ; + DEBUG2 ((" p "ID" p2 "ID"\n cols:", p, p2)) ; + ASSERT (p <= pfirst) ; + ASSERT (p2 <= pfirst) ; + for ( ; p < p2 ; p++) + { + /* add to pattern if seen for the first time */ + col = Ai [p] ; + ASSERT (col >= j && col < n_col) ; + DEBUG3 ((ID, col)) ; + if (W [col] != Wflag) + { + Ai [pdest++] = col ; + ASSERT (pdest <= pfirst) ; + /* flag this col has having been seen for row j */ + W [col] = Wflag ; + if (col < parent) + { + parent = col ; + } + } + } + DEBUG2 (("\n")) ; + + /* mark the row k as deleted */ + Up [k] = EMPTY ; + + ASSERT (Front_nrows [k] > Front_npivcol [k]) ; + thickness += (Front_nrows [k] - Front_npivcol [k]) ; + ASSERT (Front_parent [k] == j) ; + } + +#ifndef NDEBUG + DEBUG3 (("\nRow "ID" of U prior to supercolumn detection:\n", j)); + for (p = Up [j] ; p < pdest ; p++) + { + DEBUG3 ((" "ID, Ai [p])) ; + } + DEBUG3 (("\n")) ; +#endif + + /* ================================================================== */ + /* === quicky mass elimination ====================================== */ + /* ================================================================== */ + + /* this code detects some supernodes, but it might miss */ + /* some because the elimination tree (created on the fly) */ + /* is not yet post-ordered, and because the pattern of A'*A */ + /* is also computed on the fly. */ + + /* j2 is incremented because the pivot columns are not stored */ + + for (j2 = j+1 ; j2 < jnext ; j2++) + { + ASSERT (j2 >= 0 && j2 < n_col) ; + if (W [j2] != Wflag || Link [j2] != EMPTY) + { + break ; + } + } + + /* the loop above terminated with j2 at the first non-supernode */ + DEBUG1 (("jnext = "ID"\n", jnext)) ; + ASSERT (j2 <= jnext) ; + jnext = j2 ; + j2-- ; + DEBUG1 (("j2 = "ID"\n", j2)) ; + ASSERT (j2 < n_col) ; + + npivots = j2-j+1 ; + + /* rows j:j2 have the same nonzero pattern, except for columns j:j2-1 */ + + if (j2 > j) + { + /* supernode detected, prune the pattern of new row j */ + ASSERT (parent == j+1) ; + ASSERT (j2 < n_col) ; + DEBUG1 (("Supernode detected, j "ID" to j2 "ID"\n", j, j2)) ; + + parent = n_col ; + p2 = pdest ; + pdest = Up [j] ; + for (p = Up [j] ; p < p2 ; p++) + { + col = Ai [p] ; + ASSERT (col >= 0 && col < n_col) ; + ASSERT (W [col] == Wflag) ; + if (col > j2) + { + /* keep this col in the pattern of the new row j */ + Ai [pdest++] = col ; + if (col < parent) + { + parent = col ; + } + } + } + } + + DEBUG1 (("Parent ["ID"] = "ID"\n", j, parent)) ; + ASSERT (parent > j2) ; + + if (parent == n_col) + { + /* this front has no parent - it is the root of a subtree */ + parent = EMPTY ; + } + +#ifndef NDEBUG + DEBUG3 (("\nFinal row "ID" of U after supercolumn detection:\n", j)) ; + for (p = Up [j] ; p < pdest ; p++) + { + ASSERT (Ai [p] >= 0 && Ai [p] < n_col) ; + DEBUG3 ((" "ID" ("ID")", Ai [p], W [Ai [p]])) ; + ASSERT (W [Ai [p]] == Wflag) ; + } + DEBUG3 (("\n")) ; +#endif + + /* ================================================================== */ + /* === frontal matrix =============================================== */ + /* ================================================================== */ + + /* front has Front_npivcol [j] pivot columns */ + /* entire front is Front_nrows [j] -by- Front_ncols [j] */ + /* j is first column in the front */ + + npivcol = npivots ; + fallrows = thickness ; + fallcols = npivots + pdest - Up [j] ; + + /* number of pivots in the front (rows and columns) */ + fpiv = MIN (npivcol, fallrows) ; + + /* size of contribution block */ + frows = fallrows - fpiv ; + fcols = fallcols - fpiv ; + + if (frows == 0 || fcols == 0) + { + /* front has no contribution block and thus needs no parent */ + Up [j] = EMPTY ; + parent = EMPTY ; + } + + Front_npivcol [j] = npivots ; + Front_nrows [j] = fallrows ; + Front_ncols [j] = fallcols ; + Front_parent [j] = parent ; + ASSERT (npivots > 0) ; + + /* Front_parent [j] is the first column of the parent frontal matrix */ + + DEBUG1 (("\n\n==== Front "ID", pivot columns "ID":"ID" all front: "ID + "-by-"ID"\n", j, j,j+npivots-1, Front_nrows [j], Front_ncols [j])) ; + nfr++ ; + + /* ================================================================== */ + /* === prepare this row for its parent ============================== */ + /* ================================================================== */ + + if (parent != EMPTY) + { + Link [j] = Link [parent] ; + Link [parent] = j ; + } + + ASSERT (jnext > j) ; + + jlast = j ; + } + + /* ====================================================================== */ + /* === scan the fronts ================================================== */ + /* ====================================================================== */ + + *nfr_out = nfr ; + + /* use Ap for Front_child and use Link for Front_sibling [ */ + Front_child = Ap ; + Front_sibling = Link ; + + /* use W for Front_maxfr [ */ + Front_maxfr = W ; + + for (j = 0 ; j < n_col ; j++) + { + Front_child [j] = EMPTY ; + Front_sibling [j] = EMPTY ; + Front_maxfr [j] = EMPTY ; + } + + DEBUG1 (("\n\n========================================FRONTS:\n")) ; + + /* ---------------------------------------------------------------------- */ + /* find max front size for tree rooted at node j, for each front j */ + /* ---------------------------------------------------------------------- */ + + for (j = 0 ; j < n_col ; j++) + { + DEBUG1 ((""ID" : npiv "ID" nrows "ID" ncols "ID" parent "ID" ", + j, Front_npivcol [j], Front_nrows [j], Front_ncols [j], + Front_parent [j])) ; + if (Front_npivcol [j] > 0) + { + /* this is a frontal matrix */ + parent = Front_parent [j] ; + frsize = Front_nrows [j] * Front_ncols [j] ; + + DEBUG1 ((" a front, frsize "ID", true parent: "ID"\n", frsize, + parent)) ; + + Front_maxfr [j] = MAX (Front_maxfr [j], frsize) ; + DEBUG1 (("Front_maxfr [j = "ID"] = "ID"\n", j, Front_maxfr [j])) ; + + if (parent != EMPTY) + { + ASSERT (Front_npivcol [parent] > 0) ; + ASSERT (parent > j) ; + + /* find the maximum frontsize of self and children */ + Front_maxfr [parent] = MAX (Front_maxfr [parent], + Front_maxfr [j]) ; + DEBUG1 (("Front_maxfr [parent = "ID"] = "ID"\n", + parent, Front_maxfr [parent])); + } + } + DEBUG1 (("\n")) ; + } + + /* ---------------------------------------------------------------------- */ + /* place the children in link lists - bigger fronts will tend to be last */ + /* ---------------------------------------------------------------------- */ + + for (j = n_col-1 ; j >= 0 ; j--) + { + if (Front_npivcol [j] > 0) + { + /* this is a frontal matrix */ + parent = Front_parent [j] ; + if (parent != EMPTY) + { + /* place the front in link list of the children its parent */ + Front_sibling [j] = Front_child [parent] ; + Front_child [parent] = j ; + } + } + } + +#ifndef NDEBUG + DEBUG1 (("\n\n========================================FRONTS (again):\n")) ; + nfr2 = 0 ; + for (j = 0 ; j < n_col ; j++) + { + if (Front_npivcol [j] > 0) + { + DEBUG1 (( ""ID" : nfr "ID" npiv "ID" nrows "ID" ncols "ID + " parent "ID" maxfr "ID"\n", j, nfr2, + Front_npivcol [j], Front_nrows [j], Front_ncols [j], + Front_parent [j], Front_maxfr [j])) ; + + /* this is a frontal matrix */ + + /* dump the link list of children */ + DEBUG1 ((" Children: ")) ; + for (f = Front_child [j] ; f != EMPTY ; f = Front_sibling [f]) + { + DEBUG1 ((ID, f)) ; + ASSERT (Front_parent [f] == j) ; + } + DEBUG1 (("\n")) ; + + parent = Front_parent [j] ; + if (parent != EMPTY) + { + /* Assert that the parent front can absorb the child element */ + ASSERT (Front_npivcol [parent] > 0) ; + ASSERT ((Front_nrows [j] - Front_npivcol [j]) + <= Front_nrows [parent]) ; + ASSERT ((Front_ncols [j] - Front_npivcol [j]) + <= Front_ncols [parent]) ; + } + nfr2++ ; + } + } + ASSERT (nfr == nfr2) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* Order the front tree via depth-first-search */ + /* ---------------------------------------------------------------------- */ + + for (i = 0 ; i < n_col ; i++) + { + if (Front_npivcol [i] > 0 && Front_child [i] != EMPTY) + { + +#ifndef NDEBUG + DEBUG1 (("Before partial sort, front "ID"\n", i)) ; + nchild = 0 ; + for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f]) + { + DEBUG1 ((" "ID" "ID"\n", f, Front_maxfr [f])) ; + nchild++ ; + } +#endif + + /* find the biggest front in the child list */ + fprev = EMPTY ; + maxfrsize = EMPTY ; + bigfprev = EMPTY ; + bigf = EMPTY ; + for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f]) + { + frsize = Front_maxfr [f] ; + if (frsize >= maxfrsize) + { + /* this is the biggest seen so far */ + maxfrsize = frsize ; + bigfprev = fprev ; + bigf = f ; + } + fprev = f ; + } + ASSERT (bigf != EMPTY) ; + + fnext = Front_sibling [bigf] ; + + DEBUG1 (("bigf "ID" maxfrsize "ID" bigfprev "ID" fnext "ID" fprev " + ID"\n", bigf, maxfrsize, bigfprev, fnext, fprev)) ; + + if (fnext != EMPTY) + { + /* if fnext is EMPTY, then bigf is already at the end of list */ + + if (bigfprev == EMPTY) + { + /* delete bigf from the front of the list */ + Front_child [i] = fnext ; + } + else + { + /* delete bigf from the middle of the list */ + Front_sibling [bigfprev] = fnext ; + } + + /* put bigf at the end of the list */ + Front_sibling [bigf] = EMPTY ; + ASSERT (Front_child [i] != EMPTY) ; + ASSERT (fprev != bigf) ; + ASSERT (fprev != EMPTY) ; + Front_sibling [fprev] = bigf ; + } + +#ifndef NDEBUG + DEBUG1 (("After partial sort, front "ID"\n", i)) ; + for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f]) + { + DEBUG1 ((" "ID" "ID"\n", f, Front_maxfr [f])) ; + ASSERT (Front_npivcol [f] > 0) ; + nchild-- ; + } + ASSERT (nchild == 0) ; +#endif + + } + } + + /* Front_maxfr no longer needed ] */ + + /* ---------------------------------------------------------------------- */ + /* postorder the supernodal column elimination tree */ + /* ---------------------------------------------------------------------- */ + + /* use W for Front_order ( */ + Front_order = W ; + + /* use Ai as Stack for UMF_order_front_tree [ */ + Stack = Ai ; + + for (i = 0 ; i < n_col ; i++) + { + Front_order [i] = EMPTY ; + } + +#ifndef NDEBUG + UMF_nbug = n_col ; /* frontal id's are in the range 0..n_col-1 */ + UMF_fbug = nfr ; /* total number of frontal matrices */ +#endif + + k = 0 ; + for (i = 0 ; i < n_col ; i++) + { + if (Front_parent [i] == EMPTY && Front_npivcol [i] != 0) + { + DEBUG1 (("Root of front tree "ID"\n", i)) ; + k = UMF_order_front_tree (i, k, Front_child, Front_sibling, + Front_order, Stack) ; + } + } + + /* Stack no longer needed ] */ + /* Front_child, Front_sibling no longer needed ] */ + + /* ---------------------------------------------------------------------- */ + /* construct the column permutation (return in Up) */ + /* ---------------------------------------------------------------------- */ + + /* Front_order [i] = k means that front i is kth front in the new order. */ + /* i is in the range 0 to n_col-1, and k is in the range 0 to nfr-1 */ + + /* Use Ai as workspace for Winv [ */ + Winv = Ai ; + for (k = 0 ; k < nfr ; k++) + { + Winv [k] = EMPTY ; + } + + /* compute the inverse of Front_order, so that Winv [k] = i */ + /* if Front_order [i] = k */ + + DEBUG1 (("\n\nComputing output column permutation:\n")) ; + for (i = 0 ; i < n_col ; i++) + { + k = Front_order [i] ; + if (k != EMPTY) + { + DEBUG1 (("Front "ID" new order: "ID"\n", i, k)) ; + ASSERT (k >= 0 && k < nfr) ; + ASSERT (Winv [k] == EMPTY) ; + Winv [k] = i ; + } + } + + /* Use Up as output permutation */ + kk = 0 ; + for (k = 0 ; k < nfr ; k++) + { + i = Winv [k] ; + DEBUG1 (("Old Front "ID" New Front "ID" npivots "ID" nrows "ID" ncols "ID"\n", + i, k, Front_npivcol [i], Front_nrows [i], Front_ncols [i])) ; + ASSERT (i >= 0 && i < n_col) ; + ASSERT (Front_npivcol [i] > 0) ; + for (npiv = 0 ; npiv < Front_npivcol [i] ; npiv++) + { + Up [kk] = i + npiv ; + DEBUG1 ((" Cperm ["ID"] = "ID"\n", kk, Up [kk])) ; + kk++ ; + } + } + ASSERT (kk == n_col) ; + + /* Winv no longer needed ] */ + + /* ---------------------------------------------------------------------- */ + /* apply the postorder traversal to renumber the frontal matrices */ + /* ---------------------------------------------------------------------- */ + + /* use Ai as workspace */ + + UMF_apply_order (Front_npivcol, Front_order, Ai, n_col, nfr) ; + UMF_apply_order (Front_nrows, Front_order, Ai, n_col, nfr) ; + UMF_apply_order (Front_ncols, Front_order, Ai, n_col, nfr) ; + UMF_apply_order (Front_parent, Front_order, Ai, n_col, nfr) ; + + /* fix the parent to refer to the new numbering */ + for (i = 0 ; i < nfr ; i++) + { + parent = Front_parent [i] ; + if (parent != EMPTY) + { + ASSERT (parent >= 0 && parent < n_col) ; + ASSERT (Front_order [parent] >= 0 && Front_order [parent] < nfr) ; + Front_parent [i] = Front_order [parent] ; + } + } + + /* Front_order longer needed ) */ + +#ifndef NDEBUG + DEBUG1 (("\nFinal frontal matrices:\n")) ; + for (i = 0 ; i < nfr ; i++) + { + DEBUG1 (("Final front "ID": npiv "ID" nrows "ID" ncols "ID" parent " + ID"\n", i, Front_npivcol [i], Front_nrows [i], + Front_ncols [i], Front_parent [i])) ; + } +#endif + + *p_ncompactions = ncompactions ; + return (TRUE) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_apply_order.c b/src/sparse-matrix/umfpack/umf_apply_order.c new file mode 100644 index 0000000..f7ced0e --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_apply_order.c @@ -0,0 +1,42 @@ +/* ========================================================================== */ +/* === UMF_apply_order ====================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Apply post-ordering of supernodal elimination tree. +*/ + +#include "umf_internal.h" + +GLOBAL void UMF_apply_order +( + Int Front [ ], + const Int Order [ ], + Int Temp [ ], + Int n_col, + Int nfr +) +{ + Int i, k ; + for (i = 0 ; i < n_col ; i++) + { + k = Order [i] ; + if (k != EMPTY) + { + Temp [k] = Front [i] ; + } + } + + for (k = 0 ; k < nfr ; k++) + { + Front [k] = Temp [k] ; + } +} + diff --git a/src/sparse-matrix/umfpack/umf_assemble.c b/src/sparse-matrix/umfpack/umf_assemble.c new file mode 100644 index 0000000..57a5623 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_assemble.c @@ -0,0 +1,1037 @@ +/* ========================================================================== */ +/* === UMF_assemble ========================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Degree update and numerical assembly */ + +#include "umf_internal.h" +#include "umf_mem_free_tail_block.h" + +GLOBAL void UMF_assemble +( + NumericType *Numeric, + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int e, i, row, col, i2, nrows, ncols, f, tpi, extcdeg, extrdeg, rdeg0, + cdeg0, son_list, next, scan_pivrow, scan_pivcol, nrows_to_assemble, + ncols_to_assemble, ngetrows, j, j2, + nrowsleft, /* number of rows remaining in C */ + ncolsleft, /* number of columns remaining in C */ + prior_Lson, prior_Uson, *E, *Cols, *Rows, *Wm, *Woo, + *Row_tuples, *Row_degree, *Row_tlen, + *Col_tuples, *Col_degree, *Col_tlen ; + Unit *Memory, *p ; + Element *ep ; + Tuple *tp, *tp1, *tp2, *tpend ; + + Entry + *C, /* a pointer into the contribution block */ + *Fx, /* Fx [0..fnrows_max-1, 0..fncols_max-1] working array*/ + *Fcol, /* a column of F */ + *Frow ; /* a row of F */ + + Int + *Frows, /* Frows [0.. ]: row indices of F */ + *Fcols, /* Fcols [0.. ]: column indices of F */ + *Frpos, + *Fcpos, + fnrows, /* number of rows in contribution block in F */ + fncols ; /* number of columns in contribution block in F */ + +#ifndef NDEBUG + Int j3, n_row, n_col ; + n_row = Work->n_row ; + n_col = Work->n_col ; + DEBUG3 (("::Assemble SCANS 1-4\n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + fncols = Work->fncols ; + fnrows = Work->fnrows ; + Fcols = Work->Fcols ; + Frows = Work->Frows ; + Fcpos = Work->Fcpos ; + Frpos = Work->Frpos ; + Fx = Work->Fx ; + Col_degree = Numeric->Cperm ; + Row_degree = Numeric->Rperm ; + Row_tuples = Numeric->Uip ; + Row_tlen = Numeric->Uilen ; + Col_tuples = Numeric->Lip ; + Col_tlen = Numeric->Lilen ; + E = Work->E ; + Memory = Numeric->Memory ; + Wm = Work->Wm ; + Woo = Work->Woo ; /* must be of size at least fnrows_max */ + rdeg0 = Work->rdeg0 ; + cdeg0 = Work->cdeg0 ; + +#ifndef NDEBUG + DEBUG6 (("============================================\n")) ; + DEBUG6 (("Degree update, assembly.\n")) ; + DEBUG6 (("pivot row pattern: fncols="ID"\n", fncols)) ; + for (j3 = 0 ; j3 < fncols ; j3++) DEBUG6 ((ID, Fcols [j3])) ; + DEBUG6 (("\npivot col pattern: fnrows="ID"\n", fnrows)) ; + for (j3 = 0 ; j3 < fnrows ; j3++) DEBUG6 ((ID, Frows [j3])) ; + DEBUG6 (("\n")) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* determine the largest actual frontal matrix size */ + /* ---------------------------------------------------------------------- */ + + Numeric->maxfrsize = MAX (Numeric->maxfrsize, + (fnrows + Work->fnpiv) * (fncols + Work->fnpiv)) ; + + /* ---------------------------------------------------------------------- */ + /* assemble from prior elements into the current frontal matrix */ + /* ---------------------------------------------------------------------- */ + + DEBUG2 (("New assemble start [\n")) ; + + /* Currently no rows or columns are marked. No elements are scanned, */ + /* that is, (ep->next == EMPTY) is true for all elements */ + + son_list = 0 ; /* start creating son_list [ */ + + /* ---------------------------------------------------------------------- */ + /* determine if most recent element is Lson or Uson of current front */ + /* ---------------------------------------------------------------------- */ + + if (!Work->do_extend) + { + prior_Uson = ( Work->pivcol_in_front && !Work->pivrow_in_front) ; + prior_Lson = (!Work->pivcol_in_front && Work->pivrow_in_front) ; + if (prior_Uson || prior_Lson) + { + e = Work->prior_element ; + if (e != EMPTY) + { + ASSERT (E [e]) ; + p = Memory + E [e] ; + ep = (Element *) p ; + ep->next = son_list ; + son_list = e ; +#ifndef NDEBUG + DEBUG2 (("e "ID" is Prior son "ID" "ID"\n", + e, prior_Uson, prior_Lson)) ; + UMF_dump_element (Numeric, Work, e, FALSE) ; +#endif + ASSERT (E [e]) ; + } + } + } + Work->prior_element = EMPTY ; + + /* ---------------------------------------------------------------------- */ + /* SCAN1-row: scan the element lists of each new row in the pivot col */ + /* and compute the external column degree for each frontal */ + /* ---------------------------------------------------------------------- */ + + scan_pivrow = Work->scan_pivrow ; + + for (i2 = Work->fscan_row ; i2 < fnrows || scan_pivrow ; ) + { + /* Get a row */ + if (scan_pivrow) + { + /* pivot row is new to this front. Scan it */ + row = Work->pivrow ; + scan_pivrow = FALSE ; + } + else + { + row = Frows [i2++] ; + } + + DEBUG6 (("SCAN1-row: "ID"\n", row)) ; +#ifndef NDEBUG + UMF_dump_rowcol (0, Numeric, Work, row, FALSE) ; +#endif + + ASSERT (NON_PIVOTAL_ROW (row)) ; + tpi = Row_tuples [row] ; + if (!tpi) continue ; + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Row_tlen [row] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Rows = ((Int *) p) + ep->ncols ; + if (Rows [f] == EMPTY) continue ; /* row already assembled */ + ASSERT (row == Rows [f]) ; + + if (ep->cdeg < cdeg0) + { + /* first time seen in scan1-row */ + ep->cdeg = ep->nrowsleft + cdeg0 ; + DEBUG6 (("e "ID" First seen: cdeg: "ID" ", e, ep->cdeg-cdeg0)) ; + ASSERT (ep->ncolsleft > 0 && ep->nrowsleft > 0) ; + } + + ep->cdeg-- ; /* decrement external column degree */ + DEBUG6 (("e "ID" New ext col deg: "ID"\n", e, ep->cdeg - cdeg0)) ; + + /* this element is not yet in the new son list */ + if (ep->cdeg == cdeg0 && ep->next == EMPTY) + { + /* A new LUson or Uson has been found */ + ep->next = son_list ; + son_list = e ; + } + + ASSERT (ep->cdeg >= cdeg0) ; + *tp2++ = *tp ; /* leave the tuple in the list */ + } + Row_tlen [row] = tp2 - tp1 ; + } + + /* ---------------------------------------------------------------------- */ + /* SCAN1-col: scan the element lists of each new col in the pivot row */ + /* and compute the external row degree for each frontal */ + /* ---------------------------------------------------------------------- */ + + scan_pivcol = Work->scan_pivcol ; + + for (j2 = Work->fscan_col ; j2 < fncols || scan_pivcol ; ) + { + /* Get a column */ + if (scan_pivcol) + { + /* pivot col is new to this front. Scan it */ + col = Work->pivcol ; + scan_pivcol = FALSE ; + } + else + { + col = Fcols [j2++] ; + } + ASSERT (col >= 0 && col < n_col) ; + + DEBUG6 (("SCAN 1-col: "ID"\n", col)) ; +#ifndef NDEBUG + UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ; +#endif + + ASSERT (NON_PIVOTAL_COL (col)) ; + tpi = Col_tuples [col] ; + if (!tpi) continue ; + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Col_tlen [col] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + if (Cols [f] == EMPTY) continue ; /* column already assembled */ + ASSERT (col == Cols [f]) ; + + if (ep->rdeg < rdeg0) + { + /* first time seen in scan1-col */ + ep->rdeg = ep->ncolsleft + rdeg0 ; + DEBUG6 (("e "ID" First seen: rdeg: "ID" ", e, ep->rdeg-rdeg0)) ; + ASSERT (ep->ncolsleft > 0 && ep->nrowsleft > 0) ; + } + + ep->rdeg-- ; /* decrement external row degree */ + DEBUG6 (("e "ID" New ext row degree: "ID"\n", e, ep->rdeg-rdeg0)) ; + + if (ep->rdeg == rdeg0 && ep->next == EMPTY) + { + /* A new LUson or Lson has been found */ + ep->next = son_list ; + son_list = e ; + } + + ASSERT (ep->rdeg >= rdeg0) ; + *tp2++ = *tp ; /* leave the tuple in the list */ + } + Col_tlen [col] = tp2 - tp1 ; + } + + /* ---------------------------------------------------------------------- */ + /* assemble new sons via full scans */ + /* ---------------------------------------------------------------------- */ + + next = EMPTY ; + + for (e = son_list ; e > 0 ; e = next) + { + ASSERT (e > 0 && e <= Work->nel && E [e]) ; + p = Memory + E [e] ; + DEBUG2 (("New son: "ID"\n", e)) ; +#ifndef NDEBUG + UMF_dump_element (Numeric, Work, e, FALSE) ; +#endif + GET_ELEMENT (ep, p, Cols, Rows, ncols, nrows, C) ; + nrowsleft = ep->nrowsleft ; + ncolsleft = ep->ncolsleft ; + next = ep->next ; + ep->next = EMPTY ; + + extrdeg = (ep->rdeg < rdeg0) ? ncolsleft : (ep->rdeg - rdeg0) ; + extcdeg = (ep->cdeg < cdeg0) ? nrowsleft : (ep->cdeg - cdeg0) ; + ncols_to_assemble = ncolsleft - extrdeg ; + nrows_to_assemble = nrowsleft - extcdeg ; + + if (extrdeg == 0 && extcdeg == 0) + { + + /* -------------------------------------------------------------- */ + /* this is an LUson - assemble an entire contribution block */ + /* -------------------------------------------------------------- */ + + DEBUG6 (("LUson found: "ID"\n", e)) ; + + if (nrows == nrowsleft) + { + /* ---------------------------------------------------------- */ + /* no rows assembled out of this LUson yet */ + /* ---------------------------------------------------------- */ + + /* compute the compressed column offset vector*/ + /* [ use Wm [0..nrows-1] for offsets */ + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + Row_degree [row] -= ncolsleft ; + Wm [i] = Frpos [row] ; + } + + if (ncols == ncolsleft) + { + /* ------------------------------------------------------ */ + /* no rows or cols assembled out of LUson yet */ + /* ------------------------------------------------------ */ + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + Col_degree [col] -= nrowsleft ; + Fcol = Fx + Fcpos [col] ; + for (i = 0 ; i < nrows ; i++) + { + /* Fcol [Wm [i]] += C [i] ; */ + ASSEMBLE (Fcol [Wm [i]], C [i]) ; + } + C += nrows ; + } + } + else + { + /* ------------------------------------------------------ */ + /* only cols have been assembled out of LUson */ + /* ------------------------------------------------------ */ + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + if (col >= 0) + { + Col_degree [col] -= nrowsleft ; + Fcol = Fx + Fcpos [col] ; + for (i = 0 ; i < nrows ; i++) + { + /* Fcol [Wm [i]] += C [i] ; */ + ASSEMBLE (Fcol [Wm [i]], C [i]) ; + } + } + C += nrows ; + } + } + /* ] done using Wm [0..nrows-1] for offsets */ + } + else + { + /* ---------------------------------------------------------- */ + /* some rows have been assembled out of this LUson */ + /* ---------------------------------------------------------- */ + + /* compute the compressed column offset vector*/ + /* [ use Woo,Wm [0..nrowsleft-1] for offsets */ + ngetrows = 0 ; + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + if (row >= 0) + { + Row_degree [row] -= ncolsleft ; + Woo [ngetrows] = i ; + Wm [ngetrows++] = Frpos [row] ; + } + } + ASSERT (ngetrows == nrowsleft) ; + + if (ncols == ncolsleft) + { + /* ------------------------------------------------------ */ + /* only rows have been assembled out of this LUson */ + /* ------------------------------------------------------ */ + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + Col_degree [col] -= nrowsleft ; + Fcol = Fx + Fcpos [col] ; + for (i = 0 ; i < nrowsleft ; i++) + { + /* Fcol [Wm [i]] += C [Woo [i]] ; */ + ASSEMBLE (Fcol [Wm [i]], C [Woo [i]]) ; + } + C += nrows ; + } + + } + else + { + /* ------------------------------------------------------ */ + /* both rows and columns have been assembled out of LUson */ + /* ------------------------------------------------------ */ + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + if (col >= 0) + { + Col_degree [col] -= nrowsleft ; + Fcol = Fx + Fcpos [col] ; + for (i = 0 ; i < nrowsleft ; i++) + { + /* Fcol [Wm [i]] += C [Woo [i]] ; */ + ASSEMBLE (Fcol [Wm [i]], C [Woo [i]]) ; + } + } + C += nrows ; + } + } + /* ] done using Woo,Wm [0..nrowsleft-1] */ + } + + /* deallocate the element: remove from ordered list */ + UMF_mem_free_tail_block (Numeric, E [e]) ; + E [e] = 0 ; + + } + else if (extcdeg == 0) + { + + /* -------------------------------------------------------------- */ + /* this is a Uson - assemble all possible columns */ + /* -------------------------------------------------------------- */ + + DEBUG6 (("New USON: "ID"\n", e)) ; + ASSERT (extrdeg > 0) ; + + DEBUG6 (("New uson "ID" cols to do "ID"\n", e, ncols_to_assemble)) ; + + if (ncols_to_assemble > 0) + { + + if (nrows == nrowsleft) + { + /* ------------------------------------------------------ */ + /* no rows have been assembled out of this Uson yet */ + /* ------------------------------------------------------ */ + + /* compute the compressed column offset vector */ + /* [ use Wm [0..nrows-1] for offsets */ + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + ASSERT (row >= 0 && row < n_row) ; + Row_degree [row] -= ncols_to_assemble ; + Wm [i] = Frpos [row] ; + } + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + if ((col >= 0) && (Fcpos [col] >= 0)) + { + Col_degree [col] -= nrowsleft ; + Fcol = Fx + Fcpos [col] ; + for (i = 0 ; i < nrows ; i++) + { + /* Fcol [Wm [i]] += C [i] ; */ + ASSEMBLE (Fcol [Wm [i]], C [i]) ; + } + /* flag the column as assembled from Uson */ + Cols [j] = EMPTY ; + } + C += nrows ; + } + /* ] done using Wm [0..nrows-1] for offsets */ + } + else + { + /* ------------------------------------------------------ */ + /* some rows have been assembled out of this Uson */ + /* ------------------------------------------------------ */ + + /* compute the compressed column offset vector*/ + /* [ use Woo,Wm [0..nrows-1] for offsets */ + ngetrows = 0 ; + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + if (row >= 0) + { + Row_degree [row] -= ncols_to_assemble ; + ASSERT (row < n_row && Frpos [row] >= 0) ; + Woo [ngetrows] = i ; + Wm [ngetrows++] = Frpos [row] ; + } + } + ASSERT (ngetrows == nrowsleft) ; + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + if ((col >= 0) && (Fcpos [col] >= 0)) + { + Col_degree [col] -= nrowsleft ; + Fcol = Fx + Fcpos [col] ; + for (i = 0 ; i < nrowsleft ; i++) + { + /* Fcol [Wm [i]] += C [Woo [i]] ; */ + ASSEMBLE (Fcol [Wm [i]], C [Woo [i]]) ; + } + /* flag the column as assembled from Uson */ + Cols [j] = EMPTY ; + } + C += nrows ; + } + /* ] done using Woo,Wm */ + } + ep->ncolsleft = extrdeg ; + } + + } + else + { + + /* -------------------------------------------------------------- */ + /* this is an Lson - assemble all possible rows */ + /* -------------------------------------------------------------- */ + + DEBUG6 (("New LSON: "ID"\n", e)) ; + ASSERT (extrdeg == 0 && extcdeg > 0) ; + + DEBUG6 (("New lson "ID" rows to do "ID"\n", e, nrows_to_assemble)) ; + + if (nrows_to_assemble > 0) + { + + /* compute the compressed column offset vector */ + /* [ use Woo,Wm [0..nrows-1] for offsets */ + ngetrows = 0 ; + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + if ((row >= 0) && (Frpos [row] >= 0)) + { + ASSERT (row < n_row) ; + Row_degree [row] -= ncolsleft ; + Woo [ngetrows] = i ; + Wm [ngetrows++] = Frpos [row] ; + /* flag the row as assembled from the Lson */ + Rows [i] = EMPTY ; + } + } + ASSERT (nrowsleft - ngetrows == extcdeg) ; + ASSERT (ngetrows == nrows_to_assemble) ; + + if (ncols == ncolsleft) + { + /* ------------------------------------------------------ */ + /* no columns assembled out this Lson yet */ + /* ------------------------------------------------------ */ + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + ASSERT (col >= 0 && col < n_col) ; + Col_degree [col] -= nrows_to_assemble ; + Fcol = Fx + Fcpos [col] ; + for (i = 0 ; i < nrows_to_assemble ; i++) + { + /* Fcol [Wm [i]] += C [Woo [i]] ; */ + ASSEMBLE (Fcol [Wm [i]], C [Woo [i]]) ; + } + C += nrows ; + } + } + else + { + /* ------------------------------------------------------ */ + /* some columns have been assembled out of this Lson */ + /* ------------------------------------------------------ */ + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + ASSERT (col < n_col) ; + if (col >= 0) + { + Col_degree [col] -= nrows_to_assemble ; + Fcol = Fx + Fcpos [col] ; + for (i = 0 ; i < nrows_to_assemble ; i++) + { + /* Fcol [Wm [i]] += C [Woo [i]] ; */ + ASSEMBLE (Fcol [Wm [i]], C [Woo [i]]) ; + } + } + C += nrows ; + } + } + + /* ] done using Woo,Wm */ + + ep->nrowsleft = extcdeg ; + } + } + } + + /* Note that garbage collection, and build tuples */ + /* both destroy the son list. */ + + /* ] son_list now empty */ + + /* ---------------------------------------------------------------------- */ + /* If frontal matrix extended, assemble old L/Usons from new rows/cols */ + /* ---------------------------------------------------------------------- */ + + /* ---------------------------------------------------------------------- */ + /* SCAN2-row: assemble rows of old Lsons from the new rows */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG7 (("Current frontal matrix: (prior to scan2-row)\n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; +#endif + + if (Work->do_scan2row) + { + + scan_pivrow = Work->scan_pivrow ; + + for (i2 = Work->fscan_row ; i2 < fnrows || scan_pivrow ; ) + { + /* Get a row */ + if (scan_pivrow) + { + /* pivot row is new to this front. Scan it */ + row = Work->pivrow ; + scan_pivrow = FALSE ; + } + else + { + row = Frows [i2++] ; + } + ASSERT (row >= 0 && row < n_row) ; + +#ifndef NDEBUG + DEBUG6 (("SCAN2-row: "ID"\n", row)) ; + UMF_dump_rowcol (0, Numeric, Work, row, FALSE) ; +#endif + + ASSERT (NON_PIVOTAL_ROW (row)) ; + tpi = Row_tuples [row] ; + if (!tpi) continue ; + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Row_tlen [row] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + Rows = Cols + ep->ncols ; + if (Rows [f] == EMPTY) continue ; /* row already assembled */ + ASSERT (row == Rows [f] && row >= 0 && row < n_row) ; + + if (ep->rdeg == rdeg0) + { + /* ------------------------------------------------------ */ + /* this is an old Lson - assemble just one row */ + /* ------------------------------------------------------ */ + + /* flag the row as assembled from the Lson */ + Rows [f] = EMPTY ; + + nrows = ep->nrows ; + ncols = ep->ncols ; + p += UNITS (Int, ncols + nrows) ; + C = ((Entry *) p) + f ; + + DEBUG6 (("Old LSON: "ID"\n", e)) ; +#ifndef NDEBUG + UMF_dump_element (Numeric, Work, e, FALSE) ; +#endif + + nrowsleft = ep->nrowsleft ; + ncolsleft = ep->ncolsleft ; + + Frow = Fx + Frpos [row] ; + DEBUG6 (("LSON found (in scan2-row): "ID"\n", e)) ; + + Row_degree [row] -= ncolsleft ; + + if (ncols == ncolsleft) + { + /* -------------------------------------------------- */ + /* no columns assembled out this Lson yet */ + /* -------------------------------------------------- */ + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + ASSERT (col >= 0 && col < n_col) ; + Col_degree [col] -- ; + /* Frow [Fcpos [col]] += *C ; */ + ASSEMBLE (Frow [Fcpos [col]], *C) ; + C += nrows ; + } + } + else + { + /* -------------------------------------------------- */ + /* some columns have been assembled out of this Lson */ + /* -------------------------------------------------- */ + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + if (col >= 0) + { + ASSERT (col < n_col) ; + Col_degree [col] -- ; + /* Frow [Fcpos [col]] += *C ; */ + ASSEMBLE (Frow [Fcpos [col]], *C) ; + } + C += nrows ; + } + } + ep->nrowsleft-- ; + ASSERT (ep->nrowsleft > 0) ; + } + else + { + *tp2++ = *tp ; /* leave the tuple in the list */ + } + } + Row_tlen [row] = tp2 - tp1 ; + +#ifndef NDEBUG + DEBUG7 (("row assembled in scan2-row: "ID"\n", row)) ; + if (row != Work->pivrow) + { + UMF_dump_rowcol (0, Numeric, Work, row, FALSE) ; + } + DEBUG7 (("Current frontal matrix: (scan 1b)\n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; +#endif + } + } + + /* ---------------------------------------------------------------------- */ + /* SCAN2-col: assemble columns of old Usons from the new columns */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG7 (("Current frontal matrix: (prior to scan2-col)\n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; +#endif + + if (Work->do_scan2col) + { + + scan_pivcol = Work->scan_pivcol ; + + for (j2 = Work->fscan_col ; j2 < fncols || scan_pivcol ; ) + { + /* Get a column */ + if (scan_pivcol) + { + /* pivot col is new to this front. Scan it */ + col = Work->pivcol ; + scan_pivcol = FALSE ; + } + else + { + col = Fcols [j2++] ; + } + ASSERT (col >= 0 && col < n_col) ; + + DEBUG6 (("SCAN2-col: "ID"\n", col)) ; +#ifndef NDEBUG + UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ; +#endif + + ASSERT (NON_PIVOTAL_COL (col)) ; + tpi = Col_tuples [col] ; + if (!tpi) continue ; + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Col_tlen [col] ; + for ( ; tp < tpend ; tp++) + { + + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + if (Cols [f] == EMPTY) + { + continue ; /* column already assembled */ + } + ASSERT (col == Cols [f] && col >= 0 && col < n_col) ; + + if (ep->cdeg == cdeg0) + { + + /* ------------------------------------------------------ */ + /* this is an old Uson - assemble just one column */ + /* ------------------------------------------------------ */ + + /* flag the column as assembled from the Uson */ + Cols [f] = EMPTY ; + + nrows = ep->nrows ; + ncols = ep->ncols ; + Rows = Cols + ncols ; + p += UNITS (Int, ep->ncols + nrows) ; + C = ((Entry *) p) + f * nrows ; + + DEBUG6 (("Old USON: "ID"\n", e)) ; +#ifndef NDEBUG + UMF_dump_element (Numeric, Work, e, FALSE) ; +#endif + + nrowsleft = ep->nrowsleft ; + ncolsleft = ep->ncolsleft ; + + Fcol = Fx + Fcpos [col] ; + DEBUG6 (("USON found (in scan2-col): "ID"\n", e)) ; + + Col_degree [col] -= nrowsleft ; + + if (nrows == nrowsleft) + { + /* -------------------------------------------------- */ + /* no rows assembled out of this Uson yet */ + /* -------------------------------------------------- */ + + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + ASSERT (row >= 0 && row < n_row) ; + Row_degree [row]-- ; + /* Fcol [Frpos [row]] += C [i] ; */ + ASSEMBLE (Fcol [Frpos [row]], C [i]) ; + } + } + else + { + /* -------------------------------------------------- */ + /* some rows have been assembled out of this Uson */ + /* -------------------------------------------------- */ + + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + if (row >= 0) + { + ASSERT (row < n_row) ; + Row_degree [row]-- ; + /* Fcol [Frpos [row]] += C [i] ; */ + ASSEMBLE (Fcol [Frpos [row]], C [i]) ; + } + } + } + ep->ncolsleft-- ; + ASSERT (ep->ncolsleft > 0) ; + } + else + { + *tp2++ = *tp ; /* leave the tuple in the list */ + } + } + Col_tlen [col] = tp2 - tp1 ; + +#ifndef NDEBUG + DEBUG7 (("Column assembled in scan2-col: "ID"\n", col)) ; + if (col != Work->pivcol) + { + UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ; + } + DEBUG7 (("Current frontal matrix: after scan2-col\n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; +#endif + + } + + } + + /* ---------------------------------------------------------------------- */ + /* done. the remainder of this routine is used only when in debug mode */ + /* ---------------------------------------------------------------------- */ + + + +#ifndef NDEBUG + + /* ---------------------------------------------------------------------- */ + /* when debugging: make sure the assembly did everything that it could */ + /* ---------------------------------------------------------------------- */ + + DEBUG3 (("::Assemble done\n")) ; + + scan_pivrow = TRUE ; + + for (i2 = 0 ; i2 < fnrows || scan_pivrow ; ) + { + /* Get a row */ + if (scan_pivrow) + { + /* pivot row is new to this front. Scan it */ + row = Work->pivrow ; + scan_pivrow = FALSE ; + } + else + { + row = Frows [i2++] ; + } + ASSERT (row >= 0 && row < n_row) ; + + DEBUG6 (("DEBUG SCAN 1: "ID"\n", row)) ; + UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ; + + ASSERT (NON_PIVOTAL_ROW (row)) ; + tpi = Row_tuples [row] ; + if (!tpi) continue ; + tp = (Tuple *) (Memory + tpi) ; + tpend = tp + Row_tlen [row] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Rows = ((Int *) p) + ep->ncols ; + if (Rows [f] == EMPTY) continue ; /* row already assembled */ + ASSERT (row == Rows [f]) ; + extrdeg = (ep->rdeg < rdeg0) ? ep->ncolsleft : (ep->rdeg - rdeg0) ; + extcdeg = (ep->cdeg < cdeg0) ? ep->nrowsleft : (ep->cdeg - cdeg0) ; + DEBUG6 (( + "e "ID" After assembly ext row deg: "ID" ext col degree "ID"\n", + e, extrdeg, extcdeg)) ; + + /* no Lsons in any row */ + ASSERT (extrdeg > 0) ; + + /* Uson external row degree is = number of cols left */ + ASSERT (IMPLIES (extcdeg == 0, extrdeg == ep->ncolsleft)) ; + } + } + + /* ---------------------------------------------------------------------- */ + + scan_pivcol = TRUE ; + + for (j2 = 0 ; j2 < fncols || scan_pivcol ; ) + { + /* Get a column */ + if (scan_pivcol) + { + /* pivot col is new to this front. Scan it */ + col = Work->pivcol ; + scan_pivcol = FALSE ; + } + else + { + col = Fcols [j2++] ; + } + ASSERT (col >= 0 && col < n_col) ; + + DEBUG6 (("DEBUG SCAN 2: "ID"\n", col)) ; + UMF_dump_rowcol (1, Numeric, Work, col, TRUE) ; + + ASSERT (NON_PIVOTAL_COL (col)) ; + tpi = Col_tuples [col] ; + if (!tpi) continue ; + tp = (Tuple *) (Memory + tpi) ; + tpend = tp + Col_tlen [col] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + if (Cols [f] == EMPTY) continue ; /* column already assembled */ + ASSERT (col == Cols [f]) ; + extrdeg = (ep->rdeg < rdeg0) ? ep->ncolsleft : (ep->rdeg - rdeg0) ; + extcdeg = (ep->cdeg < cdeg0) ? ep->nrowsleft : (ep->cdeg - cdeg0) ; + DEBUG6 (("e "ID" After assembly ext col deg: "ID"\n", e, extcdeg)) ; + + /* no Usons in any column */ + ASSERT (extcdeg > 0) ; + + /* Lson external column degree is = number of rows left */ + ASSERT (IMPLIES (extrdeg == 0, extcdeg == ep->nrowsleft)) ; + } + } + +#endif /* NDEBUG */ + +} + diff --git a/src/sparse-matrix/umfpack/umf_blas3_update.c b/src/sparse-matrix/umfpack/umf_blas3_update.c new file mode 100644 index 0000000..0deee0e --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_blas3_update.c @@ -0,0 +1,125 @@ +/* ========================================================================== */ +/* === UMF_blas3_update ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +#include "umf_internal.h" + +GLOBAL void UMF_blas3_update +( + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Entry *A, *B, *C ; + Int k, m, n, d ; + + /* ---------------------------------------------------------------------- */ + /* perform the matrix-matrix multiply */ + /* ---------------------------------------------------------------------- */ + + DEBUG5 (("In UMF_blas3_update "ID" "ID" "ID"\n", + Work->fnpiv, Work->fnrows, Work->fncols)) ; + + k = Work->fnpiv ; + Work->fnpiv = 0 ; /* no more pivots in frontal working array */ + Work->fnzeros = 0 ; + m = Work->fnrows ; + n = Work->fncols ; + if (k == 0 || m == 0 || n == 0) + { + return ; + } + + d = Work->fnrows_max ; + C = Work->Fx ; + A = C + (Work->fncols_max - k) * d ; + B = C + d - k ; + + DEBUG5 (("DO RANK-NB UPDATE of frontal:\n")) ; + DEBUG5 (("DGEMM : "ID" "ID" "ID"\n", k, m, n)) ; + + /* ---------------------------------------------------------------------- */ + /* C = C - A*B */ + /* ---------------------------------------------------------------------- */ + + if (k == 1) + { + +#ifdef USE_NO_BLAS + + /* no BLAS available - use plain C code instead */ + Int i, j ; + Entry b_sj, *c_ij, *a_is ; + for (j = 0 ; j < n ; j++) + { + b_sj = B [j*d] ; + if (IS_NONZERO (b_sj)) + { + c_ij = & C [j*d] ; + a_is = & A [0] ; + for (i = 0 ; i < m ; i++) + { + /* (*c_ij++) -= b_sj * (*a_is++) ; */ + MULT_SUB (*c_ij, b_sj, *a_is) ; + c_ij++ ; + a_is++ ; + } + } + } + +#else + + BLAS_GER (m, n, A, B, C, d) ; + +#endif /* USE_NO_BLAS */ + + } + else + { + +#ifdef USE_NO_BLAS + + /* no BLAS available - use plain C code instead */ + Int i, j, s ; + Entry b_sj, *c_ij, *a_is ; + for (j = 0 ; j < n ; j++) + { + for (s = 0 ; s < k ; s++) + { + b_sj = B [s+j*d] ; + if (IS_NONZERO (b_sj)) + { + c_ij = & C [j*d] ; + a_is = & A [s*d] ; + for (i = 0 ; i < m ; i++) + { + /* (*c_ij++) -= b_sj * (*a_is++) ; */ + MULT_SUB (*c_ij, b_sj, *a_is) ; + c_ij++ ; + a_is++ ; + } + } + } + } + +#else + + BLAS_GEMM (m, n, k, A, B, C, d) ; + +#endif /* USE_NO_BLAS */ + + } + + DEBUG2 (("blas3 "ID" "ID" "ID"\n", k, Work->fnrows, Work->fncols)) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_build_tuples.c b/src/sparse-matrix/umfpack/umf_build_tuples.c new file mode 100644 index 0000000..c2c4710 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_build_tuples.c @@ -0,0 +1,158 @@ +/* ========================================================================== */ +/* === UMF_build_tuples ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Construct the tuple lists from a set of packed elements (no holes in + elements, no internal or external fragmentation, and a packed (0..Work->nel) + element name space). Assume no tuple lists are currently allocated, but + that the tuple lengths have been initialized by UMF_tuple_lengths. + + Returns TRUE if successful, FALSE if not enough memory. +*/ + +#include "umf_internal.h" +#include "umf_mem_alloc_tail_block.h" + +GLOBAL Int UMF_build_tuples +( + NumericType *Numeric, + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int e, nrows, ncols, nel, *Rows, *Cols, row, col, n_row, n_col, *E, + *Row_tuples, *Row_degree, *Row_tlen, tlen, tsize, + *Col_tuples, *Col_degree, *Col_tlen ; + Element *ep ; + Unit *p ; + Tuple tuple, *tp ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + E = Work->E ; + Col_degree = Numeric->Cperm ; /* for NON_PIVOTAL_COL macro */ + Row_degree = Numeric->Rperm ; /* for NON_PIVOTAL_ROW macro */ + Row_tuples = Numeric->Uip ; + Row_tlen = Numeric->Uilen ; + Col_tuples = Numeric->Lip ; + Col_tlen = Numeric->Lilen ; + n_row = Work->n_row ; + n_col = Work->n_col ; + nel = Work->nel ; + + DEBUG3 (("BUILD_TUPLES: n_row "ID" n_col "ID" nel "ID"\n", + n_row, n_col, nel)) ; + + /* ---------------------------------------------------------------------- */ + /* allocate space for the tuple lists */ + /* ---------------------------------------------------------------------- */ + + /* Garbage collection and memory reallocation have already attempted to */ + /* ensure that there is enough memory for all the tuple lists. If */ + /* memory allocation fails here, then there is nothing more to be done. */ + + for (row = 0 ; row < n_row ; row++) + { + if (NON_PIVOTAL_ROW (row)) + { + tlen = MAX (4, Row_tlen [row] + 1) ; + tsize = UNITS (Tuple, tlen) ; + Row_tuples [row] = UMF_mem_alloc_tail_block (Numeric, tsize) ; + if (!Row_tuples [row]) + { + return (FALSE) ; /* out of memory for row tuples */ + } + Row_tlen [row] = 0 ; + } + } + + /* push on stack in reverse order, so column tuples are in the order */ + /* that they will be deleted. */ + for (col = n_col-1 ; col >= 0 ; col--) + { + if (NON_PIVOTAL_COL (col)) + { + tlen = MAX (4, Col_tlen [col] + 1) ; + tsize = UNITS (Tuple, tlen) ; + Col_tuples [col] = UMF_mem_alloc_tail_block (Numeric, tsize) ; + if (!Col_tuples [col]) + { + return (FALSE) ; /* out of memory for col tuples */ + } + Col_tlen [col] = 0 ; + } + } + +#ifndef NDEBUG + UMF_dump_memory (Numeric) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* create the tuple lists (exclude element 0) */ + /* ---------------------------------------------------------------------- */ + + /* for all elements, in order of creation */ + for (e = 1 ; e <= nel ; e++) + { + DEBUG9 (("Adding tuples for element: "ID" at "ID"\n", e, E [e])) ; + ASSERT (E [e]) ; /* no external fragmentation */ + p = Numeric->Memory + E [e] ; + GET_ELEMENT_PATTERN (ep, p, Cols, Rows, ncols) ; + nrows = ep->nrows ; + ASSERT (e != 0) ; + ASSERT (e == 0 || nrows == ep->nrowsleft) ; + ASSERT (e == 0 || ncols == ep->ncolsleft) ; + tuple.e = e ; + for (tuple.f = 0 ; tuple.f < ncols ; tuple.f++) + { + col = Cols [tuple.f] ; + ASSERT (col >= 0 && col < n_col) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + ASSERT (Col_tuples [col]) ; + tp = ((Tuple *) (Numeric->Memory + Col_tuples [col])) + + Col_tlen [col]++ ; + *tp = tuple ; +#ifndef NDEBUG + UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ; +#endif + } + for (tuple.f = 0 ; tuple.f < nrows ; tuple.f++) + { + row = Rows [tuple.f] ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + ASSERT (Row_tuples [row]) ; + tp = ((Tuple *) (Numeric->Memory + Row_tuples [row])) + + Row_tlen [row]++ ; + *tp = tuple ; +#ifndef NDEBUG + UMF_dump_rowcol (0, Numeric, Work, row, FALSE) ; +#endif + } + } + + /* ---------------------------------------------------------------------- */ + /* the tuple lists are now valid, and can be scanned */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + UMF_dump_memory (Numeric) ; + UMF_dump_matrix (Numeric, Work, FALSE) ; +#endif + DEBUG3 (("BUILD_TUPLES: done\n")) ; + return (TRUE) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_build_tuples_usage.c b/src/sparse-matrix/umfpack/umf_build_tuples_usage.c new file mode 100644 index 0000000..2f87606 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_build_tuples_usage.c @@ -0,0 +1,75 @@ +/* ========================================================================== */ +/* === UMF_build_tuples_usage =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Return number of Units needed for UMF_build_tuples */ + +#include "umf_internal.h" + +GLOBAL Int UMF_build_tuples_usage +( + const Int Col_tlen [ ], + const Int Col_degree [ ], + const Int Row_tlen [ ], + const Int Row_degree [ ], + Int n_row, + Int n_col, + double *dusage /* input and output argument */ +) +{ + Int row, col, usage ; + double du ; + + /* note: tuple lengths are initialized, but the tuple lists themselves */ + /* may not be. */ + + usage = 0 ; + du = 0 ; + if (!Col_tlen || !Col_degree) + { + /* Col_tlen and Col_degree arrays are missing, so this is the */ + /* initial matrix, with one element per column. */ + usage += n_col * (1 + UNITS (Tuple, 4)) ; + du += ((double) n_col) * (1 + DUNITS (Tuple, 4)) ; + } + else + { + for (col = 0 ; col < n_col ; col++) + { + if (NON_PIVOTAL_COL (col)) + { + usage += 1 + UNITS (Tuple, MAX (4, Col_tlen [col] + 1)) ; + du += 1 + DUNITS (Tuple, MAX (4, Col_tlen [col] + 1)) ; + } + } + } + ASSERT (Row_tlen && Row_degree) ; + for (row = 0 ; row < n_row ; row++) + { + if (NON_PIVOTAL_ROW (row)) + { + usage += 1 + UNITS (Tuple, MAX (4, Row_tlen [row] + 1)) ; + du += 1 + DUNITS (Tuple, MAX (4, Row_tlen [row] + 1)) ; + } + } + + /* roundoff error in du is at most 3*n*epsilon */ + /* (here, and in UMF_kernel_init_usage) */ + /* Ignore NaN or Inf behavior here. */ + du = MAX (du, (double) usage * (1.0 + MAX_EPSILON)) ; + du += (((double) n_row) + 2*((double) n_col)) * MAX_EPSILON ; + du = ceil (du) ; + + DEBUG0 (("UMF_build_tuples_usage "ID" %g\n", usage, *dusage)) ; + + *dusage += du ; + return (usage) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_colamd.c b/src/sparse-matrix/umfpack/umf_colamd.c new file mode 100644 index 0000000..01b1251 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_colamd.c @@ -0,0 +1,3077 @@ +/* ========================================================================== */ +/* === UMF_colamd =========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* +UMF_colamd: an approximate minimum degree column ordering algorithm, + used as a preordering for UMFPACK. + +Purpose: + + Colamd computes a permutation Q such that the Cholesky factorization of + (AQ)'(AQ) has less fill-in and requires fewer floating point operations + than A'A. This also provides a good ordering for sparse partial + pivoting methods, P(AQ) = LU, where Q is computed prior to numerical + factorization, and P is computed during numerical factorization via + conventional partial pivoting with row interchanges. Colamd is the + column ordering method used in SuperLU, part of the ScaLAPACK library. + It is also available as built-in function in MATLAB Version 6, + available from MathWorks, Inc. (http://www.mathworks.com). This + routine can be used in place of colmmd in MATLAB. By default, the \ + and / operators in MATLAB perform a column ordering (using colmmd + or colamd) prior to LU factorization using sparse partial pivoting, + in the built-in MATLAB lu(A) routine. + + This code is derived from Colamd Version 2.0. + +Authors: + + The authors of the COLAMD code itself are Stefan I. Larimore and Timothy A. + Davis, University of Florida. The algorithm was developed in collaboration + with John Gilbert, Xerox PARC, and Esmond Ng, Oak Ridge National Laboratory. + The AMD metric on which this is based is by Patrick Amestoy, T. Davis, + and Iain Duff. + +Date: + + UMFPACK Version: see above. + COLAMD Version 2.0 was released on January 31, 2000. + +Acknowledgements: + + This work was supported by the National Science Foundation, under + grants DMS-9504974 and DMS-9803599. + +UMFPACK: Copyright (c) 2002 by Timothy A. Davis. All Rights Reserved. + +See the UMFPACK README file for the License for your use of this code. + +Availability: + + Both UMFPACK and the original unmodified colamd/symamd library are + available at http://www.cise.ufl.edu/research/sparse. + +Changes for inclusion in UMFPACK: + + * symamd, symamd_report, and colamd_report removed + + * additional terms added to RowInfo, ColInfo, and stats + + * Frontal matrix information computed for UMFPACK + + * routines renamed + + * column elimination tree post-ordering incorporated. In the original + version 2.0, this was performed in colamd.m. + +For more information, see: + + Amestoy, P. R. and Davis, T. A. and Duff, I. S., + An approximate minimum degree ordering algorithm, + SIAM J. Matrix Analysis and Applic, vol 17, no 4., pp 886-905, 1996. + + Davis, T. A. and Gilbert, J. R. and Larimore, S. I. and Ng, E. G., + A column approximate minimum degree ordering algorithm, + Univ. of Florida, CISE Dept., TR-00-005, Gainesville, FL + Oct. 2000. Submitted to ACM Trans. Math. Softw. + +*/ + +/* ========================================================================== */ +/* === Description of user-callable routines ================================ */ +/* ========================================================================== */ + +/* + ---------------------------------------------------------------------------- + colamd_recommended: removed for UMFPACK + ---------------------------------------------------------------------------- + + ---------------------------------------------------------------------------- + colamd_set_defaults: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + colamd_set_defaults (Int knobs [COLAMD_KNOBS]) ; + + Purpose: + + Sets the default parameters. The use of this routine is optional. + + Arguments: + + double knobs [COLAMD_KNOBS] ; Output only. + + Let c = knobs [COLAMD_DENSE_COL], r = knobs [COLAMD_DENSE_ROW]. + Colamd: rows with more than max (16, r*16*sqrt(n_col)) + entries are removed prior to ordering. Columns with more than + max (16, c*16*sqrt(n_row)) entries are removed prior to + ordering, and placed last in the output column ordering. + + Symamd: removed for UMFPACK. + + COLAMD_DENSE_ROW and COLAMD_DENSE_COL are defined as 0 and 1, + respectively, in colamd.h. Default values of these two knobs + are both 0.5. Currently, only knobs [0] and knobs [1] are + used, but future versions may use more knobs. If so, they will + be properly set to their defaults by the future version of + colamd_set_defaults, so that the code that calls colamd will + not need to change, assuming that you either use + colamd_set_defaults, or pass a (double *) NULL pointer as the + knobs array to colamd or symamd. + + ---------------------------------------------------------------------------- + colamd: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + Int UMF_colamd (Int n_row, Int n_col, Int Alen, Int *A, Int *p, + double knobs [COLAMD_KNOBS], Int stats [COLAMD_STATS]) ; + + Purpose: + + Computes a column ordering (Q) of A such that P(AQ)=LU or + (AQ)'AQ=LL' have less fill-in and require fewer floating point + operations than factorizing the unpermuted matrix A or A'A, + respectively. + + Returns: + + TRUE (1) if successful, FALSE (0) otherwise. + + Arguments: + + Int n_row ; Input argument. + + Number of rows in the matrix A. + Restriction: n_row >= 0. + Colamd returns FALSE if n_row is negative. + + Int n_col ; Input argument. + + Number of columns in the matrix A. + Restriction: n_col >= 0. + Colamd returns FALSE if n_col is negative. + + Int Alen ; Input argument. + + Restriction (see note): + Alen >= 2*nnz + 8*(n_col+1) + 6*(n_row+1) + n_col + Colamd returns FALSE if these conditions are not met. + + Note: this restriction makes an modest assumption regarding + the size of the two typedef's structures in colamd.h. + We do, however, guarantee that + + Alen >= UMF_COLAMD_RECOMMENDED (nnz, n_row, n_col) + + will be sufficient. + + Int A [Alen] ; Input and output argument. + + A is an integer array of size Alen. Alen must be at least as + large as the bare minimum value given above, but this is very + low, and can result in excessive run time. For best + performance, we recommend that Alen be greater than or equal to + UMF_COLAMD_RECOMMENDED (nnz, n_row, n_col), which adds + nnz/5 to the bare minimum value given above. + + On input, the row indices of the entries in column c of the + matrix are held in A [(p [c]) ... (p [c+1]-1)]. The row indices + in a given column c need not be in ascending order, and + duplicate row indices may be be present. However, colamd will + work a little faster if both of these conditions are met + (Colamd puts the matrix into this format, if it finds that the + the conditions are not met). + + The matrix is 0-based. That is, rows are in the range 0 to + n_row-1, and columns are in the range 0 to n_col-1. Colamd + returns FALSE if any row index is out of range. + + A holds the inverse permutation on output. + + Int p [n_col+1] ; Both input and output argument. + + p is an integer array of size n_col+1. On input, it holds the + "pointers" for the column form of the matrix A. Column c of + the matrix A is held in A [(p [c]) ... (p [c+1]-1)]. The first + entry, p [0], must be zero, and p [c] <= p [c+1] must hold + for all c in the range 0 to n_col-1. The value p [n_col] is + thus the total number of entries in the pattern of the matrix A. + Colamd returns FALSE if these conditions are not met. + + On output, if colamd returns TRUE, the array p holds the column + permutation (Q, for P(AQ)=LU or (AQ)'(AQ)=LL'), where p [0] is + the first column index in the new ordering, and p [n_col-1] is + the last. That is, p [k] = j means that column j of A is the + kth pivot column, in AQ, where k is in the range 0 to n_col-1 + (p [0] = j means that column j of A is the first column in AQ). + + If colamd returns FALSE, then no permutation is returned, and + p is undefined on output. + + double knobs [COLAMD_KNOBS] ; Input argument. + + See colamd_set_defaults for a description. + The behavior is undefined if knobs contains NaN's. + (UMFPACK does not call umf_colamd with NaN-valued knobs). + + Int stats [COLAMD_STATS] ; Output argument. + + Statistics on the ordering, and error status. + See colamd.h for related definitions. + Colamd returns FALSE if stats is not present. + + stats [0]: number of dense or empty rows ignored. + + stats [1]: number of dense or empty columns ignored (and + ordered last in the output permutation p) + Note that a row can become "empty" if it + contains only "dense" and/or "empty" columns, + and similarly a column can become "empty" if it + only contains "dense" and/or "empty" rows. + + stats [2]: number of garbage collections performed. + This can be excessively high if Alen is close + to the minimum required value. + + stats [3]: status code. < 0 is an error code. + > 1 is a warning or notice. + + 0 OK. Each column of the input matrix contained + row indices in increasing order, with no + duplicates. + + -11 Columns of input matrix jumbled + (unsorted columns or duplicate entries). + + stats [4]: the bad column index + stats [5]: the bad row index + + -1 A is a null pointer + + -2 p is a null pointer + + -3 n_row is negative + + stats [4]: n_row + + -4 n_col is negative + + stats [4]: n_col + + -5 number of nonzeros in matrix is negative + + stats [4]: number of nonzeros, p [n_col] + + -6 p [0] is nonzero + + stats [4]: p [0] + + -7 A is too small + + stats [4]: required size + stats [5]: actual size (Alen) + + -8 a column has a zero or negative number of + entries (changed for UMFPACK) + + stats [4]: column with <= 0 entries + stats [5]: number of entries in col + + -9 a row index is out of bounds + + stats [4]: column with bad row index + stats [5]: bad row index + stats [6]: n_row, # of rows of matrx + + -10 unused + + -999 (unused; see symamd.c) + + Future versions may return more statistics in the stats array. + + Example: + + See http://www.cise.ufl.edu/~davis/colamd/example.c + for a complete example. + + To order the columns of a 5-by-4 matrix with 11 nonzero entries in + the following nonzero pattern + + x 0 x 0 + x 0 x x + 0 x x 0 + 0 0 x x + x x 0 0 + + with default knobs and no output statistics, do the following: + + #include "colamd.h" + #define ALEN UMF_COLAMD_RECOMMENDED (11, 5, 4) + Int A [ALEN] = {1, 2, 5, 3, 5, 1, 2, 3, 4, 2, 4} ; + Int p [ ] = {0, 3, 5, 9, 11} ; + Int stats [COLAMD_STATS] ; + UMF_colamd (5, 4, ALEN, A, p, (double *) NULL, stats) ; + + The permutation is returned in the array p, and A is destroyed. + + + ---------------------------------------------------------------------------- + symamd: does not appear in this version for UMFPACK + ---------------------------------------------------------------------------- + + ---------------------------------------------------------------------------- + colamd_report: does not appear in this version for UMFPACK + ---------------------------------------------------------------------------- + + ---------------------------------------------------------------------------- + symamd_report: does not appear in this version for UMFPACK + ---------------------------------------------------------------------------- + +*/ + +/* ========================================================================== */ +/* === Scaffolding code definitions ======================================== */ +/* ========================================================================== */ + +/* ------------------ */ +/* UMFPACK debugging control is in umf_config.h */ +/* ------------------ */ + +/* + Our "scaffolding code" philosophy: In our opinion, well-written library + code should keep its "debugging" code, and just normally have it turned off + by the compiler so as not to interfere with performance. This serves + several purposes: + + (1) assertions act as comments to the reader, telling you what the code + expects at that point. All assertions will always be true (unless + there really is a bug, of course). + + (2) leaving in the scaffolding code assists anyone who would like to modify + the code, or understand the algorithm (by reading the debugging output, + one can get a glimpse into what the code is doing). + + (3) (gasp!) for actually finding bugs. This code has been heavily tested + and "should" be fully functional and bug-free ... but you never know... + + To enable debugging, comment out the "#define NDEBUG" above. For a MATLAB + mexFunction, you will also need to modify mexopts.sh to remove the -DNDEBUG + definition. The code will become outrageously slow when debugging is + enabled. To control the level of debugging output, set an environment + variable D to 0 (little), 1 (some), 2, 3, or 4 (lots). When debugging, + you should see the following message on the standard output: + + colamd: debug version, D = 1 (THIS WILL BE SLOW!) + + or a similar message for symamd. If you don't, then debugging has not + been enabled. + +*/ + +/* ========================================================================== */ +/* === Include files ======================================================== */ +/* ========================================================================== */ + +/* ------------------ */ +/* modified for UMFPACK: */ +#include "umf_internal.h" +#include "umf_colamd.h" +#include "umf_order_front_tree.h" +#include "umf_apply_order.h" +/* ------------------ */ + +/* ========================================================================== */ +/* === Definitions ========================================================== */ +/* ========================================================================== */ + +/* ------------------ */ +/* UMFPACK: duplicate definitions moved to umf_internal.h */ +/* ------------------ */ + +/* Row and column status */ +#define ALIVE (0) +#define DEAD (-1) + +/* Column status */ +#define DEAD_PRINCIPAL (-1) +#define DEAD_NON_PRINCIPAL (-2) + +/* Macros for row and column status update and checking. */ +#define ROW_IS_DEAD(r) ROW_IS_MARKED_DEAD (Row[r].shared2.mark) +#define ROW_IS_MARKED_DEAD(row_mark) (row_mark < ALIVE) +#define ROW_IS_ALIVE(r) (Row [r].shared2.mark >= ALIVE) +#define COL_IS_DEAD(c) (Col [c].start < ALIVE) +#define COL_IS_ALIVE(c) (Col [c].start >= ALIVE) +#define COL_IS_DEAD_PRINCIPAL(c) (Col [c].start == DEAD_PRINCIPAL) +#define KILL_ROW(r) { Row [r].shared2.mark = DEAD ; } +#define KILL_PRINCIPAL_COL(c) { Col [c].start = DEAD_PRINCIPAL ; } +#define KILL_NON_PRINCIPAL_COL(c) { Col [c].start = DEAD_NON_PRINCIPAL ; } + +/* ------------------ */ +/* UMFPACK: Colamd reporting mechanism moved to umf_internal.h */ +/* ------------------ */ + +/* ========================================================================== */ +/* === Prototypes of PRIVATE routines ======================================= */ +/* ========================================================================== */ + +PRIVATE Int init_rows_cols +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int p [], + Int stats [COLAMD_STATS] +) ; + +PRIVATE void init_scoring +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int head [], + double knobs [COLAMD_KNOBS], + Int *p_n_row2, + Int *p_n_col2, + Int *p_max_deg + /* ------------------ */ + /* added for UMFPACK */ + , Int *p_ndense_row /* number of dense rows */ + , Int *p_nempty_row /* number of original empty rows */ + , Int *p_nnewlyempty_row /* number of newly empty rows */ + , Int *p_ndense_col /* number of dense cols (excl "empty" cols) */ + , Int *p_nempty_col /* number of original empty cols */ + , Int *p_nnewlyempty_col /* number of newly empty cols */ +) ; + +PRIVATE Int find_ordering +( + Int n_row, + Int n_col, + Int Alen, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int head [], + Int n_col2, + Int max_deg, + Int pfree + /* ------------------ */ + /* added for UMFPACK: */ + , Int Front_npivcol [ ] + , Int Front_nrows [ ] + , Int Front_ncols [ ] + , Int Front_parent [ ] + , Int Front_cols [ ] + , Int *p_nfr + /* ------------------ */ +) ; + +/* ------------------ */ +/* order_children deleted for UMFPACK: */ +/* ------------------ */ + +PRIVATE void detect_super_cols +( + +#ifndef NDEBUG + Int n_col, + Colamd_Row Row [], +#endif /* NDEBUG */ + + Colamd_Col Col [], + Int A [], + Int head [], + Int row_start, + Int row_length +) ; + +PRIVATE Int garbage_collection +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int *pfree +) ; + +PRIVATE Int clear_mark +( + Int n_row, + Colamd_Row Row [] +) ; + +/* ------------------ */ +/* print_report deleted for UMFPACK */ +/* ------------------ */ + +/* ========================================================================== */ +/* === Debugging prototypes and definitions ================================= */ +/* ========================================================================== */ + +#ifndef NDEBUG + +/* ------------------ */ +/* debugging macros moved for UMFPACK */ +/* ------------------ */ + +PRIVATE void debug_deg_lists +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int head [], + Int min_score, + Int should, + Int max_deg +) ; + +PRIVATE void debug_mark +( + Int n_row, + Colamd_Row Row [], + Int tag_mark, + Int max_mark +) ; + +PRIVATE void debug_matrix +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [] +) ; + +PRIVATE void debug_structures +( + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int n_col2 +) ; + +/* ------------------ */ +/* dump_super added for UMFPACK: */ +PRIVATE void dump_super +( + Int super_c, + Colamd_Col Col [], + Int n_col +) ; +/* ------------------ */ + +#endif /* NDEBUG */ + +/* ========================================================================== */ + + + +/* ========================================================================== */ +/* === USER-CALLABLE ROUTINES: ============================================== */ +/* ========================================================================== */ + + +/* ========================================================================== */ +/* === colamd_set_defaults ================================================== */ +/* ========================================================================== */ + +/* + The colamd_set_defaults routine sets the default values of the user- + controllable parameters for colamd: + + knobs [0] rows with knobs[0]*n_col entries or more are removed + prior to ordering in colamd. Rows and columns with + knobs[0]*n_col entries or more are removed prior to + ordering in symamd and placed last in the output + ordering. + + knobs [1] columns with knobs[1]*n_row entries or more are removed + prior to ordering in colamd, and placed last in the + column permutation. Symamd ignores this knob. + + knobs [2..19] unused, but future versions might use this +*/ + +GLOBAL void UMF_colamd_set_defaults +( + /* === Parameters ======================================================= */ + + double knobs [COLAMD_KNOBS] /* knob array */ +) +{ + /* === Local variables ================================================== */ + + Int i ; + + if (!knobs) + { + return ; /* UMFPACK always passes knobs array */ + } + for (i = 0 ; i < COLAMD_KNOBS ; i++) + { + knobs [i] = 0 ; + } + knobs [COLAMD_DENSE_ROW] = 0.2 ; /* default changed for UMFPACK */ + knobs [COLAMD_DENSE_COL] = 0.2 ; /* default changed for UMFPACK */ +} + + +/* ========================================================================== */ +/* === symamd removed for UMFPACK =========================================== */ +/* ========================================================================== */ + + + +/* ========================================================================== */ +/* === colamd =============================================================== */ +/* ========================================================================== */ + +/* + The colamd routine computes a column ordering Q of a sparse matrix + A such that the LU factorization P(AQ) = LU remains sparse, where P is + selected via partial pivoting. The routine can also be viewed as + providing a permutation Q such that the Cholesky factorization + (AQ)'(AQ) = LL' remains sparse. +*/ + +GLOBAL Int UMF_colamd /* returns TRUE if successful, FALSE otherwise*/ +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows in A */ + Int n_col, /* number of columns in A */ + Int Alen, /* length of A */ + Int A [], /* row indices of A */ + Int p [], /* pointers to columns in A */ + double knobs [COLAMD_KNOBS],/* parameters (uses defaults if NULL) */ + Int stats [COLAMD_STATS] /* output statistics and error codes */ + + /* ------------------ */ + /* added for UMFPACK: each Front_ array is of size n_col */ + , Int Front_npivcol [ ] /* # pivot cols in each front */ + , Int Front_nrows [ ] /* # of rows in each front (incl. pivot rows) */ + , Int Front_ncols [ ] /* # of cols in each front (incl. pivot cols) */ + , Int Front_parent [ ] /* parent of each front */ + , Int Front_cols [ ] /* link list of pivot columns for each front */ + , Int *p_nfr /* total number of frontal matrices */ + /* ------------------ */ +) +{ + /* === Local variables ================================================== */ + + Int i ; /* loop index */ + Int nnz ; /* nonzeros in A */ + Int Row_size ; /* size of Row [], in integers */ + Int Col_size ; /* size of Col [], in integers */ + Int need ; /* minimum required length of A */ + Colamd_Row *Row ; /* pointer into A of Row [0..n_row] array */ + Colamd_Col *Col ; /* pointer into A of Col [0..n_col] array */ + Int n_col2 ; /* number of non-dense, non-empty columns */ + Int n_row2 ; /* number of non-dense, non-empty rows */ + Int ngarbage ; /* number of garbage collections performed */ + Int max_deg ; /* maximum row degree */ + double default_knobs [COLAMD_KNOBS] ; /* default knobs array */ + + /* ------------------ */ + /* debugging initializations moved for UMFPACK */ + /* ------------------ */ + + /* ------------------ */ + /* added for UMFPACK: */ + Int f, j, nchild, ndense_row, nempty_row, parent, + nrows, ncols, frsize, ndense_col, nempty_col, k, col, nfr, + *Front_child, *Front_sibling, *Front_maxfr, *Front_order, + fprev, maxfrsize, bigf, fnext, bigfprev, *Stack ; + Int nnewlyempty_col, nnewlyempty_row ; + /* ------------------ */ + + /* === Check the input arguments ======================================== */ + + if (!stats) + { + DEBUG0 (("colamd: stats not present\n")) ; + return (FALSE) ; /* UMFPACK: always passes stats [ ] */ + } + for (i = 0 ; i < COLAMD_STATS ; i++) + { + stats [i] = 0 ; + } + stats [COLAMD_STATUS] = COLAMD_OK ; + stats [COLAMD_INFO1] = -1 ; + stats [COLAMD_INFO2] = -1 ; + + if (!A) /* A is not present */ + { + /* UMFPACK: always passes A [ ] */ + DEBUG0 (("colamd: A not present\n")) ; + stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ; + return (FALSE) ; + } + + if (!p) /* p is not present */ + { + /* UMFPACK: always passes p [ ] */ + DEBUG0 (("colamd: p not present\n")) ; + stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ; + return (FALSE) ; + } + + if (n_row < 0) /* n_row must be >= 0 */ + { + /* UMFPACK: does not call UMF_colamd if n <= 0 */ + DEBUG0 (("colamd: nrow negative "ID"\n", n_row)) ; + stats [COLAMD_STATUS] = COLAMD_ERROR_nrow_negative ; + stats [COLAMD_INFO1] = n_row ; + return (FALSE) ; + } + + if (n_col < 0) /* n_col must be >= 0 */ + { + /* UMFPACK: does not call UMF_colamd if n <= 0 */ + DEBUG0 (("colamd: ncol negative "ID"\n", n_col)) ; + stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ; + stats [COLAMD_INFO1] = n_col ; + return (FALSE) ; + } + + nnz = p [n_col] ; + if (nnz < 0) /* nnz must be >= 0 */ + { + /* UMFPACK: does not call UMF_colamd if nnz < 0 */ + DEBUG0 (("colamd: number of entries negative "ID"\n", nnz)) ; + stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ; + stats [COLAMD_INFO1] = nnz ; + return (FALSE) ; + } + + if (p [0] != 0) /* p [0] must be exactly zero */ + { + DEBUG0 (("colamd: p[0] not zero "ID"\n", p [0])) ; + stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero ; + stats [COLAMD_INFO1] = p [0] ; + return (FALSE) ; + } + + /* === If no knobs, set default knobs =================================== */ + + if (!knobs) + { + /* UMFPACK: always passes the knobs */ + UMF_colamd_set_defaults (default_knobs) ; + knobs = default_knobs ; + } + + /* === Allocate the Row and Col arrays from array A ===================== */ + + Col_size = UMF_COLAMD_C (n_col) ; + Row_size = UMF_COLAMD_R (n_row) ; + need = 2*nnz + n_col + Col_size + Row_size ; + + if (need > Alen) + { + /* UMFPACK: always passes enough space */ + /* not enough space in array A to perform the ordering */ + DEBUG0 (("colamd: Need Alen >= "ID", given only Alen = "ID"\n", + need, Alen)) ; + stats [COLAMD_STATUS] = COLAMD_ERROR_A_too_small ; + stats [COLAMD_INFO1] = need ; + stats [COLAMD_INFO2] = Alen ; + return (FALSE) ; + } + + Alen -= Col_size + Row_size ; + Col = (Colamd_Col *) &A [Alen] ; + Row = (Colamd_Row *) &A [Alen + Col_size] ; + + /* === Construct the row and column data structures ===================== */ + + if (!init_rows_cols (n_row, n_col, Row, Col, A, p, stats)) + { + /* input matrix is invalid */ + DEBUG0 (("colamd: Matrix invalid\n")) ; + return (FALSE) ; + } + + /* === UMFPACK: Initialize front info =================================== */ + + for (col = 0 ; col < n_col ; col++) + { + Front_npivcol [col] = 0 ; + Front_nrows [col] = 0 ; + Front_ncols [col] = 0 ; + Front_parent [col] = EMPTY ; + Front_cols [col] = EMPTY ; + } + + /* === Initialize scores, kill dense rows/columns ======================= */ + + init_scoring (n_row, n_col, Row, Col, A, p, knobs, + &n_row2, &n_col2, &max_deg + /* ------------------ */ + /* added for UMFPACK: */ + , &ndense_row, &nempty_row, &nnewlyempty_row + , &ndense_col, &nempty_col, &nnewlyempty_col + /* ------------------ */ + ) ; + ASSERT (n_row2 == n_row - nempty_row - nnewlyempty_row - ndense_row) ; + ASSERT (n_col2 == n_col - nempty_col - nnewlyempty_col - ndense_col) ; + + /* === Order the supercolumns =========================================== */ + + ngarbage = find_ordering (n_row, n_col, Alen, Row, Col, A, p, + n_col2, max_deg, 2*nnz + /* ------------------ */ + /* added for UMFPACK: */ + , Front_npivcol, Front_nrows, Front_ncols, Front_parent, Front_cols + , &nfr + /* ------------------ */ + ) ; + + /* ------------------ */ + /* changed for UMFPACK: */ + + /* use A [0..4*nfr-1] as workspace [ [ [ */ + Front_child = A ; + Front_sibling = Front_child + nfr ; + Front_maxfr = Front_sibling + nfr ; + Front_order = Front_maxfr + nfr ; + + for (j = 0 ; j < nfr ; j++) + { + Front_child [j] = EMPTY ; + Front_sibling [j] = EMPTY ; + Front_maxfr [j] = EMPTY ; + } + + /* === find maxfr for each front ======================================== */ + + DEBUG1 (("\n\n========================================FRONTS:\n")) ; + for (j = 0 ; j < nfr ; j++) + { + parent = Front_parent [j] ; + nrows = Front_nrows [j] ; + ncols = Front_ncols [j] ; + frsize = nrows * ncols ; + DEBUG1 ((""ID" : npiv "ID" nrows "ID" ncols "ID" parent "ID" ", + j, Front_npivcol [j], nrows, ncols, parent)) ; + Front_maxfr [j] = MAX (Front_maxfr [j], frsize) ; + DEBUG1 (("Front_maxfr [j = "ID"] = "ID"\n", j, Front_maxfr[j])); + if (parent != EMPTY) + { + /* find the maximum frontsize of self and children */ + Front_maxfr [parent] = MAX (Front_maxfr [parent], Front_maxfr [j]) ; + DEBUG1 (("Front_maxfr [parent = "ID"] = "ID"\n", + parent, Front_maxfr [parent])) ; + } + } + + /* === put the fronts in the child lists ================================ */ + + /* try to put them in order of smaller to larger */ + for (j = nfr-1 ; j >= 0 ; j--) + { + parent = Front_parent [j] ; + if (parent != EMPTY) + { + /* place the front in the link list of the children its parent */ + /* bigger fronts will tend to be at the end of the list */ + Front_sibling [j] = Front_child [parent] ; + Front_child [parent] = j ; + } + } + + /* === Order the front tree via depth-first-search ====================== */ + + for (i = 0 ; i < nfr ; i++) + { + if (Front_child [i] != EMPTY) + { + DEBUG1 (("Before partial sort, front "ID"\n", i)) ; + nchild = 0 ; + for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f]) + { + DEBUG1 ((" "ID" "ID"\n", f, Front_maxfr [f])) ; + nchild++ ; + } + + /* find the biggest front in the child list */ + fprev = EMPTY ; + maxfrsize = EMPTY ; + bigfprev = EMPTY ; + bigf = EMPTY ; + for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f]) + { + frsize = Front_maxfr [f] ; + if (frsize >= maxfrsize) + { + /* this is the biggest seen so far */ + maxfrsize = frsize ; + bigfprev = fprev ; + bigf = f ; + } + fprev = f ; + } + ASSERT (bigf != EMPTY) ; + + fnext = Front_sibling [bigf] ; + + DEBUG1 (("bigf "ID" maxfrsize "ID" bigfprev "ID" fnext "ID" fprev" + ID"\n", bigf, maxfrsize, bigfprev, fnext, fprev)) ; + + if (fnext != EMPTY) + { + /* if fnext is EMPTY, then bigf is already at the end of list */ + + if (bigfprev == EMPTY) + { + /* delete bigf from the front of the list */ + Front_child [i] = fnext ; + } + else + { + /* delete bigf from the middle of the list */ + Front_sibling [bigfprev] = fnext ; + } + + /* put bigf at the end of the list */ + Front_sibling [bigf] = EMPTY ; + ASSERT (Front_child [i] != EMPTY) ; + ASSERT (fprev != bigf) ; + ASSERT (fprev != EMPTY) ; + Front_sibling [fprev] = bigf ; + } + + DEBUG1 (("After partial sort, front "ID"\n", i)) ; + for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f]) + { + DEBUG1 ((" "ID" "ID"\n", f, Front_maxfr [f])) ; + ASSERT (Front_npivcol [f] > 0) ; + nchild-- ; + } + ASSERT (nchild == 0) ; + } + } + + /* Front_maxfr no longer needed ] */ + +#ifndef NDEBUG + UMF_nbug = nfr ; /* frontal id's are in the range 0..nfr-1 */ + UMF_fbug = nfr ; /* total number of frontal matrices */ +#endif + + /* use Front_maxfr as workspace for stack */ + Stack = Front_maxfr ; + k = 0 ; + for (i = 0 ; i < nfr ; i++) + { + if (Front_parent [i] == EMPTY) + { + DEBUG1 (("Root of front tree "ID"\n", i)) ; + k = UMF_order_front_tree (i, k, Front_child, Front_sibling, + Front_order, Stack) ; + } + } + + /* Front_child, Front_sibling no longer needed ] */ + + /* use A [0..nfr-1] as workspace */ + UMF_apply_order (Front_npivcol, Front_order, A, nfr, nfr) ; + UMF_apply_order (Front_nrows, Front_order, A, nfr, nfr) ; + UMF_apply_order (Front_ncols, Front_order, A, nfr, nfr) ; + UMF_apply_order (Front_parent, Front_order, A, nfr, nfr) ; + UMF_apply_order (Front_cols, Front_order, A, nfr, nfr) ; + + /* fix the parent to refer to the new numbering */ + for (i = 0 ; i < nfr ; i++) + { + parent = Front_parent [i] ; + if (parent != EMPTY) + { + Front_parent [i] = Front_order [parent] ; + } + } + + /* Front_order longer needed ] */ + + /* === Order the columns in the fronts ================================== */ + + /* use A [0..n_col-1] as inverse permutation */ + for (i = 0 ; i < n_col ; i++) + { + A [i] = -1 ; + } + k = 0 ; + for (i = 0 ; i < nfr ; i++) + { + ASSERT (Front_npivcol [i] > 0) ; + for (col = Front_cols [i] ; col != EMPTY ; col = Col [col].nextcol) + { + ASSERT (col >= 0 && col < n_col) ; + p [k++] = col ; + ASSERT (A [col] == -1) ; + A [col] = k ; + } + } + + /* === Order the "dense" and null columns =============================== */ + + ASSERT (k == n_col2) ; + if (n_col2 < n_col) + { + for (col = 0 ; col < n_col ; col++) + { + if (A [col] == -1) + { + DEBUG1 (("colamd dense or null col "ID" k: "ID"\n", col, k)) ; + k = Col [col].shared2.order ; + ASSERT (k >= n_col2 && k < n_col) ; + p [k] = col ; + A [col] = k ; + } + } + } + + /* ------------------ */ + + /* === Return statistics in stats ======================================= */ + + /* ------------------ */ + /* modified for UMFPACK */ + stats [COLAMD_DENSE_ROW] = ndense_row ; + stats [COLAMD_EMPTY_ROW] = nempty_row ; + stats [COLAMD_NEWLY_EMPTY_ROW] = nnewlyempty_row ; + stats [COLAMD_DENSE_COL] = ndense_col ; + stats [COLAMD_EMPTY_COL] = nempty_col ; + stats [COLAMD_NEWLY_EMPTY_COL] = nnewlyempty_col ; + ASSERT (ndense_col + nempty_col + nnewlyempty_col == n_col - n_col2) ; + /* ------------------ */ + stats [COLAMD_DEFRAG_COUNT] = ngarbage ; + *p_nfr = nfr ; + DEBUG1 (("colamd: done.\n")) ; + return (TRUE) ; +} + + + + +/* ========================================================================== */ +/* === colamd_report removed for UMFPACK ==================================== */ +/* ========================================================================== */ + +/* ========================================================================== */ +/* === symamd_report removed for UMFPACK ==================================== */ +/* ========================================================================== */ + + + +/* ========================================================================== */ +/* === NON-USER-CALLABLE ROUTINES: ========================================== */ +/* ========================================================================== */ + +/* There are no user-callable routines beyond this point in the file */ + + +/* ========================================================================== */ +/* === init_rows_cols ======================================================= */ +/* ========================================================================== */ + +/* + Takes the column form of the matrix in A and creates the row form of the + matrix. Also, row and column attributes are stored in the Col and Row + structs. If the columns are un-sorted or contain duplicate row indices, + this routine will also sort and remove duplicate row indices from the + column form of the matrix. Returns FALSE if the matrix is invalid, + TRUE otherwise. Not user-callable. +*/ + +PRIVATE Int init_rows_cols /* returns TRUE if OK, or FALSE otherwise */ +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows of A */ + Int n_col, /* number of columns of A */ + Colamd_Row Row [], /* of size n_row+1 */ + Colamd_Col Col [], /* of size n_col+1 */ + Int A [], /* row indices of A, of size Alen */ + Int p [], /* pointers to columns in A, of size n_col+1 */ + Int stats [COLAMD_STATS] /* colamd statistics */ +) +{ + /* === Local variables ================================================== */ + + Int col ; /* a column index */ + Int row ; /* a row index */ + Int *cp ; /* a column pointer */ + Int *cp_end ; /* a pointer to the end of a column */ + /* rp and rp_end not used for UMFPACK */ + Int last_row ; /* previous row */ + + /* === Initialize columns, and check column pointers ==================== */ + + for (col = 0 ; col < n_col ; col++) + { + Col [col].start = p [col] ; + Col [col].length = p [col+1] - p [col] ; + + if (Col [col].length < 0) + { + /* column pointers must be non-decreasing */ + stats [COLAMD_STATUS] = COLAMD_ERROR_col_length_negative ; + stats [COLAMD_INFO1] = col ; + stats [COLAMD_INFO2] = Col [col].length ; + DEBUG0 (("colamd: col "ID" length "ID" <= 0\n", + col, Col [col].length)); + return (FALSE) ; + } + + Col [col].shared1.thickness = 1 ; + Col [col].shared2.score = 0 ; + Col [col].shared3.prev = EMPTY ; + Col [col].shared4.degree_next = EMPTY ; + + /* ------------------ */ + /* added for UMFPACK: */ + Col [col].nextcol = EMPTY ; + Col [col].lastcol = col ; + /* ------------------ */ + } + + /* p [0..n_col] no longer needed, used as "head" in subsequent routines */ + + /* === Scan columns, compute row degrees, and check row indices ========= */ + + /* ------------------ */ + /* stats [COLAMD_INFO3] = 0 ; */ + /* number of duplicate or unsorted row indices - not computed in UMFPACK */ + /* ------------------ */ + + for (row = 0 ; row < n_row ; row++) + { + Row [row].length = 0 ; + /* ------------------ */ + /* removed for UMFPACK */ + /* Row [row].shared2.mark = -1 ; */ + /* ------------------ */ + /* ------------------ */ + /* added for UMFPACK: */ + Row [row].thickness = 1 ; + Row [row].front = EMPTY ; + /* ------------------ */ + } + + for (col = 0 ; col < n_col ; col++) + { + last_row = -1 ; + + cp = &A [p [col]] ; + cp_end = &A [p [col+1]] ; + + while (cp < cp_end) + { + row = *cp++ ; + + /* make sure row indices within range */ + if (row < 0 || row >= n_row) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_row_index_out_of_bounds ; + stats [COLAMD_INFO1] = col ; + stats [COLAMD_INFO2] = row ; + /* ------------------ */ + /* not needed in UMFPACK: */ + /* stats [COLAMD_INFO3] = n_row ; */ + /* ------------------ */ + DEBUG0 (("colamd: row "ID" col "ID" out of bounds\n", row,col)); + return (FALSE) ; + } + + /* ------------------ */ + /* changed for UMFPACK */ + if (row <= last_row) + { + /* row index are unsorted or repeated (or both), thus col */ + /* is jumbled. This is an error condition for UMFPACK */ + stats [COLAMD_STATUS] = COLAMD_ERROR_jumbled_matrix ; + stats [COLAMD_INFO1] = col ; + stats [COLAMD_INFO2] = row ; + DEBUG1 (("colamd: row "ID" col "ID" unsorted/duplicate\n", + row, col)) ; + return (FALSE) ; + } + /* ------------------ */ + + /* ------------------ */ + /* changed for UMFPACK - jumbled columns not tolerated */ + Row [row].length++ ; + /* ------------------ */ + + last_row = row ; + } + } + + /* === Compute row pointers ============================================= */ + + /* row form of the matrix starts directly after the column */ + /* form of matrix in A */ + Row [0].start = p [n_col] ; + Row [0].shared1.p = Row [0].start ; + /* ------------------ */ + /* removed for UMFPACK */ + /* Row [0].shared2.mark = -1 ; */ + /* ------------------ */ + for (row = 1 ; row < n_row ; row++) + { + Row [row].start = Row [row-1].start + Row [row-1].length ; + Row [row].shared1.p = Row [row].start ; + /* ------------------ */ + /* removed for UMFPACK */ + /* Row [row].shared2.mark = -1 ; */ + /* ------------------ */ + } + + /* === Create row form ================================================== */ + + /* ------------------ */ + /* jumbled matrix case removed for UMFPACK */ + /* ------------------ */ + + ASSERT (stats [COLAMD_STATUS] == COLAMD_OK) ; + + for (col = 0 ; col < n_col ; col++) + { + cp = &A [p [col]] ; + cp_end = &A [p [col+1]] ; + while (cp < cp_end) + { + A [(Row [*cp++].shared1.p)++] = col ; + } + } + + /* === Clear the row marks and set row degrees ========================== */ + + for (row = 0 ; row < n_row ; row++) + { + Row [row].shared2.mark = 0 ; + Row [row].shared1.degree = Row [row].length ; + } + + /* ------------------ */ + /* recreate columns for jumbled matrix case removed for UMFPACK */ + /* ------------------ */ + + return (TRUE) ; +} + + +/* ========================================================================== */ +/* === init_scoring ========================================================= */ +/* ========================================================================== */ + +/* + Kills dense or empty columns and rows, calculates an initial score for + each column, and places all columns in the degree lists. Not user-callable. +*/ + +PRIVATE void init_scoring +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows of A */ + Int n_col, /* number of columns of A */ + Colamd_Row Row [], /* of size n_row+1 */ + Colamd_Col Col [], /* of size n_col+1 */ + Int A [], /* column form and row form of A */ + Int head [], /* of size n_col+1 */ + double knobs [COLAMD_KNOBS],/* parameters */ + Int *p_n_row2, /* number of non-dense, non-empty rows */ + Int *p_n_col2, /* number of non-dense, non-empty columns */ + Int *p_max_deg /* maximum row degree */ + /* ------------------ */ + /* added for UMFPACK */ + , Int *p_ndense_row /* number of dense rows */ + , Int *p_nempty_row /* number of original empty rows */ + , Int *p_nnewlyempty_row /* number of newly empty rows */ + , Int *p_ndense_col /* number of dense cols (excl "empty" cols) */ + , Int *p_nempty_col /* number of original empty cols */ + , Int *p_nnewlyempty_col /* number of newly empty cols */ + /* ------------------ */ +) +{ + /* === Local variables ================================================== */ + + Int c ; /* a column index */ + Int r, row ; /* a row index */ + Int *cp ; /* a column pointer */ + Int deg ; /* degree of a row or column */ + Int *cp_end ; /* a pointer to the end of a column */ + Int *new_cp ; /* new column pointer */ + Int col_length ; /* length of pruned column */ + Int score ; /* current column score */ + Int n_col2 ; /* number of non-dense, non-empty columns */ + Int n_row2 ; /* number of non-dense, non-empty rows */ + Int dense_row_count ; /* remove rows with more entries than this */ + Int dense_col_count ; /* remove cols with more entries than this */ + Int min_score ; /* smallest column score */ + Int max_deg ; /* maximum row degree */ + Int next_col ; /* Used to add to degree list.*/ + + /* ------------------ */ + /* added for UMFPACK */ + Int ndense_row ; /* number of dense rows */ + Int nempty_row ; /* number of empty rows */ + Int nnewlyempty_row ; /* number of newly empty rows */ + Int ndense_col ; /* number of dense cols (excl "empty" cols) */ + Int nempty_col ; /* number of original empty cols */ + Int nnewlyempty_col ; /* number of newly empty cols */ + Int ne ; + /* ------------------ */ + +#ifndef NDEBUG + Int debug_count ; /* debug only. */ +#endif /* NDEBUG */ + + /* === Extract knobs ==================================================== */ + + /* --------------------- */ + /* old dense row/column knobs: + dense_row_count = MAX (0, MIN (knobs [COLAMD_DENSE_ROW] * n_col, n_col)) ; + dense_col_count = MAX (0, MIN (knobs [COLAMD_DENSE_COL] * n_row, n_row)) ; + */ + /* new, for UMFPACK: */ + /* Note: if knobs contains a NaN, this is undefined: */ + dense_row_count = + UMFPACK_DENSE_DEGREE_THRESHOLD (knobs [COLAMD_DENSE_ROW], n_col) ; + dense_col_count = + UMFPACK_DENSE_DEGREE_THRESHOLD (knobs [COLAMD_DENSE_COL], n_row) ; + /* Make sure dense_*_count is between 0 and n: */ + dense_row_count = MAX (0, MIN (dense_row_count, n_col)) ; + dense_col_count = MAX (0, MIN (dense_col_count, n_row)) ; + /* --------------------- */ + + DEBUG1 (("colamd: densecount: "ID" "ID"\n", + dense_row_count, dense_col_count)) ; + max_deg = 0 ; + n_col2 = n_col ; + n_row2 = n_row ; + + /* --------------------- */ + /* added for UMFPACK */ + ndense_col = 0 ; + nempty_col = 0 ; + nnewlyempty_col = 0 ; + ndense_row = 0 ; + nempty_row = 0 ; + nnewlyempty_row = 0 ; + /* --------------------- */ + + /* === Kill empty columns =============================================== */ + + /* Put the empty columns at the end in their natural order, so that LU */ + /* factorization can proceed as far as possible. */ + for (c = n_col-1 ; c >= 0 ; c--) + { + deg = Col [c].length ; + if (deg == 0) + { + /* this is a empty column, kill and order it last */ + Col [c].shared2.order = --n_col2 ; + KILL_PRINCIPAL_COL (c) ; + /* --------------------- */ + /* added for UMFPACK */ + nempty_col++ ; + /* --------------------- */ + } + } + DEBUG1 (("colamd: null columns killed: "ID"\n", n_col - n_col2)) ; + + /* === Count null rows ================================================== */ + + for (r = 0 ; r < n_row ; r++) + { + deg = Row [r].shared1.degree ; + if (deg == 0) + { + /* this is an original empty row */ + nempty_row++ ; + } + } + + /* === Kill dense columns =============================================== */ + + /* Put the dense columns at the end, in their natural order */ + for (c = n_col-1 ; c >= 0 ; c--) + { + /* skip any dead columns */ + if (COL_IS_DEAD (c)) + { + continue ; + } + deg = Col [c].length ; + if (deg > dense_col_count) + { + /* this is a dense column, kill and order it last */ + Col [c].shared2.order = --n_col2 ; + /* --------------------- */ + /* added for UMFPACK */ + ndense_col++ ; + /* --------------------- */ + /* decrement the row degrees */ + cp = &A [Col [c].start] ; + cp_end = cp + Col [c].length ; + while (cp < cp_end) + { + Row [*cp++].shared1.degree-- ; + } + KILL_PRINCIPAL_COL (c) ; + } + } + DEBUG1 (("colamd: Dense and null columns killed: "ID"\n", n_col - n_col2)) ; + + /* === Kill dense and empty rows ======================================== */ + + ne = 0 ; + for (r = 0 ; r < n_row ; r++) + { + deg = Row [r].shared1.degree ; + ASSERT (deg >= 0 && deg <= n_col) ; + /* --------------------- */ + /* added for UMFPACK */ + if (deg > dense_row_count) + { + /* There is at least one dense row. Continue ordering, but */ + /* symbolic factorization will be redone after UMF_colamd is done.*/ + ndense_row++ ; + } + if (deg == 0) + { + /* this is a newly empty row, or original empty row */ + ne++ ; + } + /* --------------------- */ + if (deg > dense_row_count || deg == 0) + { + /* kill a dense or empty row */ + KILL_ROW (r) ; + /* --------------------- */ + /* added for UMFPACK */ + Row [r].thickness = 0 ; + /* --------------------- */ + --n_row2 ; + } + else + { + /* keep track of max degree of remaining rows */ + max_deg = MAX (max_deg, deg) ; + } + } + nnewlyempty_row = ne - nempty_row ; + DEBUG1 (("colamd: Dense rows killed: "ID"\n", ndense_row)) ; + DEBUG1 (("colamd: Dense and null rows killed: "ID"\n", n_row - n_row2)) ; + + /* === Compute initial column scores ==================================== */ + + /* At this point the row degrees are accurate. They reflect the number */ + /* of "live" (non-dense) columns in each row. No empty rows exist. */ + /* Some "live" columns may contain only dead rows, however. These are */ + /* pruned in the code below. */ + + /* now find the initial matlab score for each column */ + for (c = n_col-1 ; c >= 0 ; c--) + { + /* skip dead column */ + if (COL_IS_DEAD (c)) + { + continue ; + } + score = 0 ; + cp = &A [Col [c].start] ; + new_cp = cp ; + cp_end = cp + Col [c].length ; + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + /* skip if dead */ + if (ROW_IS_DEAD (row)) + { + continue ; + } + /* compact the column */ + *new_cp++ = row ; + /* add row's external degree */ + score += Row [row].shared1.degree - 1 ; + /* guard against integer overflow */ + score = MIN (score, n_col) ; + } + /* determine pruned column length */ + col_length = (Int) (new_cp - &A [Col [c].start]) ; + if (col_length == 0) + { + /* a newly-made null column (all rows in this col are "dense" */ + /* and have already been killed) */ + DEBUG2 (("Newly null killed: "ID"\n", c)) ; + Col [c].shared2.order = --n_col2 ; + KILL_PRINCIPAL_COL (c) ; + /* --------------------- */ + /* added for UMFPACK */ + nnewlyempty_col++ ; + /* --------------------- */ + } + else + { + /* set column length and set score */ + ASSERT (score >= 0) ; + ASSERT (score <= n_col) ; + Col [c].length = col_length ; + Col [c].shared2.score = score ; + } + } + DEBUG1 (("colamd: Dense, null, and newly-null columns killed: "ID"\n", + n_col-n_col2)) ; + + /* At this point, all empty rows and columns are dead. All live columns */ + /* are "clean" (containing no dead rows) and simplicial (no supercolumns */ + /* yet). Rows may contain dead columns, but all live rows contain at */ + /* least one live column. */ + +#ifndef NDEBUG + debug_structures (n_row, n_col, Row, Col, A, n_col2) ; +#endif /* NDEBUG */ + + /* === Initialize degree lists ========================================== */ + +#ifndef NDEBUG + debug_count = 0 ; +#endif /* NDEBUG */ + + /* clear the hash buckets */ + for (c = 0 ; c <= n_col ; c++) + { + head [c] = EMPTY ; + } + min_score = n_col ; + /* place in reverse order, so low column indices are at the front */ + /* of the lists. This is to encourage natural tie-breaking */ + for (c = n_col-1 ; c >= 0 ; c--) + { + /* only add principal columns to degree lists */ + if (COL_IS_ALIVE (c)) + { + DEBUG4 (("place "ID" score "ID" minscore "ID" ncol "ID"\n", + c, Col [c].shared2.score, min_score, n_col)) ; + + /* === Add columns score to DList =============================== */ + + score = Col [c].shared2.score ; + + ASSERT (min_score >= 0) ; + ASSERT (min_score <= n_col) ; + ASSERT (score >= 0) ; + ASSERT (score <= n_col) ; + ASSERT (head [score] >= EMPTY) ; + + /* now add this column to dList at proper score location */ + next_col = head [score] ; + Col [c].shared3.prev = EMPTY ; + Col [c].shared4.degree_next = next_col ; + + /* if there already was a column with the same score, set its */ + /* previous pointer to this new column */ + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = c ; + } + head [score] = c ; + + /* see if this score is less than current min */ + min_score = MIN (min_score, score) ; + +#ifndef NDEBUG + debug_count++ ; +#endif /* NDEBUG */ + + } + } + +#ifndef NDEBUG + DEBUG1 (("colamd: Live cols "ID" out of "ID", non-princ: "ID"\n", + debug_count, n_col, n_col-debug_count)) ; + ASSERT (debug_count == n_col2) ; + debug_deg_lists (n_row, n_col, Row, Col, head, min_score, n_col2, max_deg) ; +#endif /* NDEBUG */ + + /* === Return number of remaining columns, and max row degree =========== */ + + *p_n_col2 = n_col2 ; + *p_n_row2 = n_row2 ; + *p_max_deg = max_deg ; + + /* --------------------- */ + /* added for UMFPACK */ + *p_ndense_row = ndense_row ; + *p_nempty_row = nempty_row ; /* original empty rows */ + *p_nnewlyempty_row = nnewlyempty_row ; + *p_ndense_col = ndense_col ; + *p_nempty_col = nempty_col ; /* original empty cols */ + *p_nnewlyempty_col = nnewlyempty_col ; + /* --------------------- */ +} + + +/* ========================================================================== */ +/* === find_ordering ======================================================== */ +/* ========================================================================== */ + +/* + Order the principal columns of the supercolumn form of the matrix + (no supercolumns on input). Uses a minimum approximate column minimum + degree ordering method. Not user-callable. +*/ + +PRIVATE Int find_ordering /* return the number of garbage collections */ +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows of A */ + Int n_col, /* number of columns of A */ + Int Alen, /* size of A, 2*nnz + n_col or larger */ + Colamd_Row Row [], /* of size n_row+1 */ + Colamd_Col Col [], /* of size n_col+1 */ + Int A [], /* column form and row form of A */ + Int head [], /* of size n_col+1 */ + Int n_col2, /* Remaining columns to order */ + Int max_deg, /* Maximum row degree */ + Int pfree /* index of first free slot (2*nnz on entry) */ + /* ------------------ */ + /* added for UMFPACK: */ + , Int Front_npivcol [ ] + , Int Front_nrows [ ] + , Int Front_ncols [ ] + , Int Front_parent [ ] + , Int Front_cols [ ] + , Int *p_nfr /* number of fronts */ + /* ------------------ */ +) +{ + /* === Local variables ================================================== */ + + Int k ; /* current pivot ordering step */ + Int pivot_col ; /* current pivot column */ + Int *cp ; /* a column pointer */ + Int *rp ; /* a row pointer */ + Int pivot_row ; /* current pivot row */ + Int *new_cp ; /* modified column pointer */ + Int *new_rp ; /* modified row pointer */ + Int pivot_row_start ; /* pointer to start of pivot row */ + Int pivot_row_degree ; /* number of columns in pivot row */ + Int pivot_row_length ; /* number of supercolumns in pivot row */ + Int pivot_col_score ; /* score of pivot column */ + Int needed_memory ; /* free space needed for pivot row */ + Int *cp_end ; /* pointer to the end of a column */ + Int *rp_end ; /* pointer to the end of a row */ + Int row ; /* a row index */ + Int col ; /* a column index */ + Int max_score ; /* maximum possible score */ + Int cur_score ; /* score of current column */ + unsigned Int hash ; /* hash value for supernode detection */ + Int head_column ; /* head of hash bucket */ + Int first_col ; /* first column in hash bucket */ + Int tag_mark ; /* marker value for mark array */ + Int row_mark ; /* Row [row].shared2.mark */ + Int set_difference ; /* set difference size of row with pivot row */ + Int min_score ; /* smallest column score */ + Int col_thickness ; /* "thickness" (no. of columns in a supercol) */ + Int max_mark ; /* maximum value of tag_mark */ + Int pivot_col_thickness ; /* number of columns represented by pivot col */ + Int prev_col ; /* Used by Dlist operations. */ + Int next_col ; /* Used by Dlist operations. */ + Int ngarbage ; /* number of garbage collections performed */ + +#ifndef NDEBUG + Int debug_d ; /* debug loop counter */ + Int debug_step = 0 ; /* debug loop counter */ +#endif /* NDEBUG */ + + /* ------------------ */ + /* added for UMFPACK: */ + Int pivot_row_thickness ; /* number of rows represented by pivot row */ + Int nfr = 0 ; /* number of fronts */ + Int child ; + /* ------------------ */ + + /* === Initialization and clear mark ==================================== */ + + max_mark = MAX_MARK (n_col) ; /* defined in umfpack.h */ + tag_mark = clear_mark (n_row, Row) ; + min_score = 0 ; + ngarbage = 0 ; + DEBUG1 (("colamd: Ordering, n_col2="ID"\n", n_col2)) ; + + /* === Order the columns ================================================ */ + + for (k = 0 ; k < n_col2 ; /* 'k' is incremented below */) + { + +#ifndef NDEBUG + if (debug_step % 100 == 0) + { + DEBUG2 (("\n... Step k: "ID" out of n_col2: "ID"\n", k, n_col2)) ; + } + else + { + DEBUG3 (("\n-----Step k: "ID" out of n_col2: "ID"\n", k, n_col2)) ; + } + debug_step++ ; + debug_deg_lists (n_row, n_col, Row, Col, head, + min_score, n_col2-k, max_deg) ; + debug_matrix (n_row, n_col, Row, Col, A) ; +#endif /* NDEBUG */ + + /* === Select pivot column, and order it ============================ */ + + /* make sure degree list isn't empty */ + ASSERT (min_score >= 0) ; + ASSERT (min_score <= n_col) ; + ASSERT (head [min_score] >= EMPTY) ; + +#ifndef NDEBUG + for (debug_d = 0 ; debug_d < min_score ; debug_d++) + { + ASSERT (head [debug_d] == EMPTY) ; + } +#endif /* NDEBUG */ + + /* get pivot column from head of minimum degree list */ + while (head [min_score] == EMPTY && min_score < n_col) + { + min_score++ ; + } + pivot_col = head [min_score] ; + ASSERT (pivot_col >= 0 && pivot_col <= n_col) ; + next_col = Col [pivot_col].shared4.degree_next ; + head [min_score] = next_col ; + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = EMPTY ; + } + + ASSERT (COL_IS_ALIVE (pivot_col)) ; + DEBUG3 (("Pivot col: "ID"\n", pivot_col)) ; + + /* remember score for defrag check */ + pivot_col_score = Col [pivot_col].shared2.score ; + + /* the pivot column is the kth column in the pivot order */ + Col [pivot_col].shared2.order = k ; + + /* increment order count by column thickness */ + pivot_col_thickness = Col [pivot_col].shared1.thickness ; + /* ------------------ */ + /* changed for UMFPACK: */ + k += pivot_col_thickness ; + /* ------------------ */ + ASSERT (pivot_col_thickness > 0) ; + + /* === Garbage_collection, if necessary ============================= */ + + needed_memory = MIN (pivot_col_score, n_col - k) ; + if (pfree + needed_memory >= Alen) + { + pfree = garbage_collection (n_row, n_col, Row, Col, A, &A [pfree]) ; + ngarbage++ ; + /* after garbage collection we will have enough */ + ASSERT (pfree + needed_memory < Alen) ; + /* garbage collection has wiped out the Row[].shared2.mark array */ + tag_mark = clear_mark (n_row, Row) ; + +#ifndef NDEBUG + debug_matrix (n_row, n_col, Row, Col, A) ; +#endif /* NDEBUG */ + } + + /* === Compute pivot row pattern ==================================== */ + + /* get starting location for this new merged row */ + pivot_row_start = pfree ; + + /* initialize new row counts to zero */ + pivot_row_degree = 0 ; + + /* ------------------ */ + /* added for UMFPACK: */ + pivot_row_thickness = 0 ; + /* ------------------ */ + + /* [ tag pivot column as having been visited so it isn't included */ + /* in merged pivot row */ + Col [pivot_col].shared1.thickness = -pivot_col_thickness ; + + /* pivot row is the union of all rows in the pivot column pattern */ + cp = &A [Col [pivot_col].start] ; + cp_end = cp + Col [pivot_col].length ; + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + DEBUG4 (("Pivot col pattern "ID" "ID"\n", ROW_IS_ALIVE(row), row)) ; + /* skip if row is dead */ + if (ROW_IS_DEAD (row)) + { + continue ; + } + + /* ------------------ */ + /* added for UMFPACK: */ + /* sum the thicknesses of all the rows */ + ASSERT (Row [row].thickness > 0) ; + pivot_row_thickness += Row [row].thickness ; + /* ------------------ */ + + rp = &A [Row [row].start] ; + rp_end = rp + Row [row].length ; + while (rp < rp_end) + { + /* get a column */ + col = *rp++ ; + /* add the column, if alive and untagged */ + col_thickness = Col [col].shared1.thickness ; + if (col_thickness > 0 && COL_IS_ALIVE (col)) + { + /* tag column in pivot row */ + Col [col].shared1.thickness = -col_thickness ; + ASSERT (pfree < Alen) ; + /* place column in pivot row */ + A [pfree++] = col ; + pivot_row_degree += col_thickness ; + /* ------------------ */ + /* added for UMFPACK: */ + DEBUG4 (("\t\t\tNew live column in pivot row: "ID"\n",col)); + /* ------------------ */ + } +/* ------------------ */ +/* added for UMFPACK */ +#ifndef NDEBUG + if (col_thickness < 0 && COL_IS_ALIVE (col)) + { + DEBUG4 (("\t\t\tOld live column in pivot row: "ID"\n",col)); + } +#endif +/* ------------------ */ + } + } + + /* ------------------ */ + /* added for UMFPACK: */ + /* pivot_row_thickness is the number of rows in frontal matrix */ + /* both pivotal rows and nonpivotal rows */ + /* ------------------ */ + + /* clear tag on pivot column */ + Col [pivot_col].shared1.thickness = pivot_col_thickness ; /* ] */ + max_deg = MAX (max_deg, pivot_row_degree) ; + +#ifndef NDEBUG + DEBUG3 (("check2\n")) ; + debug_mark (n_row, Row, tag_mark, max_mark) ; +#endif /* NDEBUG */ + + /* === Kill all rows used to construct pivot row ==================== */ + + /* also kill pivot row, temporarily */ + cp = &A [Col [pivot_col].start] ; + cp_end = cp + Col [pivot_col].length ; + while (cp < cp_end) + { + /* may be killing an already dead row */ + row = *cp++ ; + + /* ------------------ */ + /* added for UMFPACK: */ + DEBUG2 (("Kill row in pivot col: "ID" alive? "ID", front "ID"\n", + row, ROW_IS_ALIVE (row), Row [row].front)) ; + + if (ROW_IS_ALIVE (row) && Row [row].front != EMPTY) + { + /* Row [row].front is a child of current front */ + child = Row [row].front ; + Front_parent [child] = nfr ; + } + /* ------------------ */ + + KILL_ROW (row) ; + + /* ------------------ */ + /* added for UMFPACK: */ + Row [row].thickness = 0 ; + /* ------------------ */ + } + + /* === Select a row index to use as the new pivot row =============== */ + + pivot_row_length = pfree - pivot_row_start ; + if (pivot_row_length > 0) + { + /* pick the "pivot" row arbitrarily (first row in col) */ + pivot_row = A [Col [pivot_col].start] ; + DEBUG3 (("Pivotal row is "ID"\n", pivot_row)) ; + } + else + { + /* there is no pivot row, since it is of zero length */ + pivot_row = EMPTY ; + ASSERT (pivot_row_length == 0) ; + } + ASSERT (Col [pivot_col].length > 0 || pivot_row_length == 0) ; + + /* === Approximate degree computation =============================== */ + + /* Here begins the computation of the approximate degree. The column */ + /* score is the sum of the pivot row "length", plus the size of the */ + /* set differences of each row in the column minus the pattern of the */ + /* pivot row itself. The column ("thickness") itself is also */ + /* excluded from the column score (we thus use an approximate */ + /* external degree). */ + + /* The time taken by the following code (compute set differences, and */ + /* add them up) is proportional to the size of the data structure */ + /* being scanned - that is, the sum of the sizes of each column in */ + /* the pivot row. Thus, the amortized time to compute a column score */ + /* is proportional to the size of that column (where size, in this */ + /* context, is the column "length", or the number of row indices */ + /* in that column). The number of row indices in a column is */ + /* monotonically non-decreasing, from the length of the original */ + /* column on input to colamd. */ + + /* === Compute set differences ====================================== */ + + DEBUG3 (("** Computing set differences phase. **\n")) ; + + /* pivot row is currently dead - it will be revived later. */ + + DEBUG3 (("Pivot row: \n")) ; + /* for each column in pivot row */ + rp = &A [pivot_row_start] ; + rp_end = rp + pivot_row_length ; + while (rp < rp_end) + { + col = *rp++ ; + ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ; + DEBUG3 ((" Col: "ID"\n", col)) ; + + /* clear tags used to construct pivot row pattern */ + col_thickness = -Col [col].shared1.thickness ; + ASSERT (col_thickness > 0) ; + Col [col].shared1.thickness = col_thickness ; + + /* === Remove column from degree list =========================== */ + + cur_score = Col [col].shared2.score ; + prev_col = Col [col].shared3.prev ; + next_col = Col [col].shared4.degree_next ; + ASSERT (cur_score >= 0) ; + ASSERT (cur_score <= n_col) ; + ASSERT (cur_score >= EMPTY) ; + if (prev_col == EMPTY) + { + head [cur_score] = next_col ; + } + else + { + Col [prev_col].shared4.degree_next = next_col ; + } + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = prev_col ; + } + + /* === Scan the column ========================================== */ + + cp = &A [Col [col].start] ; + cp_end = cp + Col [col].length ; + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + row_mark = Row [row].shared2.mark ; + /* skip if dead */ + if (ROW_IS_MARKED_DEAD (row_mark)) + { + continue ; + } + ASSERT (row != pivot_row) ; + set_difference = row_mark - tag_mark ; + /* check if the row has been seen yet */ + if (set_difference < 0) + { + ASSERT (Row [row].shared1.degree <= max_deg) ; + set_difference = Row [row].shared1.degree ; + } + /* subtract column thickness from this row's set difference */ + set_difference -= col_thickness ; + ASSERT (set_difference >= 0) ; + + /* ------------------ */ + /* aggressive row absorption removed for UMFPACK */ + /* ------------------ */ + + /* save the new mark */ + Row [row].shared2.mark = set_difference + tag_mark ; + + } + } + +#ifndef NDEBUG + debug_deg_lists (n_row, n_col, Row, Col, head, + min_score, n_col2-k-pivot_row_degree, max_deg) ; +#endif /* NDEBUG */ + + /* === Add up set differences for each column ======================= */ + + DEBUG3 (("** Adding set differences phase. **\n")) ; + + /* for each column in pivot row */ + rp = &A [pivot_row_start] ; + rp_end = rp + pivot_row_length ; + while (rp < rp_end) + { + /* get a column */ + col = *rp++ ; + ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ; + hash = 0 ; + cur_score = 0 ; + cp = &A [Col [col].start] ; + /* compact the column */ + new_cp = cp ; + cp_end = cp + Col [col].length ; + + DEBUG4 (("Adding set diffs for Col: "ID".\n", col)) ; + + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + ASSERT(row >= 0 && row < n_row) ; + row_mark = Row [row].shared2.mark ; + /* skip if dead */ + if (ROW_IS_MARKED_DEAD (row_mark)) + { + /* ------------------ */ + /* changed for UMFPACK: */ + DEBUG4 ((" Row "ID", dead\n", row)) ; + /* ------------------ */ + continue ; + } + /* ------------------ */ + /* changed for UMFPACK: */ + /* ASSERT (row_mark > tag_mark) ; */ + DEBUG4 ((" Row "ID", set diff "ID"\n", row, row_mark-tag_mark)); + ASSERT (row_mark >= tag_mark) ; + /* ------------------ */ + /* compact the column */ + *new_cp++ = row ; + /* compute hash function */ + hash += row ; + /* add set difference */ + cur_score += row_mark - tag_mark ; + /* integer overflow... */ + cur_score = MIN (cur_score, n_col) ; + } + + /* recompute the column's length */ + Col [col].length = (Int) (new_cp - &A [Col [col].start]) ; + + /* === Further mass elimination ================================= */ + + if (Col [col].length == 0) + { + DEBUG4 (("further mass elimination. Col: "ID"\n", col)) ; + /* nothing left but the pivot row in this column */ + KILL_PRINCIPAL_COL (col) ; + pivot_row_degree -= Col [col].shared1.thickness ; + ASSERT (pivot_row_degree >= 0) ; + /* order it */ + Col [col].shared2.order = k ; + /* increment order count by column thickness */ + k += Col [col].shared1.thickness ; + + /* ------------------ */ + /* added for UMFPACK: */ + pivot_col_thickness += Col [col].shared1.thickness ; + + /* add to column list of front ... */ +#ifndef NDEBUG + DEBUG1 (("Mass")) ; + dump_super (col, Col, n_col) ; +#endif + Col [Col [col].lastcol].nextcol = Front_cols [nfr] ; + Front_cols [nfr] = col ; + /* ------------------ */ + + } + else + { + /* === Prepare for supercolumn detection ==================== */ + + DEBUG4 (("Preparing supercol detection for Col: "ID".\n", col)); + + /* save score so far */ + Col [col].shared2.score = cur_score ; + + /* add column to hash table, for supercolumn detection */ + hash %= n_col + 1 ; + + DEBUG4 ((" Hash = "ID", n_col = "ID".\n", hash, n_col)) ; + ASSERT (hash <= n_col) ; + + head_column = head [hash] ; + if (head_column > EMPTY) + { + /* degree list "hash" is non-empty, use prev (shared3) of */ + /* first column in degree list as head of hash bucket */ + first_col = Col [head_column].shared3.headhash ; + Col [head_column].shared3.headhash = col ; + } + else + { + /* degree list "hash" is empty, use head as hash bucket */ + first_col = - (head_column + 2) ; + head [hash] = - (col + 2) ; + } + Col [col].shared4.hash_next = first_col ; + + /* save hash function in Col [col].shared3.hash */ + Col [col].shared3.hash = (Int) hash ; + ASSERT (COL_IS_ALIVE (col)) ; + } + } + + /* The approximate external column degree is now computed. */ + + /* === Supercolumn detection ======================================== */ + + DEBUG3 (("** Supercolumn detection phase. **\n")) ; + + detect_super_cols ( + +#ifndef NDEBUG + n_col, Row, +#endif /* NDEBUG */ + + Col, A, head, pivot_row_start, pivot_row_length) ; + + /* === Kill the pivotal column ====================================== */ + + KILL_PRINCIPAL_COL (pivot_col) ; + + /* ------------------ */ + /* added for UMFPACK: */ + /* add columns to column list of front */ +#ifndef NDEBUG + DEBUG1 (("Pivot")) ; + dump_super (pivot_col, Col, n_col) ; +#endif + Col [Col [pivot_col].lastcol].nextcol = Front_cols [nfr] ; + Front_cols [nfr] = pivot_col ; + /* ------------------ */ + + /* === Clear mark =================================================== */ + + tag_mark += (max_deg + 1) ; + if (tag_mark >= max_mark) + { + DEBUG2 (("clearing tag_mark\n")) ; + tag_mark = clear_mark (n_row, Row) ; + } + +#ifndef NDEBUG + DEBUG3 (("check3\n")) ; + debug_mark (n_row, Row, tag_mark, max_mark) ; +#endif /* NDEBUG */ + + /* === Finalize the new pivot row, and column scores ================ */ + + DEBUG3 (("** Finalize scores phase. **\n")) ; + DEBUG3 (("pivot_row_degree "ID"\n", pivot_row_degree)) ; + + /* for each column in pivot row */ + rp = &A [pivot_row_start] ; + /* compact the pivot row */ + new_rp = rp ; + rp_end = rp + pivot_row_length ; + while (rp < rp_end) + { + col = *rp++ ; + DEBUG3 (("Col "ID" \n", col)) ; + /* skip dead columns */ + if (COL_IS_DEAD (col)) + { + DEBUG3 (("dead\n")) ; + continue ; + } + *new_rp++ = col ; + /* add new pivot row to column */ + A [Col [col].start + (Col [col].length++)] = pivot_row ; + + /* retrieve score so far and add on pivot row's degree. */ + /* (we wait until here for this in case the pivot */ + /* row's degree was reduced due to mass elimination). */ + DEBUG3 ((" cur_score "ID" ", cur_score)) ; + cur_score = Col [col].shared2.score + pivot_row_degree ; + DEBUG3 (("ID", cur_score)) ; + + /* calculate the max possible score as the number of */ + /* external columns minus the 'k' value minus the */ + /* columns thickness */ + max_score = n_col - k - Col [col].shared1.thickness ; + DEBUG3 ((" max_score "ID" ", max_score)) ; + + /* make the score the external degree of the union-of-rows */ + cur_score -= Col [col].shared1.thickness ; + DEBUG3 ((" cur_score "ID" ", cur_score)) ; + + /* make sure score is less or equal than the max score */ + cur_score = MIN (cur_score, max_score) ; + ASSERT (cur_score >= 0) ; + + /* store updated score */ + Col [col].shared2.score = cur_score ; + DEBUG3 ((" "ID"\n", cur_score)) ; + + /* === Place column back in degree list ========================= */ + + ASSERT (min_score >= 0) ; + ASSERT (min_score <= n_col) ; + ASSERT (cur_score >= 0) ; + ASSERT (cur_score <= n_col) ; + ASSERT (head [cur_score] >= EMPTY) ; + next_col = head [cur_score] ; + Col [col].shared4.degree_next = next_col ; + Col [col].shared3.prev = EMPTY ; + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = col ; + } + head [cur_score] = col ; + + /* see if this score is less than current min */ + min_score = MIN (min_score, cur_score) ; + + } + +#ifndef NDEBUG + debug_deg_lists (n_row, n_col, Row, Col, head, + min_score, n_col2-k, max_deg) ; +#endif /* NDEBUG */ + + /* ------------------ */ + /* added for UMFPACK: */ + /* frontal matrix can have more pivot cols than pivot rows for */ + /* singular matrices. */ + + /* number of candidate pivot columns */ + Front_npivcol [nfr] = pivot_col_thickness ; + + /* all rows (not just size of contrib. block) */ + Front_nrows [nfr] = pivot_row_thickness ; + + /* all cols */ + Front_ncols [nfr] = pivot_col_thickness + pivot_row_degree ; + + Front_parent [nfr] = EMPTY ; + + pivot_row_thickness -= pivot_col_thickness ; + DEBUG1 (("Front "ID" Pivot_row_thickness after pivot cols elim: "ID"\n", + nfr, pivot_row_thickness)) ; + pivot_row_thickness = MAX (0, pivot_row_thickness) ; + /* ------------------ */ + + /* === Resurrect the new pivot row ================================== */ + + if (pivot_row_degree > 0 + /* ------------------ */ + /* added for UMFPACK: */ + && pivot_row_thickness > 0 + /* ------------------ */ + ) + { + /* update pivot row length to reflect any cols that were killed */ + /* during super-col detection and mass elimination */ + Row [pivot_row].start = pivot_row_start ; + Row [pivot_row].length = (Int) (new_rp - &A[pivot_row_start]) ; + Row [pivot_row].shared1.degree = pivot_row_degree ; + Row [pivot_row].shared2.mark = 0 ; + /* ------------------ */ + /* added for UMFPACK: */ + Row [pivot_row].thickness = pivot_row_thickness ; + Row [pivot_row].front = nfr ; + /* ------------------ */ + /* pivot row is no longer dead */ + } + + /* ------------------ */ + /* added for UMFPACK: */ + +#ifndef NDEBUG + DEBUG1 (("Front "ID" : "ID" "ID" "ID" ", nfr, + Front_npivcol [nfr], Front_nrows [nfr], Front_ncols [nfr])) ; + DEBUG1 ((" cols:[ ")) ; + debug_d = 0 ; + for (col = Front_cols [nfr] ; col != EMPTY ; col = Col [col].nextcol) + { + DEBUG1 ((" "ID, col)) ; + ASSERT (col >= 0 && col < n_col) ; + ASSERT (COL_IS_DEAD (col)) ; + debug_d++ ; + ASSERT (debug_d <= pivot_col_thickness) ; + } + ASSERT (debug_d == pivot_col_thickness) ; + DEBUG1 ((" ]\n ")) ; +#endif + nfr++ ; /* one more front */ + /* ------------------ */ + + } + + /* === All principal columns have now been ordered ====================== */ + + /* ------------------ */ + /* added for UMFPACK: */ + *p_nfr = nfr ; + /* ------------------ */ + + return (ngarbage) ; +} + + +/* ========================================================================== */ +/* === order_children deleted for UMFPACK =================================== */ +/* ========================================================================== */ + +/* ========================================================================== */ +/* === detect_super_cols ==================================================== */ +/* ========================================================================== */ + +/* + Detects supercolumns by finding matches between columns in the hash buckets. + Check amongst columns in the set A [row_start ... row_start + row_length-1]. + The columns under consideration are currently *not* in the degree lists, + and have already been placed in the hash buckets. + + The hash bucket for columns whose hash function is equal to h is stored + as follows: + + if head [h] is >= 0, then head [h] contains a degree list, so: + + head [h] is the first column in degree bucket h. + Col [head [h]].headhash gives the first column in hash bucket h. + + otherwise, the degree list is empty, and: + + -(head [h] + 2) is the first column in hash bucket h. + + For a column c in a hash bucket, Col [c].shared3.prev is NOT a "previous + column" pointer. Col [c].shared3.hash is used instead as the hash number + for that column. The value of Col [c].shared4.hash_next is the next column + in the same hash bucket. + + Assuming no, or "few" hash collisions, the time taken by this routine is + linear in the sum of the sizes (lengths) of each column whose score has + just been computed in the approximate degree computation. + Not user-callable. +*/ + +PRIVATE void detect_super_cols +( + /* === Parameters ======================================================= */ + +#ifndef NDEBUG + /* these two parameters are only needed when debugging is enabled: */ + Int n_col, /* number of columns of A */ + Colamd_Row Row [], /* of size n_row+1 */ +#endif /* NDEBUG */ + + Colamd_Col Col [], /* of size n_col+1 */ + Int A [], /* row indices of A */ + Int head [], /* head of degree lists and hash buckets */ + Int row_start, /* pointer to set of columns to check */ + Int row_length /* number of columns to check */ +) +{ + /* === Local variables ================================================== */ + + Int hash ; /* hash value for a column */ + Int *rp ; /* pointer to a row */ + Int c ; /* a column index */ + Int super_c ; /* column index of the column to absorb into */ + Int *cp1 ; /* column pointer for column super_c */ + Int *cp2 ; /* column pointer for column c */ + Int length ; /* length of column super_c */ + Int prev_c ; /* column preceding c in hash bucket */ + Int i ; /* loop counter */ + Int *rp_end ; /* pointer to the end of the row */ + Int col ; /* a column index in the row to check */ + Int head_column ; /* first column in hash bucket or degree list */ + Int first_col ; /* first column in hash bucket */ + + /* === Consider each column in the row ================================== */ + + rp = &A [row_start] ; + rp_end = rp + row_length ; + while (rp < rp_end) + { + col = *rp++ ; + if (COL_IS_DEAD (col)) + { + continue ; + } + + /* get hash number for this column */ + hash = Col [col].shared3.hash ; + ASSERT (hash <= n_col) ; + + /* === Get the first column in this hash bucket ===================== */ + + head_column = head [hash] ; + if (head_column > EMPTY) + { + first_col = Col [head_column].shared3.headhash ; + } + else + { + first_col = - (head_column + 2) ; + } + + /* === Consider each column in the hash bucket ====================== */ + + for (super_c = first_col ; super_c != EMPTY ; + super_c = Col [super_c].shared4.hash_next) + { + ASSERT (COL_IS_ALIVE (super_c)) ; + ASSERT (Col [super_c].shared3.hash == hash) ; + length = Col [super_c].length ; + + /* prev_c is the column preceding column c in the hash bucket */ + prev_c = super_c ; + + /* === Compare super_c with all columns after it ================ */ + + for (c = Col [super_c].shared4.hash_next ; + c != EMPTY ; c = Col [c].shared4.hash_next) + { + ASSERT (c != super_c) ; + ASSERT (COL_IS_ALIVE (c)) ; + ASSERT (Col [c].shared3.hash == hash) ; + + /* not identical if lengths or scores are different */ + if (Col [c].length != length || + Col [c].shared2.score != Col [super_c].shared2.score) + { + prev_c = c ; + continue ; + } + + /* compare the two columns */ + cp1 = &A [Col [super_c].start] ; + cp2 = &A [Col [c].start] ; + + for (i = 0 ; i < length ; i++) + { + /* the columns are "clean" (no dead rows) */ + ASSERT (ROW_IS_ALIVE (*cp1)) ; + ASSERT (ROW_IS_ALIVE (*cp2)) ; + /* row indices will same order for both supercols, */ + /* no gather scatter nessasary */ + if (*cp1++ != *cp2++) + { + break ; + } + } + + /* the two columns are different if the for-loop "broke" */ + if (i != length) + { + prev_c = c ; + continue ; + } + + /* === Got it! two columns are identical =================== */ + + ASSERT (Col [c].shared2.score == Col [super_c].shared2.score) ; + + Col [super_c].shared1.thickness += Col [c].shared1.thickness ; + Col [c].shared1.parent = super_c ; + KILL_NON_PRINCIPAL_COL (c) ; + + Col [c].shared2.order = EMPTY ; + /* remove c from hash bucket */ + Col [prev_c].shared4.hash_next = Col [c].shared4.hash_next ; + + /* ------------------ */ + /* added for UMFPACK: */ + /* add c to end of list of super_c */ + ASSERT (Col [super_c].lastcol >= 0) ; + ASSERT (Col [super_c].lastcol < n_col) ; + Col [Col [super_c].lastcol].nextcol = c ; + Col [super_c].lastcol = Col [c].lastcol ; +#ifndef NDEBUG + /* dump the supercolumn */ + DEBUG1 (("Super")) ; + dump_super (super_c, Col, n_col) ; +#endif + /* ------------------ */ + + } + } + + /* === Empty this hash bucket ======================================= */ + + if (head_column > EMPTY) + { + /* corresponding degree list "hash" is not empty */ + Col [head_column].shared3.headhash = EMPTY ; + } + else + { + /* corresponding degree list "hash" is empty */ + head [hash] = EMPTY ; + } + } +} + + +/* ========================================================================== */ +/* === garbage_collection =================================================== */ +/* ========================================================================== */ + +/* + Defragments and compacts columns and rows in the workspace A. Used when + all avaliable memory has been used while performing row merging. Returns + the index of the first free position in A, after garbage collection. The + time taken by this routine is linear is the size of the array A, which is + itself linear in the number of nonzeros in the input matrix. + Not user-callable. +*/ + +PRIVATE Int garbage_collection /* returns the new value of pfree */ +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows */ + Int n_col, /* number of columns */ + Colamd_Row Row [], /* row info */ + Colamd_Col Col [], /* column info */ + Int A [], /* A [0 ... Alen-1] holds the matrix */ + Int *pfree /* &A [0] ... pfree is in use */ +) +{ + /* === Local variables ================================================== */ + + Int *psrc ; /* source pointer */ + Int *pdest ; /* destination pointer */ + Int j ; /* counter */ + Int r ; /* a row index */ + Int c ; /* a column index */ + Int length ; /* length of a row or column */ + +#ifndef NDEBUG + Int debug_rows ; + DEBUG2 (("Defrag..\n")) ; + for (psrc = &A[0] ; psrc < pfree ; psrc++) ASSERT (*psrc >= 0) ; + debug_rows = 0 ; +#endif /* NDEBUG */ + + /* === Defragment the columns =========================================== */ + + pdest = &A[0] ; + for (c = 0 ; c < n_col ; c++) + { + if (COL_IS_ALIVE (c)) + { + psrc = &A [Col [c].start] ; + + /* move and compact the column */ + ASSERT (pdest <= psrc) ; + Col [c].start = (Int) (pdest - &A [0]) ; + length = Col [c].length ; + for (j = 0 ; j < length ; j++) + { + r = *psrc++ ; + if (ROW_IS_ALIVE (r)) + { + *pdest++ = r ; + } + } + Col [c].length = (Int) (pdest - &A [Col [c].start]) ; + } + } + + /* === Prepare to defragment the rows =================================== */ + + for (r = 0 ; r < n_row ; r++) + { + if (ROW_IS_ALIVE (r)) + { + if (Row [r].length == 0) + { + /* this row is of zero length. cannot compact it, so kill it */ + DEBUG3 (("Defrag row kill\n")) ; + KILL_ROW (r) ; + } + else + { + /* save first column index in Row [r].shared2.first_column */ + psrc = &A [Row [r].start] ; + Row [r].shared2.first_column = *psrc ; + ASSERT (ROW_IS_ALIVE (r)) ; + /* flag the start of the row with the one's complement of row */ + *psrc = ONES_COMPLEMENT (r) ; + +#ifndef NDEBUG + debug_rows++ ; +#endif /* NDEBUG */ + + } + } + } + + /* === Defragment the rows ============================================== */ + + psrc = pdest ; + while (psrc < pfree) + { + /* find a negative number ... the start of a row */ + if (*psrc++ < 0) + { + psrc-- ; + /* get the row index */ + r = ONES_COMPLEMENT (*psrc) ; + ASSERT (r >= 0 && r < n_row) ; + /* restore first column index */ + *psrc = Row [r].shared2.first_column ; + ASSERT (ROW_IS_ALIVE (r)) ; + + /* move and compact the row */ + ASSERT (pdest <= psrc) ; + Row [r].start = (Int) (pdest - &A [0]) ; + length = Row [r].length ; + for (j = 0 ; j < length ; j++) + { + c = *psrc++ ; + if (COL_IS_ALIVE (c)) + { + *pdest++ = c ; + } + } + Row [r].length = (Int) (pdest - &A [Row [r].start]) ; + +#ifndef NDEBUG + debug_rows-- ; +#endif /* NDEBUG */ + + } + } + /* ensure we found all the rows */ + ASSERT (debug_rows == 0) ; + + /* === Return the new value of pfree ==================================== */ + + return ((Int) (pdest - &A [0])) ; +} + + +/* ========================================================================== */ +/* === clear_mark =========================================================== */ +/* ========================================================================== */ + +/* + Clears the Row [].shared2.mark array, and returns the new tag_mark. + Return value is the new tag_mark. Not user-callable. +*/ + +PRIVATE Int clear_mark /* return the new value for tag_mark */ +( + /* === Parameters ======================================================= */ + + Int n_row, /* number of rows in A */ + Colamd_Row Row [] /* Row [0 ... n-1].shared2.mark is set to zero */ +) +{ + /* === Local variables ================================================== */ + + Int r ; + + for (r = 0 ; r < n_row ; r++) + { + if (ROW_IS_ALIVE (r)) + { + Row [r].shared2.mark = 0 ; + } + } + + /* ------------------ */ + return (1) ; + /* ------------------ */ + +} + + +/* ========================================================================== */ +/* === print_report removed for UMFPACK ===================================== */ +/* ========================================================================== */ + + + +/* ========================================================================== */ +/* === colamd debugging routines ============================================ */ +/* ========================================================================== */ + +/* When debugging is disabled, the remainder of this file is ignored. */ + +#ifndef NDEBUG + + +/* ========================================================================== */ +/* === debug_structures ===================================================== */ +/* ========================================================================== */ + +/* + At this point, all empty rows and columns are dead. All live columns + are "clean" (containing no dead rows) and simplicial (no supercolumns + yet). Rows may contain dead columns, but all live rows contain at + least one live column. +*/ + +PRIVATE void debug_structures +( + /* === Parameters ======================================================= */ + + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [], + Int n_col2 +) +{ + /* === Local variables ================================================== */ + + Int i ; + Int c ; + Int *cp ; + Int *cp_end ; + Int len ; + Int score ; + Int r ; + Int *rp ; + Int *rp_end ; + Int deg ; + + /* === Check A, Row, and Col ============================================ */ + + for (c = 0 ; c < n_col ; c++) + { + if (COL_IS_ALIVE (c)) + { + len = Col [c].length ; + score = Col [c].shared2.score ; + DEBUG4 (("initial live col "ID" "ID" "ID"\n", c, len, score)) ; + ASSERT (len > 0) ; + ASSERT (score >= 0) ; + ASSERT (Col [c].shared1.thickness == 1) ; + cp = &A [Col [c].start] ; + cp_end = cp + len ; + while (cp < cp_end) + { + r = *cp++ ; + ASSERT (ROW_IS_ALIVE (r)) ; + } + } + else + { + i = Col [c].shared2.order ; + ASSERT (i >= n_col2 && i < n_col) ; + } + } + + for (r = 0 ; r < n_row ; r++) + { + if (ROW_IS_ALIVE (r)) + { + i = 0 ; + len = Row [r].length ; + deg = Row [r].shared1.degree ; + ASSERT (len > 0) ; + ASSERT (deg > 0) ; + rp = &A [Row [r].start] ; + rp_end = rp + len ; + while (rp < rp_end) + { + c = *rp++ ; + if (COL_IS_ALIVE (c)) + { + i++ ; + } + } + ASSERT (i > 0) ; + } + } +} + + +/* ========================================================================== */ +/* === debug_deg_lists ====================================================== */ +/* ========================================================================== */ + +/* + Prints the contents of the degree lists. Counts the number of columns + in the degree list and compares it to the total it should have. Also + checks the row degrees. +*/ + +PRIVATE void debug_deg_lists +( + /* === Parameters ======================================================= */ + + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int head [], + Int min_score, + Int should, + Int max_deg +) +{ + /* === Local variables ================================================== */ + + Int deg ; + Int col ; + Int have ; + Int row ; + + /* === Check the degree lists =========================================== */ + + if (n_col > 10000 && UMF_debug <= 0) + { + return ; + } + have = 0 ; + DEBUG4 (("Degree lists: "ID"\n", min_score)) ; + for (deg = 0 ; deg <= n_col ; deg++) + { + col = head [deg] ; + if (col == EMPTY) + { + continue ; + } + DEBUG4 ((ID":", deg)) ; + while (col != EMPTY) + { + DEBUG4 ((" "ID, col)) ; + have += Col [col].shared1.thickness ; + ASSERT (COL_IS_ALIVE (col)) ; + col = Col [col].shared4.degree_next ; + } + DEBUG4 (("\n")) ; + } + DEBUG4 (("should "ID" have "ID"\n", should, have)) ; + ASSERT (should == have) ; + + /* === Check the row degrees ============================================ */ + + if (n_row > 10000 && UMF_debug <= 0) + { + return ; + } + for (row = 0 ; row < n_row ; row++) + { + if (ROW_IS_ALIVE (row)) + { + ASSERT (Row [row].shared1.degree <= max_deg) ; + } + } +} + + +/* ========================================================================== */ +/* === debug_mark =========================================================== */ +/* ========================================================================== */ + +/* + Ensures that the tag_mark is less that the maximum and also ensures that + each entry in the mark array is less than the tag mark. +*/ + +PRIVATE void debug_mark +( + /* === Parameters ======================================================= */ + + Int n_row, + Colamd_Row Row [], + Int tag_mark, + Int max_mark +) +{ + /* === Local variables ================================================== */ + + Int r ; + + /* === Check the Row marks ============================================== */ + + ASSERT (tag_mark > 0 && tag_mark <= max_mark) ; + if (n_row > 10000 && UMF_debug <= 0) + { + return ; + } + for (r = 0 ; r < n_row ; r++) + { + ASSERT (Row [r].shared2.mark < tag_mark) ; + } +} + + +/* ========================================================================== */ +/* === debug_matrix ========================================================= */ +/* ========================================================================== */ + +/* + Prints out the contents of the columns and the rows. +*/ + +PRIVATE void debug_matrix +( + /* === Parameters ======================================================= */ + + Int n_row, + Int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + Int A [] +) +{ + /* === Local variables ================================================== */ + + Int r ; + Int c ; + Int *rp ; + Int *rp_end ; + Int *cp ; + Int *cp_end ; + + /* === Dump the rows and columns of the matrix ========================== */ + + if (UMF_debug < 3) + { + return ; + } + DEBUG3 (("DUMP MATRIX:\n")) ; + for (r = 0 ; r < n_row ; r++) + { + DEBUG3 (("Row "ID" alive? "ID"\n", r, ROW_IS_ALIVE (r))) ; + if (ROW_IS_DEAD (r)) + { + continue ; + } + + /* ------------------ */ + /* changed for UMFPACK: */ + DEBUG3 (("start "ID" length "ID" degree "ID" thickness "ID"\n", + Row [r].start, Row [r].length, Row [r].shared1.degree, + Row [r].thickness)) ; + /* ------------------ */ + + rp = &A [Row [r].start] ; + rp_end = rp + Row [r].length ; + while (rp < rp_end) + { + c = *rp++ ; + DEBUG4 ((" "ID" col "ID"\n", COL_IS_ALIVE (c), c)) ; + } + } + + for (c = 0 ; c < n_col ; c++) + { + DEBUG3 (("Col "ID" alive? "ID"\n", c, COL_IS_ALIVE (c))) ; + if (COL_IS_DEAD (c)) + { + continue ; + } + /* ------------------ */ + /* changed for UMFPACK: */ + DEBUG3 (("start "ID" length "ID" shared1[thickness,parent] "ID + " shared2 [order,score] "ID"\n", Col [c].start, Col [c].length, + Col [c].shared1.thickness, Col [c].shared2.score)); + /* ------------------ */ + cp = &A [Col [c].start] ; + cp_end = cp + Col [c].length ; + while (cp < cp_end) + { + r = *cp++ ; + DEBUG4 ((" "ID" row "ID"\n", ROW_IS_ALIVE (r), r)) ; + } + + /* ------------------ */ + /* added for UMFPACK: */ + DEBUG1 (("Col")) ; + dump_super (c, Col, n_col) ; + /* ------------------ */ + + } +} + +/* ------------------ */ +/* dump_super added for UMFPACK: */ +PRIVATE void dump_super +( + Int super_c, + Colamd_Col Col [], + Int n_col +) +{ + Int col, ncols ; + + DEBUG1 ((" =[ ")) ; + ncols = 0 ; + for (col = super_c ; col != EMPTY ; col = Col [col].nextcol) + { + DEBUG1 ((" "ID, col)) ; + ASSERT (col >= 0 && col < n_col) ; + if (col != super_c) + { + ASSERT (COL_IS_DEAD (col)) ; + } + if (Col [col].nextcol == EMPTY) + { + ASSERT (col == Col [super_c].lastcol) ; + } + ncols++ ; + ASSERT (ncols <= Col [super_c].shared1.thickness) ; + } + ASSERT (ncols == Col [super_c].shared1.thickness) ; + DEBUG1 (("]\n")) ; +} +/* ------------------ */ + + +#endif /* NDEBUG */ + diff --git a/src/sparse-matrix/umfpack/umf_create_element.c b/src/sparse-matrix/umfpack/umf_create_element.c new file mode 100644 index 0000000..623693b --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_create_element.c @@ -0,0 +1,534 @@ +/* ========================================================================== */ +/* === UMF_create_element =================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Factorization of a frontal matrix is complete. Create a new element for + later assembly into a subsequent frontal matrix. Returns TRUE if + successful, FALSE if out of memory. +*/ + +#include "umf_internal.h" +#include "umf_mem_alloc_element.h" +#include "umf_mem_alloc_tail_block.h" +#include "umf_mem_free_tail_block.h" +#include "umf_get_memory.h" +#include "umf_blas3_update.h" + +GLOBAL Int UMF_create_element +( + NumericType *Numeric, + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int j, col, row, *Fcols, *Frows, fnrows, fncols, *Cols, len, needunits, t1, + t2, size, e, i, *E, *Fcpos, *Frpos, *Rows, eloc, fnrows_max, f, + got_memory, *Row_tuples, *Row_degree, *Row_tlen, *Col_tuples, max_mark, + *Col_degree, *Col_tlen, nn, n_row, n_col ; + Entry *C, *Fcol ; + Element *ep ; + Unit *p, *Memory ; + Tuple *tp, *tp1, *tp2, tuple, *tpend ; +#ifndef NDEBUG + double gprob_save ; +#endif + + /* ---------------------------------------------------------------------- */ + /* apply pending updates, if any */ + /* ---------------------------------------------------------------------- */ + + if (Work->fnpiv > 0) + { + UMF_blas3_update (Work) ; + } + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + DEBUG2 (("FRONTAL WRAPUP\n")) ; + Col_degree = Numeric->Cperm ; + Row_degree = Numeric->Rperm ; + Row_tuples = Numeric->Uip ; + Row_tlen = Numeric->Uilen ; + Col_tuples = Numeric->Lip ; + Col_tlen = Numeric->Lilen ; + n_row = Work->n_row ; + n_col = Work->n_col ; + nn = MAX (n_row, n_col) ; + Fcols = Work->Fcols ; + Frows = Work->Frows ; + Fcpos = Work->Fcpos ; + Frpos = Work->Frpos ; + Memory = Numeric->Memory ; + fncols = Work->fncols ; + fnrows = Work->fnrows ; + fnrows_max = Work->fnrows_max ; + + tp = (Tuple *) NULL ; + tp1 = (Tuple *) NULL ; + tp2 = (Tuple *) NULL ; + + /* ---------------------------------------------------------------------- */ + /* add the current frontal matrix to the degrees of each column, */ + /* and place the modified columns back in the degree lists */ + /* ---------------------------------------------------------------------- */ + + for (j = 0 ; j < fncols ; j++) + { + /* add the current frontal matrix to the degree */ + ASSERT (Fcols [j] >= 0 && Fcols [j] < n_col) ; + Col_degree [Fcols [j]] += fnrows ; + } + + /* ---------------------------------------------------------------------- */ + /* add the current frontal matrix to the degrees of each row */ + /* ---------------------------------------------------------------------- */ + + for (i = 0 ; i < fnrows ; i++) + { + /* add the current frontal matrix to the degree */ + ASSERT (Frows [i] >= 0 && Frows [i] < n_row) ; + Row_degree [Frows [i]] += fncols ; + } + + /* ---------------------------------------------------------------------- */ + /* Reset the external degree counters */ + /* ---------------------------------------------------------------------- */ + + E = Work->E ; + max_mark = MAX_MARK (nn) ; + + if (!Work->pivcol_in_front) + { + /* clear the external column degrees. no more Usons of current front */ + Work->cdeg0 += (nn + 1) ; + if (Work->cdeg0 >= max_mark) + { + /* guard against integer overflow. This is very rare */ + Work->cdeg0 = 1 ; + for (e = 0 ; e <= Work->nel ; e++) + { + if (E [e]) + { + ep = (Element *) (Memory + E [e]) ; + ep->cdeg = 0 ; + } + } + } + } + + if (!Work->pivrow_in_front) + { + /* clear the external row degrees. no more Lsons of current front */ + Work->rdeg0 += (nn + 1) ; + if (Work->rdeg0 >= max_mark) + { + /* guard against integer overflow. This is very rare */ + Work->rdeg0 = 1 ; + for (e = 0 ; e <= Work->nel ; e++) + { + if (E [e]) + { + ep = (Element *) (Memory + E [e]) ; + ep->rdeg = 0 ; + } + } + } + } + + /* ---------------------------------------------------------------------- */ + /* clear row/col offsets */ + /* ---------------------------------------------------------------------- */ + + if (!Work->pivrow_in_front) + { + for (j = 0 ; j < fncols ; j++) + { + Fcpos [Fcols [j]] = EMPTY ; + } + } + + if (!Work->pivcol_in_front) + { + for (i = 0 ; i < fnrows ; i++) + { + Frpos [Frows [i]] = EMPTY ; + } + } + + if (fncols <= 0 || fnrows <= 0) + { + /* no element to create */ + DEBUG2 (("Element evaporation\n")) ; + Work->prior_element = EMPTY ; + return (TRUE) ; + } + + /* ---------------------------------------------------------------------- */ + /* create element for later assembly */ + /* ---------------------------------------------------------------------- */ + + needunits = 0 ; + got_memory = FALSE ; + eloc = UMF_mem_alloc_element (Numeric, fnrows, fncols, &Rows, &Cols, &C, + &needunits, &ep) ; + + if (!eloc) + { + /* do garbage collection, realloc, and try again */ + if (!UMF_get_memory (Numeric, Work, needunits)) + { + return (FALSE) ; /* out of memory */ + } + got_memory = TRUE ; + Memory = Numeric->Memory ; +#ifndef NDEBUG + gprob_save = UMF_gprob ; + UMF_gprob = 0 ; +#endif + eloc = UMF_mem_alloc_element (Numeric, fnrows, fncols, &Rows, &Cols, &C, + &needunits, &ep) ; + ASSERT (eloc >= 0) ; + if (!eloc) + { + return (FALSE) ; /* out of memory */ + } +#ifndef NDEBUG + UMF_gprob = gprob_save ; +#endif + } + + e = ++(Work->nel) ; /* get the name of this new frontal matrix */ + Work->prior_element = e ; + DEBUG8 (("wrapup e "ID" nel "ID"\n", e, Work->nel)) ; + + ASSERT (e > 0 && e <= n_col + MIN (n_row,n_col)) ; + ASSERT (E [e] == 0) ; + E [e] = eloc ; + + if (Work->pivcol_in_front) + { + /* the new element is a Uson of the next frontal matrix */ + ep->cdeg = Work->cdeg0 ; + } + + if (Work->pivrow_in_front) + { + /* the new element is an Lson of the next frontal matrix */ + ep->rdeg = Work->rdeg0 ; + } + + /* ---------------------------------------------------------------------- */ + /* copy frontal matrix into the new element */ + /* ---------------------------------------------------------------------- */ + + for (i = 0 ; i < fnrows ; i++) + { + Rows [i] = Frows [i] ; + } + for (i = 0 ; i < fncols ; i++) + { + Cols [i] = Fcols [i] ; + } + Fcol = Work->Fx ; + for (j = 0 ; j < fncols ; j++) + { + for (i = 0 ; i < fnrows ; i++) + { + C [i] = Fcol [i] ; + } + Fcol += fnrows_max ; + C += fnrows ; + } + + /* ---------------------------------------------------------------------- */ + /* add tuples for the new element */ + /* ---------------------------------------------------------------------- */ + + tuple.e = e ; + + if (got_memory) + { + + /* ------------------------------------------------------------------ */ + /* UMF_get_memory ensures enough space exists for each new tuple */ + /* ------------------------------------------------------------------ */ + + /* place (e,f) in the element list of each column */ + for (tuple.f = 0 ; tuple.f < fncols ; tuple.f++) + { + col = Fcols [tuple.f] ; + ASSERT (col >= 0 && col < n_col) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + ASSERT (Col_tuples [col]) ; + tp = ((Tuple *) (Memory + Col_tuples [col])) + Col_tlen [col]++ ; + *tp = tuple ; + } + + /* ------------------------------------------------------------------ */ + + /* place (e,f) in the element list of each row */ + for (tuple.f = 0 ; tuple.f < fnrows ; tuple.f++) + { + row = Frows [tuple.f] ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (NON_PIVOTAL_ROW (row)) ; + ASSERT (Row_tuples [row]) ; + tp = ((Tuple *) (Memory + Row_tuples [row])) + Row_tlen [row]++ ; + *tp = tuple ; + } + + } + else + { + + /* ------------------------------------------------------------------ */ + /* might not have enough space for each tuple */ + /* ------------------------------------------------------------------ */ + + /* ------------------------------------------------------------------ */ + /* place (e,f) in the element list of each column */ + /* ------------------------------------------------------------------ */ + + for (tuple.f = 0 ; tuple.f < fncols ; tuple.f++) + { + col = Fcols [tuple.f] ; + ASSERT (col >= 0 && col < n_col) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + t1 = Col_tuples [col] ; + DEBUG1 (("Placing on col:"ID" , tuples at "ID"\n", + col, Col_tuples [col])) ; + + size = 0 ; + len = 0 ; + + if (t1) + { + p = Memory + t1 ; + tp = (Tuple *) p ; + size = GET_BLOCK_SIZE (p) ; + len = Col_tlen [col] ; + tp2 = tp + len ; + } + + needunits = UNITS (Tuple, len + 1) ; + DEBUG1 (("len: "ID" size: "ID" needunits: "ID"\n", + len, size, needunits)); + + if (needunits > size && t1) + { + /* prune the tuples */ + tp1 = tp ; + tp2 = tp ; + tpend = tp + len ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + ; + if (Cols [f] == EMPTY) continue ; /* already assembled */ + ASSERT (col == Cols [f]) ; + *tp2++ = *tp ; /* leave the tuple in the list */ + } + len = tp2 - tp1 ; + Col_tlen [col] = len ; + needunits = UNITS (Tuple, len + 1) ; + } + + if (needunits > size) + { + /* no room exists - reallocate elsewhere */ + DEBUG1 (("REALLOCATE Col: "ID", size "ID" to "ID"\n", + col, size, 2*needunits)) ; +#ifndef NDEBUG + UMF_allocfail = FALSE ; + if (UMF_gprob > 0) /* a double relop, but ignore NaN case */ + { + double rrr = ((double) (rand ( ))) / + (((double) RAND_MAX) + 1) ; + DEBUG1 (("Check random %e %e\n", rrr, UMF_gprob)) ; + UMF_allocfail = rrr < UMF_gprob ; + if (UMF_allocfail) DEBUG1 (("Random gar. (col tuple)\n")) ; + } +#endif + needunits = MIN (2*needunits, (Int) UNITS (Tuple, nn)) ; + t2 = UMF_mem_alloc_tail_block (Numeric, needunits) ; + if (!t2) + { + /* get memory, reconstruct all tuple lists, and return */ + return (UMF_get_memory (Numeric, Work, 0)) ; + } + Col_tuples [col] = t2 ; + tp2 = (Tuple *) (Memory + t2) ; + if (t1) + { + for (i = 0 ; i < len ; i++) + { + *tp2++ = *tp1++ ; + } + UMF_mem_free_tail_block (Numeric, t1) ; + } + } + + /* place the new (e,f) tuple in the element list of the column */ + Col_tlen [col]++ ; + *tp2 = tuple ; + } + + /* ------------------------------------------------------------------ */ + /* place (e,f) in the element list of each row */ + /* ------------------------------------------------------------------ */ + + for (tuple.f = 0 ; tuple.f < fnrows ; tuple.f++) + { + row = Frows [tuple.f] ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (NON_PIVOTAL_ROW (row)) ; + t1 = Row_tuples [row] ; + DEBUG1 (("Placing on row:"ID" , tuples at "ID"\n", + row, Row_tuples [row])) ; + + size = 0 ; + len = 0 ; + if (t1) + { + p = Memory + t1 ; + tp = (Tuple *) p ; + size = GET_BLOCK_SIZE (p) ; + len = Row_tlen [row] ; + tp2 = tp + len ; + } + + needunits = UNITS (Tuple, len + 1) ; + DEBUG1 (("len: "ID" size: "ID" needunits: "ID"\n", + len, size, needunits)) ; + + if (needunits > size && t1) + { + /* prune the tuples */ + tp1 = tp ; + tp2 = tp ; + tpend = tp + len ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) + { + continue ; /* element already deallocated */ + } + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + Rows = Cols + (ep->ncols) ; + if (Rows [f] == EMPTY) continue ; /* already assembled */ + ASSERT (row == Rows [f]) ; + *tp2++ = *tp ; /* leave the tuple in the list */ + } + len = tp2 - tp1 ; + Row_tlen [row] = len ; + needunits = UNITS (Tuple, len + 1) ; + } + + if (needunits > size) + { + /* no room exists - reallocate elsewhere */ + DEBUG1 (("REALLOCATE Row: "ID", size "ID" to "ID"\n", + row, size, 2*needunits)) ; +#ifndef NDEBUG + UMF_allocfail = FALSE ; + if (UMF_gprob > 0) /* a double relop, but ignore NaN case */ + { + double rrr = ((double) (rand ( ))) / + (((double) RAND_MAX) + 1) ; + DEBUG1 (("Check random %e %e\n", rrr, UMF_gprob)) ; + UMF_allocfail = rrr < UMF_gprob ; + if (UMF_allocfail) DEBUG1 (("Random gar. (row tuple)\n")) ; + } +#endif + needunits = MIN (2*needunits, (Int) UNITS (Tuple, nn)) ; + t2 = UMF_mem_alloc_tail_block (Numeric, needunits) ; + if (!t2) + { + /* get memory, reconstruct all tuple lists, and return */ + return (UMF_get_memory (Numeric, Work, 0)) ; + } + Row_tuples [row] = t2 ; + tp2 = (Tuple *) (Memory + t2) ; + if (t1) + { + for (i = 0 ; i < len ; i++) + { + *tp2++ = *tp1++ ; + } + UMF_mem_free_tail_block (Numeric, t1) ; + } + } + + /* place the new (e,f) tuple in the element list of the row */ + Row_tlen [row]++ ; + *tp2 = tuple ; + } + + } + + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG1 (("Done extending\nFINAL: element row pattern: len="ID"\n", fncols)); + for (j = 0 ; j < fncols ; j++) DEBUG1 ((""ID"\n", Fcols [j])) ; + DEBUG1 (("FINAL: element col pattern: len="ID"\n", fnrows)) ; + for (j = 0 ; j < fnrows ; j++) DEBUG1 ((""ID"\n", Frows [j])) ; + for (j = 0 ; j < fncols ; j++) + { + col = Fcols [j] ; + ASSERT (col >= 0 && col < n_col) ; + UMF_dump_rowcol (1, Numeric, Work, col, TRUE) ; + } + for (j = 0 ; j < fnrows ; j++) + { + row = Frows [j] ; + ASSERT (row >= 0 && row < n_row) ; + UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ; + } + + if (n_row < 1000 && n_col < 1000) + { + UMF_dump_memory (Numeric) ; + } + + DEBUG1 (("New element, after filling with stuff: "ID"\n", e)) ; + UMF_dump_element (Numeric, Work, e, TRUE) ; + + if (nn < 1000) + { + DEBUG4 (("Matrix dump, after New element: "ID"\n", e)) ; + UMF_dump_matrix (Numeric, Work, TRUE) ; + } + + DEBUG3 (("FRONTAL WRAPUP DONE\n")) ; +#endif + + return (TRUE) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_dump.c b/src/sparse-matrix/umfpack/umf_dump.c new file mode 100644 index 0000000..6428239 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_dump.c @@ -0,0 +1,1163 @@ +/* ========================================================================== */ +/* === UMF_dump ============================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* These routines, and external variables, are used only when debugging. */ +/* If debugging is disabled (for normal operation) then this entire file */ +/* becomes empty */ + +#include "umf_internal.h" + +#ifndef NDEBUG + +/* These global debugging variables and arrays do not exist if debugging */ +/* is disabled at compile time (which is the default). */ +GLOBAL Int UMF_debug = -1 ; +GLOBAL Int UMF_nbug = -999 ; +GLOBAL Int UMF_fbug = -999 ; +GLOBAL Int UMF_allocfail = FALSE ; +GLOBAL double UMF_gprob = -1.0 ; + +/* static debugging arrays used only in UMF_dump_rowcol */ +PRIVATE Int UMF_DBflag = 0 ; +PRIVATE Int UMF_DBpacked [UMF_DBMAX+1] ; +PRIVATE Int UMF_DBscatter [UMF_DBMAX+1] ; + +/* ========================================================================== */ +/* === UMF_DBinit =========================================================== */ +/* ========================================================================== */ + +/* clear the debugging arrays */ + +PRIVATE void UMF_DBinit +( + void +) +{ + Int i ; + + /* Int_MAX is defined in umfpack.h */ + if (UMF_DBflag < 1 || UMF_DBflag == Int_MAX) + { + /* clear the debugging arrays */ + UMF_DBflag = 0 ; + for (i = 0 ; i <= UMF_DBMAX ; i++) + { + UMF_DBscatter [i] = 0 ; + UMF_DBpacked [i] = 0 ; + } + } + + UMF_DBflag++ ; + + /* UMF_DBflag > UMF_DBscatter [0...UMF_DBmax] is now true */ +} + +/* ========================================================================== */ +/* === UMF_dump_dense ======================================================= */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_dense +( + Entry *Fx, + Int dim, + Int m, + Int n +) +{ + + /* dump Fx [1..m,1..n], with column dimenstion dim */ + Int i, j; + + if (UMF_debug < 7) return ; + if (!Fx) + { + DEBUG7 (("No dense matrix allocated\n")) ; + return ; + } + DEBUG8 ((" dimension= "ID" rows= "ID" cols= "ID"\n", dim, m, n)) ; + + for (i = 0 ; i < m ; i++) + { + DEBUG9 ((ID": ", i)) ; + for (j = 0 ; j < n ; j++) + { + EDEBUG9 (Fx [i+j*dim]) ; + if (j % 6 == 5) DEBUG9 (("\n ")) ; + } + DEBUG9 (("\n")) ; + } + + for (i = 0 ; i < m ; i++) + { + for (j = 0 ; j < n ; j++) + { + if (IS_ZERO (Fx [i+j*dim])) + { + DEBUG8 ((".")) ; + } + else + { + DEBUG8 (("X")) ; + } + } + DEBUG8 (("\n")) ; + } +} + +/* ========================================================================== */ +/* === UMF_dump_element ===================================================== */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_element +( + NumericType *Numeric, + WorkType *Work, + Int e, + Int clean +) +{ + + Int i, j, k, *Rows, *Cols, nrows, ncols, *E, row, col, + *Row_degree, *Col_degree ; + Entry *C ; + Element *ep ; + Unit *p ; + + if (UMF_debug < 7) return ; + DEBUG7 (("\n====================ELEMENT: "ID" ", e)) ; + if (!Numeric || !Work || !Numeric->Memory) + { + DEBUG7 ((" No Numeric, Work\n")) ; + return ; + } + DEBUG7 ((" nel: "ID" of "ID, e, Work->nel)) ; + if (e <= Work->nelorig) DEBUG7 ((" Original ")) ; + E = Work->E ; + if (!E) + { + DEBUG7 ((" No elements\n")) ; + return ; + } + if (e < 0 || e > Work->nel) + { + DEBUG7 (("e out of range!\n")) ; + return ; + } + if (!E [e]) + { + DEBUG7 ((" deallocated\n")) ; + return ; + } + DEBUG7 (("\n")) ; + Col_degree = Numeric->Cperm ; + Row_degree = Numeric->Rperm ; + + p = Numeric->Memory + E [e] ; + DEBUG7 (("ep "ID"\n", p-Numeric->Memory)) ; + GET_ELEMENT (ep, p, Cols, Rows, ncols, nrows, C) ; + DEBUG7 (("nrows "ID" nrowsleft "ID"\n", nrows, ep->nrowsleft)) ; + DEBUG7 (("ncols "ID" ncolsleft "ID"\n", ncols, ep->ncolsleft)) ; + DEBUG7 (("cdeg-cdeg0 "ID" rdeg-rdeg0 "ID" next "ID"\n", + ep->cdeg - Work->cdeg0, ep->rdeg - Work->rdeg0, ep->next)) ; + + DEBUG8 (("rows: ")) ; + k = 0 ; + for (i = 0 ; i < ep->nrows ; i++) + { + row = Rows [i] ; + if (row >= 0) + { + DEBUG8 ((" "ID, row)) ; + ASSERT (row < Work->n_row) ; + if ((k++ % 10) == 9) DEBUG8 (("\n")) ; + ASSERT (IMPLIES (clean, NON_PIVOTAL_ROW (row))) ; + } + } + + DEBUG8 (("\ncols: ")) ; + k = 0 ; + for (j = 0 ; j < ep->ncols ; j++) + { + col = Cols [j] ; + if (col >= 0) + { + DEBUG8 ((" "ID, col)) ; + ASSERT (col < Work->n_col) ; + if ((k++ % 10) == 9) DEBUG8 (("\n")) ; + ASSERT (IMPLIES (clean, NON_PIVOTAL_COL (col))) ; + } + } + + DEBUG8 (("\nvalues:\n")) ; + if (UMF_debug >= 9) + { + for (i = 0 ; i < ep->nrows ; i++) + { + row = Rows [i] ; + if (row >= 0) + { + DEBUG9 ((ID": ", row)) ; + k = 0 ; + for (j = 0 ; j < ep->ncols ; j++) + { + col = Cols [j] ; + if (col >= 0) + { + EDEBUG9 (C [i+j*ep->nrows]) ; + if (k++ % 6 == 5) DEBUG9 (("\n ")) ; + } + } + DEBUG9 (("\n")) ; + } + } + } + + DEBUG7 (("====================\n")) ; +} + + +/* ========================================================================== */ +/* === UMF_dump_rowcol ====================================================== */ +/* ========================================================================== */ + +/* dump a row or a column, from one or more memory spaces */ +/* return exact degree */ + +GLOBAL void UMF_dump_rowcol +( + Int dumpwhich, /* 0 for row, 1 for column */ + NumericType *Numeric, + WorkType *Work, + Int dumpindex, /* row or column index to dump */ + Int check_degree /* true if degree is to be checked */ +) +{ + Int f, nrows, j, jj, len, e, deg, index, n_row, n_col, *Cols, *Rows, nn, + dumpdeg, ncols, preve, *E, tpi, *Pattern, approx_deg, not_in_use ; + Tuple *tp, *tend ; + Element *ep ; + Int *Row_tuples, *Row_degree, *Row_tlen ; + Int *Col_tuples, *Col_degree, *Col_tlen ; + Entry value, *C ; + Unit *p ; + Int is_there ; + + /* clear the debugging arrays */ + UMF_DBinit () ; + + if (dumpwhich == 0) + { + DEBUG7 (("\n====================ROW: "ID, dumpindex)) ; + } + else + { + DEBUG7 (("\n====================COL: "ID, dumpindex)) ; + } + + if (dumpindex == EMPTY) + { + DEBUG7 ((" (EMPTY)\n")) ; + return ; + } + + deg = 0 ; + approx_deg = 0 ; + + if (!Numeric || !Work) + { + DEBUG7 ((" No Numeric, Work\n")) ; + return ; + } + + n_row = Work->n_row ; + n_col = Work->n_col ; + nn = MAX (n_row, n_col) ; + E = Work->E ; + + Col_degree = Numeric->Cperm ; + Row_degree = Numeric->Rperm ; + + Row_tuples = Numeric->Uip ; + Row_tlen = Numeric->Uilen ; + Col_tuples = Numeric->Lip ; + Col_tlen = Numeric->Lilen ; + + if (!E + || !Row_tuples || !Row_degree || !Row_tlen + || !Col_tuples || !Col_degree || !Col_tlen) + { + DEBUG7 ((" No E, Rows, Cols\n")) ; + return ; + } + + if (dumpwhich == 0) + { + /* dump a row */ + ASSERT (dumpindex >= 0 && dumpindex < n_row) ; + if (!NON_PIVOTAL_ROW (dumpindex)) + { + DEBUG7 ((" Pivotal\n")) ; + return ; + } + len = Row_tlen [dumpindex] ; + dumpdeg = Row_degree [dumpindex] ; + tpi = Row_tuples [dumpindex] ; + } + else + { + /* dump a column */ + ASSERT (dumpindex >= 0 && dumpindex < n_col) ; + if (!NON_PIVOTAL_COL (dumpindex)) + { + DEBUG7 ((" Pivotal\n")) ; + return ; + } + len = Col_tlen [dumpindex] ; + dumpdeg = Col_degree [dumpindex] ; + tpi = Col_tuples [dumpindex] ; + } + + p = Numeric->Memory + tpi ; + tp = (Tuple *) p ; + if (!tpi) + { + DEBUG7 ((" Nonpivotal, No tuple list tuples "ID" tlen "ID"\n", + tpi, len)) ; + return ; + } + ASSERT (p >= Numeric->Memory + Numeric->itail) ; + ASSERT (p < Numeric->Memory + Numeric->size) ; + + DEBUG7 ((" degree: "ID" len: "ID"\n", dumpdeg, len)) ; + not_in_use = (p-1)->header.size - UNITS (Tuple, len) ; + DEBUG7 ((" Tuple list: p+1: "ID" size: "ID" units, "ID" not in use\n", + p-Numeric->Memory, (p-1)->header.size, not_in_use)) ; + ASSERT (not_in_use >= 0) ; + tend = tp + len ; + preve = 0 ; + for ( ; tp < tend ; tp++) + { + /* row/col of element e, offset is f: */ + /* DEBUG8 ((" (tp="ID")\n", tp)) ; */ + e = tp->e ; + f = tp->f ; + DEBUG8 ((" (e="ID", f="ID")\n", e, f)) ; + ASSERT (e > 0 && e <= Work->nel) ; + /* dump the pattern and values */ + if (E [e]) + { + p = Numeric->Memory + E [e] ; + GET_ELEMENT (ep, p, Cols, Rows, ncols, nrows, C) ; + if (dumpwhich == 0) + { + Pattern = Cols ; + jj = ep->ncols ; + is_there = Rows [f] >= 0 ; + if (is_there) approx_deg += ep->ncolsleft ; + } + else + { + Pattern = Rows ; + jj = ep->nrows ; + is_there = Cols [f] >= 0 ; + if (is_there) approx_deg += ep->nrowsleft ; + } + if (!is_there) + { + DEBUG8 (("\t\tnot present\n")) ; + } + else + { + for (j = 0 ; j < jj ; j++) + { + index = Pattern [j] ; + value = + C [ (dumpwhich == 0) ? (f+nrows*j) : (j+nrows*f) ] ; + if (index >= 0) + { + DEBUG8 (("\t\t"ID":", index)) ; + EDEBUG8 (value) ; + DEBUG8 (("\n")) ; + if (dumpwhich == 0) + { + /* col must be in the range 0..n_col-1 */ + ASSERT (index < n_col) ; + } + else + { + /* row must be in the range 0..n_row-1 */ + ASSERT (index < n_row) ; + } + + if (nn <= UMF_DBMAX) + { + if (UMF_DBscatter [index] != UMF_DBflag) + { + UMF_DBpacked [deg++] = index ; + UMF_DBscatter [index] = UMF_DBflag ; + } + } + } + } + } + /* the (e,f) tuples should be in order of their creation */ + /* this means that garbage collection will not jumble them */ + ASSERT (preve < e) ; + preve = e ; + } + else + { + DEBUG8 (("\t\tdeallocated\n")) ; + } + } + + if (nn <= UMF_DBMAX) + { + if (deg > 0) + { + DEBUG7 ((" Assembled, actual deg: "ID" : ", deg)) ; + for (j = 0 ; j < deg ; j++) + { + index = UMF_DBpacked [j] ; + DEBUG8 ((ID" ", index)) ; + if (j % 20 == 19) DEBUG8 (("\n ")) ; + ASSERT (UMF_DBscatter [index] == UMF_DBflag) ; + } + DEBUG7 (("\n")) ; + } + } + + if (check_degree) + { + DEBUG8 ((" approx_deg "ID" dumpdeg "ID"\n", approx_deg, dumpdeg)) ; + ASSERT (approx_deg == dumpdeg) ; + } + + DEBUG7 (("====================\n")) ; + + /* deg is now the exact degree */ + /* if nn <= UMF_DBMAX, then UMF_DBscatter [i] == UMF_DBflag for every i */ + /* in the row or col, and != UMF_DBflag if not */ + + return ; +} + + +/* ========================================================================== */ +/* === UMF_dump_matrix ====================================================== */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_matrix +( + NumericType *Numeric, + WorkType *Work, + Int check_degree +) +{ + + Int e, row, col, intfrag, frag, n_row, n_col, *E, fullsize, actualsize ; + Element *ep ; + Unit *p ; + + DEBUG6 (("=================================================== MATRIX:\n")) ; + if (!Numeric || !Work) + { + DEBUG6 (("No Numeric or Work allocated\n")) ; + return ; + } + if (!Numeric->Memory) + { + DEBUG6 (("No Numeric->Memory\n")) ; + return ; + } + + n_row = Work->n_row ; + n_col = Work->n_col ; + DEBUG6 (("n_row "ID" n_col "ID" nz "ID"\n", n_row, n_col, Work->nz)) ; + DEBUG6 (("============================ ELEMENTS: "ID" \n", Work->nel)) ; + intfrag = 0 ; + E = Work->E ; + if (!E) + { + DEBUG6 (("No elements allocated\n")) ; + } + else + { + for (e = 0 ; e <= Work->nel ; e++) + { + UMF_dump_element (Numeric, Work, e, FALSE) ; + if (e > 0 && E [e]) + { + p = Numeric->Memory + E [e] ; + ep = (Element *) p ; + ASSERT (ep->nrowsleft > 0 || ep->ncolsleft > 0) ; + fullsize = GET_BLOCK_SIZE (p) ; + actualsize = GET_ELEMENT_SIZE (ep->nrowsleft,ep->ncolsleft); + frag = fullsize - actualsize ; + intfrag += frag ; + DEBUG7 (("dump el: "ID", full "ID" actual "ID" frag: "ID + " intfrag: "ID"\n", e, fullsize, actualsize, frag, + intfrag)) ; + } + } + } + + DEBUG6 (("CURRENT INTERNAL FRAG in elements: "ID" \n", intfrag)) ; + + + + DEBUG6 (("======================================== ROWS: "ID"\n", n_row)) ; + UMF_debug -= 2 ; + for (row = 0 ; row < n_row ; row++) + { + UMF_dump_rowcol (0, Numeric, Work, row, check_degree) ; + } + UMF_debug += 2 ; + DEBUG6 (("======================================== COLS: "ID"\n", n_col)) ; + UMF_debug -= 2 ; + for (col = 0 ; col < n_col ; col++) + { + UMF_dump_rowcol (1, Numeric, Work, col, check_degree) ; + } + UMF_debug += 2 ; + DEBUG6 (("============================================= END OF MATRIX:\n")); +} + + +/* ========================================================================== */ +/* === UMF_dump_current_front =============================================== */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_current_front +( + NumericType *Numeric, + WorkType *Work, + Int check +) +{ + Entry *Fx ; + Int fnrows_max, fncols_max, fnrows, fncols, fnpiv, *Frows, *Fcols, + i, j, *Fcpos, *Frpos ; + if (!Work) return ; + DEBUG7 (("\n\n========CURRENT FRONTAL MATRIX:\n")) ; + + Fx = Work->Fx ; + Frows = Work->Frows ; + Fcols = Work->Fcols ; + Frpos = Work->Frpos ; + Fcpos = Work->Fcpos ; + fnrows_max = Work->fnrows_max ; + fncols_max = Work->fncols_max ; + fnrows = Work->fnrows ; + fncols = Work->fncols ; + fnpiv = Work->fnpiv ; + + DEBUG6 (("=== fnpiv= "ID"\n", fnpiv)) ; + DEBUG6 (("fncols_max fnrows_max "ID" "ID"\n", fncols_max,fnrows_max)) ; + DEBUG6 (("fncols fnrows "ID" "ID"\n", fncols, fnrows)) ; + DEBUG6 (("Pivot row pattern:\n")) ; + for (j = 0 ; j < fncols ; j++) + { + DEBUG7 ((ID" "ID" "ID" "ID"\n", j, Fcols [j], Fcpos [Fcols [j]], + j < fncols)) ; + if (check) + { + ASSERT (Fcols [j] >= 0 && Fcols [j] < Work->n_col) ; + } + ASSERT (Fcpos [Fcols [j]] == j * fnrows_max) ; + } + DEBUG6 (("Pivot col pattern:\n")) ; + for (i = 0 ; i < fnrows ; i++) + { + DEBUG7 ((ID" "ID" "ID" "ID"\n", i, Frows [i], Frpos [Frows [i]], + i < fnrows)) ; + if (check) + { + ASSERT (Frows [i] >= 0 && Frows [i] < Work->n_row) ; + } + ASSERT (Frpos [Frows [i]] == i) ; + } + if (UMF_debug < 7) return ; + + DEBUG7 (("C block: ")) ; + UMF_dump_dense (Fx, fnrows_max, fnrows, fncols) ; + DEBUG7 (("L block: ")) ; + UMF_dump_dense (Fx+(fncols_max-fnpiv)*fnrows_max, fnrows_max, fnrows,fnpiv); + DEBUG7 (("U block: ")) ; + UMF_dump_dense (Fx+(fnrows_max-fnpiv), fnrows_max, fnpiv, fncols) ; + if (fnpiv > 0) + { + DEBUG7 (("Pivot entry: ")) ; + EDEBUG7 (Fx [(fnrows_max-fnpiv)+(fncols_max-fnpiv)*fnrows_max]) ; + DEBUG7 (("\n")) ; + } +} + +/* ========================================================================== */ +/* === UMF_dump_lu ========================================================== */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_lu +( + NumericType *Numeric +) +{ + Int i, n_row, n_col, *Cperm, *Rperm ; + + DEBUG6 (("=============================================== LU factors:\n")) ; + if (!Numeric) + { + DEBUG6 (("No LU factors allocated\n")) ; + return ; + } + n_row = Numeric->n_row ; + n_col = Numeric->n_col ; + DEBUG6 (("n_row: "ID" n_col: "ID"\n", n_row, n_col)) ; + DEBUG6 (("nLentries: "ID" nUentries: "ID"\n", + Numeric->nLentries, Numeric->nUentries)) ; + + if (Numeric->Cperm) + { + Cperm = Numeric->Cperm ; + DEBUG7 (("Column permutations: (new: old)\n")) ; + for (i = 0 ; i < n_col ; i++) + { + if (Cperm [i] != EMPTY) + { + DEBUG7 ((ID": "ID"\n", i, Cperm [i])) ; + } + } + } + else + { + DEBUG7 (("No Numeric->Cperm allocatated\n")) ; + } + + if (Numeric->Rperm) + { + Rperm = Numeric->Rperm ; + DEBUG7 (("row permutations: (new: old)\n")) ; + for (i = 0 ; i < n_row ; i++) + { + if (Rperm [i] != EMPTY) + { + DEBUG7 ((ID": "ID"\n", i, Rperm [i])) ; + } + } + } + else + { + DEBUG7 (("No Numeric->Rperm allocatated\n")) ; + } + + DEBUG6 (("========================================= END OF LU factors:\n")); +} + + +/* ========================================================================== */ +/* === UMF_dump_memory ====================================================== */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_memory +( + NumericType *Numeric +) +{ + + Unit *p ; + Int prevsize, s ; + Int found ; + + if (!Numeric) + { + DEBUG6 (("No memory space S allocated\n")) ; + return ; + } + + DEBUG6 (("\n ============================================== MEMORY:\n")) ; + if (!Numeric || !Numeric->Memory) + { + DEBUG6 (("No memory space Numeric allocated\n")) ; + return ; + } + + DEBUG6 (("S: "ID"\n", (Int) Numeric)) ; + DEBUG6 (("S->ihead : "ID"\n", Numeric->ihead)) ; + DEBUG6 (("S->itail : "ID"\n", Numeric->itail)) ; + DEBUG6 (("S->size : "ID"\n", Numeric->size)) ; + DEBUG6 (("S->ngarbage : "ID"\n", Numeric->ngarbage)) ; + DEBUG6 (("S->nrealloc : "ID"\n", Numeric->nrealloc)) ; + DEBUG6 ((" in use at head : "ID"\n", Numeric->ihead)) ; + DEBUG6 ((" free space : "ID"\n", + Numeric->itail - Numeric->ihead)) ; + DEBUG6 ((" blocks in use at tail : "ID"\n", + Numeric->size - Numeric->itail)) ; + DEBUG6 ((" total in use : "ID"\n", + Numeric->size - (Numeric->itail - Numeric->ihead))) ; + + prevsize = 0 ; + found = FALSE ; + + ASSERT (0 <= Numeric->ihead) ; + ASSERT (Numeric->ihead <= Numeric->itail) ; + ASSERT (Numeric->itail <= Numeric->size) ; + + p = Numeric->Memory + Numeric->itail ; + + while (p < Numeric->Memory + Numeric->size) + { + DEBUG8 (("p: "ID" p+1: "ID" prevsize: "ID" size: "ID, + p-Numeric->Memory, p+1-Numeric->Memory, + p->header.prevsize, p->header.size)) ; + if (p->header.size < 0) + { + DEBUG8 ((" free")) ; + } + + if (p == Numeric->Memory + Numeric->itail) + { + ASSERT (p->header.prevsize == 0) ; + } + else + { + ASSERT (p->header.prevsize > 0) ; + } + + ASSERT (p->header.size != 0) ; + s = prevsize >= 0 ? prevsize : -prevsize ; + ASSERT (p->header.prevsize == s) ; + /* no adjacent free blocks */ + ASSERT (p->header.size > 0 || prevsize > 0) ; + if (Numeric->ibig != EMPTY) + { + if (p == Numeric->Memory + Numeric->ibig) + { + ASSERT (p->header.size < 0) ; + DEBUG8 ((" <===== Numeric->ibig")) ; + found = TRUE ; + } + } + s = p->header.size ; + prevsize = s ; + s = s >= 0 ? s : -s ; + p = p + 1 + s ; + DEBUG8 (("\n")) ; + } + + ASSERT (p == Numeric->Memory + Numeric->size) ; + ASSERT (IMPLIES (Numeric->ibig != EMPTY, found)) ; + DEBUG6 (("============================================= END OF MEMORY:\n")); + +} + + +/* ========================================================================== */ +/* === UMF_dump_packed_memory =============================================== */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_packed_memory +( + NumericType *Numeric, + WorkType *Work +) +{ + Unit *p, *p3 ; + Int prevsize, col, row, *Rows, *Cols, ncols, nrows, k, esize, + *Row_tuples, *Row_degree, *Col_tuples, *Col_degree ; + Entry *C ; + Element *ep ; + + Col_degree = Numeric->Cperm ; + Row_degree = Numeric->Rperm ; + Row_tuples = Numeric->Uip ; + Col_tuples = Numeric->Lip ; + + DEBUG6 (("============================================ PACKED MEMORY:\n")) ; + if (!Numeric || !Numeric->Memory) + { + DEBUG6 (("No memory space S allocated\n")) ; + return ; + } + DEBUG6 (("S: "ID"\n", (Int) Numeric)) ; + DEBUG6 (("S->ihead : "ID"\n", Numeric->ihead)) ; + DEBUG6 (("S->itail : "ID"\n", Numeric->itail)) ; + DEBUG6 (("S->size : "ID"\n", Numeric->size)) ; + DEBUG6 (("S->ngarbage : "ID"\n", Numeric->ngarbage)) ; + DEBUG6 (("S->nrealloc : "ID"\n", Numeric->nrealloc)) ; + DEBUG6 ((" in use at head : "ID"\n", Numeric->ihead)) ; + DEBUG6 ((" free space : "ID"\n", + Numeric->itail - Numeric->ihead)) ; + DEBUG6 ((" blocks in use at tail : "ID"\n", + Numeric->size - Numeric->itail)) ; + DEBUG6 ((" total in use : "ID"\n", + Numeric->size - (Numeric->itail - Numeric->ihead))) ; + + ASSERT (0 <= Numeric->ihead) ; + ASSERT (Numeric->ihead <= Numeric->itail) ; + ASSERT (Numeric->itail <= Numeric->size) ; + + for (row = 0 ; row < Work->n_row ; row++) + { + ASSERT (IMPLIES (NON_PIVOTAL_ROW (row), !Row_tuples [row])) ; + } + for (col = 0 ; col < Work->n_col ; col++) + { + ASSERT (IMPLIES (NON_PIVOTAL_COL (col), !Col_tuples [col])) ; + } + + prevsize = 0 ; + p = Numeric->Memory + Numeric->itail ; + while (p < Numeric->Memory + Numeric->size) + { + DEBUG9 (("====================\n")) ; + DEBUG7 (("p: "ID" p+1: "ID" prevsize: "ID" size: "ID"\n", + p-Numeric->Memory, p+1-Numeric->Memory, + p->header.prevsize, p->header.size)) ; + ASSERT (p->header.size > 0) ; + + if (p == Numeric->Memory + Numeric->itail) + { + ASSERT (p->header.prevsize == 0) ; + } + else + { + ASSERT (p->header.prevsize > 0) ; + } + + ASSERT (p->header.prevsize == prevsize) ; + prevsize = p->header.size ; + + if (p != Numeric->Memory + Numeric->size - 2) + { + + p3 = p + 1 ; + + /* this is a packed element */ + GET_ELEMENT (ep, p3, Cols, Rows, ncols, nrows, C) ; + DEBUG9 (("ep "ID"\n nrows "ID" ncols "ID"\n", + (p+1)-Numeric->Memory, ep->nrows, ep->ncols)) ; + DEBUG9 (("rows:")) ; + for (k = 0 ; k < ep->nrows; k++) + { + row = Rows [k] ; + DEBUG9 ((" "ID, row)) ; + ASSERT (row >= 0 && row <= Work->n_row) ; + if ((k % 10) == 9) DEBUG9 (("\n")) ; + } + DEBUG9 (("\ncols:")) ; + for (k = 0 ; k < ep->ncols; k++) + { + col = Cols [k] ; + DEBUG9 ((" "ID, col)) ; + ASSERT (col >= 0 && col <= Work->n_col) ; + if ((k % 10) == 9) DEBUG9 (("\n")) ; + } + DEBUG9 (("\nvalues: ")) ; + if (UMF_debug >= 9) + { + UMF_dump_dense (C, ep->nrows, ep->nrows, ep->ncols) ; + } + esize = GET_ELEMENT_SIZE (ep->nrows, ep->ncols) ; + DEBUG9 (("esize: "ID"\n", esize)) ; + ASSERT (esize <= p->header.size) ; + + } + else + { + /* this is the final marker block */ + ASSERT (p->header.size == 1) ; + } + p = p + 1 + p->header.size ; + } + + ASSERT (Numeric->ibig == EMPTY) ; + ASSERT (p == Numeric->Memory + Numeric->size) ; + DEBUG6 (("======================================END OF PACKED MEMORY:\n")) ; + +} + +/* ========================================================================== */ +/* === UMF_dump_col_matrix ================================================== */ +/* ========================================================================== */ + +/* This code is the same for real or complex matrices. */ + +GLOBAL void UMF_dump_col_matrix +( + const double Ax [ ], /* Ax [0..nz-1]: real values, in column order */ +#ifdef COMPLEX + const double Az [ ], /* Az [0..nz-1]: imag values, in column order */ +#endif + const Int Ai [ ], /* Ai [0..nz-1]: row indices, in column order */ + const Int Ap [ ], /* Ap [0..n_col]: column pointers */ + Int n_row, /* number of rows of A */ + Int n_col, /* number of columns of A */ + Int nz /* number of entries */ +) +{ + Int col, p, p1, p2, row ; + if (!Ai || !Ap) return ; + DEBUG6 (("============================================ COLUMN FORM:\n")) ; + + + ASSERT (n_col >= 0) ; + nz = Ap [n_col] ; + DEBUG2 (("UMF_dump_col: nz "ID"\n", nz)) ; + DEBUG2 (("n_row "ID" & "ID"\n", n_row, &n_row)) ; + DEBUG2 (("n_col "ID" & "ID"\n", n_col, &n_col)) ; + DEBUG2 (("Ap & "ID" to & "ID"\n", Ap, Ap + (n_col+1) - 1)) ; + DEBUG2 (("Ai & "ID" to & "ID"\n", Ai, Ai + (nz) - 1)) ; + if (Ax) DEBUG2 (("Ax & "ID" to & "ID"\n", Ax, Ax + (nz) - 1)) ; + +#ifdef COMPLEX + if (Az) DEBUG2 (("Az & "ID" to & "ID"\n", Az, Az + (nz) - 1)) ; +#endif + + DEBUG6 ((" n_row = "ID", n_col ="ID" nz = "ID" Ap [0] "ID", Ap [n] "ID"\n", + n_row, n_col, nz, Ap [0], Ap [n_col])) ; + ASSERT (Ap [0] == 0) ; + ASSERT (Ap [n_col] == nz) ; + for (col = 0 ; col < n_col ; col++) + { + p1 = Ap [col] ; + p2 = Ap [col+1] ; + DEBUG6 (("col: "ID", length "ID"\n", col, p2 - p1)) ; + ASSERT (p2 >= p1) ; + for (p = p1 ; p < p2 ; p++) + { + row = Ai [p] ; + ASSERT (row >= 0 && row < n_row) ; + DEBUG6 (("\t"ID" ", row)) ; + if (Ax) + { +#ifdef COMPLEX + if (Az) + { + DEBUG6 ((" (%e+%ei) ", Ax [p], Az [p])) ; + } + else + { + DEBUG6 ((" %e", Ax [p])) ; + } +#else + DEBUG6 ((" %e", Ax [p])) ; +#endif + } + DEBUG6 (("\n")) ; + } + } + DEBUG6 (("========================================== COLUMN FORM done\n")) ; +} + + +/* ========================================================================== */ +/* === UMF_dump_chain ======================================================= */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_chain +( + Int frontid, + Int Front_parent [ ], + Int Front_npivcol [ ], + Int Front_nrows [ ], + Int Front_ncols [ ], + Int nfr +) +{ + Int i, len = 0 ; + + /* print a list of contiguous parents */ + i = frontid ; + ASSERT (Front_parent [i] == EMPTY || + (Front_parent [i] > i && Front_parent [i] < nfr)) ; + + len++ ; + DEBUG3 (("Chain:\n "ID" ["ID","ID"]("ID"-by-"ID")\n", i, + Front_npivcol [i], + MIN (Front_npivcol [i], Front_nrows [i]), + Front_nrows [i], + Front_ncols [i])) ; + + for (i = frontid ; i < nfr ; i++) + { + ASSERT (Front_parent [i] == EMPTY || + (Front_parent [i] > i && Front_parent [i] < nfr)) ; + if (Front_parent [i] == i+1) + { + len++ ; + DEBUG3 (("\t"ID" ["ID","ID"]("ID"-by-"ID")\n", i+1, + Front_npivcol [i+1], + MIN (Front_npivcol [i+1], Front_nrows [i+1]), + Front_nrows [i+1], + Front_ncols [i+1])) ; + } + else + { + DEBUG2 (("Length of chain: "ID"\n", len)) ; + return ; + } + } +} + + +/* ========================================================================== */ +/* === UMF_dump_start ======================================================= */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_start +( + void +) +{ + FILE *ff ; + + /* get the debug print level from the "debug.umf" file, if it exists */ + UMF_debug = 0 ; + ff = fopen ("debug.umf", "r") ; + if (ff) + { + (void) fscanf (ff, ID, &UMF_debug) ; + (void) fclose (ff) ; + } + + DEBUG0 (("umfpack: debug version (SLOW!) ")) ; + + DEBUG0 ((" BLAS: ")) ; + +#if defined (USE_NO_BLAS) + DEBUG0 (("none.")) ; +#elif defined (USE_C_BLAS) + DEBUG0 (("C-BLAS.")) ; +#elif defined (USE_MATLAB_BLAS) + DEBUG0 (("built-in MATLAB BLAS.")) ; +#elif defined (USE_SUNPERF_BLAS) + DEBUG0 (("Sun Performance Library BLAS.")) ; +#elif defined (USE_SCSL_BLAS) + DEBUG0 (("SGI SCSL BLAS.")) ; +#elif defined (USE_FORTRAN_BLAS) + DEBUG0 (("Fortran BLAS.")) ; +#endif + + DEBUG0 ((" MATLAB: ")) ; +#ifdef MATLAB_MEX_FILE + DEBUG0 (("mexFunction.\n")) ; +#else +#ifdef MATHWORKS + DEBUG0 (("yes (uses MathWorks internal ut* routines).\n")) ; +#else + DEBUG0 (("no.\n")) ; +#endif +#endif + + UMF_gprob = -1.0 ; + ff = fopen ("gprob.umf", "r") ; + if (ff) + { + (void) fscanf (ff, "%lg", &UMF_gprob) ; + (void) fclose (ff) ; + } + + if (UMF_gprob > 1.0) UMF_gprob = 1.0 ; + DEBUG1 (("factor: UMF_gprob: %e UMF_debug "ID"\n", UMF_gprob, UMF_debug)) ; + + DEBUG2 (("sizeof: (bytes / int / Units) \n")) ; + DEBUG2 (("sizeof (Int) %u %u %u\n", + sizeof (Int), sizeof (Int) / sizeof (int), UNITS (Int, 1) )) ; + DEBUG2 (("sizeof (int) %u %u %u\n", + sizeof (int), sizeof (int) / sizeof (int), UNITS (int, 1) )) ; + DEBUG2 (("sizeof (size_t) %u %u %u\n", + sizeof (size_t), sizeof (size_t) / sizeof (size_t), UNITS (size_t, 1) )) ; + DEBUG2 (("sizeof (long) %u %u %u\n", + sizeof (long), sizeof (long) / sizeof (long), UNITS (long, 1) )) ; + DEBUG2 (("sizeof (double) %u %u %u\n", + sizeof (double), sizeof (double) / sizeof (int), UNITS (double, 1) )) ; + DEBUG2 (("sizeof (Unit) %u %u %u\n", + sizeof (Unit), sizeof (Unit) / sizeof (int), UNITS (Unit, 1) )) ; + DEBUG2 (("sizeof (Entry) %u %u %u\n", + sizeof (Entry), sizeof (Entry) / sizeof (int), UNITS (Entry, 1) )) ; + DEBUG2 (("sizeof (Tuple) %u %u %u\n", + sizeof (Tuple), sizeof (Tuple) / sizeof (int), UNITS (Tuple, 1) )) ; + DEBUG2 (("sizeof (Tuple *) %u %u %u\n", + sizeof (Tuple *), sizeof (Tuple *) / sizeof (int), UNITS (Tuple *, 1) )) ; + DEBUG2 (("sizeof (Element) %u %u %u\n", + sizeof (Element), sizeof (Element) / sizeof (int), UNITS (Element, 1) )) ; + DEBUG2 (("sizeof (Element *) %u %u %u\n", + sizeof (Element *), sizeof (Element *) / sizeof (int), + UNITS (Element *, 1) )) ; + DEBUG2 (("sizeof (WorkType) %u %u %u\n", + sizeof (WorkType), sizeof (WorkType) / sizeof (int), + UNITS (WorkType, 1) )) ; + DEBUG2 (("sizeof (NumericType) %u %u %u\n", + sizeof (NumericType), sizeof (NumericType) / sizeof (int), + UNITS (NumericType, 1) )) ; + DEBUG2 (("sizeof (SymbolicType) %u %u %u\n", + sizeof (SymbolicType), sizeof (SymbolicType) / sizeof (int), + UNITS (SymbolicType, 1) )) ; + +} + + +/* ========================================================================== */ +/* === UMF_dump_rowmerge ==================================================== */ +/* ========================================================================== */ + +GLOBAL void UMF_dump_rowmerge +( + NumericType *Numeric, + SymbolicType *Symbolic, + WorkType *Work +) +{ + Int *Front_leftmostdesc, *Front_1strow, *Front_new1strow, row1, row2, + fleftmost, nfr, n_row, *Row_degree, i, frontid, row ; + + nfr = Symbolic->nfr ; + DEBUG3 (("\n================== Row merge sets: nfr "ID"\n", nfr)) ; + Front_leftmostdesc = Symbolic->Front_leftmostdesc ; + Front_1strow = Symbolic->Front_1strow ; + Front_new1strow = Work->Front_new1strow ; + n_row = Symbolic->n_row ; + Row_degree = Numeric->Rperm ; + frontid = Work->frontid ; + + for (i = frontid ; i <= nfr ; i++) + { + DEBUG3 (("----------------------\n")) ; + if (i == nfr) DEBUG3 (("Dummy: ")) ; + DEBUG3 (("Front "ID" 1strow "ID" new1strow "ID" leftmostdesc "ID, + i, Front_1strow [i], Front_new1strow [i], Front_leftmostdesc [i])) ; + DEBUG3 ((" parent "ID" pivcol\n", Symbolic->Front_parent [i], + Symbolic->Front_npivcol [i])) ; + + if (i == nfr) + { + fleftmost = -1 ; + row1 = Front_new1strow [i] ; + row2 = n_row-1 ; + } + else + { + fleftmost = Front_leftmostdesc [i] ; + row1 = Front_new1strow [fleftmost] ; + row2 = Front_1strow [i+1] - 1 ; + } + DEBUG3 (("Leftmost: "ID" Rows ["ID" to "ID"], search ["ID" to "ID"]\n", + fleftmost, Front_1strow [i], row2, row1, row2)) ; + + for (row = row1 ; row <= row2 ; row++) + { + ASSERT (row >= 0 && row < n_row) ; + DEBUG3 ((" Row "ID" live: "ID"\n", row, NON_PIVOTAL_ROW (row))) ; + } + } +} + +#endif /* NDEBUG */ diff --git a/src/sparse-matrix/umfpack/umf_extend_front.c b/src/sparse-matrix/umfpack/umf_extend_front.c new file mode 100644 index 0000000..99335f0 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_extend_front.c @@ -0,0 +1,673 @@ +/* ========================================================================== */ +/* === UMF_extend_front ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Called by kernel. */ + +#include "umf_internal.h" + +GLOBAL void UMF_extend_front +( + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Entry *Fx, *Flast, *Fd, *Fs, *Fcol, *Frow, temp, *Flrow, *Fu ; + Int j, i, fnpiv, *Frows, row, col, i2, *Wrow, *Wcol, + *Frpos, *Fcpos, *Fcols, pivcol, pivrow, fnrows_extended, rrdeg, ccdeg, + fncols_extended, fnrows_max, fncols_max, fnrows, fncols, + fspos, fdpos, fs, j2, j3, col2, row2, fncols_orig ; +#ifndef NDEBUG + Int debug_ok ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + Frows = Work->Frows ; + Frpos = Work->Frpos ; + Fcols = Work->Fcols ; + Fcpos = Work->Fcpos ; + Fx = Work->Fx ; + pivcol = Work->pivcol ; + pivrow = Work->pivrow ; + fnrows_max = Work->fnrows_max ; + fncols_max = Work->fncols_max ; + fnrows = Work->fnrows ; + fncols = Work->fncols ; + fncols_orig = fncols ; + rrdeg = Work->rrdeg ; + ccdeg = Work->ccdeg ; + +#ifndef NDEBUG + DEBUG2 (("EXTEND FRONT\n")) ; + if (fncols == 0 || fnrows == 0) + { + DEBUG2 (("Extending empty front "ID" "ID"\n", fnrows,fncols)) ; + } + DEBUG6 (("Pivot row, before shift and extension: "ID"\n", fncols)) ; + for (j = 0 ; j < fncols ; j++) + { + DEBUG7 ((" "ID" "ID" "ID" "ID"\n", + j, Fcols [j], Fcpos [Fcols [j]], j < fncols)) ; + ASSERT (Fcpos [Fcols [j]] == j * fnrows_max) ; + } + DEBUG6 (("Pivot col, before shift and extension: "ID"\n", fnrows)) ; + for (i = 0 ; i < fnrows ; i++) + { + DEBUG7 ((" "ID" "ID" "ID" "ID"\n", + i, Frows [i], Frpos [Frows [i]], i < fnrows)) ; + ASSERT (Frpos [Frows [i]] == i) ; + } + if (Work->pivcol_in_front) + { + DEBUG6 (("Extended part of pivot col, before shift/extension: "ID"\n", + fnrows)) ; + for (i = fnrows ; i < fnrows + ccdeg ; i++) + { + DEBUG7 ((" "ID" "ID" "ID" "ID"\n", + i, Frows [i], Frpos [Frows [i]], i < fnrows)) ; + ASSERT (Frpos [Frows [i]] == i) ; + } + } + DEBUG2 (("Work->fnpiv "ID"\n", Work->fnpiv)) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* pivot column already updated */ + /* ---------------------------------------------------------------------- */ + + fnpiv = Work->fnpiv ; + + /* ====================================================================== */ + /* === Shift pivot row and column ======================================= */ + /* ====================================================================== */ + + /* ---------------------------------------------------------------------- */ + /* move the pivot column into place */ + /* ---------------------------------------------------------------------- */ + + fdpos = (fncols_max - fnpiv - 1) * fnrows_max ; + Fd = Fx + fdpos ; /* Fd: destination of pivot column */ + +#ifndef NDEBUG + /* + DEBUG7 (("Complete frontal matrix prior to pivcol swap (incl unused):\n")) ; + UMF_dump_dense (Fx, fnrows_max, fnrows_max, fncols_max) ; + */ +#endif + + if (Work->pivcol_in_front) + { + + fspos = Fcpos [pivcol] ; + fs = fspos / fnrows_max ; + + /* Fs: current position of pivot column */ + Fs = Fx + fspos ; + + /* Flast: position of last column in front */ + Flast = Fx + (fncols - 1) * fnrows_max ; + + /* ------------------------------------------------------------------ */ + /* pivot column is in current front - shift into place */ + /* ------------------------------------------------------------------ */ + + DEBUG6 (("Swap/shift pivot column in front\n")) ; + DEBUG6 (("fspos: "ID" flpos: "ID" fdpos: "ID"\n", + fspos, (fncols - 1) * fnrows_max, fdpos)) ; + + if (Flast != Fd) + { + if (Fs == Flast) + { + /* ---------------------------------------------------------- */ + /* move Fs => Fd */ + /* ---------------------------------------------------------- */ + + DEBUG6 (("col case 1\n")) ; + + /* column of the contribution block: */ + for (i = 0 ; i < fnrows ; i++) + { + Fd [i] = Fs [i] ; + } + +#ifndef NDEBUG + /* column of the U2 block */ + for (i = fnrows_max - fnpiv ; i < fnrows_max ; i++) + { + ASSERT (IS_ZERO (Fs [i])) ; + } +#endif + + } + else + { + /* ---------------------------------------------------------- */ + /* move Fs => Fd */ + /* move Flast => Fs */ + /* ---------------------------------------------------------- */ + + DEBUG6 (("col case 2\n")) ; + + /* column of the contribution block: */ + for (i = 0 ; i < fnrows ; i++) + { + Fd [i] = Fs [i] ; + Fs [i] = Flast [i] ; + } + /* column of the U2 block */ + for (i = fnrows_max - fnpiv ; i < fnrows_max ; i++) + { + ASSERT (IS_ZERO (Fs [i])) ; + Fs [i] = Flast [i] ; + } + } + } + else if (Fs != Fd) + { + /* -------------------------------------------------------------- */ + /* swap Fs <=> Fd */ + /* -------------------------------------------------------------- */ + + DEBUG6 (("col case 3\n")) ; + + /* column of the contribution block: */ + for (i = 0 ; i < fnrows ; i++) + { + temp = Fd [i] ; + Fd [i] = Fs [i] ; + Fs [i] = temp ; + } + /* column of the U2 block */ + for (i = fnrows_max - fnpiv ; i < fnrows_max ; i++) + { + ASSERT (IS_ZERO (Fs [i])) ; + Fs [i] = Fd [i] ; + } + } + + /* move column Flast to Fs in the Fcols pattern */ + col2 = Fcols [fncols - 1] ; + Fcols [fs] = col2 ; + Fcpos [col2] = fspos ; + + /* one less column in the contribution block */ + fncols = --(Work->fncols) ; + + } + else + { + /* ------------------------------------------------------------------ */ + /* pivot column is not in front - tack onto L block */ + /* ------------------------------------------------------------------ */ + + DEBUG6 (("Pivot column not in front\ncol case 5\n")) ; + + /* column of L */ + for (i = 0 ; i < fnrows ; i++) + { + CLEAR (Fd [i]) ; + } + /* column of U2 */ + for (i = fnrows_max - fnpiv ; i < fnrows_max ; i++) + { + CLEAR (Fd [i]) ; + } + + } + + /* move pivot column to Fd */ + Fcpos [pivcol] = fdpos ; + + /* scan starts at the first new column in Fcols */ + /* also scan the pivot column if it was not in the front */ + Work->fscan_col = fncols ; + +#ifndef NDEBUG + DEBUG6 (("Pivot row, after shift but before extension: "ID"\n", fncols)) ; + for (j = 0 ; j < fncols ; j++) + { + DEBUG7 ((" "ID" "ID" "ID" "ID"\n", + j, Fcols [j], Fcpos [Fcols [j]], j < fncols)) ; + ASSERT (Fcpos [Fcols [j]] == j * fnrows_max) ; + } + /* + DEBUG7 (("Complete frontal matrix after pivot col swap (incl unused):\n")) ; + UMF_dump_dense (Fx, fnrows_max, fnrows_max, fncols_max) ; + */ +#endif + + /* ---------------------------------------------------------------------- */ + /* move pivot row into place */ + /* ---------------------------------------------------------------------- */ + + fdpos = fnrows_max - fnpiv - 1 ; + Fd = Fx + fdpos ; /* Fd: destination of pivot row */ + + if (Work->pivrow_in_front) + { + + fspos = Frpos [pivrow] ; + + /* Fs: current position of pivot column in front */ + Fs = Fx + fspos ; + + /* Flast: position of last row in front */ + Flast = Fx + (fnrows - 1) ; + + /* ------------------------------------------------------------------ */ + /* pivot row is in current front - shift into place */ + /* ------------------------------------------------------------------ */ + + DEBUG6 (("Swap/shift pivot row in front:\n")) ; + DEBUG6 (("fspos: "ID" flpos: "ID" fdpos: "ID"\n", + fspos, fnrows-1, fdpos)) ; + + if (Flast != Fd) + { + if (Fs == Flast) + { + /* ---------------------------------------------------------- */ + /* move Fs => Fd */ + /* ---------------------------------------------------------- */ + + DEBUG6 (("row case 1\n")) ; + + /* row of the contribution block: */ + j2 = fncols * fnrows_max ; + for (j = 0 ; j < j2 ; j += fnrows_max) + { + Fd [j] = Fs [j] ; + } + + /* row of the L2 block: */ + j2 = fncols_max * fnrows_max ; + j = (fncols_max - fnpiv - 1) * fnrows_max ; + for ( ; j < j2 ; j += fnrows_max) + { + Fd [j] = Fs [j] ; + } + + } + else + { + + /* ---------------------------------------------------------- */ + /* move Fs => Fd */ + /* move Flast => Fs */ + /* ---------------------------------------------------------- */ + + DEBUG6 (("row case 2\n")) ; + + /* row of the contribution block: */ + j2 = fncols * fnrows_max ; + for (j = 0 ; j < j2 ; j += fnrows_max) + { + Fd [j] = Fs [j] ; + Fs [j] = Flast [j] ; + } + /* row of the L2 block: */ + j2 = fncols_max * fnrows_max ; + j = (fncols_max - fnpiv - 1) * fnrows_max ; + for ( ; j < j2 ; j += fnrows_max) + { + Fd [j] = Fs [j] ; + Fs [j] = Flast [j] ; + } + } + } + else if (Fs != Fd) + { + + /* -------------------------------------------------------------- */ + /* swap Fs <=> Fd */ + /* -------------------------------------------------------------- */ + + DEBUG6 (("row case 3\n")) ; + + /* row of the contribution block: */ + j2 = fncols * fnrows_max ; + for (j = 0 ; j < j2 ; j += fnrows_max) + { + temp = Fd [j] ; + Fd [j] = Fs [j] ; + Fs [j] = temp ; + } + /* row of the L2 block: */ + j2 = fncols_max * fnrows_max ; + j = (fncols_max - fnpiv - 1) * fnrows_max ; + for ( ; j < j2 ; j += fnrows_max) + { + temp = Fd [j] ; + Fd [j] = Fs [j] ; + Fs [j] = temp ; + } + } + + /* move row Flast to Fs in the Frows pattern */ + row2 = Frows [fnrows-1] ; + Frows [fspos] = row2 ; + Frpos [row2] = fspos ; + + + if (Work->pivcol_in_front && ccdeg > 0) + { + + /* move row Fe to Flast in the extended Frows pattern */ + row2 = Frows [fnrows + ccdeg - 1] ; + Frows [fnrows-1] = row2 ; + Frpos [row2] = fnrows-1 ; + } + + /* one less row in the contribution block */ + fnrows = --(Work->fnrows) ; + + /* ------------------------------------------------------------------ */ + /* update pivot row */ + /* ------------------------------------------------------------------ */ + + if (fnpiv > 0 && fncols > 0) + { + DEBUG6 (("Update pivot row (but not pivot entry itself):\n")) ; + Fu = Fd + 1 ; + Flrow = Fd + (fncols_max - fnpiv) * fnrows_max ; + +#ifdef USE_NO_BLAS + + /* no BLAS available - use plain C code instead */ + j2 = 0 ; + for (j = 0 ; j < fncols ; j++) + { + i2 = 0 ; + for (i = 0 ; i < fnpiv ; i++) + { + /* Fd [j2] -= Fu [i+j*fnrows_max] * Flrow [i2] ; */ + MULT_SUB (Fd [j2], Fu [i+j*fnrows_max], Flrow [i2]) ; + i2 += fnrows_max ; + } + j2 += fnrows_max ; + } + +#else + + BLAS_GEMV_ROW (fnpiv, fncols, Fu, Flrow, Fd, fnrows_max) ; + +#endif /* USE_NO_BLAS */ + + } + + } + else + { + /* ------------------------------------------------------------------ */ + /* pivot row is not in front - tack onto U block */ + /* ------------------------------------------------------------------ */ + + DEBUG6 (("Pivot row not in current front\nrow case 5\n")) ; + + /* row of U */ + j2 = fncols * fnrows_max ; + for (j = 0 ; j < j2 ; j += fnrows_max) + { + CLEAR (Fd [j]) ; + } + /* row of L2 */ + j2 = fncols_max * fnrows_max ; + j = (fncols_max - fnpiv - 1) * fnrows_max ; + for ( ; j < j2 ; j += fnrows_max) + { + CLEAR (Fd [j]) ; + } + + } + + /* move pivot row to Fd */ + Frpos [pivrow] = fdpos ; + + /* scan1 starts at the first new row in Frows */ + /* also scan the pivot row if it was not in the front */ + Work->fscan_row = fnrows ; + +#ifndef NDEBUG + debug_ok = TRUE ; + DEBUG6 (("Pivot col, before shift but before extension: "ID"\n", fnrows)) ; + for (i = 0 ; i < fnrows ; i++) + { + DEBUG7 ((" "ID" "ID" "ID" "ID"\n", + i, Frows [i], Frpos [Frows [i]], i < fnrows)) ; + debug_ok = debug_ok && (Frpos [Frows [i]] == i) ; + } + ASSERT (debug_ok) ; +#endif + + /* ====================================================================== */ + /* === EXTEND PATTERN OF FRONT ========================================== */ + /* ====================================================================== */ + + /* ---------------------------------------------------------------------- */ + /* extend row pattern of the front with the new pivot column extension */ + /* ---------------------------------------------------------------------- */ + + fnrows_extended = fnrows ; + fncols_extended = fncols ; + + if (Work->pivcol_in_front) + { + /* extended pattern and position already in Frows and Frpos */ + fnrows_extended += ccdeg ; + +#ifndef NDEBUG + debug_ok = TRUE ; + for (i = fnrows ; i < fnrows + ccdeg ; i++) + { + row = Frows [i] ; + DEBUG2 ((" row:: "ID" (ext)\n", row)) ; + debug_ok = debug_ok && (Frpos [row] == i) && (row != pivrow) ; + } + ASSERT (debug_ok) ; +#endif + + } + else + { + /* extended pattern is in Wcol, not yet in the front */ + Wcol = Work->Wcol ; + ASSERT (Wcol == Work->Wm) ; + for (i = 0 ; i < ccdeg ; i++) + { + row = Wcol [i] ; + DEBUG2 ((" row:: "ID" (ext)\n", row)) ; + ASSERT (Frpos [row] == EMPTY); + ASSERT (row != pivrow) ; + Frows [fnrows_extended] = row ; + Frpos [row] = fnrows_extended ; + fnrows_extended++ ; + } + } + + ASSERT (fnpiv + fnrows_extended <= fnrows_max) ; + + /* ---------------------------------------------------------------------- */ + /* extend the column pattern of the front with the new pivot row */ + /* ---------------------------------------------------------------------- */ + + if (Work->pivrow_in_front) + { + if (Work->pivcol_in_front) + { + + /* fill in the hole made when the pivot column was removed */ + if (rrdeg > fncols_orig) + { + Fcols [fncols] = Fcols [--rrdeg] ; + fncols_extended = rrdeg ; + for (i = fncols ; i < rrdeg ; i++) + { +#ifndef NDEBUG + col = Fcols [i] ; + ASSERT (col != pivcol) ; + DEBUG2 ((" col:: "ID" (ext)\n", col)) ; + ASSERT (Fcpos [col] < 0) ; +#endif + Fcpos [Fcols [i]] = i * fnrows_max ; + } + } + } + else + { + Wrow = Work->Wrow ; + for (i = fncols_orig ; i < rrdeg ; i++) + { + col = Wrow [i] ; + if (col != pivcol) + { + DEBUG2 ((" col:: "ID" (ext)\n", col)) ; + ASSERT (Fcpos [col] < 0) ; + Fcols [fncols_extended] = col ; + Fcpos [col] = fncols_extended * fnrows_max ; + fncols_extended++ ; + } + } + } + } + else + { + Wrow = Work->Wrow ; + for (i = 0 ; i < rrdeg ; i++) + { + col = Wrow [i] ; + if (col != pivcol && Fcpos [col] < 0) + { + DEBUG2 ((" col:: "ID" (ext)\n", col)) ; + Fcols [fncols_extended] = col ; + Fcpos [col] = fncols_extended * fnrows_max ; + fncols_extended++ ; + } + } + } + + +#ifndef NDEBUG + ASSERT (fnpiv + fncols_extended <= fncols_max) ; + DEBUG6 (("Pivot row, after shift and extension: "ID" "ID"\n", + fncols,fncols_extended)) ; + for (j = 0 ; j < fncols_extended ; j++) + { + DEBUG7 ((" "ID" "ID" "ID" "ID"\n", + j, Fcols [j], Fcpos [Fcols [j]], j < fncols)) ; + ASSERT (Fcpos [Fcols [j]] == j * fnrows_max) ; + } + DEBUG6 (("Pivot col, after shift and extension: "ID" "ID"\n", + fnrows,fnrows_extended)) ; + for (i = 0 ; i < fnrows_extended ; i++) + { + DEBUG7 ((" "ID" "ID" "ID" "ID"\n", + i, Frows [i], Frpos [Frows [i]], i < fnrows)) ; + ASSERT (Frpos [Frows [i]] == i) ; + } + /* + DEBUG7 (("Complete frontal matrix after all swaps (incl unused):\n")) ; + UMF_dump_dense (Fx, fnrows_max, fnrows_max, fncols_max) ; + */ +#endif + + /* ====================================================================== */ + /* Prepare for degree update and next local pivot search */ + /* ====================================================================== */ + +#ifndef NDEBUG + DEBUG6 (("JUST BEFORE SCAN3A/4A:\nPivot row pattern:\n")) ; + for (j = 0 ; j < fncols_extended ; j++) + { + DEBUG7 ((ID" "ID" "ID" "ID"\n", + j, Fcols [j], Fcpos [Fcols [j]], j < fncols)) ; + ASSERT (Fcpos [Fcols [j]] == j * fnrows_max) ; + } + DEBUG6 (("Pivot col pattern:\n")) ; + for (i = 0 ; i < fnrows_extended ; i++) + { + DEBUG7 ((ID" "ID" "ID" "ID"\n", + i, Frows [i], Frpos [Frows [i]], i < fnrows)) ; + ASSERT (Frpos [Frows [i]] == i) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* Finished with step fnpiv (except for assembly and scale of pivot col) */ + /* ---------------------------------------------------------------------- */ + + fnpiv = ++(Work->fnpiv) ; + +#ifndef NDEBUG + DEBUG6 (("EXT: pivot row pattern: len="ID"\n", fncols_extended)) ; + for (j = 0 ; j < fncols_extended ; j++) DEBUG7 ((ID"\n", Fcols [j])) ; + DEBUG6 (("EXT: pivot col pattern: len="ID"\n", fnrows_extended)) ; + for (j = 0 ; j < fnrows_extended ; j++) DEBUG7 ((ID"\n", Frows [j])) ; +#endif + + /* ====================================================================== */ + /* === EXTEND NUMERICAL FRONT =========================================== */ + /* ====================================================================== */ + + /* ---------------------------------------------------------------------- */ + /* Zero the newly extended frontal matrix */ + /* ---------------------------------------------------------------------- */ + + Fcol = Fx + fncols * fnrows_max ; + i2 = fnrows_max - fnpiv ; + for (j = fncols ; j < fncols_extended ; j++) + { + /* zero the new columns in the contribution block: */ + for (i = 0 ; i < fnrows_extended ; i++) + { + CLEAR (Fcol [i]) ; + } + /* zero the new columns in U block: */ + for (i = i2 ; i < fnrows_max ; i++) + { + CLEAR (Fcol [i]) ; + } + Fcol += fnrows_max ; + } + + Frow = Fx + fnrows ; + j3 = fncols_max - fnpiv ; + for (i = fnrows ; i < fnrows_extended ; i++) + { + /* zero the new rows in the contribution block: */ + for (j = 0 ; j < fncols ; j++) + { + CLEAR (Frow [j * fnrows_max]) ; + } + /* zero the new rows in L block: */ + for (j = j3 ; j < fncols_max ; j++) + { + CLEAR (Frow [j * fnrows_max]) ; + } + Frow++ ; + } + + /* ---------------------------------------------------------------------- */ + /* finalize extended row and column pattern of the frontal matrix */ + /* ---------------------------------------------------------------------- */ + + Work->fnrows = fnrows_extended ; + Work->fncols = fncols_extended ; + + Work->scan_pivcol = !Work->pivcol_in_front ; + Work->scan_pivrow = !Work->pivrow_in_front ; + +} diff --git a/src/sparse-matrix/umfpack/umf_free.c b/src/sparse-matrix/umfpack/umf_free.c new file mode 100644 index 0000000..df8cf4b --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_free.c @@ -0,0 +1,45 @@ +/* ========================================================================== */ +/* === UMF_free ============================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Free a block previously allocated by UMF_malloc and return NULL. + Usage is p = UMF_free (p), to ensure that we don't free it twice. + Also maintains the UMFPACK malloc count. +*/ + +#include "umf_internal.h" + +#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG) +#include "umf_malloc.h" +#endif + +GLOBAL void *UMF_free +( + void *p +) +{ + if (p) + { + + /* see umf_config.h for the memory allocator selection */ + FREE (p) ; + +#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG) + /* One more object has been free'd. Keep track of the count. */ + /* (purely for sanity checks). */ + UMF_malloc_count-- ; +#endif + + } + + return ((void *) NULL) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_garbage_collection.c b/src/sparse-matrix/umfpack/umf_garbage_collection.c new file mode 100644 index 0000000..0ffe43c --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_garbage_collection.c @@ -0,0 +1,403 @@ +/* ========================================================================== */ +/* === UMF_garbage_collection =============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Compress the elements at the tail of Numeric->Memory, and delete the tuples. + Elements are renumbered. The new numbering space is compressed, and + in the order of element creation (original elements of A first, followed + by the new elements in the order that they were formed). +*/ + +#include "umf_internal.h" + +GLOBAL void UMF_garbage_collection +( + NumericType *Numeric, + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int size, e, n_row, n_col, nrows, ncols, nrowsleft, ncolsleft, prevsize, + csize, size2, i2, j2, i, j, cdeg, rdeg, *E, row, col, n_inner, + *Rows, *Cols, *Rows2, *Cols2, nel, e2, *Row_tuples, *Col_tuples, + *Row_degree, *Col_degree ; + Entry *C, *C1, *C3, *C2 ; + Unit *psrc, *pdest, *p, *pnext ; + Element *epsrc, *epdest ; + +#ifndef NDEBUG + Int nmark ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + Col_degree = Numeric->Cperm ; /* for NON_PIVOTAL_COL macro */ + Row_degree = Numeric->Rperm ; /* for NON_PIVOTAL_ROW macro */ + Row_tuples = Numeric->Uip ; + Col_tuples = Numeric->Lip ; + E = Work->E ; + n_row = Work->n_row ; + n_col = Work->n_col ; + n_inner = MIN (n_row, n_col) ; + + /* note that the tuple lengths (Col_tlen and Row_tlen) are updated, but */ + /* the tuple lists themselves are stale and are about to be destroyed */ + /* and recreated. Do not attempt to scan them until they are recreated. */ + +#ifndef NDEBUG + DEBUG0 (("::::GARBAGE COLLECTION::::\n")) ; + UMF_dump_memory (Numeric) ; +#endif + + Numeric->ngarbage++ ; + + /* ---------------------------------------------------------------------- */ + /* delete the tuple lists by marking the blocks as free */ + /* ---------------------------------------------------------------------- */ + + /* do not modify Row_tlen and Col_tlen */ + /* those are needed for UMF_build_tuples */ + + for (row = 0 ; row < n_row ; row++) + { + if (NON_PIVOTAL_ROW (row) && Row_tuples [row]) + { + DEBUG2 (("row "ID" tuples "ID"\n", row, Row_tuples [row])) ; + p = Numeric->Memory + Row_tuples [row] - 1 ; + DEBUG2 (("Freeing tuple list row "ID", p-S "ID", size "ID"\n", + row, p-Numeric->Memory, p->header.size)) ; + ASSERT (p->header.size > 0) ; + ASSERT (p >= Numeric->Memory + Numeric->itail) ; + ASSERT (p < Numeric->Memory + Numeric->size) ; + p->header.size = -p->header.size ; + Row_tuples [row] = 0 ; + } + } + + for (col = 0 ; col < n_col ; col++) + { + if (NON_PIVOTAL_COL (col) && Col_tuples [col]) + { + DEBUG2 (("col "ID" tuples "ID"\n", col, Col_tuples [col])) ; + p = Numeric->Memory + Col_tuples [col] - 1 ; + DEBUG2 (("Freeing tuple list col "ID", p-S "ID", size "ID"\n", + col, p-Numeric->Memory, p->header.size)) ; + ASSERT (p->header.size > 0) ; + ASSERT (p >= Numeric->Memory + Numeric->itail) ; + ASSERT (p < Numeric->Memory + Numeric->size) ; + p->header.size = -p->header.size ; + Col_tuples [col] = 0 ; + } + } + + /* ---------------------------------------------------------------------- */ + /* mark the elements, and compress the name space */ + /* ---------------------------------------------------------------------- */ + + nel = Work->nel ; + +#ifndef NDEBUG + nmark = 0 ; +#endif + + e2 = 0 ; + ASSERT (!E [0]) ; /* current version never uses element zero */ + + for (e = 0 ; e <= nel ; e++) /* for all elements in order of creation */ + { + if (E [e]) + { + psrc = Numeric->Memory + E [e] ; + psrc-- ; /* get the header of this block */ + if (e > 0) + { + e2++ ; /* do not renumber element zero */ + } + ASSERT (psrc->header.size > 0) ; + psrc->header.size = e2 ; /* store the new name in the header */ +#ifndef NDEBUG + nmark++ ; +#endif + DEBUG7 ((ID":: Mark e "ID" at psrc-S "ID", new e "ID"\n", + nmark, e, psrc-Numeric->Memory, e2)) ; + E [e] = 0 ; + + if (e == Work->nelorig) + { + /* this is the highest numbered original element */ + Work->nelorig = e2 ; + } + if (e == Work->prior_element) + { + Work->prior_element = e2 ; + } + } + } + + /* all 1..e2 are now in use (element zero may or may not be in use) */ + Work->nel = e2 ; + nel = Work->nel ; + +#ifndef NDEBUG + for (e = 0 ; e <= n_col + n_inner ; e++) + { + ASSERT (!E [e]) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* compress the elements */ + /* ---------------------------------------------------------------------- */ + + /* point to tail marker block of size 1 + header */ + psrc = Numeric->Memory + Numeric->size - 2 ; + pdest = psrc ; + prevsize = psrc->header.prevsize ; + DEBUG7 (("Starting the compression:\n")) ; + + while (prevsize > 0) + { + + /* ------------------------------------------------------------------ */ + /* move up to the next element above the current header, and */ + /* get the element name and size */ + /* (if it is an element, the name will be positive) */ + /* ------------------------------------------------------------------ */ + + size = prevsize ; + psrc -= (size + 1) ; + e = psrc->header.size ; + prevsize = psrc->header.prevsize ; + /* top block at tail has prevsize of 0 */ + + /* a free block will have a negative size, so skip it */ + /* otherwise, if size > 0, it holds the element name, not the size */ + + DEBUG8 (("psrc-S: "ID" prevsize: "ID" size: "ID, psrc-Numeric->Memory, + prevsize, size)) ; + + if (e >= 0) + { + + /* -------------------------------------------------------------- */ + /* this is an element, compress and move from psrc down to pdest */ + /* -------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG7 (("\n")) ; + DEBUG7 ((ID":: Move element "ID": from: "ID" \n", + nmark, e, psrc-Numeric->Memory)) ; + nmark-- ; + ASSERT (e <= nel) ; + ASSERT (E [e] == 0) ; +#endif + + /* -------------------------------------------------------------- */ + /* get the element scalars, and pointers to C, Rows, and Cols: */ + /* -------------------------------------------------------------- */ + + p = psrc + 1 ; + GET_ELEMENT (epsrc, p, Cols, Rows, ncols, nrows, C) ; + nrowsleft = epsrc->nrowsleft ; + ncolsleft = epsrc->ncolsleft ; + cdeg = epsrc->cdeg ; + rdeg = epsrc->rdeg ; + +#ifndef NDEBUG + DEBUG7 ((" nrows "ID" nrowsleft "ID"\n", nrows, nrowsleft)) ; + DEBUG7 ((" ncols "ID" ncolsleft "ID"\n", ncols, ncolsleft)) ; + DEBUG8 ((" Rows:")) ; + for (i = 0 ; i < nrows ; i++) DEBUG8 ((" "ID, Rows [i])) ; + DEBUG8 (("\n Cols:")) ; + for (j = 0 ; j < ncols ; j++) DEBUG8 ((" "ID, Cols [j])) ; + DEBUG8 (("\n")) ; +#endif + + /* -------------------------------------------------------------- */ + /* determine the layout of the new element */ + /* -------------------------------------------------------------- */ + + csize = nrowsleft * ncolsleft ; + size2 = UNITS (Element, 1) + + UNITS (Int, nrowsleft + ncolsleft) + + UNITS (Entry, csize) ; + + DEBUG7 (("Old size "ID" New size "ID"\n", size, size2)) ; + + pnext = pdest ; + pnext->header.prevsize = size2 ; + pdest -= (size2 + 1) ; + DEBUG7 (("Move element from psrc-S "ID" to pdest-S "ID"\n", + psrc-Numeric->Memory, pdest-Numeric->Memory)); + + ASSERT (e > 0 || size == size2) ; + ASSERT (size2 <= size) ; + ASSERT (psrc + 1 + size <= pnext) ; + ASSERT (psrc <= pdest) ; + + p = pdest + 1 ; + epdest = (Element *) p ; + p += UNITS (Element, 1) ; + Cols2 = (Int *) p ; + Rows2 = Cols2 + ncolsleft ; + p += UNITS (Int, nrowsleft + ncolsleft) ; + C2 = (Entry *) p ; + + ASSERT (epdest >= epsrc) ; + ASSERT (Rows2 >= Rows) ; + ASSERT (Cols2 >= Cols) ; + ASSERT (C2 >= C) ; + ASSERT (p + UNITS (Entry, csize) == pnext) ; + + /* -------------------------------------------------------------- */ + /* move the contribution block */ + /* -------------------------------------------------------------- */ + + /* overlap = psrc + size + 1 > pdest ; */ + + if (nrowsleft < nrows || ncolsleft < ncols) + { + + /* ---------------------------------------------------------- */ + /* compress contribution block in place prior to moving it */ + /* ---------------------------------------------------------- */ + + DEBUG7 (("Compress C in place prior to move (incl unused):\n")); +#ifndef NDEBUG + UMF_dump_dense (C, nrows, nrows, ncols) ; +#endif + C1 = C ; + C3 = C ; + for (j = 0 ; j < ncols ; j++) + { + if (Cols [j] >= 0) + { + for (i = 0 ; i < nrows ; i++) + { + if (Rows [i] >= 0) + { + *C3++ = C1 [i] ; + } + } + } + C1 += nrows ; + } + ASSERT (C3-C == csize) ; + DEBUG8 (("Newly compressed contrib. block (all in use):\n")) ; +#ifndef NDEBUG + UMF_dump_dense (C, nrowsleft, nrowsleft, ncolsleft) ; +#endif + } + + /* shift the contribution block down */ + C += csize ; + C2 += csize ; + for (i = 0 ; i < csize ; i++) + { + *--C2 = *--C ; + } + + /* -------------------------------------------------------------- */ + /* move the row indices */ + /* -------------------------------------------------------------- */ + + i2 = nrowsleft ; + for (i = nrows - 1 ; i >= 0 ; i--) + { + ASSERT (Rows2+i2 >= Rows+i) ; + if (Rows [i] >= 0) + { + Rows2 [--i2] = Rows [i] ; + } + } + ASSERT (i2 == 0) ; + + j2 = ncolsleft ; + for (j = ncols - 1 ; j >= 0 ; j--) + { + ASSERT (Cols2+j2 >= Cols+j) ; + if (Cols [j] >= 0) + { + Cols2 [--j2] = Cols [j] ; + } + } + ASSERT (j2 == 0) ; + + /* -------------------------------------------------------------- */ + /* construct the new header */ + /* -------------------------------------------------------------- */ + + /* E [0...e] is now valid */ + E [e] = (pdest + 1) - Numeric->Memory ; + epdest = (Element *) (pdest + 1) ; + + epdest->next = EMPTY ; /* destroys the son list */ + epdest->ncols = ncolsleft ; + epdest->nrows = nrowsleft ; + epdest->ncolsleft = ncolsleft ; + epdest->nrowsleft = nrowsleft ; + epdest->rdeg = rdeg ; + epdest->cdeg = cdeg ; + + ASSERT (size2 <= size) ; + pdest->header.prevsize = 0 ; + pdest->header.size = size2 ; + + DEBUG7 (("After moving it:\n")) ; +#ifndef NDEBUG + UMF_dump_element (Numeric, Work, e, FALSE) ; +#endif + } + +#ifndef NDEBUG + else + { + DEBUG8 ((" free\n")) ; + } +#endif + DEBUG7 (("psrc "ID" tail "ID"\n", + psrc-Numeric->Memory, Numeric->itail)) ; + } + + ASSERT (psrc == Numeric->Memory + Numeric->itail) ; + ASSERT (nmark == 0) ; + + /* ---------------------------------------------------------------------- */ + /* final tail pointer */ + /* ---------------------------------------------------------------------- */ + + ASSERT (pdest >= Numeric->Memory + Numeric->itail) ; + Numeric->itail = pdest - Numeric->Memory ; + pdest->header.prevsize = 0 ; + Numeric->ibig = EMPTY ; + Numeric->tail_usage = Numeric->size - Numeric->itail ; + + /* ---------------------------------------------------------------------- */ + /* clear the unused E [nel+1 .. n_col + n_inner] */ + /* ---------------------------------------------------------------------- */ + + for (e = nel+1 ; e <= n_col + n_inner ; e++) + { + E [e] = 0 ; + } + +#ifndef NDEBUG + UMF_dump_packed_memory (Numeric, Work) ; +#endif + + DEBUG8 (("::::GARBAGE COLLECTION DONE::::\n")) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_get_memory.c b/src/sparse-matrix/umfpack/umf_get_memory.c new file mode 100644 index 0000000..af7a0c7 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_get_memory.c @@ -0,0 +1,210 @@ +/* ========================================================================== */ +/* === UMF_get_memory ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Reallocate the workspace (Numeric->Memory) and shift elements downwards. + needunits: increase in size so that the free space is at least this many + Units (to which the tuple lengths is added). + + Return TRUE if successful, FALSE if out of memory. +*/ + +#include "umf_internal.h" +#include "umf_garbage_collection.h" +#include "umf_tuple_lengths.h" +#include "umf_build_tuples.h" +#include "umf_mem_free_tail_block.h" +#include "umf_realloc.h" + +GLOBAL Int UMF_get_memory +( + NumericType *Numeric, + WorkType *Work, + Int needunits +) +{ + Int i, minsize, newsize, newmem, costly, row, col, *Row_tlen, *Col_tlen, + n_row, n_col, *Row_degree, *Col_degree ; + Unit *mnew, *p ; + double nsize, bsize ; + + /* ---------------------------------------------------------------------- */ + /* get and check parameters */ + /* ---------------------------------------------------------------------- */ + + ASSERT (Numeric) ; + ASSERT (Work) ; + ASSERT (Numeric->Memory) ; + +#ifndef NDEBUG + DEBUG1 (("::::GET MEMORY::::\n")) ; + UMF_dump_memory (Numeric) ; +#endif + + n_row = Work->n_row ; + n_col = Work->n_col ; + Row_degree = Numeric->Rperm ; /* for NON_PIVOTAL_ROW macro */ + Col_degree = Numeric->Cperm ; /* for NON_PIVOTAL_COL macro */ + Row_tlen = Numeric->Uilen ; + Col_tlen = Numeric->Lilen ; + + /* ---------------------------------------------------------------------- */ + /* initialize the tuple list lengths */ + /* ---------------------------------------------------------------------- */ + + for (row = 0 ; row < n_row ; row++) + { + if (NON_PIVOTAL_ROW (row)) + { + Row_tlen [row] = 0 ; + } + } + for (col = 0 ; col < n_col ; col++) + { + if (NON_PIVOTAL_COL (col)) + { + Col_tlen [col] = 0 ; + } + } + + /* ---------------------------------------------------------------------- */ + /* determine how much memory is needed for the tuples */ + /* ---------------------------------------------------------------------- */ + + nsize = (double) needunits + 2 ; + needunits += UMF_tuple_lengths (Numeric, Work, &nsize) ; + needunits += 2 ; /* add 2, so that newmem >= 2 is true if realloc'd */ + + /* note: Col_tlen and Row_tlen are updated, but the tuple lists */ + /* themselves are not. Do not attempt to scan the tuple lists. */ + /* They are now stale, and are about to be destroyed and recreated. */ + + /* ---------------------------------------------------------------------- */ + /* determine the desired new size of memory */ + /* ---------------------------------------------------------------------- */ + + DEBUG0 (("UMF_get_memory: needunits: "ID"\n", needunits)) ; + + minsize = Numeric->size + needunits ; + nsize += (double) Numeric->size ; + + bsize = ((double) Int_MAX) / sizeof (Unit) - 1 ; + + if (nsize > bsize) /* a double relop, but ignore NaN case */ + { + return (FALSE) ; /* the minimum size is larger than Int_MAX */ + } + + newsize = (Int) (UMF_REALLOC_INCREASE * ((double) minsize)) ; + nsize *= UMF_REALLOC_INCREASE * (1.0 + MAX_EPSILON) ; + + if (newsize < 0 || nsize > bsize) + { + newsize = (Int) bsize ; /* we cannot increase the size beyond bsize */ + } + else + { + newsize = MAX (newsize, minsize) ; + } + + DEBUG0 (( + "REALLOC MEMORY: needunits "ID" old size: "ID" new size: "ID" Units \n", + needunits, Numeric->size, newsize)) ; + + /* Forget where the biggest free block is (we no longer need it) */ + /* since garbage collection will occur shortly. */ + Numeric->ibig = EMPTY ; + + /* ---------------------------------------------------------------------- */ + /* reallocate the memory, if possible, and make it bigger */ + /* ---------------------------------------------------------------------- */ + + mnew = (Unit *) NULL ; + while (!mnew) + { + mnew = (Unit *) UMF_realloc (Numeric->Memory, newsize, sizeof (Unit)) ; + if (!mnew) + { + if (newsize == minsize) /* last realloc attempt failed */ + { + /* We failed to get the minimum. Just stick with the */ + /* current allocation and hope that garbage collection */ + /* can recover enough space. */ + mnew = Numeric->Memory ; /* no new memory available */ + newsize = Numeric->size ; + } + else + { + /* otherwise, reduce the request and keep trying */ + newsize = (Int) (UMF_REALLOC_REDUCTION * ((double) newsize)) ; + newsize = MAX (minsize, newsize) ; + } + } + } + ASSERT (mnew) ; + + /* see if realloc had to copy, rather than just extend memory */ + costly = (mnew != Numeric->Memory) ; + + /* ---------------------------------------------------------------------- */ + /* extend the tail portion of memory downwards */ + /* ---------------------------------------------------------------------- */ + + Numeric->Memory = mnew ; + + newmem = newsize - Numeric->size ; + ASSERT (newmem == 0 || newmem >= 2) ; + + if (newmem >= 2) + { + /* reallocation succeeded */ + + /* point to the old tail marker block of size 1 + header */ + p = Numeric->Memory + Numeric->size - 2 ; + + /* create a new block out of the newly extended memory */ + p->header.size = newmem - 1 ; + i = Numeric->size - 1 ; + p += newmem ; + + /* create a new tail marker block */ + p->header.prevsize = newmem - 1 ; + p->header.size = 1 ; + + Numeric->size = newsize ; + + /* free the new block */ + UMF_mem_free_tail_block (Numeric, i) ; + + Numeric->nrealloc++ ; + + if (costly) + { + Numeric->ncostly++ ; + } + + } + DEBUG1 (("Done with realloc memory\n")) ; + + /* ---------------------------------------------------------------------- */ + /* garbage collection on the tail of Numeric->memory (destroys tuples) */ + /* ---------------------------------------------------------------------- */ + + UMF_garbage_collection (Numeric, Work) ; + + /* ---------------------------------------------------------------------- */ + /* rebuild the tuples */ + /* ---------------------------------------------------------------------- */ + + return (UMF_build_tuples (Numeric, Work)) ; + +} + diff --git a/src/sparse-matrix/umfpack/umf_init_front.c b/src/sparse-matrix/umfpack/umf_init_front.c new file mode 100644 index 0000000..15a33cb --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_init_front.c @@ -0,0 +1,201 @@ +/* ========================================================================== */ +/* === UMF_init_front ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +#include "umf_internal.h" + +GLOBAL void UMF_init_front +( + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int i, fsize, pivrow, pivcol, j, fnrows_max, fncols_max, + row, col, *Frows, *Fcols, *Fcpos, *Frpos, fncols, fnrows, src, dest, + *Wrow ; + Entry *Fx, *F ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + /* current front is defined by pivot row and column */ + + pivrow = Work->pivrow ; + pivcol = Work->pivcol ; + + Frows = Work->Frows ; + Fcols = Work->Fcols ; + Frpos = Work->Frpos ; + Fcpos = Work->Fcpos ; + + Work->fnpiv = 1 ; + Work->fnzeros = 0 ; + + /* dynamic front dimensions, but fixed across one chain */ + fnrows_max = Work->fnrows_max ; + fncols_max = Work->fncols_max ; + + fsize = fnrows_max * fncols_max ; + + /* ---------------------------------------------------------------------- */ + /* place pivot column pattern in frontal matrix */ + /* ---------------------------------------------------------------------- */ + + if (Work->pivcol_in_front) + { + /* append the pivot column extension */ + /* note that all we need to do is increment the size, since the */ + /* candidate pivot column pattern is already in place in */ + /* Frows [0 ... Work->fnrows-1] (the old pattern), and */ + /* Frows [Work->fnrows ... Work->fnrows + Work->ccdeg - 1] (the new */ + /* pattern). */ + + /* if both pivrow and pivcol are in front, then we extend the old one */ + /* in UMF_extend_front, rather than starting a new one here. */ + ASSERT (!Work->pivrow_in_front) ; + + dest = Work->fnrows ; + Work->fscan_row = dest ; /* only scan the new rows */ + dest += Work->ccdeg ; + } + else + { + /* this is a completely new column */ + dest = 0 ; + Work->fscan_row = 0 ; /* scan all the rows */ + + ASSERT (Work->Wcol == Work->Wm) ; + for (i = 0 ; i < Work->ccdeg ; i++) + { + row = Work->Wcol [i] ; + Frows [dest] = row ; + Frpos [row] = dest ; + dest++ ; + } + } + + Work->fnrows = dest ; + + /* place pivot row index into position */ + Frpos [pivrow] = fnrows_max - 1 ; + +#ifndef NDEBUG + DEBUG3 (("New Pivot col "ID" now in front, length "ID"\n", + pivcol, Work->fnrows)) ; + for (i = 0 ; i < Work->fnrows ; i++) + { + DEBUG4 (("row "ID" position "ID"\n", Frows [i], Frpos [Frows [i]])) ; + ASSERT (Frpos [Frows [i]] == i) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* place pivot row pattern in frontal matrix */ + /* ---------------------------------------------------------------------- */ + + if (Work->pivrow_in_front) + { + /* append the pivot row extension */ + src = Work->fncols ; + dest = Work->fncols ; + Work->fscan_col = dest ; /* only scan the new columns */ + } + else + { + /* this is a completely new row */ + src = 0 ; + dest = 0 ; + Work->fscan_col = 0 ; /* scan all the columns */ + } + + Wrow = Work->Wrow ; + for ( ; src < Work->rrdeg ; src++) + { + col = Wrow [src] ; + if (col != pivcol) + { + Fcols [dest] = col ; + Fcpos [col] = dest * fnrows_max ; + dest++ ; + } + } + + Work->fncols = dest ; + + /* place pivot column index into position */ + Fcpos [pivcol] = (fncols_max - 1) * fnrows_max ; + +#ifndef NDEBUG + DEBUG3 (("New Pivot row "ID" now in front, length "ID" fnrows_max "ID"\n", + pivrow, Work->fncols, fnrows_max)) ; + for (j = 0 ; j < Work->fncols ; j++) + { + DEBUG4 (("col "ID" position "ID" ("ID")\n", + Fcols [j], Fcpos [Fcols [j]], j*fnrows_max)) ; + ASSERT (Fcpos [Fcols [j]] == j * fnrows_max) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* clear the frontal matrix */ + /* ---------------------------------------------------------------------- */ + + fncols = Work->fncols ; + fnrows = Work->fnrows ; + Fx = Work->Fx ; + + /* clear the contribution block and the pivot row */ + F = Fx ; + for (j = 0 ; j < fncols ; j++) + { + for (i = 0 ; i < fnrows ; i++) + { + ASSERT ((&F[i] >= Fx) && (&F[i] < Fx+fsize)) ; + CLEAR (F [i]) ; + } + ASSERT ((&F[fnrows_max-1] >= Fx) && (&F[fnrows_max-1] < Fx+fsize)) ; + CLEAR (F [fnrows_max - 1]) ; + F += fnrows_max ; + } + + /* clear the pivot column (excl pivot itself) */ + F = Fx + Fcpos [pivcol] ; + for (j = 0 ; j < fnrows ; j++) + { + ASSERT ((&F[j] >= Fx) && (&F[j] < Fx+fsize)) ; + CLEAR (F [j]) ; + } + + /* clear the pivot entry */ + j = fsize-1 ; + DEBUG2 (("j "ID" fsize "ID"\n", j, fsize)) ; + ASSERT ((&Fx[j] >= Fx) && (&Fx[j] < Fx+fsize)) ; + CLEAR (Fx [j]) ; + + /* ---------------------------------------------------------------------- */ + /* current workspace usage: */ + /* ---------------------------------------------------------------------- */ + + /* Fx [0..fnrows_max-1, 0..fncols_max-1]: */ + /* space for the new frontal matrix. */ + /* Fx (i,j) is located at Fx [i+j*fnrows_max] */ + + /* ---------------------------------------------------------------------- */ + /* make sure the pivot row and column are scanned in assembly phase */ + /* ---------------------------------------------------------------------- */ + + Work->scan_pivrow = TRUE ; + Work->scan_pivcol = TRUE ; + +} diff --git a/src/sparse-matrix/umfpack/umf_is_permutation.c b/src/sparse-matrix/umfpack/umf_is_permutation.c new file mode 100644 index 0000000..ad54b31 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_is_permutation.c @@ -0,0 +1,54 @@ +/* ========================================================================== */ +/* === UMF_is_permutation =================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Return TRUE if P is a r-permutation vector, FALSE otherwise */ +/* P [0..r-1] must be an r-permutation of 0..n-1 */ + +#include "umf_internal.h" + +GLOBAL Int UMF_is_permutation +( + const Int P [ ], /* permutation of size r */ + Int W [ ], /* workspace of size n */ + Int n, + Int r +) +{ + Int i, k ; + + if (!P) + { + /* if P is (Int *) NULL, this is the identity permutation */ + return (TRUE) ; + } + + ASSERT (W) ; + + for (i = 0 ; i < n ; i++) + { + W [i] = FALSE ; + } + for (k = 0 ; k < r ; k++) + { + i = P [k] ; + if (i < 0 || i >= n) + { + return (FALSE) ; + } + if (W [i]) + { + return (FALSE) ; + } + W [i] = TRUE ; + } + return (TRUE) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_kernel.c b/src/sparse-matrix/umfpack/umf_kernel.c new file mode 100644 index 0000000..318088b --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_kernel.c @@ -0,0 +1,251 @@ +/* ========================================================================== */ +/* === UMF_kernel =========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Primary factorization routine. Called by UMFPACK_numeric. + Returns: + UMFPACK_OK if successful, + UMFPACK_ERROR_out_of_memory if out of memory, or + UMFPACK_ERROR_different_pattern if pattern of matrix (Ap and/or Ai) + has changed since the call to UMFPACK_*symbolic. +*/ + +#include "umf_internal.h" +#include "umf_init_front.h" +#include "umf_assemble.h" +#include "umf_scale_column.h" +#include "umf_local_search.h" +#include "umf_create_element.h" +#include "umf_extend_front.h" +#include "umf_blas3_update.h" +#include "umf_kernel_init.h" +#include "umf_kernel_wrapup.h" + + +GLOBAL Int UMF_kernel +( + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int j, frontid1, frontid2, chain, nchains, *Chain_start, status, + *Chain_maxrows, *Chain_maxcols, *Front_npivcol, jmax, nb ; + + /* ---------------------------------------------------------------------- */ + /* initialize memory space and load the matrix */ + /* ---------------------------------------------------------------------- */ + + if (!UMF_kernel_init (Ap, Ai, Ax, +#ifdef COMPLEX + Az, +#endif + Numeric, Work, Symbolic)) + { + /* UMF_kernel_init is guaranteed to succeed, since UMFPACK_numeric */ + /* either allocates enough space or if not, UMF_kernel does not get */ + /* called. So running out of memory here is a fatal error, and means */ + /* that the user changed Ap and/or Ai since the call to */ + /* UMFPACK_*symbolic. */ + return (UMFPACK_ERROR_different_pattern) ; + } + + /* ---------------------------------------------------------------------- */ + /* get the symbolic factorization */ + /* ---------------------------------------------------------------------- */ + + nchains = Symbolic->nchains ; + Chain_start = Symbolic->Chain_start ; + Chain_maxrows = Symbolic->Chain_maxrows ; + Chain_maxcols = Symbolic->Chain_maxcols ; + Front_npivcol = Symbolic->Front_npivcol ; + DEBUG0 (("Starting Kernel, nchains "ID"\n", nchains)) ; + nb = Symbolic->nb ; + Work->nextcand = 0 ; + + /* ---------------------------------------------------------------------- */ + /* factorize each chain of frontal matrices */ + /* ---------------------------------------------------------------------- */ + + for (chain = 0 ; chain < nchains ; chain++) + { + ASSERT (Work->fnrows == 0 && Work->fncols == 0 && Work->fnpiv == 0) ; + frontid1 = Chain_start [chain] ; + frontid2 = Chain_start [chain+1] - 1 ; + Work->fnrows_max = Chain_maxrows [chain] ; + Work->fncols_max = Chain_maxcols [chain] ; + DEBUG2 (("Starting chain "ID". start "ID" end "ID" maxrows "ID + " maxcols "ID"\n", chain, frontid1, frontid2, Work->fnrows_max, + Work->fncols_max)) ; + + /* ------------------------------------------------------------------ */ + /* factorize each front in the chain */ + /* ------------------------------------------------------------------ */ + + for (Work->frontid = frontid1 ; Work->frontid <= frontid2 ; Work->frontid++) + { + + /* -------------------------------------------------------------- */ + /* there are no 0-by-c or r-by-0 fronts, where c and r are > 0 */ + /* -------------------------------------------------------------- */ + + /* a front is either 0-by-0, or r-by-c */ + DEBUG2 (("\n\n::: "ID" : Npiv: "ID" size "ID"-by-"ID"\n", + Work->frontid, Work->npiv, + Work->fnrows, Work->fncols)) ; + ASSERT ((Work->fnrows == 0 && Work->fncols == 0) + ||(Work->fnrows != 0 && Work->fncols != 0)) ; + + /* -------------------------------------------------------------- */ + /* Initialize the pivot column candidate set */ + /* -------------------------------------------------------------- */ + + /* next pivot (in range 0 to n-1) */ + Work->ncand = Front_npivcol [Work->frontid] ; + jmax = MIN (MAX_CANDIDATES, Work->ncand) ; + for (j = 0 ; j < jmax ; j++) + { + Work->Candidates [j] = Work->nextcand++ ; + DEBUG3 ((""ID" Candidate: "ID"\n", j, Work->Candidates [j])) ; + } + + /* -------------------------------------------------------------- */ + /* Assemble and factorize the current frontal matrix */ + /* -------------------------------------------------------------- */ + + while (Work->ncand > 0) + { + + /* ---------------------------------------------------------- */ + /* get the pivot row and column */ + /* ---------------------------------------------------------- */ + + status = UMF_local_search (Numeric, Work, Symbolic) ; + if (status == UMFPACK_ERROR_different_pattern) + { + return (UMFPACK_ERROR_different_pattern) ; + } + if (status == UMFPACK_WARNING_singular_matrix) + { + /* no pivot found, try again */ + DEBUG0 (("No pivot found; try again, ncand: "ID"\n", + Work->ncand)) ; + continue ; + } + + /* ---------------------------------------------------------- */ + /* extend the frontal matrix, or start a new one */ + /* ---------------------------------------------------------- */ + + if (Work->do_extend) + { + /* apply pending updates if front would grow too much */ + if (Work->do_update) + { + UMF_blas3_update (Work) ; + } + /* extend the current front */ + UMF_extend_front (Work) ; + } + else + { + /* finish the current front (if any) and start a new one */ + if (!UMF_create_element (Numeric, Work)) + { + return (UMFPACK_ERROR_out_of_memory) ; + } + UMF_init_front (Work) ; + } + + /* ---------------------------------------------------------- */ + /* Numerical & symbolic assembly into current frontal matrix */ + /* ---------------------------------------------------------- */ + + UMF_assemble (Numeric, Work) ; + + /* ---------------------------------------------------------- */ + /* scale the pivot column, and save row and column of U and L */ + /* ---------------------------------------------------------- */ + + if (!UMF_scale_column (Numeric, Work)) + { + return (UMFPACK_ERROR_out_of_memory) ; + } + + /* ---------------------------------------------------------- */ + /* Numerical update if enough pivots accumulated */ + /* ---------------------------------------------------------- */ + + if (Work->fnpiv >= nb) + { + UMF_blas3_update (Work) ; + } + + Work->pivrow_in_front = FALSE ; + Work->pivcol_in_front = FALSE ; + + /* ---------------------------------------------------------- */ + /* If front is empty, evaporate it */ + /* ---------------------------------------------------------- */ + + if (Work->fnrows == 0 || Work->fncols == 0) + { + /* This does not create an element, just evaporates. */ + /* It ensures that a front is not 0-by-c or c-by-0. */ + DEBUG1 (("Evaporate empty front:\n")) ; + (void) UMF_create_element (Numeric, Work) ; + Work->fnrows = 0 ; + Work->fncols = 0 ; + } + } + } + + /* ------------------------------------------------------------------ */ + /* Wrapup the current frontal matrix. This is the last in a chain */ + /* in the column elimination tree. The next frontal matrix */ + /* cannot overlap with the current one, which will be its sibling */ + /* in the column etree. */ + /* ------------------------------------------------------------------ */ + + if (!UMF_create_element (Numeric, Work)) + { + return (UMFPACK_ERROR_out_of_memory) ; + } + + /* ------------------------------------------------------------------ */ + /* current front is now empty */ + /* ------------------------------------------------------------------ */ + + Work->fnrows = 0 ; + Work->fncols = 0 ; + } + + /* ---------------------------------------------------------------------- */ + /* end the last Lchain and Uchain and finalize the LU factors */ + /* ---------------------------------------------------------------------- */ + + UMF_kernel_wrapup (Numeric, Symbolic, Work) ; + + /* note that the matrix may be singular */ + return (UMFPACK_OK) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_kernel_init.c b/src/sparse-matrix/umfpack/umf_kernel_init.c new file mode 100644 index 0000000..2eb7270 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_kernel_init.c @@ -0,0 +1,301 @@ +/* ========================================================================== */ +/* === UMF_kernel_init ====================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Initialize the kernel, build tuple lists. Assumes elements are packed. + Returns TRUE if successful, FALSE if out of memory or if the pattern has + changed since UMFPACK_*symbolic. UMFPACK_numeric allocates at least enough + space for UMF_kernel_init to succeed; otherwise it does not call + UMF_kernel_init. So an out-of-memory condition means that the pattern must + have gotten larger. +*/ + +#include "umf_internal.h" +#include "umf_tuple_lengths.h" +#include "umf_build_tuples.h" +#include "umf_mem_init_memoryspace.h" +#include "umf_mem_alloc_element.h" + +GLOBAL Int UMF_kernel_init +( + const Int Ap [ ], /* user's input matrix (not modified) */ + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int row, k, oldcol, size, e, p1, p2, p, *ip, nz, *Rows, *Cols, *E, i, *Upos, + *Lpos, n_row, n_col, *Wp, *Cperm_init, *Frpos, *Fcpos, *Row_degree, nn, + *Row_tlen, *Col_degree, *Col_tlen, deg, oldrow, newrow, ilast, + *Rperm_init, col, n_inner ; + double unused = 0 ; + Entry *xp, *C ; + Element *ep ; + +#ifndef NDEBUG + double gprob_save = UMF_gprob ; + UMF_gprob = 0 ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + DEBUG0 (("KERNEL INIT\n")) ; + + n_row = Symbolic->n_row ; + n_col = Symbolic->n_col ; + nn = MAX (n_row, n_col) ; + n_inner = MIN (n_row, n_col) ; + ASSERT (n_row > 0 && n_col > 0) ; + Cperm_init = Symbolic->Cperm_init ; + Rperm_init = Symbolic->Rperm_init ; + nz = Ap [n_col] ; + if (nz < 0 || Ap [0] != 0 || nz != Symbolic->nz) + { + return (FALSE) ; /* pattern changed */ + } + + /* ---------------------------------------------------------------------- */ + /* initialize the Numeric->Memory space for LU, elements, and tuples */ + /* ---------------------------------------------------------------------- */ + + UMF_mem_init_memoryspace (Numeric) ; + + /* ---------------------------------------------------------------------- */ + /* initialize the Work and Numeric objects */ + /* ---------------------------------------------------------------------- */ + + /* current front is empty */ + Work->fnpiv = 0 ; + Work->fncols = 0 ; + Work->fnrows = 0 ; + Work->fnzeros = 0 ; + + Work->overlap = 0 ; + Work->nz = nz ; + Work->prior_element = EMPTY ; + Work->ulen = 0 ; + Work->llen = 0 ; + Work->npiv = 0 ; + Work->frontid = 0 ; + + Row_degree = Numeric->Rperm ; + Col_degree = Numeric->Cperm ; + /* Row_tuples = Numeric->Uip ; */ + Row_tlen = Numeric->Uilen ; + /* Col_tuples = Numeric->Lip ; */ + Col_tlen = Numeric->Lilen ; + + Frpos = Work->Frpos ; + Fcpos = Work->Fcpos ; + Wp = Work->Wp ; + + /* D = Numeric->D ; */ + Upos = Numeric->Upos ; + Lpos = Numeric->Lpos ; + /* D [0..min(n_row,n_col)] = 0 ; no need to initialize this */ + + for (row = 0 ; row <= n_row ; row++) + { + Lpos [row] = EMPTY ; + /* Row_tuples [row] = 0 ; set in UMF_build_tuples */ + Row_degree [row] = 0 ; + Row_tlen [row] = 0 ; + /* Frpos [row] = EMPTY ; do this later */ + } + + for (col = 0 ; col <= n_col ; col++) + { + Upos [col] = EMPTY ; + /* Col_tuples [col] = 0 ; set in UMF_build_tuples */ + /* Col_degree [col] = 0 ; initialized below */ + Col_tlen [col] = 0 ; + Fcpos [col] = EMPTY ; + } + + for (i = 0 ; i <= nn ; i++) + { + Wp [i] = EMPTY ; + } + Work->Wpflag = -2 ; + + /* When cleared, Wp [0..nn] is < 0 and > wpflag. */ + /* In row search, Wp [col] is set to wpflag, which is negative. */ + /* In col search, Wp [row] is set to a position, which is >= 0. */ + /* Wp is cleared immediately after the row and col searches. */ + + /* no need to initialize Wm, Wio, Woi, and Woo */ + + /* clear the external degree counters */ + Work->cdeg0 = 1 ; + Work->rdeg0 = 1 ; + + E = Work->E ; + + Numeric->n_row = n_row ; + Numeric->n_col = n_col ; + Numeric->npiv = 0 ; + Numeric->nnzpiv = 0 ; + Numeric->min_udiag = 0.0 ; + Numeric->max_udiag = 0.0 ; + Numeric->rcond = 0.0 ; + Numeric->isize = 0 ; + Numeric->nLentries = 0 ; + Numeric->nUentries = 0 ; + Numeric->lnz = 0 ; + Numeric->unz = 0 ; + Numeric->maxfrsize = 0 ; + Numeric->flops = 0. ; + + /* ---------------------------------------------------------------------- */ + /* allocate the elements, copy the columns of A, and initialize degrees */ + /* ---------------------------------------------------------------------- */ + + /* also apply the row and column pre-ordering. */ + + DEBUG3 (("LOAD_MATRIX:\n")) ; + + /* construct the inverse row permutation (use Frpos as temp) */ + for (newrow = 0 ; newrow < n_row ; newrow++) + { + oldrow = Rperm_init [newrow] ; + ASSERT (oldrow >= 0 && oldrow < n_row) ; + Frpos [oldrow] = newrow ; + } + + e = 0 ; + E [e] = 0 ; + for (k = 0 ; k < n_col ; k++) + { + oldcol = Cperm_init [k] ; + ASSERT (oldcol >= 0 && oldcol < n_col) ; + p1 = Ap [oldcol] ; + p2 = Ap [oldcol+1] ; + deg = p2 - p1 ; + Col_degree [k] = deg ; + if (deg < 0) + { + return (FALSE) ; /* pattern changed */ + } + else if (deg > 0) + { + e++ ; + E [e] = UMF_mem_alloc_element (Numeric, deg, 1, &Rows, &Cols, &C, + &size, &ep) ; + if (E [e] <= 0) + { + /* We ran out of memory, which can only mean that */ + /* the pattern (Ap and or Ai) has changed (gotten larger). */ + DEBUG0 (("Pattern has gotten larger - alloc el. failed\n")) ; + DEBUG0 (("column k = "ID" size "ID"\n", k, size)) ; + return (FALSE) ; /* pattern changed */ + } + Cols [0] = k ; + ip = Rows ; + xp = C ; + ilast = -1 ; + for (p = p1 ; p < p2 ; p++) + { + oldrow = Ai [p] ; + if (oldrow <= ilast || oldrow >= n_row) + { + return (FALSE) ; /* pattern changed */ + } + ilast = oldrow ; + newrow = Frpos [oldrow] ; + *ip++ = newrow ; + ASSIGN (*xp, Ax [p], Az [p]) ; + xp++ ; + Row_degree [newrow]++ ; + } + } + } + + Work->nel = e ; + Work->nelorig = e ; + + Col_degree [n_col] = 0 ; + + for (e++ ; e <= n_col + n_inner ; e++) + { + E [e] = 0 ; + } + + /* Frpos no longer needed */ + for (row = 0 ; row <= n_row ; row++) + { + Frpos [row] = EMPTY ; + } + + /* ---------------------------------------------------------------------- */ + /* build the tuple lists */ + /* ---------------------------------------------------------------------- */ + + /* if the memory usage changes, then the pattern has changed */ + + (void) UMF_tuple_lengths (Numeric, Work, &unused) ; + if (!UMF_build_tuples (Numeric, Work)) + { + /* We ran out of memory, which can only mean that */ + /* the pattern (Ap and or Ai) has changed (gotten larger). */ + DEBUG0 (("Pattern has gotten larger - build tuples failed\n")) ; + return (FALSE) ; /* pattern changed */ + } + + Numeric->init_usage = Numeric->max_usage ; + if (Symbolic->num_mem_init_usage != Numeric->init_usage) + { + /* the pattern (Ap and or Ai) has changed */ + DEBUG0 (("Pattern has changed in size\n")) ; + return (FALSE) ; /* pattern changed */ + } + + /* NOTE: this test does not detect all changes to Ap and/or Ai since the */ + /* last call to UMFPACK_*symbolic. The pattern can change with the memory*/ + /* usage remaining the same. */ + + /* ---------------------------------------------------------------------- */ + /* construct the row merge sets */ + /* ---------------------------------------------------------------------- */ + + for (i = 0 ; i <= Symbolic->nfr ; i++) + { + Work->Front_new1strow [i] = Symbolic->Front_1strow [i] ; + } + +#ifndef NDEBUG + UMF_dump_rowmerge (Numeric, Symbolic, Work) ; + DEBUG6 (("Column form of original matrix:\n")) ; + UMF_dump_col_matrix (Ax, +#ifdef COMPLEX + Az, +#endif + Ai, Ap, n_row, n_col, nz) ; + UMF_gprob = gprob_save ; + UMF_dump_memory (Numeric) ; + UMF_dump_matrix (Numeric, Work, FALSE) ; + DEBUG0 (("kernel init done...\n")) ; +#endif + + return (TRUE) ; + +} + diff --git a/src/sparse-matrix/umfpack/umf_kernel_init_usage.c b/src/sparse-matrix/umfpack/umf_kernel_init_usage.c new file mode 100644 index 0000000..9020415 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_kernel_init_usage.c @@ -0,0 +1,54 @@ +/* ========================================================================== */ +/* === UMF_kernel_init_usage ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Return number of Units needed for initial elements by UMF_kernel_init */ +/* (including the tuples). */ + +#include "umf_internal.h" +#include "umf_build_tuples_usage.h" + +GLOBAL Int UMF_kernel_init_usage +( + const Int Ap [ ], + const Int Row_degree [ ], + Int n_row, + Int n_col, + double *dusage /* input and output argument */ +) +{ + Int col, cdeg, usage ; + + /* elements: */ + usage = 0 ; + for (col = 0 ; col < n_col ; col++) + { + DEBUG2 (("col "ID" Ap["ID"] = "ID" Ap ["ID"] "ID"\n", + col, col+1, Ap [col+1], col, Ap [col])) ; + cdeg = Ap [col+1] - Ap [col] ; + ASSERT (cdeg >= 0) ; + if (cdeg > 0) + { + usage += GET_ELEMENT_SIZE (cdeg, 1) + 1 ; + *dusage += DGET_ELEMENT_SIZE (cdeg, 1) + 1 ; + } + } + + DEBUG0 (("UMF_kernel_init_usage : original elements: "ID" %g\n", + usage, *dusage)) ; + + /* tuples: */ + usage += UMF_build_tuples_usage ((Int *) NULL, (Int *) NULL, + Row_degree, Row_degree, n_row, n_col, dusage) ; + + DEBUG0 (("UMF_kernel_init_usage : all: "ID" %g\n", usage, *dusage)) ; + return (usage) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_kernel_wrapup.c b/src/sparse-matrix/umfpack/umf_kernel_wrapup.c new file mode 100644 index 0000000..f686271 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_kernel_wrapup.c @@ -0,0 +1,356 @@ +/* ========================================================================== */ +/* === UMF_kernel_wrapup ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* The matrix is factorized. Finish the LU data structure. */ + +#include "umf_internal.h" + +GLOBAL void UMF_kernel_wrapup +( + NumericType *Numeric, + SymbolicType *Symbolic, + WorkType *Work +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int i, k, col, row, llen, ulen, *ip, *Rperm, *Cperm, *Lilen, npiv, lp, + *Uilen, *Lip, *Uip, *Cperm_init, up, pivrow, pivcol, *Lpos, *Upos, *Wr, + *Wc, *Wp, *Frpos, *Fcpos, *Row_degree, *Col_degree, *Rperm_init, + n_row, n_col, n_inner ; + Entry *D ; + +#ifndef NDEBUG + UMF_dump_matrix (Numeric, Work, FALSE) ; +#endif + + DEBUG0 (("Kernel complete, Starting Kernel wrapup\n")) ; + n_row = Symbolic->n_row ; + n_col = Symbolic->n_col ; + n_inner = MIN (n_row, n_col) ; + Rperm = Numeric->Rperm ; + Cperm = Numeric->Cperm ; + Lilen = Numeric->Lilen ; + Uilen = Numeric->Uilen ; + Upos = Numeric->Upos ; + Lpos = Numeric->Lpos ; + Lip = Numeric->Lip ; + Uip = Numeric->Uip ; + D = Numeric->D ; + + npiv = Work->npiv ; + Numeric->npiv = npiv ; + Numeric->ulen = Work->ulen ; + + ASSERT (n_row == Numeric->n_row) ; + ASSERT (n_col == Symbolic->n_col) ; + DEBUG0 (("Wrap-up: npiv "ID" ulen "ID"\n", npiv, Numeric->ulen)) ; + ASSERT (npiv <= n_inner) ; + + /* this will be nonzero only if matrix is singular or rectangular */ + ASSERT (IMPLIES (npiv == n_col, Work->ulen == 0)) ; + + /* ---------------------------------------------------------------------- */ + /* check if matrix is singular or rectangular */ + /* ---------------------------------------------------------------------- */ + + Col_degree = Cperm ; /* for NON_PIVOTAL_COL macro */ + Row_degree = Rperm ; /* for NON_PIVOTAL_ROW macro */ + + if (npiv < n_row) + { + /* finalize the row permutation */ + k = npiv ; + for (row = 0 ; row < n_row ; row++) + { + if (NON_PIVOTAL_ROW (row)) + { + Rperm [row] = ONES_COMPLEMENT (k) ; + ASSERT (!NON_PIVOTAL_ROW (row)) ; + Lpos [row] = EMPTY ; + Uip [row] = EMPTY ; + Uilen [row] = 0 ; + k++ ; + } + } + ASSERT (k == n_row) ; + } + + if (npiv < n_col) + { + /* finalize the col permutation */ + k = npiv ; + for (col = 0 ; col < n_col ; col++) + { + if (NON_PIVOTAL_COL (col)) + { + Cperm [col] = ONES_COMPLEMENT (k) ; + ASSERT (!NON_PIVOTAL_COL (col)) ; + Upos [col] = EMPTY ; + Lip [col] = EMPTY ; + Lilen [col] = 0 ; + k++ ; + } + } + ASSERT (k == n_col) ; + } + + if (npiv < n_inner) + { + /* finalize the diagonal of U */ + for (k = npiv ; k < n_inner ; k++) + { + CLEAR (D [k]) ; + } + } + + /* save the pattern of the last row of U */ + if (Numeric->ulen > 0) + { + Numeric->Upattern = Work->Upattern ; + Work->Upattern = (Int *) NULL ; + } + + ASSERT (Numeric->nnzpiv <= npiv) ; + if (Numeric->nnzpiv < n_inner) + { + /* the rest of the diagonal is zero, so min_udiag becomes 0 */ + Numeric->min_udiag = 0.0 ; + } + + /* ---------------------------------------------------------------------- */ + /* size n_row, n_col workspaces that can be used here: */ + /* ---------------------------------------------------------------------- */ + + Frpos = Work->Frpos ; /* of size n_row+1 */ + Fcpos = Work->Fcpos ; /* of size n_col+1 */ + Wp = Work->Wp ; /* of size MAX(n_row,n_col)+1 */ + /* Work->Upattern ; cannot be used (in Numeric) */ + Wr = Work->Lpattern ; /* of size n_row+1 */ + Wc = Work->E ; /* of size n_col+n_inner+1 */ + + /* ---------------------------------------------------------------------- */ + /* construct Rperm from inverse permutations */ + /* ---------------------------------------------------------------------- */ + + /* use Frpos for temporary copy of inverse row permutation [ */ + + for (pivrow = 0 ; pivrow < n_row ; pivrow++) + { + k = Rperm [pivrow] ; + ASSERT (k < 0) ; + k = ONES_COMPLEMENT (k) ; + ASSERT (k >= 0 && k < n_row) ; + Wp [k] = pivrow ; + Frpos [pivrow] = k ; + } + for (k = 0 ; k < n_row ; k++) + { + Rperm [k] = Wp [k] ; + } + + /* ---------------------------------------------------------------------- */ + /* construct Cperm from inverse permutation */ + /* ---------------------------------------------------------------------- */ + + /* use Fcpos for temporary copy of inverse column permutation [ */ + + for (pivcol = 0 ; pivcol < n_col ; pivcol++) + { + k = Cperm [pivcol] ; + ASSERT (k < 0) ; + k = ONES_COMPLEMENT (k) ; + ASSERT (k >= 0 && k < n_col) ; + Wp [k] = pivcol ; + /* save a copy of the inverse column permutation in Fcpos */ + Fcpos [pivcol] = k ; + } + for (k = 0 ; k < n_col ; k++) + { + Cperm [k] = Wp [k] ; + } + +#ifndef NDEBUG + for (k = 0 ; k < n_col ; k++) + { + col = Cperm [k] ; + ASSERT (col >= 0 && col < n_col) ; + ASSERT (Fcpos [col] == k) ; /* col is the kth pivot */ + } + for (k = 0 ; k < n_row ; k++) + { + row = Rperm [k] ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (Frpos [row] == k) ; /* row is the kth pivot */ + } +#endif + +#ifndef NDEBUG + UMF_dump_lu (Numeric) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* permute Lpos, Upos, Lilen, Lip, Uilen, and Uip */ + /* ---------------------------------------------------------------------- */ + + for (k = 0 ; k < npiv ; k++) + { + pivrow = Rperm [k] ; + Wr [k] = Uilen [pivrow] ; + Wp [k] = Uip [pivrow] ; + } + + for (k = 0 ; k < npiv ; k++) + { + Uilen [k] = Wr [k] ; + Uip [k] = Wp [k] ; + } + + for (k = 0 ; k < npiv ; k++) + { + pivrow = Rperm [k] ; + Wp [k] = Lpos [pivrow] ; + } + + for (k = 0 ; k < npiv ; k++) + { + Lpos [k] = Wp [k] ; + } + + for (k = 0 ; k < npiv ; k++) + { + pivcol = Cperm [k] ; + Wc [k] = Lilen [pivcol] ; + Wp [k] = Lip [pivcol] ; + } + + for (k = 0 ; k < npiv ; k++) + { + Lilen [k] = Wc [k] ; + Lip [k] = Wp [k] ; + } + + for (k = 0 ; k < npiv ; k++) + { + pivcol = Cperm [k] ; + Wp [k] = Upos [pivcol] ; + } + + for (k = 0 ; k < npiv ; k++) + { + Upos [k] = Wp [k] ; + } + + /* ---------------------------------------------------------------------- */ + /* terminate the last Uchain and last Lchain */ + /* ---------------------------------------------------------------------- */ + + Upos [npiv] = EMPTY ; + Lpos [npiv] = EMPTY ; + Uip [npiv] = EMPTY ; + Lip [npiv] = EMPTY ; + Uilen [npiv] = 0 ; + Lilen [npiv] = 0 ; + + /* ---------------------------------------------------------------------- */ + /* convert U to the new pivot order */ + /* ---------------------------------------------------------------------- */ + + for (k = 0 ; k < npiv ; k++) + { + up = Uip [k] ; + if (up < 0) + { + /* this is the start of a new Uchain (with a pattern) */ + ulen = Uilen [k] ; + DEBUG4 (("K "ID" New U. ulen "ID" End_Uchain 1\n", k, ulen)) ; + if (ulen > 0) + { + up = -up ; + ip = (Int *) (Numeric->Memory + up) ; + for (i = 0 ; i < ulen ; i++) + { + col = *ip ; + DEBUG4 ((" old col "ID" new col "ID"\n", col, Fcpos [col])); + ASSERT (col >= 0 && col < n_col) ; + *ip++ = Fcpos [col] ; + } + } + } + } + + ulen = Numeric->ulen ; + if (ulen > 0) + { + /* convert last pivot row of U to the new pivot order */ + DEBUG4 (("K "ID" (last)\n")) ; + for (i = 0 ; i < ulen ; i++) + { + col = Numeric->Upattern [i] ; + DEBUG4 ((" old col "ID" new col "ID"\n", col, Fcpos [col])) ; + Numeric->Upattern [i] = Fcpos [col] ; + } + } + + /* Fcpos no longer needed ] */ + + /* ---------------------------------------------------------------------- */ + /* convert L to the new pivot order */ + /* ---------------------------------------------------------------------- */ + + for (k = 0 ; k < npiv ; k++) + { + llen = Lilen [k] ; + DEBUG4 (("K "ID" New L. llen "ID" \n", k, llen)) ; + if (llen > 0) + { + lp = Lip [k] ; + if (lp < 0) + { + /* this starts a new Lchain */ + lp = -lp ; + } + ip = (Int *) (Numeric->Memory + lp) ; + for (i = 0 ; i < llen ; i++) + { + row = *ip ; + DEBUG4 ((" old row "ID" new row "ID"\n", row, Frpos [row])) ; + ASSERT (row >= 0 && row < n_row) ; + *ip++ = Frpos [row] ; + } + } + } + + /* Frpos no longer needed ] */ + + /* ---------------------------------------------------------------------- */ + /* combine symbolic and numeric permutations */ + /* ---------------------------------------------------------------------- */ + + Cperm_init = Symbolic->Cperm_init ; + Rperm_init = Symbolic->Rperm_init ; + + for (k = 0 ; k < n_row ; k++) + { + Rperm [k] = Rperm_init [Rperm [k]] ; + } + + for (k = 0 ; k < n_col ; k++) + { + Cperm [k] = Cperm_init [Cperm [k]] ; + } + + /* Work object will be freed immediately upon return (to UMF_kernel */ + /* and then to UMFPACK_numeric). */ +} diff --git a/src/sparse-matrix/umfpack/umf_local_search.c b/src/sparse-matrix/umfpack/umf_local_search.c new file mode 100644 index 0000000..d70ba6f --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_local_search.c @@ -0,0 +1,1487 @@ +/* ========================================================================== */ +/* === UMF_local_search ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Perform pivot search to find pivot row and pivot column. + + The pivot column is selected from the candidate set (a supercolumn from + colamd), and then removed from that set. + + Called by kernel. + + Modifies front, but keeps current Frows and Fcols unchanged (new entries + are appended). This can be undone if the current front must be written + out after the pivot is found. The numerical values of the front are + unchanged. + + The current frontal matrix might be empty (Fx not allocated, Work->fnrows + and Work->fncols are both zero). + + Returns UMFPACK_OK if successful, or UMFPACK_WARNING_singular_matrix, or + UMFPACK_ERROR_different_pattern if not. + +*/ + +/* ========================================================================== */ + +#include "umf_internal.h" +#include "umf_row_search.h" +#include "umf_mem_free_tail_block.h" + +#define IN 0 +#define OUT 1 + +#define IN_IN 0 +#define IN_OUT 1 +#define OUT_IN 2 +#define OUT_OUT 3 + +/* ========================================================================== */ + +GLOBAL Int UMF_local_search +( + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Entry *Fx, *Fl, *Fu, *Fs, *C, *Fd ; + double relax, relax2, relax3 ; + Int pos, nrows, *Cols, *Rows, e, f, jmax, status, max_cdeg, fnzeros, + j, col, i, row, cdeg [2], rdeg [2][2], fnpiv, nothing [2], new_LUsize, + pivrow [2][2], pivcol [2], *Wp, *Fcpos, *Frpos, new_fnzeros, + *Wm, *Wio, *Woi, *Woo, *Frows, *Fcols, fnrows, fncols, *E, + deg, nr_in, nc, src, dest, thiscost, bestcost, nr_out, *Wcol, do_update, + extra_cols, extra_rows, extra_zeros, relaxed_front, do_extend, + fnrows_max, fncols_max, tpi, *Col_tuples, *Col_degree, *Col_tlen, + jj, jcand [2], freebie [2], did_rowmerge ; + Unit *Memory, *p ; + Tuple *tp, *tpend, *tp1, *tp2 ; + Element *ep ; + +#ifndef NDEBUG + Int debug_ok, n_row, n_col, *Row_degree ; + Row_degree = Numeric->Rperm ; /* for NON_PIVOTAL_ROW macro only */ +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + Memory = Numeric->Memory ; + E = Work->E ; + Col_degree = Numeric->Cperm ; + + Col_tuples = Numeric->Lip ; + Col_tlen = Numeric->Lilen ; + + Wp = Work->Wp ; + Wm = Work->Wm ; + Woi = Work->Woi ; + Wio = Work->Wio ; + Woo = Work->Woo ; /* size at least fncols_max */ + Fx = Work->Fx ; + Fcpos = Work->Fcpos ; + Frpos = Work->Frpos ; + Frows = Work->Frows ; + Fcols = Work->Fcols ; + fnrows = Work->fnrows ; + fncols = Work->fncols ; + fnrows_max = Work->fnrows_max ; + fncols_max = Work->fncols_max ; + fnpiv = Work->fnpiv ; + nothing [0] = EMPTY ; + nothing [1] = EMPTY ; + relax = Numeric->relax ; + relax2 = Numeric->relax2 ; + relax3 = Numeric->relax3 ; + fnzeros = Work->fnzeros ; + new_fnzeros = fnzeros ; + jj = EMPTY ; + + /* The pivot column degree cannot exceed max_cdeg */ + max_cdeg = fnrows_max - fnpiv ; + +#ifndef NDEBUG + n_row = Work->n_row ; + n_col = Work->n_col ; + DEBUG2 (("LOCAL SEARCH: current frontal matrix: # candidates: "ID"\n", + Work->ncand)) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; + if (UMF_debug > 0 || MAX (n_row, n_col) < 1000) + { + for (i = 0 ; i < MAX (n_row, n_col) ; i++) + { + ASSERT (Wp [i] < 0) ; + ASSERT (Wp [i] > Work->Wpflag) ; + } + } + ASSERT (Work->Wpflag < EMPTY) ; + DEBUG2 (("candidates: ")) ; + for (j = 0 ; j < Work->ncand ; j++) DEBUG2 ((ID" ", Work->Candidates [j])); + DEBUG2 (("\n")) ; +#endif + + /* ====================================================================== */ + /* === PIVOT SEARCH ===================================================== */ + /* ====================================================================== */ + + /* initialize */ + + pivcol [IN] = EMPTY ; + pivcol [OUT] = EMPTY ; + + /* Int_MAX is defined in umfpack.h */ + cdeg [IN] = Int_MAX ; + cdeg [OUT] = Int_MAX ; + + pivrow [IN][IN] = EMPTY ; + pivrow [IN][OUT] = EMPTY ; + pivrow [OUT][IN] = EMPTY ; + pivrow [OUT][OUT] = EMPTY ; + + rdeg [IN][IN] = Int_MAX ; + rdeg [IN][OUT] = Int_MAX ; + rdeg [OUT][IN] = Int_MAX ; + rdeg [OUT][OUT] = Int_MAX ; + + freebie [IN] = FALSE ; + freebie [OUT] = FALSE ; + + Work->pivot_case = EMPTY ; + bestcost = EMPTY ; + + nr_out = EMPTY ; + nr_in = EMPTY ; + + jcand [IN] = EMPTY ; + jcand [OUT] = EMPTY ; + + /* ---------------------------------------------------------------------- */ + /* find shortest column in the front, and shortest column not in the */ + /* front, from the candidate pivot column set */ + /* ---------------------------------------------------------------------- */ + + /* If there are too many candidates, then only look at the first */ + /* MAX_CANDIDATES of them. Otherwise, if there are O(n) candidates, */ + /* this code could take O(n^2) time. */ + + jmax = MIN (MAX_CANDIDATES, Work->ncand) ; + +#ifndef NDEBUG + DEBUG2 (("Max candidates "ID", Work->ncand "ID" jmax "ID"\n", + MAX_CANDIDATES, Work->ncand, jmax)) ; +#endif + + /* get the first candidate */ + + col = Work->Candidates [0] ; + DEBUG3 (("Pivot column candidate: "ID" j = "ID"\n", col, j)) ; + ASSERT (col >= 0 && col < n_col) ; + deg = Col_degree [col] ; + +#ifndef NDEBUG + DEBUG3 (("Pivot column candidate: "ID" cost: "ID" Fcpos[col] "ID"\n", + col, deg, Fcpos [col])) ; + UMF_dump_rowcol (1, Numeric, Work, col, TRUE) ; +#endif + + if (Fcpos [col] >= 0) + { + /* best column in front, so far */ + pivcol [IN] = col ; + cdeg [IN] = deg ; + jcand [IN] = 0 ; + } + else + { + /* best column not in front, so far */ + pivcol [OUT] = col ; + cdeg [OUT] = deg ; + jcand [OUT] = 0 ; + } + + /* look at the rest of the candidates */ + + for (j = 1 ; j < jmax ; j++) + { + col = Work->Candidates [j] ; + DEBUG3 (("Pivot column candidate: "ID" j = "ID"\n", col, j)) ; + ASSERT (col >= 0 && col < n_col) ; + deg = Col_degree [col] ; +#ifndef NDEBUG + DEBUG3 (("Pivot column candidate: "ID" cost: "ID" Fcpos[col] "ID"\n", + col, deg, Fcpos [col])) ; + UMF_dump_rowcol (1, Numeric, Work, col, TRUE) ; +#endif + if (Fcpos [col] >= 0) + { +#ifndef NDEBUG + Int fs ; + fs = Fcpos [col] / fnrows_max ; + ASSERT (fs >= 0 && fs < fncols) ; +#endif + if (deg < cdeg [IN] || (deg == cdeg [IN] && col < pivcol [IN])) + { + /* best column in front, so far */ + pivcol [IN] = col ; + cdeg [IN] = deg ; + jcand [IN] = j ; + } + } + else + { + if (deg < cdeg [OUT] || (deg == cdeg [OUT] && col < pivcol [OUT])) + { + /* best column not in front, so far */ + pivcol [OUT] = col ; + cdeg [OUT] = deg ; + jcand [OUT] = j ; + } + } + } + + DEBUG2 (("Pivcol in "ID" out "ID"\n", pivcol [IN], pivcol [OUT])) ; + ASSERT ((pivcol [IN] >= 0 && pivcol [IN] < n_col) + || (pivcol [OUT] >= 0 && pivcol [OUT] < n_col)) ; + + cdeg [IN] = EMPTY ; + cdeg [OUT] = EMPTY ; + + /* ---------------------------------------------------------------------- */ + /* construct candidate column in front, and search for pivot rows */ + /* ---------------------------------------------------------------------- */ + + if (pivcol [IN] != EMPTY) + { + +#ifndef NDEBUG + DEBUG2 (("col[IN] column "ID" in front at position = "ID"\n", + pivcol [IN], Fcpos [pivcol [IN]])) ; + UMF_dump_rowcol (1, Numeric, Work, pivcol [IN], TRUE) ; +#endif + + /* the only way we can have a pivcol[IN] is if the front is not empty */ + ASSERT (fnrows > 0 && fncols > 0) ; + + Fs = Fx + Fcpos [pivcol [IN]] ; + + /* ------------------------------------------------------------------ */ + /* update column in front (permanent) */ + /* ------------------------------------------------------------------ */ + + DEBUG6 (("Update pivot column:\n")) ; + Fl = Fx + (fncols_max - fnpiv) * fnrows_max ; + Fu = Fs + (fnrows_max - fnpiv) ; + +#ifdef USE_NO_BLAS + + /* no BLAS available - use plain C code instead */ + for (j = 0 ; j < fnpiv ; j++) + { + Entry Fuj ; + Fuj = Fu [j] ; + if (IS_NONZERO (Fuj)) + { + for (i = 0 ; i < fnrows ; i++) + { + /* Fs [i] -= Fuj * Fl [i+j*fnrows_max] ; */ + MULT_SUB (Fs [i], Fuj, Fl [i+j*fnrows_max]) ; + } + } + } + +#else + + BLAS_GEMV_COL (fnrows, fnpiv, Fl, Fu, Fs, fnrows_max) ; + +#endif /* USE_NO_BLAS */ + + /* zero out the column of U, in case this doesn't become pivot column */ + for (j = 0 ; j < fnpiv ; j++) + { + CLEAR (Fu [j]) ; + } + + /* ------------------------------------------------------------------ */ + +#ifndef NDEBUG + DEBUG6 (("Fs after update: fnrows="ID"\n", fnrows)) ; + DEBUG6 ((" Work->fnpiv="ID" \n", fnpiv)) ; + for (i = 0 ; i < fnrows ; i++) + { + DEBUG6 ((ID" "ID" "ID, i, Frows [i], Frpos [Frows [i]])) ; + EDEBUG6 (Fs [i]) ; + DEBUG6 (("\n")) ; + } +#endif + + /* ------------------------------------------------------------------ */ + /* construct the candidate column in the front */ + /* ------------------------------------------------------------------ */ + + cdeg [IN] = fnrows ; + +#ifndef NDEBUG + /* check Frpos */ + DEBUG5 (("COL ASSEMBLE: cdeg "ID"\nREDUCE COL in "ID" max_cdeg "ID"\n", + cdeg [IN], pivcol [IN], max_cdeg)) ; + for (i = 0 ; i < cdeg [IN] ; i++) + { + row = Frows [i] ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (Frpos [row] == i) ; + } + if (UMF_debug > 0 || n_row < 1000) + { + Int cnt = cdeg [IN] ; + for (row = 0 ; row < n_row ; row++) + { + if (Frpos [row] == EMPTY) cnt++ ; + } + ASSERT (cnt == n_row) ; + } +#endif + + ASSERT (pivcol [IN] >= 0 && pivcol [IN] < n_col) ; + ASSERT (NON_PIVOTAL_COL (pivcol [IN])) ; + + tpi = Col_tuples [pivcol [IN]] ; + if (tpi) + { + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Col_tlen [pivcol [IN]] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + if (Cols [f] == EMPTY) continue ; /* column already assembled */ + ASSERT (pivcol [IN] == Cols [f]) ; + + Rows = Cols + ep->ncols ; + nrows = ep->nrows ; + p += UNITS (Int, ep->ncols + nrows) ; + C = ((Entry *) p) + f * nrows ; + + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + if (row >= 0) /* skip this if already gone from element */ + { + ASSERT (row < n_row) ; + pos = Frpos [row] ; + if (pos < 0) + { + if (cdeg [IN] >= max_cdeg) + { + return (UMFPACK_ERROR_different_pattern) ; + } + /* new entry in the pattern - save Frpos */ + Frpos [row] = cdeg [IN] ; + Frows [cdeg [IN]] = row ; + + /* this will be discarded */ + Fs [cdeg [IN]++] = C [i] ; + } + else + { + /* entry already in pattern - sum the values */ + /* Fs [pos] += C [i] ; */ + ASSEMBLE (Fs [pos], C [i]) ; + if (pos < fnrows) + { + /* just did a permanent assembly of a single */ + /* entry into the current front */ + CLEAR (C [i]) ; + } + } + } + } + + *tp2++ = *tp ; /* leave the tuple in the list */ + + } + Col_tlen [pivcol [IN]] = tp2 - tp1 ; + } + + /* ------------------------------------------------------------------ */ + +#ifndef NDEBUG + DEBUG4 (("Reduced column: cdeg in "ID"\n", cdeg [IN])) ; + for (i = 0 ; i < cdeg [IN] ; i++) + { + DEBUG6 ((" "ID" "ID" "ID, i, Frows [i], Frpos [Frows [i]])) ; + EDEBUG6 (Fs [i]) ; + DEBUG6 (("\n")) ; + ASSERT (i == Frpos [Frows [i]]) ; + } + ASSERT (cdeg [IN] + fnpiv <= fnrows_max) ; +#endif + + /* ------------------------------------------------------------------ */ + /* cdeg [IN] is now the exact degree of this column */ + /* ------------------------------------------------------------------ */ + + nr_in = cdeg [IN] - fnrows ; + + /* since there are no 0-by-x fronts, if there is a pivcol [IN] the */ + /* front must have at least one row. */ + ASSERT (cdeg [IN] > 0) ; + + /* new degree of pivcol [IN], excluding current front is nr_in */ + /* column expands by nr_in rows */ + + /* ------------------------------------------------------------------ */ + /* search for two candidate pivot rows */ + /* ------------------------------------------------------------------ */ + + /* for the IN_IN pivot row (if any), */ + /* extend the pattern in place, using Fcols */ + status = UMF_row_search (Numeric, Work, Symbolic, cdeg [IN], Frows, + pivrow [IN], rdeg [IN], Fcols, Wio, nothing, Fs, pivcol [IN], + freebie) ; + ASSERT (!freebie [IN] && !freebie [OUT]) ; + + /* ------------------------------------------------------------------ */ + /* fatal error if matrix pattern has changed since symbolic analysis */ + /* ------------------------------------------------------------------ */ + + if (status == UMFPACK_ERROR_different_pattern) + { + return (UMFPACK_ERROR_different_pattern) ; + } + + /* ------------------------------------------------------------------ */ + /* we now must have a structural pivot */ + /* ------------------------------------------------------------------ */ + + /* Since the pivcol[IN] exists, there must be at least one row in the */ + /* current frontal matrix, and so we must have found a structural */ + /* pivot. The numerical value might be zero, of course. */ + + ASSERT (status != UMFPACK_WARNING_singular_matrix) ; + + /* ------------------------------------------------------------------ */ + /* evaluate IN_IN option */ + /* ------------------------------------------------------------------ */ + + if (pivrow [IN][IN] != EMPTY) + { + /* the current front would become an (implicit) LUson */ + /* cost is how much the current front would expand */ + + /* pivrow[IN][IN] candidates are not found via row merge search */ + + ASSERT (cdeg [IN] > 0) ; + nc = rdeg [IN][IN] - fncols ; + + thiscost = + /* each column in front (except pivot column) grows by nr_in: */ + (nr_in * (fncols - 1)) + + /* new columns not in old front: */ + (nc * (cdeg [IN] - 1)) ; + + /* no extra cost to relaxed amalgamation */ + + ASSERT (fnrows + nr_in == cdeg [IN]) ; + ASSERT (fncols + nc == rdeg [IN][IN]) ; + + /* relaxed_front = ((fncols-1) + nc) * ((fnrows-1) + nr_in) ; */ + do_extend = TRUE ; + + DEBUG2 (("Evaluating option IN-IN:\n")) ; + DEBUG2 (("Work->fnzeros "ID" fnpiv "ID" nr_in "ID" nc "ID"\n", + Work->fnzeros, fnpiv, nr_in, nc)) ; + DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ; + + /* determine if BLAS-3 update should be applied before extending. */ + /* update if too many zero entries accumulate in the LU block */ + fnzeros = Work->fnzeros + fnpiv * (nr_in + nc) ; + + DEBUG2 (("fnzeros "ID"\n", fnzeros)) ; + + new_LUsize = (fnpiv+1) * (fnrows + nr_in + fncols + nc) ; + + DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ; + DEBUG2 (("relax2 %g\n", relax2)) ; + + /* relax2 parameter uses a double relop, but ignore NaN case: */ + do_update = (((double) fnzeros) / ((double) new_LUsize)) > relax2 ; + + DEBUG2 (("do_update "ID"\n", do_update)) + + DEBUG2 (("option IN IN : nr "ID" nc "ID" cost "ID"(0) relax "ID + "\n", nr_in, nc, thiscost, do_extend)) ; + + /* this is the best option seen so far */ + Work->pivot_case = IN_IN ; + bestcost = thiscost ; + + /* do the amalgamation and extend the front */ + Work->do_extend = do_extend ; + Work->do_update = do_update ; + new_fnzeros = fnzeros ; + + } + + /* ------------------------------------------------------------------ */ + /* evaluate IN_OUT option */ + /* ------------------------------------------------------------------ */ + + if (pivrow [IN][OUT] != EMPTY) + { + /* the current front would become a Uson of the new front */ + + ASSERT (cdeg [IN] > 0) ; + + /* must be at least one row outside the front */ + /* (the pivrow [IN][OUT] itself) */ + ASSERT (nr_in >= 1) ; + + /* count columns not in current front */ + nc = 0 ; +#ifndef NDEBUG + debug_ok = FALSE ; +#endif + for (i = 0 ; i < rdeg [IN][OUT] ; i++) + { + col = Wio [i] ; + DEBUG6 (("counting col "ID" Fcpos[] = "ID"\n", col, + Fcpos [col])) ; + ASSERT (col >= 0 && col < n_col) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + if (Fcpos [col] < 0) nc++ ; +#ifndef NDEBUG + /* we must see the pivot column somewhere */ + if (col == pivcol [IN]) + { + ASSERT (Fcpos [col] >= 0) ; + debug_ok = TRUE ; + } +#endif + } + ASSERT (debug_ok) ; + + thiscost = + /* each row in front grows by nc: */ + (nc * fnrows) + + /* new rows not affected by front: */ + ((nr_in-1) * (rdeg [IN][OUT]-1)) ; + + /* check the cost of relaxed IN_OUT amalgamation */ + + extra_cols = ((fncols-1) + nc ) - (rdeg [IN][OUT] - 1) ; + ASSERT (extra_cols >= 0) ; + ASSERT (fncols + nc == extra_cols + rdeg [IN][OUT]) ; + extra_zeros = (nr_in-1) * extra_cols ; /* symbolic fill-in */ + + ASSERT (fnrows + nr_in == cdeg [IN]) ; + ASSERT (fncols + nc == rdeg [IN][OUT] + extra_cols) ; + + /* size of relaxed front: */ + relaxed_front = ((fncols-1) + nc) * (fnrows + (nr_in-1)) ; + + /* do relaxed amalgamation if the extra zeros are no more */ + /* than a fraction (default 0.25) of the relaxed front */ + /* if relax = 0: no extra zeros allowed */ + /* if relax = +inf: always amalgamate */ + + /* relax parameter uses a double relop, but ignore NaN case: */ + do_extend = ((double) extra_zeros) + < (relax * (double) relaxed_front) ; + + if (do_extend) + { + /* count the cost of relaxed amalgamation */ + thiscost += extra_zeros ; + + DEBUG2 (("Evaluating option IN-OUT:\n")) ; + DEBUG2 (("Work->fnzeros "ID" fnpiv "ID" nr_in "ID" nc "ID"\n", + Work->fnzeros, fnpiv, nr_in, nc)) ; + DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ; + + /* determine if BLAS-3 update to be applied before extending. */ + /* update if too many zero entries accumulate in the LU block */ + fnzeros = Work->fnzeros + fnpiv * (nr_in + nc) ; + + DEBUG2 (("fnzeros "ID"\n", fnzeros)) ; + + new_LUsize = (fnpiv+1) * (fnrows + nr_in + fncols + nc) ; + + DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ; + DEBUG2 (("relax3 %g\n", relax3)) ; + + /* relax3 parameter uses a double relop, but ignore NaN case: */ + do_update = (((double) fnzeros) / ((double) new_LUsize)) > relax3 ; + + DEBUG2 (("do_update "ID"\n", do_update)) + } + else + { + do_update = TRUE ; + fnzeros = 0 ; + DEBUG2 (("IN-OUT do_update forced true: "ID"\n", do_update)) + } + + DEBUG2 (("option IN OUT: nr "ID" nc "ID" cost "ID"("ID") relax "ID + "\n", nr_in, nc, thiscost, extra_zeros, do_extend)) ; + + if (bestcost == EMPTY || thiscost < bestcost) + { + /* this is the best option seen so far */ + Work->pivot_case = IN_OUT ; + bestcost = thiscost ; + Work->do_extend = do_extend ; + Work->do_update = do_update ; + new_fnzeros = fnzeros ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* construct candidate column not in front, and search for pivot rows */ + /* ---------------------------------------------------------------------- */ + + if (bestcost != 0 && pivcol [OUT] != EMPTY) + { + +#ifndef NDEBUG + DEBUG2 (("out_col column "ID" NOT in front at position = "ID"\n", + pivcol [OUT], Fcpos [pivcol [OUT]])) ; + UMF_dump_rowcol (1, Numeric, Work, pivcol [OUT], TRUE) ; +#endif + + /* Find an empty column in the current frontal matrix to use */ + /* as workspace. There must be one; otherwise, there couldn't be a */ + /* candidate pivot column outside the current front. */ + + /* Fd: destination of final pivot column, currently unoccupied */ + /* Use Fd as temporary workspace to construct the pivcol [OUT] */ + Fd = Fx + (fncols_max - fnpiv - 1) * fnrows_max ; + + ASSERT (fncols + fnpiv < fncols_max) ; + + /* ------------------------------------------------------------------ */ + /* construct the candidate column (currently not in the front) */ + /* ------------------------------------------------------------------ */ + + /* Construct the column in Fd, Wm, using Wp for the positions: */ + /* Wm [0..cdeg [OUT]-1] list of row indices in the column */ + /* Fd [0..cdeg [OUT]-1] list of corresponding numerical values */ + /* Wp [0..n-1] starts as all negative, and ends that way too. */ + + cdeg [OUT] = 0 ; + +#ifndef NDEBUG + /* check Wp */ + DEBUG5 (("COL ASSEMBLE: cdeg 0\nREDUCE COL out "ID"\n", pivcol [OUT])) ; + if (UMF_debug > 0 || MAX (n_row, n_col) < 1000) + { + for (i = 0 ; i < MAX (n_row, n_col) ; i++) + { + ASSERT (Wp [i] < 0) ; + ASSERT (Wp [i] > Work->Wpflag) ; + } + } + DEBUG5 (("max_cdeg: "ID"\n", max_cdeg)) ; +#endif + + ASSERT (pivcol [OUT] >= 0 && pivcol [OUT] < n_col) ; + ASSERT (NON_PIVOTAL_COL (pivcol [OUT])) ; + + tpi = Col_tuples [pivcol [OUT]] ; + if (tpi) + { + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Col_tlen [pivcol [OUT]] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) continue ; /* element already deallocated */ + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + if (Cols [f] == EMPTY) continue ; /* column already assembled */ + ASSERT (pivcol [OUT] == Cols [f]) ; + + Rows = Cols + ep->ncols ; + nrows = ep->nrows ; + p += UNITS (Int, ep->ncols + nrows) ; + C = ((Entry *) p) + f * nrows ; + + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + if (row >= 0) /* skip this if already gone from element */ + { + ASSERT (row < n_row) ; + pos = Wp [row] ; + if (pos < 0) + { + if (cdeg [OUT] >= max_cdeg) + { + return (UMFPACK_ERROR_different_pattern) ; + } + /* new entry in the pattern - save Wp */ + Wp [row] = cdeg [OUT] ; + Wm [cdeg [OUT]] = row ; + Fd [cdeg [OUT]++] = C [i] ; + } + else + { + /* entry already in pattern - sum the values */ + /* Fd [pos] += C [i] ; */ + ASSEMBLE (Fd [pos], C [i]) ; + } + } + } + + *tp2++ = *tp ; /* leave the tuple in the list */ + } + Col_tlen [pivcol [OUT]] = tp2 - tp1 ; + } + + /* ------------------------------------------------------------------ */ + +#ifndef NDEBUG + DEBUG4 (("Reduced column: cdeg out "ID"\n", cdeg [OUT])) ; + for (i = 0 ; i < cdeg [OUT] ; i++) + { + DEBUG6 ((" "ID" "ID" "ID, i, Wm [i], Wp [Wm [i]])) ; + EDEBUG6 (Fd [i]) ; + DEBUG6 (("\n")) ; + ASSERT (i == Wp [Wm [i]]) ; + } +#endif + + /* ------------------------------------------------------------------ */ + /* Clear Wp */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < cdeg [OUT] ; i++) + { + Wp [Wm [i]] = EMPTY ; /* clear Wp */ + } + + /* ------------------------------------------------------------------ */ + /* new degree of pivcol [OUT] is cdeg [OUT] */ + /* ------------------------------------------------------------------ */ + + /* search for two candidate pivot rows */ + status = UMF_row_search (Numeric, Work, Symbolic, cdeg [OUT], Wm, + pivrow [OUT], rdeg [OUT], Woi, Woo, pivrow [IN], Fd, pivcol [OUT], + freebie) ; + + /* ------------------------------------------------------------------ */ + /* fatal error if matrix pattern has changed since symbolic analysis */ + /* ------------------------------------------------------------------ */ + + if (status == UMFPACK_ERROR_different_pattern) + { + return (UMFPACK_ERROR_different_pattern) ; + } + + /* ------------------------------------------------------------------ */ + /* check for rectangular, singular matrix */ + /* ------------------------------------------------------------------ */ + + if (status == UMFPACK_WARNING_singular_matrix) + { + /* Pivot column is empty, and row-merge set is empty too. */ + /* The matrix is structurally singular. */ + + DEBUG0 (("Warning: pivcol [OUT]: "ID" discard\n", pivcol [OUT])) ; + + /* remove the failed pivcol [OUT] from candidate set */ + jj = jcand [OUT] ; + ASSERT (jj >= 0 && jj < jmax) ; + ASSERT (pivcol [OUT] == Work->Candidates [jj]) ; + if (Work->ncand > MAX_CANDIDATES) + { + Work->Candidates [jj] = Work->nextcand++ ; + } + else + { + col = Work->Candidates [Work->ncand - 1] ; + Work->Candidates [jj] = col ; + Work->Candidates [Work->ncand - 1] = 0 ; + if (col == pivcol [IN]) + { + ASSERT (jcand [IN] == Work->ncand-1) ; + jcand [IN] = jj ; + } + } + Work->ncand-- ; + + /* delete all of the tuples, and all contributions to this column */ + DEBUG1 (("Prune tuples of dead outcol: "ID"\n", pivcol [OUT])) ; + Col_tlen [pivcol [OUT]] = 0 ; + UMF_mem_free_tail_block (Numeric, Col_tuples [pivcol [OUT]]) ; + Col_tuples [pivcol [OUT]] = 0 ; + + if (pivcol [IN] == EMPTY) + { + /* no pivot found at all */ + return (UMFPACK_WARNING_singular_matrix) ; + } + } + + /* ------------------------------------------------------------------ */ + + /* Fd [0 .. cdeg[OUT]-1], the workspace column in the frontal matrix, */ + /* no longer needed */ + + if (freebie [IN]) + { + /* the "in" row is the same as the "in" row for the "in" column */ + Woi = Fcols ; + rdeg [OUT][IN] = rdeg [IN][IN] ; + DEBUG4 (("Freebie in, row "ID"\n", pivrow [IN][IN])) ; + } + + if (freebie [OUT]) + { + /* the "out" row is the same as the "out" row for the "in" column */ + Woo = Wio ; + rdeg [OUT][OUT] = rdeg [IN][OUT] ; + DEBUG4 (("Freebie out, row "ID"\n", pivrow [IN][OUT])) ; + } + + /* ------------------------------------------------------------------ */ + /* evaluate OUT_IN option */ + /* ------------------------------------------------------------------ */ + + if (pivrow [OUT][IN] != EMPTY) + { + /* the current front would become an Lson of the new front */ + + did_rowmerge = (cdeg [OUT] == 0) ; + if (did_rowmerge) + { + /* pivrow [OUT][IN] was found via row merge search */ + /* it is not (yet) in the pivot column pattern (add it now) */ + if (cdeg [OUT] >= max_cdeg) + { + return (UMFPACK_ERROR_different_pattern) ; + } + /* new entry in the pattern */ + Wm [0] = pivrow [OUT][IN] ; + cdeg [OUT] = 1 ; + ASSERT (nr_out == EMPTY) ; + } + + nc = rdeg [OUT][IN] - fncols ; + ASSERT (nc >= 1) ; + + /* count rows not in current front */ + nr_out = 0 ; +#ifndef NDEBUG + debug_ok = FALSE ; +#endif + for (i = 0 ; i < cdeg [OUT] ; i++) + { + row = Wm [i] ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (NON_PIVOTAL_ROW (row)) ; + if (Frpos [row] < 0 || Frpos [row] >= fnrows) nr_out++ ; +#ifndef NDEBUG + /* we must see the pivot row somewhere */ + if (row == pivrow [OUT][IN]) + { + ASSERT (Frpos [row] >= 0) ; + debug_ok = TRUE ; + } +#endif + } + ASSERT (debug_ok) ; + + thiscost = + /* each column in front grows by nr_out: */ + (nr_out * fncols) + + /* new cols not affected by front: */ + ((nc-1) * (cdeg [OUT]-1)) ; + + /* check the cost of relaxed OUT_IN amalgamation */ + + extra_rows = ((fnrows-1) + nr_out) - (cdeg [OUT] - 1) ; + ASSERT (extra_rows >= 0) ; + ASSERT (fnrows + nr_out == extra_rows + cdeg [OUT]) ; + extra_zeros = (nc-1) * extra_rows ; /* symbolic fill-in */ + + ASSERT (fnrows + nr_out == cdeg [OUT] + extra_rows) ; + ASSERT (fncols + nc == rdeg [OUT][IN]) ; + + /* size of relaxed front: */ + relaxed_front = (fncols + (nc-1)) * ((fnrows-1) + nr_out) ; + + /* do relaxed amalgamation if the extra zeros are no more */ + /* than a fraction (default 0.25) of the relaxed front */ + /* if relax = 0: no extra zeros allowed */ + /* if relax = +inf: always amalgamate */ + if (did_rowmerge) + { + do_extend = FALSE ; + } + else + { + /* relax parameter uses a double relop, but ignore NaN case: */ + do_extend = ((double) extra_zeros) + < (relax * (double) relaxed_front) ; + } + + if (do_extend) + { + /* count the cost of relaxed amalgamation */ + thiscost += extra_zeros ; + + /* determine if BLAS-3 update to be applied before extending. */ + /* update if too many zero entries accumulate in the LU block */ + + DEBUG2 (("Evaluating option OUT-IN:\n")) ; + DEBUG2 ((" Work->fnzeros "ID" fnpiv "ID" nr_out "ID" nc "ID"\n", + Work->fnzeros, fnpiv, nr_out, nc)) ; + DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ; + + fnzeros = Work->fnzeros + fnpiv * (nr_out + nc) ; + + DEBUG2 (("fnzeros "ID"\n", fnzeros)) ; + + new_LUsize = (fnpiv+1) * (fnrows + nr_out + fncols + nc) ; + + DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ; + DEBUG2 (("relax3 %g\n", relax3)) ; + + /* relax3 parameter uses a double relop, but ignore NaN case: */ + do_update = (((double) fnzeros) / ((double) new_LUsize)) > relax3 ; + + DEBUG2 (("do_update "ID"\n", do_update)) + + } + else + { + do_update = TRUE ; + fnzeros = 0 ; + DEBUG2 (("OUT-IN do_update forced true: "ID"\n", do_update)) + } + + DEBUG2 (("option OUT IN : nr "ID" nc "ID" cost "ID"("ID") relax "ID + "\n", nr_out, nc, thiscost, extra_zeros, do_extend)) ; + + if (bestcost == EMPTY || thiscost < bestcost) + { + /* this is the best option seen so far */ + Work->pivot_case = OUT_IN ; + bestcost = thiscost ; + Work->do_extend = do_extend ; + Work->do_update = do_update ; + new_fnzeros = fnzeros ; + } + } + + /* ------------------------------------------------------------------ */ + /* evaluate OUT_OUT option */ + /* ------------------------------------------------------------------ */ + + if (pivrow [OUT][OUT] != EMPTY) + { + + did_rowmerge = (cdeg [OUT] == 0) ; + if (did_rowmerge) + { + /* pivrow [OUT][OUT] was found via row merge search */ + /* it is not (yet) in the pivot column pattern (add it now) */ + if (cdeg [OUT] >= max_cdeg) + { + return (UMFPACK_ERROR_different_pattern) ; + } + /* new entry in the pattern */ + Wm [0] = pivrow [OUT][OUT] ; + cdeg [OUT] = 1 ; + ASSERT (nr_out == EMPTY) ; + nr_out = 1 ; + } + + /* count rows not in current front */ + if (nr_out == EMPTY) + { + nr_out = 0 ; +#ifndef NDEBUG + debug_ok = FALSE ; +#endif + for (i = 0 ; i < cdeg [OUT] ; i++) + { + row = Wm [i] ; + ASSERT (row >= 0 && row < n_row) ; + ASSERT (NON_PIVOTAL_ROW (row)) ; + if (Frpos [row] < 0 || Frpos [row] >= fnrows) nr_out++ ; +#ifndef NDEBUG + /* we must see the pivot row somewhere */ + if (row == pivrow [OUT][OUT]) + { + ASSERT (Frpos [row] < 0 || Frpos [row] >= fnrows) ; + debug_ok = TRUE ; + } +#endif + } + ASSERT (debug_ok) ; + } + + /* count columns not in current front */ + nc = 0 ; +#ifndef NDEBUG + debug_ok = FALSE ; +#endif + for (i = 0 ; i < rdeg [OUT][OUT] ; i++) + { + col = Woo [i] ; + ASSERT (col >= 0 && col < n_col) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + if (Fcpos [col] < 0) nc++ ; +#ifndef NDEBUG + /* we must see the pivot column somewhere */ + if (col == pivcol [OUT]) + { + ASSERT (Fcpos [col] < 0) ; + debug_ok = TRUE ; + } +#endif + } + ASSERT (debug_ok) ; + + extra_cols = (fncols + (nc-1)) - (rdeg [OUT][OUT] - 1) ; + extra_rows = (fnrows + (nr_out-1)) - (cdeg [OUT] - 1) ; + ASSERT (extra_rows >= 0) ; + ASSERT (extra_cols >= 0) ; + extra_zeros = ((nc-1) * extra_rows) + ((nr_out-1) * extra_cols) ; + + ASSERT (fnrows + nr_out == cdeg [OUT] + extra_rows) ; + ASSERT (fncols + nc == rdeg [OUT][OUT] + extra_cols) ; + + thiscost = + /* new columns: */ + ((nc-1) * (cdeg [OUT]-1)) + + /* old columns in front grow by nr_out-1: */ + ((nr_out-1) * (fncols - extra_cols)) ; + + /* size of relaxed front: */ + relaxed_front = (fncols + (nc-1)) * (fnrows + (nr_out-1)) ; + + /* do relaxed amalgamation if the extra zeros are no more */ + /* than a fraction (default 0.25) of the relaxed front */ + /* if relax = 0: no extra zeros allowed */ + /* if relax = +inf: always amalgamate */ + if (did_rowmerge) + { + do_extend = FALSE ; + } + else + { + /* relax parameter uses a double relop, but ignore NaN case: */ + do_extend = ((double) extra_zeros) + < (relax * (double) relaxed_front) ; + } + + if (do_extend) + { + /* count the cost of relaxed amalgamation */ + thiscost += extra_zeros ; + + DEBUG2 (("Evaluating option OUT-OUT:\n")) ; + DEBUG2 (("Work->fnzeros "ID" fnpiv "ID" nr_out "ID" nc "ID"\n", + Work->fnzeros, fnpiv, nr_out, nc)) ; + DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ; + + /* determine if BLAS-3 update to be applied before extending. */ + /* update if too many zero entries accumulate in the LU block */ + fnzeros = Work->fnzeros + fnpiv * (nr_out + nc) ; + + DEBUG2 (("fnzeros "ID"\n", fnzeros)) ; + + new_LUsize = (fnpiv+1) * (fnrows + nr_out + fncols + nc) ; + + DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ; + DEBUG2 (("relax3 %g\n", relax3)) ; + + /* relax3 parameter uses a double relop, but ignore NaN case: */ + do_update = (((double) fnzeros) / ((double) new_LUsize)) > relax3 ; + + DEBUG2 (("do_update "ID"\n", do_update)) + + } + else + { + do_update = TRUE ; + fnzeros = 0 ; + DEBUG2 (("OUT-OUT do_update forced true: "ID"\n", do_update)) + } + + DEBUG2 (("option OUT OUT: nr "ID" nc "ID" cost "ID"\n", + rdeg [OUT][OUT], cdeg[OUT], thiscost)) ; + + if (bestcost == EMPTY || thiscost < bestcost) + { + /* this is the best option seen so far */ + Work->pivot_case = OUT_OUT ; + bestcost = thiscost ; + Work->do_extend = do_extend ; + Work->do_update = do_update ; + new_fnzeros = fnzeros ; + } + } + } + + /* At this point, a structural pivot has been found. */ + /* It may be numerically zero, however. */ + ASSERT (Work->pivot_case != EMPTY) ; + DEBUG2 (("local seach, best option "ID", best cost "ID"\n", + Work->pivot_case, bestcost)) ; + + /* ====================================================================== */ + /* Pivot row and column, and extension, now determined */ + /* ====================================================================== */ + + Work->fnzeros = new_fnzeros ; + + /* ---------------------------------------------------------------------- */ + /* finalize the pivot row and column */ + /* ---------------------------------------------------------------------- */ + + switch (Work->pivot_case) + { + case IN_IN: + DEBUG2 (("IN-IN option selected\n")) ; + Work->pivcol_in_front = TRUE ; + Work->pivrow_in_front = TRUE ; + Work->pivcol = pivcol [IN] ; + Work->pivrow = pivrow [IN][IN] ; + Work->Wcol = (Int *) NULL ; /* not accessed */ + Work->ccdeg = nr_in ; + Work->Wrow = Fcols ; + Work->rrdeg = rdeg [IN][IN] ; + jj = jcand [IN] ; + break ; + + case IN_OUT: + DEBUG2 (("IN-OUT option selected\n")) ; + Work->pivcol_in_front = TRUE ; + Work->pivrow_in_front = FALSE ; + Work->pivcol = pivcol [IN] ; + Work->pivrow = pivrow [IN][OUT] ; + Work->Wcol = (Int *) NULL ; /* not accessed */ + Work->ccdeg = nr_in ; + Work->Wrow = Wio ; + Work->rrdeg = rdeg [IN][OUT] ; + jj = jcand [IN] ; + break ; + + case OUT_IN: + DEBUG2 (("OUT-IN option selected\n")) ; + Work->pivcol_in_front = FALSE ; + Work->pivrow_in_front = TRUE ; + Work->pivcol = pivcol [OUT] ; + Work->pivrow = pivrow [OUT][IN] ; + Work->Wcol = Wm ; + Work->ccdeg = cdeg [OUT] ; + /* Wrow might be equivalenced to Fcols (Freebie in): */ + Work->Wrow = Woi ; + Work->rrdeg = rdeg [OUT][IN] ; + /* Work->Wrow[0..fncols-1] is not there. See Fcols instead */ + jj = jcand [OUT] ; + break ; + + case OUT_OUT: + DEBUG2 (("OUT-OUT option selected\n")) ; + Work->pivcol_in_front = FALSE ; + Work->pivrow_in_front = FALSE ; + Work->pivcol = pivcol [OUT] ; + Work->pivrow = pivrow [OUT][OUT] ; + Work->Wcol = Wm ; + Work->ccdeg = cdeg [OUT] ; + /* Wrow might be equivalenced to Wio (Freebie out): */ + Work->Wrow = Woo ; + Work->rrdeg = rdeg [OUT][OUT] ; + jj = jcand [OUT] ; + break ; + + } + + Wcol = Work->Wcol ; + + if (!Work->pivcol_in_front && pivcol [IN] != EMPTY) + { + /* clear Frpos if pivcol [IN] was searched, but not selected */ + for (i = fnrows ; i < cdeg [IN] ; i++) + { + Frpos [Frows [i]] = EMPTY; + } + } + + /* ---------------------------------------------------------------------- */ + /* remove pivot column from candidate pivot column list */ + /* ---------------------------------------------------------------------- */ + + ASSERT (jj >= 0 && jj < jmax) ; + ASSERT (Work->pivcol == Work->Candidates [jj]) ; + if (Work->ncand > MAX_CANDIDATES) + { + Work->Candidates [jj] = Work->nextcand++ ; + } + else + { + Work->Candidates [jj] = Work->Candidates [Work->ncand - 1] ; + Work->Candidates [Work->ncand - 1] = 0 ; + } + Work->ncand-- ; + + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + if (!Work->pivcol_in_front) + { + DEBUG2 (("All of Wcol, size "ID"\n", Work->ccdeg)) ; + ASSERT (Wcol == Work->Wm) ; + for (i = 0 ; i < Work->ccdeg ; i++) + { + row = Wcol [i] ; + DEBUG2 (("Wcol row "ID" Frpos[row] "ID"\n", row, Frpos [row])) ; + } + } + else + { + DEBUG2 (("All of old Frows, size "ID"\n", fnrows)) ; + for (i = 0 ; i < fnrows ; i++) + { + row = Frows [i] ; + DEBUG2 (("old Frows row "ID" Frpos[row] "ID"\n", row, Frpos [row])); + } + DEBUG2 (("All of (new part of Frows), size "ID"\n",Work->ccdeg)); + for (i = fnrows ; i < fnrows + Work->ccdeg ; i++) + { + row = Frows [i] ; + DEBUG2 (("new Frows row "ID" Frpos[row] "ID"\n", row, Frpos [row])) ; + } + } + DEBUG2 (("All of Wrow, size "ID"\n", Work->rrdeg)) ; + if (Work->pivrow_in_front) + { + for (i = 0 ; i < fncols ; i++) + { + col = Fcols [i] ; + DEBUG2 (("Wrow col "ID" Fcpos[col] "ID"\n", col, Fcpos [col])) ; + } + DEBUG2 (("---\n")) ; + for (i = fncols ; i < Work->rrdeg ; i++) + { + col = Work->Wrow [i] ; + DEBUG2 (("Wrow col "ID" Fcpos[col] "ID"\n", col, Fcpos [col])) ; + } + } + else + { + for (i = 0 ; i < Work->rrdeg ; i++) + { + col = Work->Wrow [i] ; + DEBUG2 (("Wrow col "ID" Fcpos[col] "ID"\n", col, Fcpos [col])) ; + } + } +#endif + + /* ---------------------------------------------------------------------- */ + /* remove pivot row index from pattern of pivot column pattern, */ + /* unless extend_front needs it. Compress the pivot column pattern. */ + /* ---------------------------------------------------------------------- */ + + src = 0 ; + dest = 0 ; + + if (Work->pivcol_in_front) + { + + pos = Frpos [Work->pivrow] ; +#ifndef NDEBUG + if ( Work->pivrow_in_front) + { + ASSERT (pos < fnrows && pos >= 0) ; + } + else + { + ASSERT (pos < cdeg [IN] && pos >= fnrows) ; + } +#endif + + if (!Work->pivrow_in_front) + { + ASSERT (Work->ccdeg > 0) ; + ASSERT (cdeg [IN] > fnrows) ; + + /* remove the pivot row index by shifting the last entry into */ + /* its position */ + cdeg [IN]-- ; + Work->ccdeg-- ; + row = Frows [cdeg [IN]] ; + Frows [pos] = row ; + Frpos [row] = pos ; + + } + + } + else + { + if (Work->do_extend) + { + /* all cases, when front is being extended */ + for ( ; src < Work->ccdeg ; src++) + { + ASSERT (Wcol == Work->Wm) ; + row = Wcol [src] ; + DEBUG2 (("Pruning Wcol, row "ID" (do_extend)", row)) ; + if (row != Work->pivrow && Frpos [row] < 0) + { + DEBUG2 ((" keep")) ; + Wcol [dest++] = row ; + } + DEBUG2 (("\n")) ; + ASSERT (IMPLIES (Work->pivcol_in_front, Frpos [row] < 0)) ; + } + /* now Wcol contains only the new rows */ + } + else + { + for ( ; src < Work->ccdeg ; src++) + { + ASSERT (Wcol == Work->Wm) ; + row = Wcol [src] ; + DEBUG2 (("Pruning Wcol, row "ID" (no extend)", row)) ; + if (row != Work->pivrow) + { + DEBUG2 ((" keep")) ; + Wcol [dest++] = row ; + } + DEBUG2 (("\n")) ; + } + } + Work->ccdeg = dest ; + } + + + /* ---------------------------------------------------------------------- */ + /* determine whether to do scan2-row and scan2-col */ + /* ---------------------------------------------------------------------- */ + + if (Work->do_extend) + { + Work->do_scan2row = (fncols > 0) ; + Work->do_scan2col = (fnrows > 0) ; + } + else + { + Work->do_scan2row = (fncols > 0) && Work->pivrow_in_front ; + Work->do_scan2col = (fnrows > 0) && Work->pivcol_in_front ; + } + + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG2 (("LOCAL SEARCH DONE: pivot column "ID" pivot row: "ID, + Work->pivcol, Work->pivrow)) ; + DEBUG2 ((" do_extend: "ID"\n", Work->do_extend)) ; + DEBUG2 (("do_update: "ID"\n", Work->do_update)) ; + UMF_dump_rowcol (0, Numeric, Work, Work->pivrow, TRUE) ; + DEBUG2 (("Pivot Wrow "ID":\n", Work->pivrow)) ; + if (Work->pivrow_in_front || Work->do_extend) + { + for (i = 0 ; i < fncols ; i++) + { + DEBUG3 ((" col:: "ID" \n", Fcols [i])) ; + } + } + for (i = ((Work->pivrow_in_front) ? fncols : 0) ; i < Work->rrdeg ; i++) + { + if (col != Work->pivcol && Fcpos [col] < 0) + { + DEBUG3 ((" col:: "ID" (new)\n", Work->Wrow [i])) ; + } + } + UMF_dump_rowcol (1, Numeric, Work, Work->pivcol, TRUE) ; + DEBUG2 (("Pivot "ID":\n", Work->pivcol)) ; + if (Work->pivcol_in_front || Work->do_extend) + { + for (i = 0 ; i < fnrows ; i++) + { + DEBUG3 ((" row:: "ID" \n", Frows [i])) ; + } + } + if (Work->pivcol_in_front) + { + for (i = fnrows ; i < fnrows + Work->ccdeg ; i++) + { + row = Frows [i] ; + DEBUG3 ((" row:: "ID" (new, in Frows already)\n", Frows [i])) ; + ASSERT (row != Work->pivrow) ; + } + } + else + { + for (i = 0 ; i < Work->ccdeg ; i++) + { + ASSERT (Wcol == Work->Wm) ; + row = Wcol [i] ; + DEBUG3 ((" row:: "ID" (new)\n", row)) ; + ASSERT (row != Work->pivrow) ; + } + } +#endif + + /* ---------------------------------------------------------------------- */ + /* Pivot row and column have been found */ + /* ---------------------------------------------------------------------- */ + + /* + If pivot column index is in the front: + pivot column pattern (or just extension) is in + Work->Wcol [0..Work->ccdeg-1] + otherwise + pivot column pattern is in Frows [0.. fnrows + Work->ccdeg-1], + and Frpos [row] is already set properly. + In both cases, the pivot row index has been removed from + the pivot column pattern. + + ------------------------------------------------------------------------ + + If pivot row index is in the front: + pivot row pattern (or just extension) is in + Work->Wrow [fncols..Work->rrdeg-1] + otherwise + pivot row pattern is in Work->Wrow [0..Work->rrdeg-1] + The pivot row pattern has not been pruned. + + */ + + return (UMFPACK_OK) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_lsolve.c b/src/sparse-matrix/umfpack/umf_lsolve.c new file mode 100644 index 0000000..e091b38 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_lsolve.c @@ -0,0 +1,120 @@ +/* ========================================================================== */ +/* === UMF_lsolve =========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* solves Lx = b, where L is the lower triangular factor of a matrix */ +/* B is overwritten with the solution X. */ +/* Returns the floating point operation count */ + +#include "umf_internal.h" + +GLOBAL double UMF_lsolve +( + NumericType *Numeric, + Entry X [ ], /* b on input, solution x on output */ + Int Pattern [ ] /* a work array of size n */ +) +{ + Int k, deg, *ip, j, row, *Lpos, *Lilen, *Lip, llen, lp, newLchain, + pos, npiv ; + Entry *xp, xk ; + + /* ---------------------------------------------------------------------- */ + + if (Numeric->n_row != Numeric->n_col) return (0.) ; + npiv = Numeric->npiv ; + Lpos = Numeric->Lpos ; + Lilen = Numeric->Lilen ; + Lip = Numeric->Lip ; + deg = 0 ; + +#ifndef NDEBUG + DEBUG4 (("Lsolve start:\n")) ; + for (j = 0 ; j < Numeric->n_row ; j++) + { + DEBUG4 (("Lsolve start "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } +#endif + + for (k = 0 ; k < npiv ; k++) + { + + /* ------------------------------------------------------------------ */ + /* make column of L in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + lp = Lip [k] ; + newLchain = (lp < 0) ; + if (newLchain) + { + lp = -lp ; + deg = 0 ; + DEBUG4 (("start of chain for column of L\n")) ; + } + + /* remove pivot row */ + pos = Lpos [k] ; + if (pos != EMPTY) + { + DEBUG4 ((" k "ID" removing row "ID" at position "ID"\n", + k, Pattern [pos], pos)) ; + ASSERT (!newLchain) ; + ASSERT (deg > 0) ; + ASSERT (pos >= 0 && pos < deg) ; + ASSERT (Pattern [pos] == k) ; + Pattern [pos] = Pattern [--deg] ; + } + + /* concatenate the pattern */ + ip = (Int *) (Numeric->Memory + lp) ; + llen = Lilen [k] ; + for (j = 0 ; j < llen ; j++) + { + row = *ip++ ; + DEBUG4 ((" row "ID" k "ID"\n", row, k)) ; + ASSERT (row > k) ; + Pattern [deg++] = row ; + } + + /* ------------------------------------------------------------------ */ + /* use column k of L */ + /* ------------------------------------------------------------------ */ + + xk = X [k] ; + if (IS_NONZERO (xk)) + { + xp = (Entry *) (Numeric->Memory + lp + UNITS (Int, llen)) ; + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" row "ID" k "ID" value", Pattern [j], k)) ; + EDEBUG4 (*xp) ; + DEBUG4 (("\n")) ; + /* X [Pattern [j]] -= xk * (*xp) ; */ + MULT_SUB (X [Pattern [j]], xk, *xp) ; + xp++ ; + } + } + } + +#ifndef NDEBUG + for (j = 0 ; j < Numeric->n_row ; j++) + { + DEBUG4 (("Lsolve done "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } + DEBUG4 (("Lsolve done.\n")) ; +#endif + + return (MULTSUB_FLOPS * ((double) Numeric->lnz)) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_ltsolve.c b/src/sparse-matrix/umfpack/umf_ltsolve.c new file mode 100644 index 0000000..238bfba --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_ltsolve.c @@ -0,0 +1,187 @@ +/* ========================================================================== */ +/* === UMF_ltsolve ========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Solves L'x = b or L.'x=b, where L is the lower triangular factor of a */ +/* matrix. B is overwritten with the solution X. */ +/* Returns the floating point operation count */ + +#include "umf_internal.h" + +GLOBAL double +#ifdef CONJUGATE_SOLVE +UMF_lhsolve /* solve L'x=b (complex conjugate transpose) */ +#else +UMF_ltsolve /* solve L.'x=b (array transpose) */ +#endif +( + NumericType *Numeric, + Entry X [ ], /* b on input, solution x on output */ + Int Pattern [ ] /* a work array of size n */ +) +{ + Int k, deg, *ip, j, row, *Lpos, *Lilen, kstart, kend, *Lip, llen, + lp, pos, npiv ; + Entry *xp, xk ; + + /* ---------------------------------------------------------------------- */ + + if (Numeric->n_row != Numeric->n_col) return (0.) ; + npiv = Numeric->npiv ; + Lpos = Numeric->Lpos ; + Lilen = Numeric->Lilen ; + Lip = Numeric->Lip ; + kstart = npiv ; + +#ifndef NDEBUG + DEBUG4 (("Ltsolve start:\n")) ; + for (j = 0 ; j < Numeric->n_row ; j++) + { + DEBUG4 (("Ltsolve start "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } +#endif + + for (kend = npiv-1 ; kend >= 0 ; kend = kstart-1) + { + + /* ------------------------------------------------------------------ */ + /* find the start of this Lchain */ + /* ------------------------------------------------------------------ */ + + /* for (kstart = kend ; kstart >= 0 && Lip [kstart] > 0 ; kstart--) ; */ + kstart = kend ; + while (kstart >= 0 && Lip [kstart] > 0) + { + kstart-- ; + } + + /* the Lchain goes from kstart to kend */ + + /* ------------------------------------------------------------------ */ + /* scan the whole chain to find the pattern of the last column of L */ + /* ------------------------------------------------------------------ */ + + deg = 0 ; + DEBUG4 (("start of chain for column of L\n")) ; + for (k = kstart ; k <= kend ; k++) + { + ASSERT (k >= 0 && k < npiv) ; + + /* -------------------------------------------------------------- */ + /* make column k of L in Pattern [0..deg-1] */ + /* -------------------------------------------------------------- */ + + /* remove pivot row */ + pos = Lpos [k] ; + if (pos != EMPTY) + { + DEBUG4 ((" k "ID" removing row "ID" at position "ID"\n", + k, Pattern [pos], pos)) ; + ASSERT (k != kstart) ; + ASSERT (deg > 0) ; + ASSERT (pos >= 0 && pos < deg) ; + ASSERT (Pattern [pos] == k) ; + Pattern [pos] = Pattern [--deg] ; + } + + /* concatenate the pattern */ + lp = Lip [k] ; + if (k == kstart) + { + lp = -lp ; + } + ASSERT (lp > 0) ; + ip = (Int *) (Numeric->Memory + lp) ; + llen = Lilen [k] ; + for (j = 0 ; j < llen ; j++) + { + row = *ip++ ; + DEBUG4 ((" row "ID" k "ID"\n", row, k)) ; + ASSERT (row > k) ; + Pattern [deg++] = row ; + } + + } + /* Pattern [0..deg-1] is now the pattern of column kend */ + + /* ------------------------------------------------------------------ */ + /* solve using this chain, in reverse order */ + /* ------------------------------------------------------------------ */ + + DEBUG4 (("Unwinding Lchain\n")) ; + for (k = kend ; k >= kstart ; k--) + { + + /* -------------------------------------------------------------- */ + /* use column k of L */ + /* -------------------------------------------------------------- */ + + ASSERT (k >= 0 && k < npiv) ; + lp = Lip [k] ; + if (k == kstart) + { + lp = -lp ; + } + ASSERT (lp > 0) ; + llen = Lilen [k] ; + xp = (Entry *) (Numeric->Memory + lp + UNITS (Int, llen)) ; + xk = X [k] ; + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" row "ID" k "ID" value", Pattern [j], k)) ; + EDEBUG4 (*xp) ; + DEBUG4 (("\n")) ; +#ifdef CONJUGATE_SOLVE + /* xk -= X [Pattern [j]] * conjugate (*xp) ; */ + MULT_SUB_CONJ (xk, X [Pattern [j]], *xp) ; +#else + /* xk -= X [Pattern [j]] * (*xp) ; */ + MULT_SUB (xk, X [Pattern [j]], *xp) ; +#endif + xp++ ; + } + X [k] = xk ; + + /* -------------------------------------------------------------- */ + /* construct column k-1 of L */ + /* -------------------------------------------------------------- */ + + /* un-concatenate the pattern */ + deg -= llen ; + + /* add pivot row */ + pos = Lpos [k] ; + if (pos != EMPTY) + { + DEBUG4 ((" k "ID" adding row "ID" at position "ID"\n", + k, k, pos)) ; + ASSERT (k != kstart) ; + ASSERT (pos >= 0 && pos <= deg) ; + Pattern [deg++] = Pattern [pos] ; + Pattern [pos] = k ; + } + } + } + +#ifndef NDEBUG + for (j = 0 ; j < Numeric->n_row ; j++) + { + DEBUG4 (("Ltsolve done "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } + DEBUG4 (("Ltsolve done.\n")) ; +#endif + + return (MULTSUB_FLOPS * ((double) Numeric->lnz)) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_malloc.c b/src/sparse-matrix/umfpack/umf_malloc.c new file mode 100644 index 0000000..98f5567 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_malloc.c @@ -0,0 +1,81 @@ +/* ========================================================================== */ +/* === UMF_malloc =========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Allocate a block of n objects, each of a given size. This routine does not + handle the case when the size is 1 (allocating char's) because of potential + integer overflow. UMFPACK never does that. + Also maintains the UMFPACK malloc count. +*/ + +#include "umf_internal.h" + +#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG) + +/* + UMF_malloc_count is a count of the objects malloc'd by UMFPACK. + It is increased by 7 by UMFPACK_*symbolic, and by 15 or 16 by + UMFPACK_*numeric. It is reduced by the same amount by the corresponding + UMFPACK_free_* routines. If you suspect a memory leak in your program + (caused by not properly destroying the Symbolic and Numeric objects) + then compile with -DUMF_MALLOC_COUNT and check value of UMF_malloc_count. + By default, UMF_MALLOC_COUNT is not defined, and thus UMFPACK has + no global variables. +*/ + +GLOBAL Int UMF_malloc_count = 0 ; + +#endif + + +GLOBAL void *UMF_malloc +( + Int n_objects, + size_t size_of_object +) +{ + size_t size ; + void *p ; + +#ifdef UMF_TCOV_TEST + /* For exhaustive statement coverage testing only! */ + /* Pretend to fail, to test out-of-memory conditions. */ + umf_fail-- ; + if (umf_fail <= umf_fail_hi && umf_fail >= umf_fail_lo) { return ((void *) NULL) ; } +#endif + + /* make sure that we allocate something */ + n_objects = MAX (1, n_objects) ; + + size = (size_t) n_objects ; + ASSERT (size_of_object > 1) ; + if (size > Int_MAX / size_of_object) + { + /* object is too big for integer pointer arithmetic */ + return ((void *) NULL) ; + } + size *= size_of_object ; + + /* see umf_config.h for the memory allocator selection */ + p = ALLOCATE (size) ; + +#if defined (UMF_MALLOC_COUNT) || !defined (NDEBUG) + if (p) + { + /* One more object has been malloc'ed. Keep track of the count. */ + /* (purely for sanity checks). */ + UMF_malloc_count++ ; + } +#endif + + return (p) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_mem_alloc_element.c b/src/sparse-matrix/umfpack/umf_mem_alloc_element.c new file mode 100644 index 0000000..ac45b51 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_mem_alloc_element.c @@ -0,0 +1,98 @@ +/* ========================================================================== */ +/* === UMF_mem_alloc_element ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* The UMF_mem_* routines manage the Numeric->Memory memory space. */ + +/* Allocate a nrows-by-ncols element, and initialize it. */ +/* Returns the index into Numeric->Memory if successful, or 0 on failure. */ + +#include "umf_internal.h" +#include "umf_mem_alloc_tail_block.h" + +GLOBAL Int UMF_mem_alloc_element +( + NumericType *Numeric, + Int nrows, + Int ncols, + Int **Rows, + Int **Cols, + Entry **C, + Int *size, + Element **epout +) +{ + + Element *ep ; + Unit *p ; + Int i ; + + ASSERT (Numeric) ; + ASSERT (Numeric->Memory) ; + +#ifndef NDEBUG + UMF_allocfail = FALSE ; + if (UMF_gprob > 0) + { + double rrr = ((double) (rand ( ))) / (((double) RAND_MAX) + 1) ; + DEBUG4 (("Check random %e %e\n", rrr, UMF_gprob)) ; + UMF_allocfail = rrr < UMF_gprob ; /* a double relop, but ignore NaN case */ + if (UMF_allocfail) + { + DEBUG0 (("Random garbage collection (alloc_element)\n")); + } + } +#endif + + *size = GET_ELEMENT_SIZE (nrows, ncols) ; + if (INT_OVERFLOW (DGET_ELEMENT_SIZE (nrows, ncols) + 1)) + { + DEBUG0 (("alloc element failed - problem too large\n")) ; + return (0) ; /* problem is too large */ + } + + i = UMF_mem_alloc_tail_block (Numeric, *size) ; + (*size)++ ; + if (!i) + { + DEBUG0 (("alloc element failed - out of memory\n")) ; + return (0) ; /* out of memory */ + } + p = Numeric->Memory + i ; + + ep = (Element *) p ; + + DEBUG2 (("alloc_element done ("ID" x "ID"): p: "ID" i "ID"\n", + nrows, ncols, p-Numeric->Memory, i)) ; + + /* Element data structure, in order: */ + p += UNITS (Element, 1) ; /* (1) Element header */ + *Cols = (Int *) p ; /* (2) col [0..ncols-1] indices */ + *Rows = *Cols + ncols ; /* (3) row [0..nrows-1] indices */ + p += UNITS (Int, ncols + nrows) ; + *C = (Entry *) p ; /* (4) C [0..nrows-1, 0..ncols-1] */ + + ep->nrows = nrows ; /* initialize the header information */ + ep->ncols = ncols ; + ep->nrowsleft = nrows ; + ep->ncolsleft = ncols ; + ep->cdeg = 0 ; + ep->rdeg = 0 ; + ep->next = EMPTY ; + + DEBUG2 (("new block size: "ID" ", GET_BLOCK_SIZE (Numeric->Memory + i))) ; + DEBUG2 (("Element size needed "ID"\n", GET_ELEMENT_SIZE (nrows, ncols))) ; + + *epout = ep ; + + /* return the offset into Numeric->Memory */ + return (i) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_mem_alloc_head_block.c b/src/sparse-matrix/umfpack/umf_mem_alloc_head_block.c new file mode 100644 index 0000000..3d62a89 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_mem_alloc_head_block.c @@ -0,0 +1,46 @@ +/* ========================================================================== */ +/* === UMF_mem_alloc_head_block ============================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* The UMF_mem_* routines manage the Numeric->Memory memory space. */ + +/* allocate nunits from head of Numeric->Memory. No header allocated. */ +/* Returns the index into Numeric->Memory if successful, or 0 on failure. */ + +#include "umf_internal.h" + +GLOBAL Int UMF_mem_alloc_head_block +( + NumericType *Numeric, + Int nunits +) +{ + Int p, usage ; + DEBUG2 (("GET BLOCK: from head, size "ID" ", nunits)) ; + + ASSERT (Numeric) ; + ASSERT (Numeric->Memory) ; + + if (nunits > (Numeric->itail - Numeric->ihead)) + { + DEBUG2 ((" failed\n")) ; + return (0) ; + } + + /* return p as an offset from Numeric->Memory */ + p = Numeric->ihead ; + Numeric->ihead += nunits ; + + DEBUG2 ((ID"\n", p)) ; + usage = Numeric->ihead + Numeric->tail_usage ; + Numeric->max_usage = MAX (Numeric->max_usage, usage) ; + return (p) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_mem_alloc_tail_block.c b/src/sparse-matrix/umfpack/umf_mem_alloc_tail_block.c new file mode 100644 index 0000000..f8f9b39 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_mem_alloc_tail_block.c @@ -0,0 +1,133 @@ +/* ========================================================================== */ +/* === UMF_mem_alloc_tail_block ============================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* The UMF_mem_* routines manage the Numeric->Memory memory space. */ + +#include "umf_internal.h" + +/* allocate nunits from tail of Numeric->Memory */ +/* (requires nunits+1, for header). */ +/* Returns the index into Numeric->Memory if successful, or 0 on failure. */ + +GLOBAL Int UMF_mem_alloc_tail_block +( + NumericType *Numeric, + Int nunits +) +{ + Int bigsize, usage ; + Unit *p, *pnext, *pbig ; + + ASSERT (Numeric) ; + ASSERT (Numeric->Memory) ; + +#ifndef NDEBUG + if (UMF_allocfail) + { + /* pretend to fail, to test garbage_collection */ + UMF_allocfail = FALSE ; /* don't fail the next time */ + return (0) ; + } + DEBUG2 (("UMF_mem_alloc_tail_block, size: "ID" + 1 = "ID": ", + nunits, nunits+1)) ; +#endif + + bigsize = 0 ; + pbig = (Unit *) NULL ; + + ASSERT (nunits > 0) ; /* size must be positive */ + if (Numeric->ibig != EMPTY) + { + ASSERT (Numeric->ibig > Numeric->itail) ; + ASSERT (Numeric->ibig < Numeric->size) ; + pbig = Numeric->Memory + Numeric->ibig ; + bigsize = -pbig->header.size ; + ASSERT (bigsize > 0) ; /* Numeric->ibig is free */ + ASSERT (pbig->header.prevsize >= 0) ; /* prev. is not free */ + } + + if (pbig && bigsize >= nunits) + { + + /* use the biggest block, somewhere in middle of memory */ + p = pbig ; + pnext = p + 1 + bigsize ; + /* next is in range */ + ASSERT (pnext < Numeric->Memory + Numeric->size) ; + /* prevsize of next = this size */ + ASSERT (pnext->header.prevsize == bigsize) ; + /* next is not free */ + ASSERT (pnext->header.size > 0) ; + bigsize -= nunits + 1 ; + + if (bigsize < 4) + { + /* internal fragmentation would be too small */ + /* allocate the entire free block */ + p->header.size = -p->header.size ; + DEBUG2 (("GET BLOCK: p: "ID" size: "ID", all of big: "ID" size: " + ID"\n", p-Numeric->Memory, nunits, Numeric->ibig, + p->header.size)) ; + /* no more biggest block */ + Numeric->ibig = EMPTY ; + + } + else + { + + /* allocate just the first nunits Units of the free block */ + p->header.size = nunits ; + /* make a new free block */ + Numeric->ibig += nunits + 1 ; + pbig = Numeric->Memory + Numeric->ibig ; + pbig->header.size = -bigsize ; + pbig->header.prevsize = nunits ; + pnext->header.prevsize = bigsize ; + DEBUG2 (("GET BLOCK: p: "ID" size: "ID", some of big: "ID" left: " + ID"\n", p-Numeric->Memory, nunits, Numeric->ibig, bigsize)) ; + } + + } + else + { + + /* allocate from the top of tail */ + pnext = Numeric->Memory + Numeric->itail ; + DEBUG2 (("GET BLOCK: from tail ")) ; + if ((nunits + 1) > (Numeric->itail - Numeric->ihead)) + { + DEBUG2 (("\n")) ; + return (0) ; + } + Numeric->itail -= (nunits + 1) ; + p = Numeric->Memory + Numeric->itail ; + p->header.size = nunits ; + p->header.prevsize = 0 ; + pnext->header.prevsize = nunits ; + DEBUG2 (("p: "ID" size: "ID", new tail "ID"\n", + p-Numeric->Memory, nunits, Numeric->itail)) ; + } + + Numeric->tail_usage += p->header.size + 1 ; + usage = Numeric->ihead + Numeric->tail_usage ; + Numeric->max_usage = MAX (Numeric->max_usage, usage) ; + +#ifndef NDEBUG + UMF_debug -= 10 ; + UMF_dump_memory (Numeric) ; + UMF_debug += 10 ; +#endif + + /* p points to the header. Add one to point to the usable block itself. */ + /* return the offset into Numeric->Memory */ + return ((p - Numeric->Memory) + 1) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_mem_free_tail_block.c b/src/sparse-matrix/umfpack/umf_mem_free_tail_block.c new file mode 100644 index 0000000..520277f --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_mem_free_tail_block.c @@ -0,0 +1,149 @@ +/* ========================================================================== */ +/* === UMF_mem_free_tail_block ============================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* The UMF_mem_* routines manage the Numeric->Memory memory space. */ + +/* free a block from the tail of Numeric->memory */ + +#include "umf_internal.h" + +GLOBAL void UMF_mem_free_tail_block +( + NumericType *Numeric, + Int i +) +{ + Unit *pprev, *pnext, *p, *pbig ; + Int sprev ; + + ASSERT (Numeric) ; + ASSERT (Numeric->Memory) ; + if (i == EMPTY || i == 0) return ; /* already deallocated */ + + /* ---------------------------------------------------------------------- */ + /* get the block */ + /* ---------------------------------------------------------------------- */ + + p = Numeric->Memory + i ; + + p-- ; /* get the corresponding header */ + DEBUG2 (("free block: p: "ID, p-Numeric->Memory)) ; + ASSERT (p >= Numeric->Memory + Numeric->itail) ; + ASSERT (p < Numeric->Memory + Numeric->size) ; + ASSERT (p->header.size > 0) ; /* block not already free */ + ASSERT (p->header.prevsize >= 0) ; + + Numeric->tail_usage -= p->header.size + 1 ; + + /* ---------------------------------------------------------------------- */ + /* merge with next free block, if any */ + /* ---------------------------------------------------------------------- */ + + pnext = p + 1 + p->header.size ; + DEBUG2 (("size: "ID" next: "ID" ", p->header.size, pnext-Numeric->Memory)) ; + ASSERT (pnext < Numeric->Memory + Numeric->size) ; + ASSERT (pnext->header.prevsize == p->header.size) ; + ASSERT (pnext->header.size != 0) ; + + if (pnext->header.size < 0) + { + /* next block is also free - merge with current block */ + p->header.size += (-pnext->header.size) + 1 ; + DEBUG2 ((" NEXT FREE ")) ; + } + + /* ---------------------------------------------------------------------- */ + /* merge with previous free block, if any */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + if (p == Numeric->Memory + Numeric->itail) + { + DEBUG2 ((" at top of tail ")) ; + ASSERT (p->header.prevsize == 0) ; + } +#endif + + if (p > Numeric->Memory + Numeric->itail) + { + ASSERT (p->header.prevsize > 0) ; + pprev = p - 1 - p->header.prevsize ; + DEBUG2 ((" prev: "ID" ", pprev-Numeric->Memory)) ; + ASSERT (pprev >= Numeric->Memory + Numeric->itail) ; + sprev = pprev->header.size ; + if (sprev < 0) + { + /* previous block is also free - merge it with current block */ + ASSERT (p->header.prevsize == -sprev) ; + pprev->header.size = p->header.size + (-sprev) + 1 ; + p = pprev ; + DEBUG2 ((" PREV FREE ")) ; + /* note that p may now point to Numeric->itail */ + } +#ifndef NDEBUG + else + { + ASSERT (p->header.prevsize == sprev) ; + } +#endif + } + + /* ---------------------------------------------------------------------- */ + /* free the block, p */ + /* ---------------------------------------------------------------------- */ + + pnext = p + 1 + p->header.size ; + ASSERT (pnext < Numeric->Memory + Numeric->size) ; + + if (p == Numeric->Memory + Numeric->itail) + { + /* top block in list is freed */ + Numeric->itail = pnext - Numeric->Memory ; + pnext->header.prevsize = 0 ; + DEBUG2 ((" NEW TAIL : "ID" ", Numeric->itail)) ; + ASSERT (pnext->header.size > 0) ; + if (Numeric->ibig != EMPTY && Numeric->ibig <= Numeric->itail) + { + /* the big free block is now above the tail */ + Numeric->ibig = EMPTY ; + } + } + else + { + /* keep track of the biggest free block seen */ + if (Numeric->ibig == EMPTY) + { + Numeric->ibig = p - Numeric->Memory ; + } + else + { + pbig = Numeric->Memory + Numeric->ibig ; + if (-(pbig->header.size) < p->header.size) + { + Numeric->ibig = p - Numeric->Memory ; + } + } + /* flag the block as free, somewhere in the middle of the tail */ + pnext->header.prevsize = p->header.size ; + p->header.size = -p->header.size ; + } + + DEBUG2 (("new p: "ID" freesize: "ID"\n", p-Numeric->Memory, + -p->header.size)) ; + +#ifndef NDEBUG + UMF_debug -= 10 ; + UMF_dump_memory (Numeric) ; + UMF_debug += 10 ; +#endif + +} + diff --git a/src/sparse-matrix/umfpack/umf_mem_init_memoryspace.c b/src/sparse-matrix/umfpack/umf_mem_init_memoryspace.c new file mode 100644 index 0000000..c4d1c5f --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_mem_init_memoryspace.c @@ -0,0 +1,65 @@ +/* ========================================================================== */ +/* === UMF_mem_init_memoryspace ============================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* The UMF_mem_* routines manage the Numeric->Memory memory space. */ + +#include "umf_internal.h" + +/* initialize the LU and element workspace (Numeric->Memory) */ + +GLOBAL void UMF_mem_init_memoryspace +( + NumericType *Numeric +) +{ + Unit *p ; + + ASSERT (Numeric) ; + ASSERT (Numeric->size >= 4) ; + DEBUG0 (("Init memory space, size "ID"\n", Numeric->size)) ; + + Numeric->ngarbage = 0 ; + Numeric->nrealloc = 0 ; + Numeric->ncostly = 0 ; + Numeric->ibig = EMPTY ; + Numeric->ihead = 0 ; + Numeric->itail = Numeric->size ; + +#ifndef NDEBUG + UMF_allocfail = FALSE ; +#endif + + /* allocate the 2-unit tail marker block and initialize it */ + Numeric->itail -= 2 ; + p = Numeric->Memory + Numeric->itail ; + DEBUG2 (("p "ID" tail "ID"\n", p-Numeric->Memory, Numeric->itail)) ; + Numeric->tail_usage = 2 ; + p->header.prevsize = 0 ; + p->header.size = 1 ; + + /* allocate a 1-unit head marker block at the head of memory */ + /* this is done so that an offset of zero is treated as a NULL pointer */ + Numeric->ihead++ ; + + /* initial usage in Numeric->Memory */ + Numeric->max_usage = 3 ; + Numeric->init_usage = Numeric->max_usage ; + + /* Note that UMFPACK_*symbolic ensures that Numeric->Memory is of size */ + /* at least 3, so this initialization will always succeed. */ + +#ifndef NDEBUG + DEBUG2 (("init_memoryspace, all free (except one unit at head\n")) ; + UMF_dump_memory (Numeric) ; +#endif + +} + diff --git a/src/sparse-matrix/umfpack/umf_order_front_tree.c b/src/sparse-matrix/umfpack/umf_order_front_tree.c new file mode 100644 index 0000000..d322c65 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_order_front_tree.c @@ -0,0 +1,100 @@ +/* ========================================================================== */ +/* === UMF_order_front_tree ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Post-ordering of supernodal column elimination tree. +*/ + +#include "umf_internal.h" + +GLOBAL Int UMF_order_front_tree +( + Int root, + Int k, + Int Front_child [ ], /* input argument, destroyed */ + const Int Front_sibling [ ], + Int Front_order [ ], + Int Stack [ ] +) +{ + Int f, head, h, i ; + +/* recursive version (Stack [ ] is not used): + i = root ; + for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f]) + { + k = UMF_order_front_tree (f, k, Front_child, Front_sibling, + Front_order) ; + } + Front_order [i] = k++ ; + return (k) ; +*/ + + /* push root on the stack */ + head = 0 ; + Stack [0] = root ; + + while (head >= 0) + { + /* get head of stack */ + i = Stack [head] ; + DEBUG1 (("head of stack "ID" \n", i)) ; + ASSERT (i >= 0 && i < UMF_nbug && head <= UMF_fbug) ; + + if (Front_child [i] != EMPTY) + { + /* the children of i are not yet ordered */ + /* push each child onto the stack in reverse order */ + /* so that small ones at the head of the list get popped first */ + /* and the biggest one at the end of the list gets popped last */ + for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f]) + { + head++ ; + } + h = head ; + ASSERT (head <= UMF_fbug) ; + for (f = Front_child [i] ; f != EMPTY ; f = Front_sibling [f]) + { + Stack [h--] = f ; + DEBUG1 (("push "ID" on stack\n", f)) ; + ASSERT (f >= 0 && f < UMF_nbug) ; + } + ASSERT (Stack [h] == i) ; + + /* delete child list so that i gets ordered next time we see it */ + Front_child [i] = EMPTY ; + } + else + { + /* the children of i (if there were any) are already ordered */ + /* remove i from the stack and order it. Front i is kth front */ + head-- ; + DEBUG1 (("pop "ID" order "ID"\n", i, k)) ; + Front_order [i] = k++ ; + ASSERT (k <= UMF_fbug) ; + } + +#ifndef NDEBUG + DEBUG1 (("\nStack:")) ; + for (h = head ; h >= 0 ; h--) + { + Int j = Stack [h] ; + DEBUG1 ((" "ID, j)) ; + ASSERT (j >= 0 && j < UMF_nbug) ; + } + DEBUG1 (("\n\n")) ; + ASSERT (head < UMF_fbug) ; +#endif + + } + return (k) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_realloc.c b/src/sparse-matrix/umfpack/umf_realloc.c new file mode 100644 index 0000000..34ccc68 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_realloc.c @@ -0,0 +1,56 @@ +/* ========================================================================== */ +/* === UMF_realloc ========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Realloc a block previously allocated by UMF_malloc. + Return NULL on failure (in which case the block is still allocated, and will + be kept at is present size). This routine is only used for Numeric->Memory. +*/ + +#include "umf_internal.h" + + + +GLOBAL void *UMF_realloc +( + void *p, + Int n_objects, + size_t size_of_object +) +{ + size_t size ; + +#ifdef UMF_TCOV_TEST + /* For exhaustive statement coverage testing only! */ + /* Pretend to fail, to test out-of-memory conditions. */ + umf_realloc_fail-- ; + if (umf_realloc_fail <= umf_realloc_hi && umf_realloc_fail >= umf_realloc_lo) { return ((void *) NULL) ; } +#endif + + /* make sure that we allocate something */ + n_objects = MAX (1, n_objects) ; + + size = (size_t) n_objects ; + ASSERT (size_of_object > 1) ; + if (size > Int_MAX / size_of_object) + { + /* object is too big for integer pointer arithmetic */ + return ((void *) NULL) ; + } + size *= size_of_object ; + + DEBUG0 (("UMF_realloc n_objects "ID" size_of_object "ID"\n", + n_objects, (Int) size_of_object)) ; + + /* see umf_config.h for the memory allocator selection */ + return (REALLOCATE (p, size)) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_row_search.c b/src/sparse-matrix/umfpack/umf_row_search.c new file mode 100644 index 0000000..1d65afc --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_row_search.c @@ -0,0 +1,626 @@ +/* ========================================================================== */ +/* === UMF_row_search ======================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Find two candidate pivot rows in a column: the best one in the front, + and the best one not in the front. Return the two pivot row patterns and + their exact degrees. Called by UMF_local_search. + + Returns UMFPACK_OK if successful, or UMFPACK_WARNING_singular_matrix or + UMFPACK_ERROR_different_pattern if not. + +*/ + +#include "umf_internal.h" + +#define IN 0 +#define OUT 1 + +GLOBAL Int UMF_row_search +( + NumericType *Numeric, + WorkType *Work, + SymbolicType *Symbolic, + Int cdeg, /* length of column */ + const Int Pattern [ ], /* pattern of column */ + Int pivrow [2], /* pivrow [IN] and pivrow [OUT] */ + Int rdeg [2], /* rdeg [IN] and rdeg [OUT] */ + Int W_i [ ], /* pattern of pivrow [IN], */ + /* either Fcols or Woi */ + Int W_o [ ], /* pattern of pivrow [OUT], */ + /* either Wio or Woo */ + Int prior_pivrow [2], /* the two other rows just scanned, if any */ + const Entry Fcol [ ], /* numerical values in column */ + /* (a column in the front) */ + + Int pivcol, /* the candidate column being searched */ + Int freebie [ ] +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + double maxval, toler, value ; + Int i, row, deg, *Wp, col, *Frpos, fnrows, *E, j, ncols, *Cols, *Rows, + e, f, wpflag, *Fcpos, fncols, tpi, max_rdeg, nans_in_col ; + Tuple *tp, *tpend, *tp1, *tp2 ; + Unit *Memory, *p ; + Element *ep ; + Int *Row_tuples, *Row_degree, *Row_tlen ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + Row_degree = Numeric->Rperm ; + Row_tuples = Numeric->Uip ; + Row_tlen = Numeric->Uilen ; + Wp = Work->Wp ; + Frpos = Work->Frpos ; + E = Work->E ; + Memory = Numeric->Memory ; + fnrows = Work->fnrows ; + + /* pivot row degree cannot exceed max_rdeg */ + max_rdeg = Work->fncols_max - Work->fnpiv ; + + /* ---------------------------------------------------------------------- */ + /* scan pivot column for candidate rows */ + /* ---------------------------------------------------------------------- */ + + maxval = 0.0 ; + nans_in_col = FALSE ; + for (i = 0 ; i < cdeg ; i++) + { + APPROX_ABS (value, Fcol [i]) ; + if (SCALAR_IS_NAN (value)) + { + nans_in_col = TRUE ; + maxval = value ; + break ; + } + /* This test can now ignore the NaN case: */ + maxval = MAX (maxval, value) ; + } + + /* if maxval is zero, the matrix is numerically singular */ + + toler = Numeric->relpt * maxval ; + if (SCALAR_IS_ZERO (toler)) + { + /* guard against underflow, and relpt=0 means relpt=1 */ + toler = maxval ; + } + DEBUG5 (("Row_search begins [ maxval %g toler %g\n", maxval, toler)) ; + if (SCALAR_IS_NAN (toler)) + { + nans_in_col = TRUE ; + } + + if (!nans_in_col) + { + for (i = 0 ; i < cdeg ; i++) + { + double a ; + APPROX_ABS (a, Fcol [i]) ; + + /* No NaN's exist in this column */ + ASSERT (!SCALAR_IS_NAN (a)) ; + ASSERT (!SCALAR_IS_NAN (toler)) ; + + if (a >= toler) /* a double relop, but no NaN's exist here. */ + { + row = Pattern [i] ; + deg = Row_degree [row] ; +#ifndef NDEBUG + DEBUG6 ((ID" Candidate row "ID" deg "ID" absval %g\n", i, row, + deg, a)) ; + UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ; +#endif + + if (Frpos [row] >= 0 && Frpos [row] < fnrows) + { + /* row is in the current front */ + DEBUG4 ((" in front\n")) ; + if (deg < rdeg [IN] + || (deg == rdeg [IN] && row < pivrow [IN])) + { + /* best row in front, so far */ + pivrow [IN] = row ; + rdeg [IN] = deg ; + } + } + else + { + /* row is not in the current front */ + DEBUG4 ((" NOT in front\n")) ; + if (deg < rdeg [OUT] + || (deg == rdeg[OUT] && row < pivrow[OUT])) + { + /* best row not in front, so far */ + pivrow [OUT] = row ; + rdeg [OUT] = deg ; + } + } + } + } + } + + /* if cdeg > 0 then we must have found a pivot row ... unless NaN's */ + /* exist. Try with no numerical tests if no pivot found. */ + + if (cdeg > 0 && pivrow [IN] == EMPTY && pivrow [OUT] == EMPTY) + { + /* cleanup for the NaN case */ + DEBUG0 (("Found a NaN in pivot column!\n")) ; + + /* grab the first entry in the pivot column, ignoring degree and */ + /* numerical stability. */ + row = Pattern [0] ; + deg = Row_degree [row] ; + if (Frpos [row] >= 0 && Frpos [row] < fnrows) + { + /* row is in the current front */ + DEBUG4 ((" in front\n")) ; + pivrow [IN] = row ; + rdeg [IN] = deg ; + } + else + { + /* row is not in the current front */ + DEBUG4 ((" NOT in front\n")) ; + pivrow [OUT] = row ; + rdeg [OUT] = deg ; + } + + /* We are now guaranteed to have a pivot, no matter how broken */ + /* (non-IEEE compliant) the underlying numerical operators are. */ + /* This is particularly a problem for Microsoft compilers (they do */ + /* not handle NaN's properly). Now try to find a sparser pivot, if */ + /* possible. */ + + for (i = 1 ; i < cdeg ; i++) + { + row = Pattern [i] ; + deg = Row_degree [row] ; + + if (Frpos [row] >= 0 && Frpos [row] < fnrows) + { + /* row is in the current front */ + DEBUG4 ((" in front\n")) ; + if (deg < rdeg [IN] || (deg == rdeg [IN] && row < pivrow [IN])) + { + /* best row in front, so far */ + pivrow [IN] = row ; + rdeg [IN] = deg ; + } + } + else + { + /* row is not in the current front */ + DEBUG4 ((" NOT in front\n")) ; + if (deg < rdeg [OUT] || (deg == rdeg[OUT] && row < pivrow[OUT])) + { + /* best row not in front, so far */ + pivrow [OUT] = row ; + rdeg [OUT] = deg ; + } + } + } + } + + /* We found a pivot if there are entries (even zero ones) in pivot col */ + ASSERT (IMPLIES (cdeg > 0, pivrow [IN] != EMPTY || pivrow [OUT] != EMPTY)) ; + + /* If there are no entries in the pivot column, then no pivot is found */ + ASSERT (IMPLIES (cdeg== 0, pivrow [IN] == EMPTY && pivrow [OUT] == EMPTY)) ; + + /* ---------------------------------------------------------------------- */ + /* check for singular matrix */ + /* ---------------------------------------------------------------------- */ + + if (cdeg == 0) + { + if (fnrows > 0) + { + /* + Get the pivrow [OUT][IN] from the current front. + The frontal matrix looks like this: + + pivcol[OUT] + | + v + x x x x 0 <- so grab this row as the pivrow [OUT][IN]. + x x x x 0 + x x x x 0 + 0 0 0 0 0 + + The current frontal matrix has some rows in it. The degree + of the pivcol[OUT] is zero. The column is empty, and the + current front does not contribute to it. + + */ + pivrow [IN] = Work->Frows [0] ; + DEBUG0 (("Got zero pivrow[OUT][IN] "ID" from current front\n", + pivrow [IN])) ; + } + else + { + + /* + Get a pivot row from the row-merge tree, use as + pivrow [OUT][OUT]. pivrow [IN] remains EMPTY. + This can only happen if the current front is 0-by-0. + */ + + Int *Front_leftmostdesc, *Front_1strow, *Front_new1strow, row1, + row2, fleftmost, nfr, n_row, frontid ; + + ASSERT (Work->fncols == 0) ; + + Front_leftmostdesc = Symbolic->Front_leftmostdesc ; + Front_1strow = Symbolic->Front_1strow ; + Front_new1strow = Work->Front_new1strow ; + nfr = Symbolic->nfr ; + n_row = Numeric->n_row ; + frontid = Work->frontid ; + + DEBUG0 (("Warning: pivcol: "ID" is empty front "ID"\n", + pivcol, frontid)) ; +#ifndef NDEBUG + DEBUG1 (("Calling dump rowmerge\n")) ; + UMF_dump_rowmerge (Numeric, Symbolic, Work) ; +#endif + + /* Row-merge set is the non-pivotal rows in the range */ + /* Front_new1strow [Front_leftmostdesc [frontid]] to */ + /* Front_1strow [frontid+1] - 1. */ + /* If this is empty, then use the empty rows, in the range */ + /* Front_new1strow [nfr] to n_row-1. */ + /* If this too is empty, then pivrow [OUT] will be empty. */ + /* In both cases, update Front_new1strow [...]. */ + + fleftmost = Front_leftmostdesc [frontid] ; + row1 = Front_new1strow [fleftmost] ; + row2 = Front_1strow [frontid+1] - 1 ; + DEBUG1 (("Leftmost: "ID" Rows ["ID" to "ID"] srch ["ID" to "ID"]\n", + fleftmost, Front_1strow [frontid], row2, row1, row2)) ; + + /* look in the range row1 ... row2 */ + for (row = row1 ; row <= row2 ; row++) + { + DEBUG2 ((" Row: "ID"\n", row)) ; + if (NON_PIVOTAL_ROW (row)) + { + /* found it */ + DEBUG2 ((" Row: "ID" found\n", row)) ; + ASSERT (Frpos [row] == EMPTY) ; + pivrow [OUT] = row ; + break ; + } + } + Front_new1strow [fleftmost] = row ; + + if (pivrow [OUT] == EMPTY) + { + /* not found, look in empty row set in "dummy" front */ + row1 = Front_new1strow [nfr] ; + row2 = n_row-1 ; + DEBUG2 (("Empty: "ID" Rows ["ID" to "ID"] srch["ID" to "ID"]\n", + nfr, Front_1strow [nfr], row2, row1, row2)) ; + + /* look in the range row1 ... row2 */ + for (row = row1 ; row <= row2 ; row++) + { + DEBUG2 ((" Empty Row: "ID"\n", row)) ; + if (NON_PIVOTAL_ROW (row)) + { + /* found it */ + DEBUG2 ((" Empty Row: "ID" found\n", row)) ; + ASSERT (Frpos [row] == EMPTY) ; + pivrow [OUT] = row ; + break ; + } + } + Front_new1strow [nfr] = row ; + } + + if (pivrow [OUT] == EMPTY) + { + /* Row-merge set is empty. We can just discard */ + /* the candidate pivot column. */ + DEBUG0 (("Warning: row-merge set empty\n")) ; + return (UMFPACK_WARNING_singular_matrix) ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* construct the candidate row in the front, if any */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG4 (("pivrow [IN]: "ID"\n", pivrow [IN])) ; + UMF_dump_rowcol (0, Numeric, Work, pivrow [IN], TRUE) ; +#endif + + if (pivrow [IN] != EMPTY) + { + + /* the row merge candidate row is not pivrow [IN] */ + freebie [IN] = (pivrow [IN] == prior_pivrow [IN]) && (cdeg > 0) ; + ASSERT (cdeg >= 0) ; + + if (!freebie [IN]) + { + /* include current front in the degree of this row */ + + Fcpos = Work->Fcpos ; + fncols = Work->fncols ; + + wpflag = Work->Wpflag ; + ASSERT (wpflag < EMPTY) ; + + /* -------------------------------------------------------------- */ + /* construct the pattern of the IN row */ + /* -------------------------------------------------------------- */ + +#ifndef NDEBUG + /* check Fcols */ + DEBUG5 (("ROW ASSEMBLE: rdeg "ID"\nREDUCE ROW "ID"\n", + fncols, pivrow [IN])) ; + for (j = 0 ; j < fncols ; j++) + { + col = Work->Fcols [j] ; + ASSERT (col >= 0 && col < Work->n_col) ; + ASSERT (Fcpos [col] >= 0) ; + } + if (UMF_debug > 0 || Work->n_col < 1000) + { + Int cnt = fncols ; + for (col = 0 ; col < Work->n_col ; col++) + { + if (Fcpos [col] < 0) cnt++ ; + } + ASSERT (cnt == Work->n_col) ; + } + /* check Wp */ + if (UMF_debug > 0 || MAX (Work->n_row, Work->n_col) < 1000) + { + for (i = 0 ; i < MAX (Work->n_row, Work->n_col) ; i++) + { + ASSERT (Wp [i] < 0) ; + ASSERT (Wp [i] > wpflag) ; + } + } +#endif + + rdeg [IN] = fncols ; + + ASSERT (pivrow [IN] >= 0 && pivrow [IN] < Work->n_row) ; + ASSERT (NON_PIVOTAL_ROW (pivrow [IN])) ; + + /* add the pivot column itself */ + if (Wp [pivcol] > wpflag && Fcpos [pivcol] < 0) + { + DEBUG0 (("Adding pivot col to pivrow [IN] pattern\n")) ; + if (rdeg [IN] >= max_rdeg) + { + return (UMFPACK_ERROR_different_pattern) ; + } + Wp [pivcol] = wpflag ; + W_i [rdeg [IN]++] = pivcol ; + } + + tpi = Row_tuples [pivrow [IN]] ; + if (tpi) + { + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Row_tlen [pivrow [IN]] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) + { + continue ; /* element already deallocated */ + } + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + ncols = ep->ncols ; + Rows = Cols + ncols ; + if (Rows [f] == EMPTY) + { + continue ; /* row already assembled */ + } + ASSERT (pivrow [IN] == Rows [f]) ; + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + if ((col >= 0) && (Wp [col] > wpflag) && Fcpos [col] <0) + { + if (rdeg [IN] >= max_rdeg) + { + return (UMFPACK_ERROR_different_pattern) ; + } + Wp [col] = wpflag ; + W_i [rdeg [IN]++] = col ; + } + } + + *tp2++ = *tp ; /* leave the tuple in the list */ + } + Row_tlen [pivrow [IN]] = tp2 - tp1 ; + } + +#ifndef NDEBUG + DEBUG4 (("Reduced IN row:\n")) ; + for (j = 0 ; j < fncols ; j++) + { + DEBUG6 ((" "ID" "ID" "ID"\n", + j, Work->Fcols [j], Fcpos [Work->Fcols [j]])) ; + ASSERT (Fcpos [Work->Fcols [j]] >= 0) ; + } + for (j = fncols ; j < rdeg [IN] ; j++) + { + DEBUG6 ((" "ID" "ID" "ID"\n", j, W_i [j], Wp [W_i [j]])); + ASSERT (Wp [W_i [j]] == wpflag) ; + } + /* mark the end of the pattern in case we scan it by mistake */ + /* Note that this means W_i must be of size >= fncols_max + 1 */ + W_i [rdeg [IN]] = EMPTY ; +#endif + + /* rdeg [IN] is now the exact degree of the IN row */ + + /* clear Work->Wp. All Wp [0..n] is now negative, and */ + /* greater than Work->wpflag */ + Work->Wpflag-- ; + } + + } + + /* ---------------------------------------------------------------------- */ + /* construct the candidate row not in the front, if any */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG4 (("pivrow [OUT]: "ID"\n", pivrow [OUT])) ; + UMF_dump_rowcol (0, Numeric, Work, pivrow [OUT], TRUE) ; +#endif + + /* If this is a candidate row from the row merge set, force it to be */ + /* scanned (ignore prior_pivrow [OUT]). */ + + if (pivrow [OUT] != EMPTY) + { + freebie [OUT] = (pivrow [OUT] == prior_pivrow [OUT]) && cdeg > 0 ; + ASSERT (cdeg >= 0) ; + + if (!freebie [OUT]) + { + + wpflag = Work->Wpflag ; + ASSERT (wpflag < EMPTY) ; + + /* -------------------------------------------------------------- */ + /* construct the pattern of the row */ + /* -------------------------------------------------------------- */ + +#ifndef NDEBUG + /* check Wp */ + if (UMF_debug > 0 || MAX (Work->n_row, Work->n_col) < 1000) + { + for (i = 0 ; i < MAX (Work->n_row, Work->n_col) ; i++) + { + ASSERT (Wp [i] < 0) ; + ASSERT (Wp [i] > wpflag) ; + } + } +#endif + + rdeg [OUT] = 0 ; + + ASSERT (pivrow [OUT] >= 0 && pivrow [OUT] < Work->n_row) ; + ASSERT (NON_PIVOTAL_ROW (pivrow [OUT])) ; + + /* add the pivot column itself */ + if (Wp [pivcol] > wpflag) + { + DEBUG0 (("Adding pivot col to pivrow [OUT] pattern\n")) ; + if (rdeg [OUT] >= max_rdeg) + { + return (UMFPACK_ERROR_different_pattern) ; + } + Wp [pivcol] = wpflag ; + W_o [rdeg [OUT]++] = pivcol ; + } + + tpi = Row_tuples [pivrow [OUT]] ; + if (tpi) + { + tp = (Tuple *) (Memory + tpi) ; + tp1 = tp ; + tp2 = tp ; + tpend = tp + Row_tlen [pivrow [OUT]] ; + for ( ; tp < tpend ; tp++) + { + e = tp->e ; + ASSERT (e > 0 && e <= Work->nel) ; + if (!E [e]) + { + continue ; /* element already deallocated */ + } + f = tp->f ; + p = Memory + E [e] ; + ep = (Element *) p ; + p += UNITS (Element, 1) ; + Cols = (Int *) p ; + ncols = ep->ncols ; + Rows = Cols + ncols ; + if (Rows [f] == EMPTY) + { + continue ; /* row already assembled */ + } + ASSERT (pivrow [OUT] == Rows [f]) ; + + for (j = 0 ; j < ncols ; j++) + { + col = Cols [j] ; + if ((col >= 0) && (Wp [col] > wpflag)) + { + if (rdeg [OUT] >= max_rdeg) + { + return (UMFPACK_ERROR_different_pattern) ; + } + Wp [col] = wpflag ; + W_o [rdeg [OUT]++] = col ; + } + } + + *tp2++ = *tp ; /* leave the tuple in the list */ + } + Row_tlen [pivrow [OUT]] = tp2 - tp1 ; + } + +#ifndef NDEBUG + DEBUG4 (("Reduced row OUT:\n")) ; + for (j = 0 ; j < rdeg [OUT] ; j++) + { + DEBUG6 ((" "ID" "ID" "ID"\n", j, W_o [j], Wp [W_o [j]])) ; + ASSERT (Wp [W_o [j]] == wpflag) ; + } + /* mark the end of the pattern in case we scan it by mistake */ + /* Note that this means W_o must be of size >= fncols_max + 1 */ + W_o [rdeg [OUT]] = EMPTY ; +#endif + + /* rdeg [OUT] is now the exact degree of the row */ + + /* clear Work->Wp. All Wp [0..n] is now negative, and */ + /* greather than Work->Wpflag */ + Work->Wpflag-- ; + } + + } + DEBUG5 (("Row_search end ] \n")) ; + + return (UMFPACK_OK) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_scale_column.c b/src/sparse-matrix/umfpack/umf_scale_column.c new file mode 100644 index 0000000..2c778a8 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_scale_column.c @@ -0,0 +1,773 @@ +/* ========================================================================== */ +/* === UMF_scale_column ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Scale the current pivot column, and log the permutation. + Store the LU factors. Called by the kernel. + + Returns TRUE if successful, FALSE if out of memory. +*/ + +#include "umf_internal.h" +#include "umf_mem_alloc_head_block.h" +#include "umf_mem_free_tail_block.h" +#include "umf_get_memory.h" + +/* ========================================================================== */ + +GLOBAL Int UMF_scale_column +( + NumericType *Numeric, + WorkType *Work +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int i, k, k1, fnrows_max, fnrows, fncols, *Frpos, *Fcpos, pos, row, col, + pivrow, pivcol, *Frows, *Fcols, *Lpattern, *Upattern, *Lpos, *Upos, + llen, ulen, fncols_max, fnpiv, uilen, lnz, unz, *Row_tuples, + *Col_tuples, *Rperm, *Cperm, *Lilen, *Uilen, *Lip, *Uip, *Li, *Ui, + pivcol_position, newLchain, newUchain, pivrow_position, p, size, lip, + uip, lnzi, lnzx, unzx, lnz2i, lnz2x, unz2i, unz2x, zero_pivot, + is_nonzero, nan_pivot ; + Entry *D, x, pivot_value, *Fx, *Fcol, *Frow, *Lval, *Uval ; + double d ; + +#ifndef NDEBUG + Int *Col_degree, *Row_degree ; + UMF_allocfail = FALSE ; + if (UMF_gprob > 0) /* a double relop, but ignore NaN case */ + { + double rrr = ((double) (rand ( ))) / (((double) RAND_MAX) + 1) ; + DEBUG4 (("Check random %e %e\n", rrr, UMF_gprob)) ; + UMF_allocfail = rrr < UMF_gprob ; /* a double relop, but ignore NaN case */ + if (UMF_allocfail) + { + DEBUG1 (("Random garbage collection (scale_column)\n")) ; + } + } +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + fnrows = Work->fnrows ; + fncols = Work->fncols ; + + /* ---------------------------------------------------------------------- */ + + Rperm = Numeric->Rperm ; + Cperm = Numeric->Cperm ; + Lpos = Numeric->Lpos ; + Upos = Numeric->Upos ; + Lilen = Numeric->Lilen ; + Uilen = Numeric->Uilen ; + + Lip = Numeric->Lip ; + Uip = Numeric->Uip ; + D = Numeric->D ; + + /* ---------------------------------------------------------------------- */ + + k = Work->npiv++ ; + + Fx = Work->Fx ; + fnrows_max = Work->fnrows_max ; + fncols_max = Work->fncols_max ; + fnpiv = Work->fnpiv ; + Frpos = Work->Frpos ; + Fcpos = Work->Fcpos ; + Frows = Work->Frows ; + Fcols = Work->Fcols ; + pivrow = Work->pivrow ; + pivcol = Work->pivcol ; + + ASSERT (pivrow >= 0 && pivrow < Work->n_row) ; + ASSERT (pivcol >= 0 && pivcol < Work->n_col) ; + ASSERT (k < MIN (Work->n_row, Work->n_col)) ; + +#ifndef NDEBUG + Col_degree = Numeric->Cperm ; /* for NON_PIVOTAL_COL macro */ + Row_degree = Numeric->Rperm ; /* for NON_PIVOTAL_ROW macro */ + if (k % 1000 == 0) DEBUG0 (("step "ID"\n", k)) ; +#endif + + Row_tuples = Numeric->Uip ; + Col_tuples = Numeric->Lip ; + + Lpattern = Work->Lpattern ; + llen = Work->llen ; + Upattern = Work->Upattern ; + ulen = Work->ulen ; + + /* ---------------------------------------------------------------------- */ + + /* Frpos [row] >= 0 for each row in pivot column pattern. */ + /* offset into pattern is given by: */ + /* Frpos [row] == offset - 1 */ + /* Frpos [pivrow] is the offset of the latest pivot row */ + + /* Fcpos [col] >= 0 for each col in pivot row pattern. */ + /* Fcpos [col] == (offset - 1) * fnrows_max */ + /* Fcpos [pivcol] is the offset of the latest pivot column */ + + /* Fcols [0..fncols-1] is the pivot row pattern (excl pivot cols) */ + /* Frows [0..fnrows-1] is the pivot col pattern (excl pivot rows) */ + +#ifndef NDEBUG + DEBUG7 (("Current frontal matrix: (prior to pivcol scale)\n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; +#endif + + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + { + Int row, col, i, lcnt, ucnt ; + + DEBUG2 (("Store column of L, k = "ID", llen "ID"\n", k, llen)) ; + for (i = 0 ; i < llen ; i++) + { + row = Lpattern [i] ; + ASSERT (row >= 0 && row < Work->n_row) ; + DEBUG2 ((" Lpattern["ID"] "ID" Lpos "ID, i, row, Lpos [row])) ; + if (row == pivrow) DEBUG2 ((" <- pivot row")) ; + DEBUG2 (("\n")) ; + ASSERT (NON_PIVOTAL_ROW (row)) ; + ASSERT (i == Lpos [row]) ; + } + + DEBUG2 (("Store row of U, k = "ID", ulen "ID"\n", k, ulen)) ; + for (i = 0 ; i < ulen ; i++) + { + col = Upattern [i] ; + DEBUG2 ((" Upattern["ID"] "ID, i, col)) ; + if (col == pivcol) DEBUG2 ((" <- pivot col")) ; + DEBUG2 (("\n")) ; + ASSERT (col >= 0 && col < Work->n_col) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + ASSERT (i == Upos [col]) ; + } + + lcnt = 0 ; + ucnt = 0 ; + if (Work->n_row < 1000) + { + for (row = 0 ; row < Work->n_row ; row++) + { + if (NON_PIVOTAL_ROW (row) && Lpos [row] != EMPTY) lcnt++ ; + } + ASSERT (lcnt == llen) ; + } + if (Work->n_col < 1000) + { + for (col = 0 ; col < Work->n_col ; col++) + { + if (NON_PIVOTAL_COL (col) && Upos [col] != EMPTY) ucnt++ ; + } + ASSERT (ucnt == ulen) ; + } + } +#endif + + /* ---------------------------------------------------------------------- */ + /* remove pivot row from L */ + /* ---------------------------------------------------------------------- */ + + /* remove pivot row index from current column of L */ + /* if a new Lchain starts, then all entries are removed later */ + DEBUG2 (("Removing pivrow from Lpattern, k = "ID"\n", k)) ; + ASSERT (NON_PIVOTAL_ROW (pivrow)) ; + pivrow_position = Lpos [pivrow] ; + if (pivrow_position != EMPTY) + { + /* place the last entry in the column in the */ + /* position of the pivot row index */ + ASSERT (pivrow == Lpattern [pivrow_position]) ; + row = Lpattern [--llen] ; + ASSERT (NON_PIVOTAL_ROW (row)) ; + Lpattern [pivrow_position] = row ; + Lpos [row] = pivrow_position ; + Lpos [pivrow] = EMPTY ; + } + + /* ---------------------------------------------------------------------- */ + /* store the pivot value, for the diagonal matrix D */ + /* ---------------------------------------------------------------------- */ + + /* fnpiv-th pivot in frontal matrix located in */ + /* Fx (fnrows_max-fnpiv, fncols_max-fnpiv) */ + + Fcol = Fx + (fncols_max - fnpiv) * fnrows_max ; + pivot_value = Fcol [fnrows_max - fnpiv] ; + D [k] = pivot_value ; + + ABS (d, pivot_value) ; + zero_pivot = SCALAR_IS_ZERO (d) ; + nan_pivot = SCALAR_IS_NAN (d) ; + + if (k == 0) + { + Numeric->min_udiag = d ; + Numeric->max_udiag = d ; + } + else + { + /* min (abs (diag (U))) behaves as follows: If any entry is zero, + then the result is zero (regardless of the presence of NaN's). + Otherwise, if any entry is NaN, then the result is NaN. Otherwise, + the result is the smallest absolute value on the diagonal of U. + */ + + if (SCALAR_IS_NONZERO (Numeric->min_udiag)) + { + if (zero_pivot || nan_pivot) + { + Numeric->min_udiag = d ; + } + else if (!SCALAR_IS_NAN (Numeric->min_udiag)) + { + /* d and min_udiag are both non-NaN */ + Numeric->min_udiag = MIN (Numeric->min_udiag, d) ; + } + } + + /* + max (abs (diag (U))) behaves as follows: If any entry is NaN + then the result is NaN. Otherise, the result is the largest + absolute value on the diagonal of U. + */ + + if (nan_pivot) + { + Numeric->max_udiag = d ; + } + else if (!SCALAR_IS_NAN (Numeric->max_udiag)) + { + /* d and max_udiag are both non-NaN */ + Numeric->max_udiag = MAX (Numeric->max_udiag, d) ; + } + } + + if (!zero_pivot) + { + /* the pivot is nonzero, but might be Inf or NaN */ + Numeric->nnzpiv++ ; + } + DEBUG4 (("Pivot abs value: %g nnzpiv: "ID" D["ID"]=", d, Numeric->nnzpiv, k)) ; + EDEBUG4 (pivot_value) ; + DEBUG4 (("\n")) ; + + /* ---------------------------------------------------------------------- */ + /* scale pivot column and count nonzeros in kth column of L */ + /* ---------------------------------------------------------------------- */ + + lnz = 0 ; + lnz2i = 0 ; + lnz2x = llen ; + for (i = 0 ; i < fnrows ; i++) + { + EDEBUG4 (Fcol [i]) ; + EDEBUG4 (pivot_value) ; + if (IS_NONZERO (Fcol [i])) + { + /* Fcol [i] is nonzero, NaN, or Inf */ + /* Fcol [i] = Fcol [i] / pivot_value ; */ + DIV (x, Fcol [i], pivot_value) ; + Fcol [i] = x ; + /* underflow may have occured, so check if result is zero */ + is_nonzero = IS_NONZERO (x) ; + } + else + { + /* Fcol [i] is zero. Do not divide by pivot value. */ + is_nonzero = FALSE ; + } + DEBUG4 (("pivot column: "ID" is_nonzero "ID, i, is_nonzero)) ; + EDEBUG4 (Fcol [i]) ; + DEBUG4 (("\n")) ; + + /* if we start a new Lchain: */ + if (is_nonzero) + { + /* one new integer and one new Entry */ + DEBUG4 ((" got an entry \n")) ; + lnz++ ; + } + + /* if we continue the prior Lchain: */ + row = Frows [i] ; + ASSERT (row != pivrow) ; + ASSERT (NON_PIVOTAL_ROW (row)) ; + pos = Lpos [row] ; + if (pos == EMPTY && is_nonzero) + { + /* row is not in the Lpattern, add it if Fcol [i] is nonzero */ + lnz2i++ ; + lnz2x++ ; + } + DEBUG3 (("Scale L col, row "ID" pos "ID" scaled value", row, pos)) ; + EDEBUG3 (Fcol [i]) ; + DEBUG3 ((" ::: lnz "ID" lnz2i "ID" lnz2x "ID"\n", lnz, lnz2i, lnz2x)) ; + } + + /* determine if we start a new Lchain or continue the old one */ + if (llen == 0 || zero_pivot) + { + /* llen == 0 means there is no prior Lchain */ + /* D [k] == 0 means the pivot column is empty */ + newLchain = TRUE ; + } + else + { + newLchain = + /* storage for starting a new Lchain */ + UNITS (Entry, lnz) + UNITS (Int, lnz) + <= + /* storage for continuing a prior Lchain */ + UNITS (Entry, lnz2x) + UNITS (Int, lnz2i) ; + } + + if (newLchain) + { + /* start a new chain for column k of L */ + DEBUG2 (("Start new Lchain, k = "ID"\n", k)) ; + + pivrow_position = EMPTY ; + + /* clear the prior Lpattern */ + for (i = 0 ; i < llen ; i++) + { + row = Lpattern [i] ; + ASSERT (NON_PIVOTAL_ROW (row)) ; + Lpos [row] = EMPTY ; + } + llen = 0 ; + + lnzi = lnz ; + lnzx = lnz ; + } + else + { + /* continue the prior Lchain */ + DEBUG2 (("Continue Lchain, k = "ID"\n", k)) ; + lnzi = lnz2i ; + lnzx = lnz2x ; + } + + /* ---------------------------------------------------------------------- */ + /* count the nonzeros in the row of U */ + /* ---------------------------------------------------------------------- */ + + /* store the numerical entries and find new nonzeros */ + Frow = Fx + (fnrows_max - fnpiv) ; + + unz = 0 ; + unz2i = 0 ; + unz2x = ulen ; + DEBUG2 (("unz2x is "ID"\n", unz2x)) ; + + /* if row k does not end a Uchain, pivcol will not be included in ulen */ + + ASSERT (NON_PIVOTAL_COL (pivcol)) ; + pivcol_position = Upos [pivcol] ; + if (pivcol_position != EMPTY) + { + unz2x-- ; + DEBUG2 (("(exclude pivcol) unz2x is now "ID"\n", unz2x)) ; + } + + ASSERT (unz2x >= 0) ; + + for (i = 0 ; i < fncols ; i++) + { + x = Frow [i * fnrows_max] ; + is_nonzero = IS_NONZERO (x) ; + + /* if we start a new Uchain */ + if (is_nonzero) + { + unz++ ; + DEBUG2 (("If Unew: "ID, unz)) ; + EDEBUG2 (x) ; + DEBUG2 (("\n")) ; + } + + /* if we continue the prior Uchain */ + col = Fcols [i] ; + ASSERT (col != pivcol) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + pos = Upos [col] ; + if (pos == EMPTY && is_nonzero) + { + /* add this new nonzero entry to the U pattern, if nonzero */ + unz2i++ ; + unz2x++ ; + DEBUG2 (("If old: "ID" :", unz2x)) ; + EDEBUG2 (x) ; + DEBUG2 (("\n")) ; + } + } + + ASSERT (IMPLIES (k == 0, ulen == 0)) ; + + /* determine if we start a new Uchain or continue the old one */ + if (ulen == 0 || zero_pivot) + { + /* ulen == 0 means there is no prior Uchain */ + /* D [k] == 0 means the matrix is singular (pivot row might */ + /* not be empty, however, but start a new Uchain to prune zero */ + /* entries for the deg > 0 test in UMF_u*solve) */ + newUchain = TRUE ; + } + else + { + newUchain = + /* approximate storage for starting a new Uchain */ + UNITS (Entry, unz) + UNITS (Int, unz) + <= + /* approximate storage for continuing a prior Uchain */ + UNITS (Entry, unz2x) + UNITS (Int, unz2i) ; + + /* this would be exact, except for the Int to Unit rounding, */ + /* because the Upattern is stored only at the end of the Uchain */ + } + + /* ---------------------------------------------------------------------- */ + /* allocate space for the column of L and the row of U */ + /* ---------------------------------------------------------------------- */ + + size = UNITS (Int, lnzi) + UNITS (Entry, lnzx) ; + if (newUchain) + { + /* store the pattern of the last row in the prior Uchain */ + size += UNITS (Int, ulen) ; + unzx = unz ; + } + else + { + unzx = unz2x ; + } + size += UNITS (Entry, unzx) ; + + p = UMF_mem_alloc_head_block (Numeric, size) ; + if (!p) + { + /* do garbage collection, realloc, and try again */ + if (!UMF_get_memory (Numeric, Work, size)) + { + return (FALSE) ; /* out of memory */ + } + p = UMF_mem_alloc_head_block (Numeric, size) ; + } + if (!p) + { + return (FALSE) ; /* out of memory */ + } + + /* ---------------------------------------------------------------------- */ + /* store the column of L */ + /* ---------------------------------------------------------------------- */ + + lip = p ; + + Li = (Int *) (Numeric->Memory + p) ; + p += UNITS (Int, lnzi) ; + Lval = (Entry *) (Numeric->Memory + p) ; + p += UNITS (Entry, lnzx) ; + + for (i = 0 ; i < lnzx ; i++) + { + CLEAR (Lval [i]) ; + } + + /* store the numerical entries */ + + if (newLchain) + { + /* flag the first column in the Lchain by negating Lip [k] */ + lip = -lip ; + + ASSERT (llen == 0) ; + for (i = 0 ; i < fnrows ; i++) + { + x = Fcol [i] ; + DEBUG4 (("Store column, i "ID" is_nonzero(x) "ID"\n", i, + IS_NONZERO (x))) ; + EDEBUG4 (x) ; + EDEBUG4 (Fcol [i]) ; + DEBUG4 (("\n")) ; + + if (IS_NONZERO (x)) + { + DEBUG4 (("Store column, i "ID" is_nonzero(x) "ID"\n", i, + IS_NONZERO (x))) ; + + row = Frows [i] ; + ASSERT (NON_PIVOTAL_ROW (row)) ; + pos = llen++ ; + Lpattern [pos] = row ; + Lpos [row] = pos ; + Li [pos] = row ; + Lval [pos] = x ; + DEBUG2 (("(newLchain) New entry in Lpattern: row "ID" pos "ID + "\n", row, pos)) ; + DEBUG2 (("(newLchain) Store Lval row "ID" pos "ID" value", + row, pos)) ; + EDEBUG2 (x) ; + DEBUG2 (("\n")) ; + } + } + } + else + { + ASSERT (llen > 0) ; + for (i = 0 ; i < fnrows ; i++) + { + x = Fcol [i] ; + if (IS_NONZERO (x)) + { + row = Frows [i] ; + ASSERT (NON_PIVOTAL_ROW (row)) ; + pos = Lpos [row] ; + if (pos == EMPTY) + { + /* add this new nonzero entry to the L pattern */ + pos = llen++ ; + DEBUG2 (("New entry in Lpattern: row "ID" pos "ID"\n", + row, pos)) ; + ASSERT (llen <= lnzx) ; + Lpattern [pos] = row ; + Lpos [row] = pos ; + *Li++ = row ; + } + DEBUG2 (("Store Lval row "ID" pos "ID" value", row, pos)) ; + EDEBUG2 (x) ; + DEBUG2 (("\n")) ; + ASSERT (row == Lpattern [pos]) ; + ASSERT (pos < lnzx) ; + Lval [pos] = x ; + } + } + } + DEBUG4 (("llen "ID" lnzx "ID"\n", llen, lnzx)) ; + ASSERT (llen == lnzx) ; + ASSERT (lnz <= llen) ; + + Numeric->lnz += lnz ; + Numeric->nLentries += lnzx ; + Work->llen = llen ; + Numeric->isize += lnzi ; + + /* ---------------------------------------------------------------------- */ + /* store the row of U */ + /* ---------------------------------------------------------------------- */ + + uip = p ; + + if (newUchain) + { + /* starting a new Uchain - flag this by negating Uip [k] */ + uip = -uip ; + DEBUG2 (("Start new Uchain, k = "ID"\n", k)) ; + + pivcol_position = EMPTY ; + + /* end the prior Uchain */ + /* save the current Upattern, and then */ + /* clear it and start a new Upattern */ + DEBUG2 (("Ending prior chain, k-1 = "ID"\n", k-1)) ; + uilen = ulen ; + Ui = (Int *) (Numeric->Memory + p) ; + Numeric->isize += ulen ; + p += UNITS (Int, ulen) ; + for (i = 0 ; i < ulen ; i++) + { + col = Upattern [i] ; + ASSERT (col >= 0 && col < Work->n_col) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + Upos [col] = EMPTY ; + Ui [i] = col ; + } + + ulen = 0 ; + + } + else + { + /* continue the prior Uchain */ + DEBUG2 (("Continue Uchain, k = "ID"\n", k)) ; + ASSERT (k > 0) ; + + /* remove pivot col index from current row of U */ + /* if a new Uchain starts, then all entries are removed later */ + DEBUG2 (("Removing pivcol from Upattern, k = "ID"\n", k)) ; + + if (pivcol_position != EMPTY) + { + /* place the last entry in the row in the */ + /* position of the pivot col index */ + ASSERT (pivcol == Upattern [pivcol_position]) ; + col = Upattern [--ulen] ; + ASSERT (col >= 0 && col < Work->n_col) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + Upattern [pivcol_position] = col ; + Upos [col] = pivcol_position ; + Upos [pivcol] = EMPTY ; + } + + /* this row continues the Uchain. Keep track of how much */ + /* to trim from the k-th length to get the length of the */ + /* (k-1)st row of U */ + uilen = unz2i ; + + } + + Uval = (Entry *) (Numeric->Memory + p) ; + /* p += UNITS (Entry, unzx), no need to increment p */ + + for (i = 0 ; i < unzx ; i++) + { + CLEAR (Uval [i]) ; + } + + if (newUchain) + { + ASSERT (ulen == 0) ; + for (i = 0 ; i < fncols ; i++) + { + x = Frow [i * fnrows_max] ; + if (IS_NONZERO (x)) + { + /* add this new nonzero entry to the U pattern */ + col = Fcols [i] ; + ASSERT (col >= 0 && col < Work->n_col) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + pos = ulen++ ; + Upattern [pos] = col ; + Upos [col] = pos ; + Uval [pos] = x ; + DEBUG2 (("(newUchain) New entry in Upattern: col "ID" pos "ID + "\n", col, pos)) ; + DEBUG2 (("(newUchain) Store Uval col "ID" pos "ID" value", + col, pos)) ; + EDEBUG2 (x) ; + DEBUG2 (("\n")) ; + } + } + } + else + { + + ASSERT (ulen > 0) ; + + /* store the numerical entries and find new nonzeros */ + + for (i = 0 ; i < fncols ; i++) + { + x = Frow [i * fnrows_max] ; + if (IS_NONZERO (x)) + { + col = Fcols [i] ; + ASSERT (col >= 0 && col < Work->n_col) ; + ASSERT (NON_PIVOTAL_COL (col)) ; + pos = Upos [col] ; + if (pos == EMPTY) + { + /* add this new nonzero entry to the U pattern */ + ASSERT (ulen < unzx) ; + pos = ulen++ ; + Upattern [pos] = col ; + Upos [col] = pos ; + DEBUG2 (("New entry in Upattern: col "ID" pos "ID"\n", + col, pos)) ; + } + DEBUG2 (("Store Uval col "ID" pos "ID" value", col, pos)) ; + EDEBUG2 (x) ; + DEBUG2 (("\n")) ; + ASSERT (col == Upattern [pos]) ; + ASSERT (pos < unzx) ; + Uval [pos] = x ; + } + } + } + + ASSERT (ulen == unzx) ; + ASSERT (unz <= ulen) ; + Numeric->unz += unz ; + Numeric->nUentries += unzx ; + Work->ulen = ulen ; + DEBUG1 (("Work->ulen = "ID" at end of pivot step, k: "ID"\n", ulen, k)) ; + + /* ---------------------------------------------------------------------- */ + /* count the "true" flops, based on LU pattern only */ + /* ---------------------------------------------------------------------- */ + + /* the outer product is not done here, but in the BLAS */ + Numeric->flops += DIV_FLOPS * lnz /* scale pivot column */ + + MULTSUB_FLOPS * (lnz*unz) ; /* outer product */ + + /* ====================================================================== */ + /* A pivot step is complete */ + /* ====================================================================== */ + +#ifndef NDEBUG + DEBUG7 (("Current frontal matrix: (after pivcol scale)\n")) ; + UMF_dump_current_front (Numeric, Work, TRUE) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* remove pivot row and column from frontal pattern */ + /* ---------------------------------------------------------------------- */ + + Frpos [pivrow] = EMPTY ; + Fcpos [pivcol] = EMPTY ; + + /* ---------------------------------------------------------------------- */ + /* deallocate the pivot row and pivot column tuples */ + /* ---------------------------------------------------------------------- */ + + UMF_mem_free_tail_block (Numeric, Row_tuples [pivrow]) ; + UMF_mem_free_tail_block (Numeric, Col_tuples [pivcol]) ; + + /* ---------------------------------------------------------------------- */ + /* the pivot column is fully assembled and scaled, and is now the */ + /* k-th column of L. The pivot row is the k-th row of U. */ + /* ---------------------------------------------------------------------- */ + + DEBUG5 (("number of pivots prior to this one: "ID"\n", k)) ; + ASSERT (NON_PIVOTAL_ROW (pivrow)) ; + ASSERT (NON_PIVOTAL_COL (pivcol)) ; + + /* save row and column inverse permutation */ + k1 = ONES_COMPLEMENT (k) ; + Rperm [pivrow] = k1 ; /* aliased with Row_degree */ + Cperm [pivcol] = k1 ; /* aliased with Col_degree */ + + ASSERT (!NON_PIVOTAL_ROW (pivrow)) ; + ASSERT (!NON_PIVOTAL_COL (pivcol)) ; + + Lpos [pivrow] = pivrow_position ; + Upos [pivcol] = pivcol_position ; + + Lip [pivcol] = lip ; /* aliased with Col_tuples */ + Lilen [pivcol] = lnzi ; /* aliased with Col_tlen */ + + Uip [pivrow] = uip ; /* aliased with Row_tuples */ + Uilen [pivrow] = uilen ; /* aliased with Row_tlen */ + + return (TRUE) ; + +} + diff --git a/src/sparse-matrix/umfpack/umf_set_stats.c b/src/sparse-matrix/umfpack/umf_set_stats.c new file mode 100644 index 0000000..6497b4d --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_set_stats.c @@ -0,0 +1,109 @@ +/* ========================================================================== */ +/* === UMF_set_stats ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Sets statistics in Info array. Calculates everything in double precision, + rather than Int or size_t, so that usage estimates can be computed even if + the problem is so large that it would cause integer overflow. + + This routine has many double relop's, but the NaN case is ignored. +*/ + +#include "umf_internal.h" +#include "umf_symbolic_usage.h" + +GLOBAL void UMF_set_stats +( + double Info [ ], + SymbolicType *Symbolic, + double max_usage, /* peak size of Numeric->Memory, in Units */ + double num_mem_size, /* final size of Numeric->Memory, in Units */ + double flops, /* "true flops" */ + double lnz, /* nz in L */ + double unz, /* nz in U */ + double maxfrsize, /* largest front size */ + double ulen, /* size of Numeric->Upattern */ + double npiv, /* number of pivots found */ + Int what /* ESTIMATE or ACTUAL */ +) +{ + + double sym_size, work_usage, nn, n_row, n_col, n_inner, num_On_size1, + num_On_size2, num_usage, maxncols, maxnrows ; + + n_col = Symbolic->n_col ; + n_row = Symbolic->n_row ; + nn = MAX (n_row, n_col) ; + n_inner = MIN (n_row, n_col) ; + maxncols = Symbolic->maxncols ; + maxnrows = Symbolic->maxnrows ; + + /* final Symbolic object size */ + sym_size = UMF_symbolic_usage (Symbolic->n_row, Symbolic->n_col, + Symbolic->nchains, Symbolic->nfr) ; + + /* size of O(n) part of Numeric object during factorization, */ + /* except Numeric->Memory and Numeric->Upattern */ + num_On_size1 = + DUNITS (NumericType, 1) /* Numeric structure */ + + DUNITS (Entry, n_inner+1) /* D */ + + 4 * DUNITS (Int, n_row+1) /* Rperm, Lpos, Uilen, Uip */ + + 4 * DUNITS (Int, n_col+1) ; /* Cperm, Upos, Lilen, Lip */ + + /* size of O(n) part of Numeric object after factorization, */ + /* except Numeric->Memory and Numeric->Upattern */ + num_On_size2 = + DUNITS (NumericType, 1) /* Numeric structure */ + + DUNITS (Entry, n_inner+1) /* D */ + + DUNITS (Int, n_row+1) /* Rperm */ + + DUNITS (Int, n_col+1) /* Cperm */ + + 4 * DUNITS (Int, npiv+1) ; /* Lpos, Uilen, Uip, Upos, Lilen, Lip */ + + /* peak size of Numeric->Memory */ + Info [UMFPACK_VARIABLE_PEAK + what] = max_usage ; + + /* final size of Numeric->Memory */ + Info [UMFPACK_VARIABLE_FINAL + what] = num_mem_size ; + + /* final size of Numeric object, including Numeric->Memory and ->Upattern */ + Info [UMFPACK_NUMERIC_SIZE + what] = + num_On_size2 + + num_mem_size /* final Numeric->Memory size */ + + DUNITS (Int, ulen) ; /* Numeric->Upattern (from Work->Upattern) */ + + /* largest front size (Work->Fx size, or actual size used) */ + Info [UMFPACK_MAX_FRONT_SIZE + what] = maxfrsize ; + + /* UMF_kernel work usage */ + work_usage = + /* Work-> arrays */ + DUNITS (Entry, Symbolic->maxfrsize) /* Fx */ + + 2 * DUNITS (Int, n_row+1) /* Frpos, Lpattern */ + + 2 * DUNITS (Int, n_col+1) /* Fcpos, Upattern */ + + DUNITS (Int, n_col+n_inner+1) /* E */ + + DUNITS (Int, nn + 1) /* Wp */ + + 3 * DUNITS (Int, maxncols + 1) /* Fcols, Wio, Woi */ + + 2 * DUNITS (Int, maxnrows + 1) /* Frows, Wm */ + + DUNITS (Int, MAX (maxnrows, maxncols) + 1) /* Woo */ + + DUNITS (Int, Symbolic->nfr + 1) ; /* Front_new1strow */ + + /* Peak memory for just UMFPACK_numeric. This excludes Numeric->Upattern */ + /* since it includes the equivalenced Work->Upattern array. */ + num_usage = sym_size + num_On_size1 + work_usage + max_usage ; + + /* peak memory usage for both UMFPACK_*symbolic and UMFPACK_numeric. */ + Info [UMFPACK_PEAK_MEMORY + what] = + MAX (Symbolic->peak_sym_usage, num_usage) ; + + Info [UMFPACK_FLOPS + what] = flops ; + Info [UMFPACK_LNZ + what] = lnz ; + Info [UMFPACK_UNZ + what] = unz ; +} diff --git a/src/sparse-matrix/umfpack/umf_solve.c b/src/sparse-matrix/umfpack/umf_solve.c new file mode 100644 index 0000000..c16d8c3 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_solve.c @@ -0,0 +1,1024 @@ +/* ========================================================================== */ +/* === UMF_solve ============================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + Not user-callable. Solves a linear system using the numerical factorization + computed by UMFPACK_numeric. No workspace is dynamically allocated. Counts + flops, but excludes floating-point comparisons (thus real abs (...) are + zero flops, but complex abs (...) takes 6 flops). + + Returns UMFPACK_OK if successful, UMFPACK_ERROR_argument_missing if + required arguments are missing, UMFPACK_ERROR_invalid_system if the sys + string is not valid or if the matrix A is not square. + + Uses the sparse backward error method of Arioli, Demmel, and Duff + (Solving sparse linear systems with sparse backward error, SIAM J. Matrix + Analysis and Applic., vol 10, pp. 165-190). +*/ + +#include "umf_internal.h" +#include "umf_lsolve.h" +#include "umf_usolve.h" +#include "umf_ltsolve.h" +#include "umf_utsolve.h" + +PRIVATE Int do_step +( + double omega [3], + Int step, + const double B2 [ ], + Entry X [ ], + const Entry W [ ], + const double Y [ ], + const double Z2 [ ], + Entry S [ ], + Int n, + double Info [UMFPACK_INFO] +) ; + +/* ========================================================================== */ +/* === UMF_solve ============================================================ */ +/* ========================================================================== */ + +GLOBAL Int UMF_solve +( + Int sys, + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], + double Xx [ ], + const double Bx [ ], +#ifdef COMPLEX + const double Az [ ], + double Xz [ ], + const double Bz [ ], +#endif + NumericType *Numeric, + Int irstep, + double Info [UMFPACK_INFO], + Int Pattern [ ], /* size n */ + double SolveWork [ ] /* if irstep>0 real: size 5*n. complex:10*n */ + /* otherwise real: size n. complex: 4*n */ +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int *Rperm, *Cperm, i, n, p, step, j, nz, status ; + Entry axx, wi, xj, zi, xi, aij, *W, *Z, *S, *X, bi ; + double omega [3], d, *Z2, *Y, z2i, yi, *B2 ; + + /* ---------------------------------------------------------------------- */ + /* initializations */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + UMF_dump_lu (Numeric) ; + ASSERT (Numeric && Xx && Bx && Pattern && SolveWork && Info) ; +#ifdef COMPLEX + ASSERT (Xz && Bz) ; +#endif +#endif + + nz = 0 ; + omega [0] = 0. ; + omega [1] = 0. ; + omega [2] = 0. ; + Rperm = Numeric->Rperm ; + Cperm = Numeric->Cperm ; + Info [UMFPACK_SOLVE_FLOPS] = 0. ; + Info [UMFPACK_IR_TAKEN] = 0 ; + Info [UMFPACK_IR_ATTEMPTED] = 0 ; + + /* UMFPACK_solve does not call this routine if A is rectangular */ + ASSERT (Numeric->n_row == Numeric->n_col) ; + n = Numeric->n_row ; + if (Numeric->nnzpiv < n + || SCALAR_IS_ZERO (Numeric->rcond) || SCALAR_IS_NAN (Numeric->rcond)) + { + /* Note that systems involving just L return UMFPACK_OK, even if */ + /* A is singular (L is always has a unit diagonal). */ + DEBUG0 (("Warning, matrix is singular in umf_solve\n")) ; + status = UMFPACK_WARNING_singular_matrix ; + irstep = 0 ; + } + else + { + status = UMFPACK_OK ; + } + irstep = MAX (0, irstep) ; /* make sure irstep is >= 0 */ + + W = (Entry *) SolveWork ; /* Entry W [0..n-1] */ + + Z = (Entry *) NULL ; /* unused if no iterative refinement */ + S = (Entry *) NULL ; + Y = (double *) NULL ; + Z2 = (double *) NULL ; + B2 = (double *) NULL ; + +#ifdef COMPLEX + X = (Entry *) (SolveWork + 2*n) ; /* Entry X [0..n-1] */ + if (irstep > 0) + { + if (!Ap || !Ai || !Ax || !Az) + { + return (UMFPACK_ERROR_argument_missing) ; + } + Z = (Entry *) (SolveWork + 4*n) ; /* Entry Z [0..n-1] */ + S = (Entry *) (SolveWork + 6*n) ; /* Entry S [0..n-1] */ + Y = (double *) (SolveWork + 8*n) ; /* double Y [0..n-1] */ + B2 = (double *) (SolveWork + 9*n) ; /* double B2 [0..n-1] */ + Z2 = (double *) Z ; /* double Z2 [0..n-1], equiv. to Z */ + } +#else + X = (Entry *) Xx ; /* Entry X [0..n-1] */ + if (irstep > 0) + { + if (!Ap || !Ai || !Ax) + { + return (UMFPACK_ERROR_argument_missing) ; + } + Z = (Entry *) (SolveWork + n) ; /* Entry Z [0..n-1] */ + S = (Entry *) (SolveWork + 2*n) ; /* Entry S [0..n-1] */ + Y = (double *) (SolveWork + 3*n) ; /* double Y [0..n-1] */ + B2 = (double *) (SolveWork + 4*n) ; /* double B2 [0..n-1] */ + Z2 = (double *) Z ; /* double Z2 [0..n-1], equiv. to Z */ + } +#endif + + /* ---------------------------------------------------------------------- */ + /* determine which system to solve */ + /* ---------------------------------------------------------------------- */ + + if (sys == UMFPACK_A) + { + + /* ------------------------------------------------------------------ */ + /* solve Ax=b with optional iterative refinement */ + /* ------------------------------------------------------------------ */ + + if (irstep > 0) + { + + /* -------------------------------------------------------------- */ + /* using iterative refinement: compute Y and B2 */ + /* -------------------------------------------------------------- */ + + nz = Ap [n] ; + Info [UMFPACK_NZ] = nz ; + + /* A is stored by column */ + /* Y (i) = ||A_i||, 1-norm of row i of A */ + for (i = 0 ; i < n ; i++) + { + Y [i] = 0. ; + } + Info [UMFPACK_SOLVE_FLOPS] += (ABS_FLOPS + 1) * nz ; + for (j = 0 ; j < n ; j++) + { + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + /* Y [Ai [p]] += ABS (Ax [p]) ; */ + ASSIGN (aij, Ax [p], Az [p]) ; + ABS (d, aij) ; + Y [Ai [p]] += d ; + } + } + + /* B2 = abs (B) */ + Info [UMFPACK_SOLVE_FLOPS] += ABS_FLOPS * n ; + for (i = 0 ; i < n ; i++) + { + /* B2 [i] = ABS (B [i]) ; */ + ASSIGN (bi, Bx [i], Bz [i]) ; + ABS (B2 [i], bi) ; + } + + } + + for (step = 0 ; step <= irstep ; step++) + { + + /* -------------------------------------------------------------- */ + /* Solve Ax=b (step 0): */ + /* x = Q (U \ (L \ (Pb))) */ + /* and then perform iterative refinement (step > 0): */ + /* x = x + Q (U \ (L \ (P (b-Ax)))) */ + /* -------------------------------------------------------------- */ + + if (step == 0) + { + for (i = 0 ; i < n ; i++) + { + /* W [i] = B [Rperm [i]] ; */ + ASSIGN (W [i], Bx [Rperm [i]], Bz [Rperm [i]]) ; + } + } + else + { + for (i = 0 ; i < n ; i++) + { + /* Z [i] = B [i] ; */ + ASSIGN (Z [i], Bx [i], Bz [i]) ; + } + Info [UMFPACK_SOLVE_FLOPS] += MULTSUB_FLOPS * nz ; + for (i = 0 ; i < n ; i++) + { + xi = X [i] ; + for (p = Ap [i] ; p < Ap [i+1] ; p++) + { + /* Z [Ai [p]] -= Ax [p] * xi ; */ + ASSIGN (aij, Ax [p], Az [p]) ; + MULT_SUB (Z [Ai [p]], aij, xi) ; + } + } + for (i = 0 ; i < n ; i++) + { + W [i] = Z [Rperm [i]] ; + } + } + Info [UMFPACK_SOLVE_FLOPS] += UMF_lsolve (Numeric, W, Pattern) ; + Info [UMFPACK_SOLVE_FLOPS] += UMF_usolve (Numeric, W, Pattern) ; + if (step == 0) + { + for (i = 0 ; i < n ; i++) + { + X [Cperm [i]] = W [i] ; + } + } + else + { + Info [UMFPACK_SOLVE_FLOPS] += ASSEMBLE_FLOPS * n ; + for (i = 0 ; i < n ; i++) + { + /* X [Cperm [i]] += W [i] ; */ + ASSEMBLE (X [Cperm [i]], W [i]) ; + } + } + + /* -------------------------------------------------------------- */ + /* sparse backward error estimate */ + /* -------------------------------------------------------------- */ + + if (irstep > 0) + { + + /* ---------------------------------------------------------- */ + /* A is stored by column */ + /* W (i) = (b-Ax)_i, residual */ + /* Z2 (i) = (|A||x|)_i */ + /* ---------------------------------------------------------- */ + + for (i = 0 ; i < n ; i++) + { + /* W [i] = B [i] ; */ + ASSIGN (W [i], Bx [i], Bz [i]) ; + Z2 [i] = 0. ; + } + Info [UMFPACK_SOLVE_FLOPS] += + (MULT_FLOPS + DECREMENT_FLOPS + ABS_FLOPS + 1) * nz ; + for (j = 0 ; j < n ; j++) + { + xj = X [j] ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + i = Ai [p] ; + + /* axx = Ax [p] * xj ; */ + ASSIGN (aij, Ax [p], Az [p]) ; + MULT (axx, aij, xj) ; + + /* W [i] -= axx ; */ + DECREMENT (W [i], axx) ; + + /* Z2 [i] += ABS (axx) ; */ + ABS (d, axx) ; + Z2 [i] += d ; + } + } + + if (do_step (omega, step, B2, X, W, Y, Z2, S, n, Info)) + { + /* iterative refinement is done */ + break ; + } + + } + + } + + } + else if (sys == UMFPACK_At) + { + + /* ------------------------------------------------------------------ */ + /* solve A'x=b with optional iterative refinement */ + /* ------------------------------------------------------------------ */ + + /* A' is the complex conjugate transpose */ + + if (irstep > 0) + { + + /* -------------------------------------------------------------- */ + /* using iterative refinement: compute Y */ + /* -------------------------------------------------------------- */ + + nz = Ap [n] ; + Info [UMFPACK_NZ] = nz ; + + /* A' is stored by row */ + /* Y (i) = ||A'_i||, 1-norm of row i of A' */ + Info [UMFPACK_SOLVE_FLOPS] += (ABS_FLOPS + 1) * nz ; + for (i = 0 ; i < n ; i++) + { + yi = 0. ; + for (p = Ap [i] ; p < Ap [i+1] ; p++) + { + /* yi += ABS (Ax [p]) ; */ + /* note that abs (aij) is the same as abs (conj (aij)) */ + ASSIGN (aij, Ax [p], Az [p]) ; + ABS (d, aij) ; + yi += d ; + } + Y [i] = yi ; + } + + /* B2 = abs (B) */ + for (i = 0 ; i < n ; i++) + { + /* B2 [i] = ABS (B [i]) ; */ + ASSIGN (bi, Bx [i], Bz [i]) ; + ABS (B2 [i], bi) ; + } + + } + + for (step = 0 ; step <= irstep ; step++) + { + + /* -------------------------------------------------------------- */ + /* Solve A'x=b (step 0): */ + /* x = P' (L' \ (U' \ (Q'b))) */ + /* and then perform iterative refinement (step > 0): */ + /* x = x + P' (L' \ (U' \ (Q' (b-A'x)))) */ + /* -------------------------------------------------------------- */ + + if (step == 0) + { + for (i = 0 ; i < n ; i++) + { + /* W [i] = B [Cperm [i]] ; */ + ASSIGN (W [i], Bx [Cperm [i]], Bz [Cperm [i]]) ; + } + } + else + { + for (i = 0 ; i < n ; i++) + { + /* Z [i] = B [i] ; */ + ASSIGN (Z [i], Bx [i], Bz [i]) ; + } + Info [UMFPACK_SOLVE_FLOPS] += MULTSUB_FLOPS * nz ; + for (i = 0 ; i < n ; i++) + { + zi = Z [i] ; + for (p = Ap [i] ; p < Ap [i+1] ; p++) + { + /* zi -= conjugate (Ax [p]) * X [Ai [p]] ; */ + ASSIGN (aij, Ax [p], Az [p]) ; + MULT_SUB_CONJ (zi, X [Ai [p]], aij) ; + } + Z [i] = zi ; + } + for (i = 0 ; i < n ; i++) + { + W [i] = Z [Cperm [i]] ; + } + } + Info [UMFPACK_SOLVE_FLOPS] += UMF_uhsolve (Numeric, W, Pattern) ; + Info [UMFPACK_SOLVE_FLOPS] += UMF_lhsolve (Numeric, W, Pattern) ; + if (step == 0) + { + for (i = 0 ; i < n ; i++) + { + X [Rperm [i]] = W [i] ; + } + } + else + { + Info [UMFPACK_SOLVE_FLOPS] += ASSEMBLE_FLOPS * n ; + for (i = 0 ; i < n ; i++) + { + /* X [Rperm [i]] += W [i] ; */ + ASSEMBLE (X [Rperm [i]], W [i]) ; + } + } + + /* -------------------------------------------------------------- */ + /* sparse backward error estimate */ + /* -------------------------------------------------------------- */ + + if (irstep > 0) + { + + /* ---------------------------------------------------------- */ + /* A' is stored by row */ + /* W (i) = (b-A'x)_i, residual */ + /* Z (i) = (|A'||x|)_i */ + /* ---------------------------------------------------------- */ + + Info [UMFPACK_SOLVE_FLOPS] += + (MULT_FLOPS + DECREMENT_FLOPS + ABS_FLOPS + 1) * nz ; + for (i = 0 ; i < n ; i++) + { + /* wi = B [i] ; */ + ASSIGN (wi, Bx [i], Bz [i]) ; + z2i = 0. ; + for (p = Ap [i] ; p < Ap [i+1] ; p++) + { + /* axx = conjugate (Ax [p]) * X [Ai [p]] ; */ + ASSIGN (aij, Ax [p], Az [p]) ; + MULT_CONJ (axx, X [Ai [p]], aij) ; + + /* wi -= axx ; */ + DECREMENT (wi, axx) ; + + /* z2i += ABS (axx) ; */ + ABS (d, axx) ; + z2i += d ; + } + W [i] = wi ; + Z2 [i] = z2i ; + } + + if (do_step (omega, step, B2, X, W, Y, Z2, S, n, Info)) + { + /* iterative refinement is done */ + break ; + } + + } + + } + + } + else if (sys == UMFPACK_Aat) + { + + /* ------------------------------------------------------------------ */ + /* solve A.'x=b with optional iterative refinement */ + /* ------------------------------------------------------------------ */ + + /* A' is the array transpose */ + + if (irstep > 0) + { + + /* -------------------------------------------------------------- */ + /* using iterative refinement: compute Y */ + /* -------------------------------------------------------------- */ + + nz = Ap [n] ; + Info [UMFPACK_NZ] = nz ; + + /* A.' is stored by row */ + /* Y (i) = ||A.'_i||, 1-norm of row i of A.' */ + Info [UMFPACK_SOLVE_FLOPS] += (ABS_FLOPS + 1) * nz ; + for (i = 0 ; i < n ; i++) + { + yi = 0. ; + for (p = Ap [i] ; p < Ap [i+1] ; p++) + { + /* yi += ABS (Ax [p]) ; */ + ASSIGN (aij, Ax [p], Az [p]) ; + ABS (d, aij) ; + yi += d ; + } + Y [i] = yi ; + } + + /* B2 = abs (B) */ + for (i = 0 ; i < n ; i++) + { + /* B2 [i] = ABS (B [i]) ; */ + ASSIGN (bi, Bx [i], Bz [i]) ; + ABS (B2 [i], bi) ; + } + + } + + for (step = 0 ; step <= irstep ; step++) + { + + /* -------------------------------------------------------------- */ + /* Solve A.'x=b (step 0): */ + /* x = P' (L.' \ (U.' \ (Q'b))) */ + /* and then perform iterative refinement (step > 0): */ + /* x = x + P' (L.' \ (U.' \ (Q' (b-A.'x)))) */ + /* -------------------------------------------------------------- */ + + if (step == 0) + { + for (i = 0 ; i < n ; i++) + { + /* W [i] = B [Cperm [i]] ; */ + ASSIGN (W [i], Bx [Cperm [i]], Bz [Cperm [i]]) ; + } + } + else + { + for (i = 0 ; i < n ; i++) + { + /* Z [i] = B [i] ; */ + ASSIGN (Z [i], Bx [i], Bz [i]) ; + } + Info [UMFPACK_SOLVE_FLOPS] += MULTSUB_FLOPS * nz ; + for (i = 0 ; i < n ; i++) + { + zi = Z [i] ; + for (p = Ap [i] ; p < Ap [i+1] ; p++) + { + /* zi -= Ax [p] * X [Ai [p]] ; */ + ASSIGN (aij, Ax [p], Az [p]) ; + MULT_SUB (zi, aij, X [Ai [p]]) ; + } + Z [i] = zi ; + } + for (i = 0 ; i < n ; i++) + { + W [i] = Z [Cperm [i]] ; + } + } + Info [UMFPACK_SOLVE_FLOPS] += UMF_utsolve (Numeric, W, Pattern) ; + Info [UMFPACK_SOLVE_FLOPS] += UMF_ltsolve (Numeric, W, Pattern) ; + if (step == 0) + { + for (i = 0 ; i < n ; i++) + { + X [Rperm [i]] = W [i] ; + } + } + else + { + Info [UMFPACK_SOLVE_FLOPS] += ASSEMBLE_FLOPS * n ; + for (i = 0 ; i < n ; i++) + { + /* X [Rperm [i]] += W [i] ; */ + ASSEMBLE (X [Rperm [i]], W [i]) ; + } + } + + /* -------------------------------------------------------------- */ + /* sparse backward error estimate */ + /* -------------------------------------------------------------- */ + + if (irstep > 0) + { + + /* ---------------------------------------------------------- */ + /* A.' is stored by row */ + /* W (i) = (b-A.'x)_i, residual */ + /* Z (i) = (|A.'||x|)_i */ + /* ---------------------------------------------------------- */ + + Info [UMFPACK_SOLVE_FLOPS] += + (MULT_FLOPS + DECREMENT_FLOPS + ABS_FLOPS + 1) * nz ; + for (i = 0 ; i < n ; i++) + { + /* wi = B [i] ; */ + ASSIGN (wi, Bx [i], Bz [i]) ; + z2i = 0. ; + for (p = Ap [i] ; p < Ap [i+1] ; p++) + { + /* axx = Ax [p] * X [Ai [p]] ; */ + ASSIGN (aij, Ax [p], Az [p]) ; + MULT (axx, aij, X [Ai [p]]) ; + + /* wi -= axx ; */ + DECREMENT (wi, axx) ; + + /* z2i += ABS (axx) ; */ + ABS (d, axx) ; + z2i += d ; + } + W [i] = wi ; + Z2 [i] = z2i ; + } + + if (do_step (omega, step, B2, X, W, Y, Z2, S, n, Info)) + { + /* iterative refinement is done */ + break ; + } + + } + + } + + } + else if (sys == UMFPACK_Pt_L) + { + + /* ------------------------------------------------------------------ */ + /* Solve P'Lx=b: x = L \ Pb */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [Rperm [i]] ; */ + ASSIGN (X [i], Bx [Rperm [i]], Bz [Rperm [i]]) ; + } + Info [UMFPACK_SOLVE_FLOPS] = UMF_lsolve (Numeric, X, Pattern) ; + status = UMFPACK_OK ; + + } + else if (sys == UMFPACK_L) + { + + /* ------------------------------------------------------------------ */ + /* Solve Lx=b: x = L \ b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [i] ; */ + ASSIGN (X [i], Bx [i], Bz [i]) ; + } + Info [UMFPACK_SOLVE_FLOPS] = UMF_lsolve (Numeric, X, Pattern) ; + status = UMFPACK_OK ; + + } + else if (sys == UMFPACK_Lt_P) + { + + /* ------------------------------------------------------------------ */ + /* Solve L'Px=b: x = P' (L' \ b) */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* W [i] = B [i] ; */ + ASSIGN (W [i], Bx [i], Bz [i]) ; + } + Info [UMFPACK_SOLVE_FLOPS] = UMF_lhsolve (Numeric, W, Pattern) ; + for (i = 0 ; i < n ; i++) + { + X [Rperm [i]] = W [i] ; + } + status = UMFPACK_OK ; + + } + else if (sys == UMFPACK_Lat_P) + { + + /* ------------------------------------------------------------------ */ + /* Solve L.'Px=b: x = P' (L.' \ b) */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* W [i] = B [i] ; */ + ASSIGN (W [i], Bx [i], Bz [i]) ; + } + Info [UMFPACK_SOLVE_FLOPS] = UMF_ltsolve (Numeric, W, Pattern) ; + for (i = 0 ; i < n ; i++) + { + X [Rperm [i]] = W [i] ; + } + status = UMFPACK_OK ; + + } + else if (sys == UMFPACK_Lt) + { + + /* ------------------------------------------------------------------ */ + /* Solve L'x=b: x = L' \ b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [i] ; */ + ASSIGN (X [i], Bx [i], Bz [i]) ; + } + Info [UMFPACK_SOLVE_FLOPS] = UMF_lhsolve (Numeric, X, Pattern) ; + status = UMFPACK_OK ; + + } + else if (sys == UMFPACK_Lat) + { + + /* ------------------------------------------------------------------ */ + /* Solve L.'x=b: x = L.' \ b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [i] ; */ + ASSIGN (X [i], Bx [i], Bz [i]) ; + } + Info [UMFPACK_SOLVE_FLOPS] = UMF_ltsolve (Numeric, X, Pattern) ; + status = UMFPACK_OK ; + + } + else if (sys == UMFPACK_U_Qt) + { + + /* ------------------------------------------------------------------ */ + /* Solve UQ'x=b: x = Q (U \ b) */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* W [i] = B [i] ; */ + ASSIGN (W [i], Bx [i], Bz [i]) ; + } + Info [UMFPACK_SOLVE_FLOPS] = UMF_usolve (Numeric, W, Pattern) ; + for (i = 0 ; i < n ; i++) + { + X [Cperm [i]] = W [i] ; + } + + } + else if (sys == UMFPACK_U) + { + + /* ------------------------------------------------------------------ */ + /* Solve Ux=b: x = U \ b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [i] ; */ + ASSIGN (X [i], Bx [i], Bz [i]) ; + } + Info [UMFPACK_SOLVE_FLOPS] = UMF_usolve (Numeric, X, Pattern) ; + + } + else if (sys == UMFPACK_Q_Ut) + { + + /* ------------------------------------------------------------------ */ + /* Solve QU'x=b: x = U' \ Q'b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [Cperm [i]] ; */ + ASSIGN (X [i], Bx [Cperm [i]], Bz [Cperm [i]]) ; + } + Info [UMFPACK_SOLVE_FLOPS] = UMF_uhsolve (Numeric, X, Pattern) ; + + } + else if (sys == UMFPACK_Q_Uat) + { + + /* ------------------------------------------------------------------ */ + /* Solve QU.'x=b: x = U.' \ Q'b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [Cperm [i]] ; */ + ASSIGN (X [i], Bx [Cperm [i]], Bz [Cperm [i]]) ; + } + Info [UMFPACK_SOLVE_FLOPS] = UMF_utsolve (Numeric, X, Pattern) ; + + } + else if (sys == UMFPACK_Ut) + { + + /* ------------------------------------------------------------------ */ + /* Solve U'x=b: x = U' \ b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [i] ; */ + ASSIGN (X [i], Bx [i], Bz [i]) ; + } + Info [UMFPACK_SOLVE_FLOPS] = UMF_uhsolve (Numeric, X, Pattern) ; + + } + else if (sys == UMFPACK_Uat) + { + + /* ------------------------------------------------------------------ */ + /* Solve U'x=b: x = U' \ b */ + /* ------------------------------------------------------------------ */ + + for (i = 0 ; i < n ; i++) + { + /* X [i] = B [i] ; */ + ASSIGN (X [i], Bx [i], Bz [i]) ; + } + Info [UMFPACK_SOLVE_FLOPS] = UMF_utsolve (Numeric, X, Pattern) ; + + } + else + { + return (UMFPACK_ERROR_invalid_system) ; + } + +#ifdef COMPLEX + /* copy the solution back, from Entry X [ ] to double Xx [ ]and Xz [ ]*/ + for (i = 0 ; i < n ; i++) + { + Xx [i] = REAL_COMPONENT (X [i]) ; + Xz [i] = IMAG_COMPONENT (X [i]) ; + } +#endif + + /* return UMFPACK_OK, or UMFPACK_WARNING_singular_matrix */ + /* Note that systems involving just L will return UMFPACK_OK */ + return (status) ; +} + + +/* ========================================================================== */ +/* === do_step ============================================================== */ +/* ========================================================================== */ + +/* Perform one step of iterative refinement, for Ax=b or A'x=b */ + +PRIVATE Int do_step /* return TRUE if iterative refinement done */ +( + double omega [3], + Int step, /* which step of iterative refinement to do */ + const double B2 [ ], /* abs (B) */ + Entry X [ ], + const Entry W [ ], + const double Y [ ], + const double Z2 [ ], + Entry S [ ], + Int n, + double Info [UMFPACK_INFO] +) +{ + double last_omega [3], tau, nctau, d1, wd1, d2, wd2, xi, yix, wi, xnorm ; + Int i ; + + /* DBL_EPSILON is a standard ANSI C term defined in <float.h> */ + /* It is the smallest positive x such that 1.0+x != 1.0 */ + + nctau = 1000 * n * DBL_EPSILON ; + DEBUG0 (("nctau = %30.20e\n", nctau)) ; + + /* for approximate flop count, assume d1 > tau is always true */ + Info [UMFPACK_SOLVE_FLOPS] += (2*ABS_FLOPS + 5) * n ; + + /* ---------------------------------------------------------------------- */ + /* save the last iteration in case we need to reinstate it */ + /* ---------------------------------------------------------------------- */ + + last_omega [0] = omega [0] ; + last_omega [1] = omega [1] ; + last_omega [2] = omega [2] ; + + /* ---------------------------------------------------------------------- */ + /* compute sparse backward errors: omega [1] and omega [2] */ + /* ---------------------------------------------------------------------- */ + + /* xnorm = ||x|| maxnorm */ + xnorm = 0.0 ; + for (i = 0 ; i < n ; i++) + { + /* xi = ABS (X [i]) ; */ + ABS (xi, X [i]) ; + if (SCALAR_IS_NAN (xi)) + { + xnorm = xi ; + break ; + } + /* no NaN's to consider here: */ + xnorm = MAX (xnorm, xi) ; + } + + omega [1] = 0. ; + omega [2] = 0. ; + for (i = 0 ; i < n ; i++) + { + yix = Y [i] * xnorm ; + tau = (yix + B2 [i]) * nctau ; + d1 = Z2 [i] + B2 [i] ; + /* wi = ABS (W [i]) ; */ + ABS (wi, W [i]) ; + if (SCALAR_IS_NAN (d1)) + { + omega [1] = d1 ; + omega [2] = d1 ; + break ; + } + if (SCALAR_IS_NAN (tau)) + { + omega [1] = tau ; + omega [2] = tau ; + break ; + } + if (d1 > tau) /* a double relop, but no NaN's here */ + { + wd1 = wi / d1 ; + if (SCALAR_IS_NAN (wd1)) + { + omega [1] = wd1 ; + break ; + } + /* no NaN's to consider here: */ + omega [1] = MAX (omega [1], wd1) ; + } + else if (tau > 0.0) /* a double relop, but no NaN's here */ + { + d2 = Z2 [i] + yix ; + wd2 = wi / d2 ; + if (SCALAR_IS_NAN (wd2)) + { + omega [2] = wd2 ; + break ; + } + /* no NaN's to consider here: */ + omega [2] = MAX (omega [2], wd2) ; + } + } + + omega [0] = omega [1] + omega [2] ; + Info [UMFPACK_OMEGA1] = omega [1] ; + Info [UMFPACK_OMEGA2] = omega [2] ; + + /* ---------------------------------------------------------------------- */ + /* stop the iterations if the backward error is small, or NaN */ + /* ---------------------------------------------------------------------- */ + + Info [UMFPACK_IR_TAKEN] = step ; + Info [UMFPACK_IR_ATTEMPTED] = step ; + + if (SCALAR_IS_NAN (omega [0])) + { + DEBUG0 (("omega[0] is NaN - done.\n")) ; + if (step > 0) + { + DEBUG0 (("last iteration better\n")) ; + for (i = 0 ; i < n ; i++) + { + X [i] = S [i] ; + } + Info [UMFPACK_OMEGA1] = last_omega [1] ; + Info [UMFPACK_OMEGA2] = last_omega [2] ; + Info [UMFPACK_IR_TAKEN] = step - 1 ; + } + return (TRUE) ; + } + + if (omega [0] < DBL_EPSILON) /* double relop, but no NaN case here */ + { + DEBUG0 (("omega[0] too small - done.\n")) ; + return (TRUE) ; + } + + /* ---------------------------------------------------------------------- */ + /* stop if insufficient decrease in omega */ + /* ---------------------------------------------------------------------- */ + + /* double relop, but no NaN case here: */ + if (step > 0 && omega [0] > last_omega [0] / 2) + { + DEBUG0 (("stop refinement\n")) ; + if (omega [0] > last_omega [0]) + { + /* last iteration better than this one, reinstate it */ + DEBUG0 (("last iteration better\n")) ; + for (i = 0 ; i < n ; i++) + { + X [i] = S [i] ; + } + Info [UMFPACK_OMEGA1] = last_omega [1] ; + Info [UMFPACK_OMEGA2] = last_omega [2] ; + } + Info [UMFPACK_IR_TAKEN] = step - 1 ; + return (TRUE) ; + } + + /* ---------------------------------------------------------------------- */ + /* save current solution in case we need to reinstate */ + /* ---------------------------------------------------------------------- */ + + for (i = 0 ; i < n ; i++) + { + S [i] = X [i] ; + } + + /* ---------------------------------------------------------------------- */ + /* iterative refinement continues */ + /* ---------------------------------------------------------------------- */ + + return (FALSE) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_symbolic_usage.c b/src/sparse-matrix/umfpack/umf_symbolic_usage.c new file mode 100644 index 0000000..a9b25ba --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_symbolic_usage.c @@ -0,0 +1,33 @@ +/* ========================================================================== */ +/* === UMF_symbolic_usage =================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Returns the final size of the Symbolic object, in Units */ + +#include "umf_internal.h" + +GLOBAL double UMF_symbolic_usage +( + Int n_row, + Int n_col, + Int nchains, + Int nfr +) +{ + double units = + DUNITS (SymbolicType, 1) /* Symbolic structure */ + + DUNITS (Int, n_col+1) /* Cperm_init */ + + DUNITS (Int, n_row+1) /* Rperm_init */ + + 3*DUNITS (Int, nchains+1) /* Chain_ */ + + 4*DUNITS (Int, nfr+1) ; /* Front_ */ + + return (units) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_transpose.c b/src/sparse-matrix/umfpack/umf_transpose.c new file mode 100644 index 0000000..1b23aa7 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_transpose.c @@ -0,0 +1,385 @@ +/* ========================================================================== */ +/* === UMF_transpose ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Not user-callable. Computes a permuted transpose, R = (A (P,Q(1:nq)))' in + MATLAB notation, where R is in column-form. A is n_row-by-n_col, the + row-form matrix R is n_row-by-nq, where nq <= n_col. A may be singular. + The complex version can do transpose (') or array transpose (.'). + + Uses Gustavson's method (Two Fast Algorithms for Sparse Matrices: + Multiplication and Permuted Transposition, ACM Trans. on Math. Softw., + vol 4, no 3, pp. 250-269). +*/ + +#include "umf_internal.h" +#include "umf_is_permutation.h" + +/* ========================================================================== */ + +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, nz, newj, ilast, do_values ; + + /* ---------------------------------------------------------------------- */ + /* check inputs */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + ASSERT (n_col >= 0) ; + nz = Ap ? Ap [n_col] : 0 ; + DEBUG2 (("UMF_transpose: nz "ID"\n", nz)) ; + DEBUG2 (("n_row "ID" & "ID"\n", n_row, &n_row)) ; + DEBUG2 (("n_col "ID" & "ID"\n", n_col, &n_col)) ; + DEBUG2 (("Ap & "ID" to & "ID"\n", Ap, Ap + (n_col+1) - 1)) ; + DEBUG2 (("Ai & "ID" to & "ID"\n", Ai, Ai + (nz) - 1)) ; + if (Ax) DEBUG2 (("Ax & "ID" to & "ID"\n", Ax, Ax + (nz) - 1)) ; + if (P) DEBUG2 (("P & "ID" to & "ID"\n", P , P + (n_row) - 1)) ; + if (Q) DEBUG2 (("Q & "ID" to & "ID"\n", Q , Q + (n_col) - 1)) ; + DEBUG2 (("Rp & "ID" to & "ID"\n", Rp, Rp + (n_row+1) - 1)) ; + DEBUG2 (("Ri & "ID" to & "ID"\n", Ri, Ri + (nz) - 1)) ; + if (Rx) DEBUG2 (("Rx & "ID" to & "ID"\n", Rx, Rx + (nz) - 1)) ; + DEBUG2 (("W & "ID" to & "ID"\n", W, W + MAX(n_row,n_col) - 1)) ; +#ifdef COMPLEX + if (Az) DEBUG2 (("Az & "ID" to & "ID"\n", Az, Az + (nz) - 1)) ; + if (Rz) DEBUG2 (("Rz & "ID" to & "ID"\n", Rz, Rz + (nz) - 1)) ; + DEBUG2 (("do_conjugate "ID" & "ID"\n", do_conjugate, &do_conjugate)) ; +#endif +#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) ; + } + + nz = Ap [n_col] ; + if (nz < 0) /* nz must be >= 0 */ + { + return (UMFPACK_ERROR_nz_negative) ; + } + + if (!UMF_is_permutation (P, W, n_row, n_row) || + !UMF_is_permutation (Q, W, nq, nq)) + { + return (UMFPACK_ERROR_invalid_permutation) ; + } + + if (Ap [0] != 0) + { + return (UMFPACK_ERROR_Ap0_nonzero) ; + } + + for (j = 0 ; j < n_col ; j++) + { + if (Ap [j] > Ap [j+1]) + { + return (UMFPACK_ERROR_col_length_negative) ; + } + } + + for (j = 0 ; j < n_col ; j++) + { + ilast = -1 ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + i = Ai [p] ; + if (i < 0 || i >= n_row) + { + return (UMFPACK_ERROR_row_index_out_of_bounds) ; + } + if (i <= ilast) + { + return (UMFPACK_ERROR_jumbled_matrix) ; + } + ilast = i ; + } + } + } + +#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) + { + 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) + { + 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 + do_values = do_values && Az && Rz ; +#endif + +#ifdef COMPLEX + if (do_conjugate && do_values) + { + if (Q) + { + + /* 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,:)' */ + 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 +#endif + { + if (Q) + { + if (do_values) + { + + /* 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 + } + } + + } + 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) + { + + /* 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 + } + } + + } + 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) + { + 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]) ; +#endif + + return (UMFPACK_OK) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_tuple_lengths.c b/src/sparse-matrix/umfpack/umf_tuple_lengths.c new file mode 100644 index 0000000..12fea0f --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_tuple_lengths.c @@ -0,0 +1,104 @@ +/* ========================================================================== */ +/* === UMF_tuple_lengths ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Determine the tuple list lengths, and the amount of memory required for */ +/* them. Return the amount of memory needed to store all the tuples. */ +/* This routine assumes that the tuple lists themselves are either already */ +/* deallocated, or will be shortly (so Row[ ].tlen and Col[ ].tlen are */ +/* overwritten) */ + +#include "umf_internal.h" +#include "umf_build_tuples_usage.h" + +GLOBAL Int UMF_tuple_lengths +( + NumericType *Numeric, + WorkType *Work, + double *dusage +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int e, nrows, ncols, nel, i, *Rows, *Cols, row, col, n_row, n_col, *E, + *Row_degree, *Row_tlen, *Col_degree, *Col_tlen, usage ; + Element *ep ; + Unit *p ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + E = Work->E ; + Row_degree = Numeric->Rperm ; + Col_degree = Numeric->Cperm ; + Row_tlen = Numeric->Uilen ; + Col_tlen = Numeric->Lilen ; + n_row = Work->n_row ; + n_col = Work->n_col ; + nel = Work->nel ; + + DEBUG3 (("TUPLE_LENGTHS: n_row "ID" n_col "ID" nel "ID"\n", + n_row, n_col, nel)) ; + + /* tuple list lengths already initialized to zero */ + + /* ---------------------------------------------------------------------- */ + /* scan each element: count tuple list lengths (include element 0) */ + /* ---------------------------------------------------------------------- */ + + for (e = 0 ; e <= nel ; e++) /* for all elements, in any order */ + { + if (E [e]) + { +#ifndef NDEBUG + UMF_dump_element (Numeric, Work, e, FALSE) ; +#endif + p = Numeric->Memory + E [e] ; + GET_ELEMENT_PATTERN (ep, p, Cols, Rows, ncols) ; + nrows = ep->nrows ; + ASSERT (e != 0) ; + for (i = 0 ; i < nrows ; i++) + { + row = Rows [i] ; + ASSERT (row == EMPTY || (row >= 0 && row < n_row)) ; + if (row >= 0) + { + ASSERT (NON_PIVOTAL_ROW (row)) ; + Row_tlen [row] ++ ; + } + } + for (i = 0 ; i < ncols ; i++) + { + col = Cols [i] ; + ASSERT (col == EMPTY || (col >= 0 && col < n_col)) ; + if (col >= 0) + { + ASSERT (NON_PIVOTAL_COL (col)) ; + Col_tlen [col] ++ ; + } + } + } + } + + /* note: tuple lengths are now modified, but the tuple lists are not */ + /* updated to reflect that fact. */ + + /* ---------------------------------------------------------------------- */ + /* determine the required memory to hold all the tuple lists */ + /* ---------------------------------------------------------------------- */ + + usage = UMF_build_tuples_usage (Col_tlen, Col_degree, + Row_tlen, Row_degree, n_row, n_col, dusage) ; + return (usage) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_usolve.c b/src/sparse-matrix/umfpack/umf_usolve.c new file mode 100644 index 0000000..4347eb4 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_usolve.c @@ -0,0 +1,167 @@ +/* ========================================================================== */ +/* === UMF_usolve =========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* solves Ux = b, where U is the upper triangular factor of a matrix. */ +/* B is overwritten with the solution X. */ +/* Returns the floating point operation count */ + +#include "umf_internal.h" + +GLOBAL double UMF_usolve +( + NumericType *Numeric, + Entry X [ ], /* b on input, solution x on output */ + Int Pattern [ ] /* a work array of size n */ +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int k, deg, j, *ip, col, *Upos, *Uilen, pos, + *Uip, n, ulen, up, newUchain, npiv ; + Entry *xp, xk, *D ; + double flops ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + if (Numeric->n_row != Numeric->n_col) return (0.) ; + n = Numeric->n_row ; + npiv = Numeric->npiv ; + Upos = Numeric->Upos ; + Uilen = Numeric->Uilen ; + Uip = Numeric->Uip ; + D = Numeric->D ; + + /* start by counting the division by D [0..n-1] */ + flops = n ; + +#ifndef NDEBUG + DEBUG4 (("Usolve start: npiv = "ID" n = "ID"\n", npiv, n)) ; + for (j = 0 ; j < n ; j++) + { + DEBUG4 (("Usolve start "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } +#endif + + /* handle the singular part of D, up to just before the last pivot */ + flops += (n-npiv) ; + for (k = n-1 ; k >= npiv ; k--) + { + /* Note that D [k] is identically zero. */ + ASSERT (IS_ZERO (D [k])) ; + xk = X [k] ; + /* X [k] = xk / D [k] ; */ + DIV (X [k], xk, D [k]) ; + } + + deg = Numeric->ulen ; + if (deg > 0) + { + /* make last pivot row of U (singular matrices only) */ + for (j = 0 ; j < deg ; j++) + { + DEBUG1 (("Last row of U: j="ID"\n", j)) ; + DEBUG1 (("Last row of U: Upattern[j]="ID"\n", + Numeric->Upattern [j]) ); + Pattern [j] = Numeric->Upattern [j] ; + } + } + + for (k = npiv-1 ; k >= 0 ; k--) + { + + /* ------------------------------------------------------------------ */ + /* use row k of U */ + /* ------------------------------------------------------------------ */ + + up = Uip [k] ; + ulen = Uilen [k] ; + newUchain = (up < 0) ; + if (newUchain) + { + up = -up ; + xp = (Entry *) (Numeric->Memory + up + UNITS (Int, ulen)) ; + } + else + { + xp = (Entry *) (Numeric->Memory + up) ; + } + + xk = X [k] ; + flops += 2*deg ; + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" k "ID" col "ID" value", k, Pattern [j])) ; + EDEBUG4 (*xp) ; + DEBUG4 (("\n")) ; + /* xk -= X [Pattern [j]] * (*xp) ; */ + MULT_SUB (xk, X [Pattern [j]], *xp) ; + xp++ ; + } + + /* Go ahead and divide by zero if D [k] is zero */ + /* X [k] = xk / D [k] ; */ + DIV (X [k], xk, D [k]) ; + + /* ------------------------------------------------------------------ */ + /* make row k-1 of U in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + if (newUchain) + { + /* next row is a new Uchain */ + deg = ulen ; + ASSERT (IMPLIES (k == 0, deg == 0)) ; + DEBUG4 (("end of chain for row of U "ID" deg "ID"\n", k-1, deg)) ; + ip = (Int *) (Numeric->Memory + up) ; + for (j = 0 ; j < deg ; j++) + { + col = *ip++ ; + DEBUG4 ((" k "ID" col "ID"\n", k-1, col)) ; + ASSERT (k <= col) ; + Pattern [j] = col ; + } + } + else + { + deg -= ulen ; + DEBUG4 (("middle of chain for row of U "ID" deg "ID"\n", k, deg)) ; + ASSERT (deg >= 0) ; + pos = Upos [k] ; + if (pos != EMPTY) + { + /* add the pivot column */ + DEBUG4 (("k "ID" add pivot entry at pos "ID"\n", k, pos)) ; + ASSERT (pos >= 0 && pos <= deg) ; + Pattern [deg++] = Pattern [pos] ; + Pattern [pos] = k ; + } + } + } + +#ifndef NDEBUG + for (j = 0 ; j < n ; j++) + { + DEBUG4 (("Usolve done "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } + DEBUG4 (("Usolve done.\n")) ; +#endif + + return (DIV_FLOPS * ((double) n) + MULTSUB_FLOPS * ((double) Numeric->unz)); +} + diff --git a/src/sparse-matrix/umfpack/umf_utsolve.c b/src/sparse-matrix/umfpack/umf_utsolve.c new file mode 100644 index 0000000..19eeb64 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_utsolve.c @@ -0,0 +1,255 @@ +/* ========================================================================== */ +/* === UMF_utsolve ========================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* solves U'x = b or U.'x=b, where U is the upper triangular factor of a */ +/* matrix. B is overwritten with the solution X. */ +/* Returns the floating point operation count */ + +#include "umf_internal.h" + +GLOBAL double +#ifdef CONJUGATE_SOLVE +UMF_uhsolve /* solve U'x=b (complex conjugate transpose) */ +#else +UMF_utsolve /* solve U.'x=b (array transpose) */ +#endif +( + NumericType *Numeric, + Entry X [ ], /* b on input, solution x on output */ + Int Pattern [ ] /* a work array of size n */ +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int k, deg, j, *ip, col, *Upos, *Uilen, kstart, kend, up, + *Uip, n, uhead, ulen, pos, npiv ; + Entry *xp, xk, *D ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + if (Numeric->n_row != Numeric->n_col) return (0.) ; + n = Numeric->n_row ; + npiv = Numeric->npiv ; + Upos = Numeric->Upos ; + Uilen = Numeric->Uilen ; + Uip = Numeric->Uip ; + D = Numeric->D ; + kend = 0 ; + +#ifndef NDEBUG + DEBUG4 (("Utsolve start: npiv "ID" n "ID"\n", npiv, n)) ; + for (j = 0 ; j < n ; j++) + { + DEBUG4 (("Utsolve start "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } +#endif + + for (kstart = 0 ; kstart < npiv ; kstart = kend + 1) + { + + /* ------------------------------------------------------------------ */ + /* find the end of this Uchain */ + /* ------------------------------------------------------------------ */ + + DEBUG4 (("kstart "ID" kend "ID"\n", kstart, kend)) ; + /* for (kend = kstart ; kend < npiv && Uip [kend+1] > 0 ; kend++) ; */ + kend = kstart ; + while (kend < npiv && Uip [kend+1] > 0) + { + kend++ ; + } + + /* ------------------------------------------------------------------ */ + /* scan the whole Uchain to find the pattern of the first row of U */ + /* ------------------------------------------------------------------ */ + + k = kend+1 ; + DEBUG4 (("\nKend "ID" K "ID"\n", kend, k)) ; + + /* ------------------------------------------------------------------ */ + /* start with last row in Uchain of U in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + if (k == npiv) + { + deg = Numeric->ulen ; + if (deg > 0) + { + /* make last pivot row of U (singular matrices only) */ + for (j = 0 ; j < deg ; j++) + { + Pattern [j] = Numeric->Upattern [j] ; + } + } + } + else + { + ASSERT (k >= 0 && k < npiv) ; + up = -Uip [k] ; + ASSERT (up > 0) ; + deg = Uilen [k] ; + DEBUG4 (("end of chain for row of U "ID" deg "ID"\n", k-1, deg)) ; + ip = (Int *) (Numeric->Memory + up) ; + for (j = 0 ; j < deg ; j++) + { + col = *ip++ ; + DEBUG4 ((" k "ID" col "ID"\n", k-1, col)) ; + ASSERT (k <= col) ; + Pattern [j] = col ; + } + } + + /* empty the stack at the bottom of Pattern */ + uhead = n ; + + for (k = kend ; k > kstart ; k--) + { + /* Pattern [0..deg-1] is the pattern of row k of U */ + + /* -------------------------------------------------------------- */ + /* make row k-1 of U in Pattern [0..deg-1] */ + /* -------------------------------------------------------------- */ + + ASSERT (k >= 0 && k < npiv) ; + ulen = Uilen [k] ; + /* delete, and push on the stack */ + for (j = 0 ; j < ulen ; j++) + { + ASSERT (uhead >= deg) ; + Pattern [--uhead] = Pattern [--deg] ; + } + DEBUG4 (("middle of chain for row of U "ID" deg "ID"\n", k, deg)) ; + ASSERT (deg >= 0) ; + + pos = Upos [k] ; + if (pos != EMPTY) + { + /* add the pivot column */ + DEBUG4 (("k "ID" add pivot entry at position "ID"\n", k, pos)) ; + ASSERT (pos >= 0 && pos <= deg) ; + Pattern [deg++] = Pattern [pos] ; + Pattern [pos] = k ; + } + } + + /* Pattern [0..deg-1] is now the pattern of the first row in Uchain */ + + /* ------------------------------------------------------------------ */ + /* solve using this Uchain, in reverse order */ + /* ------------------------------------------------------------------ */ + + DEBUG4 (("Unwinding Uchain\n")) ; + for (k = kstart ; k <= kend ; k++) + { + + /* -------------------------------------------------------------- */ + /* construct row k */ + /* -------------------------------------------------------------- */ + + ASSERT (k >= 0 && k < npiv) ; + pos = Upos [k] ; + if (pos != EMPTY) + { + /* remove the pivot column */ + DEBUG4 (("k "ID" add pivot entry at position "ID"\n", k, pos)) ; + ASSERT (k > kstart) ; + ASSERT (pos >= 0 && pos < deg) ; + ASSERT (Pattern [pos] == k) ; + Pattern [pos] = Pattern [--deg] ; + } + + up = Uip [k] ; + ulen = Uilen [k] ; + if (k > kstart) + { + /* concatenate the deleted pattern; pop from the stack */ + for (j = 0 ; j < ulen ; j++) + { + ASSERT (deg <= uhead && uhead < n) ; + Pattern [deg++] = Pattern [uhead++] ; + } + DEBUG4 (("middle of chain, row of U "ID" deg "ID"\n", k, deg)) ; + ASSERT (deg >= 0) ; + } + + /* -------------------------------------------------------------- */ + /* use row k of U */ + /* -------------------------------------------------------------- */ + + /* Go ahead and divide by zero if D [k] is zero. */ +#ifdef CONJUGATE_SOLVE + /* xk = X [k] / conjugate (D [k]) ; */ + DIV_CONJ (xk, X [k], D [k]) ; +#else + /* xk = X [k] / D [k] ; */ + DIV (xk, X [k], D [k]) ; +#endif + X [k] = xk ; + + if (IS_NONZERO (xk)) + { + if (k == kstart) + { + up = -up ; + xp = (Entry *) (Numeric->Memory + up + UNITS (Int, ulen)) ; + } + else + { + xp = (Entry *) (Numeric->Memory + up) ; + } + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" k "ID" col "ID" value", k, Pattern [j])) ; + EDEBUG4 (*xp) ; + DEBUG4 (("\n")) ; +#ifdef CONJUGATE_SOLVE + /* X [Pattern [j]] -= xk * conjugate (*xp) ; */ + MULT_SUB_CONJ (X [Pattern [j]], xk, *xp) ; +#else + /* X [Pattern [j]] -= xk * (*xp) ; */ + MULT_SUB (X [Pattern [j]], xk, *xp) ; +#endif + xp++ ; + } + } + } + ASSERT (uhead == n) ; + } + + for (k = npiv ; k < n ; k++) + { + /* Note that D [k] is identically zero. */ + ASSERT (IS_ZERO (D [k])) ; + /* For conjugate solve, D [k] == conjugate (D [k]), in this case */ + /* xk = X [k] / D [k] ; */ + DIV (xk, X [k], D [k]) ; + X [k] = xk ; + } + +#ifndef NDEBUG + for (j = 0 ; j < n ; j++) + { + DEBUG4 (("Utsolve done "ID": ", j)) ; + EDEBUG4 (X [j]) ; + DEBUG4 (("\n")) ; + } + DEBUG4 (("Utsolve done.\n")) ; +#endif + + return (DIV_FLOPS * ((double) n) + MULTSUB_FLOPS * ((double) Numeric->unz)); +} + diff --git a/src/sparse-matrix/umfpack/umf_valid_numeric.c b/src/sparse-matrix/umfpack/umf_valid_numeric.c new file mode 100644 index 0000000..b1fd91d --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_valid_numeric.c @@ -0,0 +1,48 @@ +/* ========================================================================== */ +/* === UMF_valid_numeric ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* Returns TRUE if the Numeric object is valid, FALSE otherwise. */ +/* Does not check everything. UMFPACK_report_numeric checks more. */ + +#include "umf_internal.h" + +GLOBAL Int UMF_valid_numeric +( + NumericType *Numeric +) +{ + /* This routine does not check the contents of the individual arrays, so */ + /* it can miss some errors. All it checks for is the presence of the */ + /* arrays, and the Numeric "valid" entry. */ + + if (!Numeric) + { + return (FALSE) ; + } + + if (Numeric->valid != NUMERIC_VALID) + { + /* Numeric does not point to a NumericType object */ + return (FALSE) ; + } + + if (Numeric->n_row <= 0 || Numeric->n_col <= 0 || !Numeric->D || + !Numeric->Rperm || !Numeric->Cperm || + !Numeric->Lpos || !Numeric->Upos || + !Numeric->Lilen || !Numeric->Uilen || !Numeric->Lip || !Numeric->Uip || + !Numeric->Memory || (Numeric->ulen > 0 && !Numeric->Upattern)) + { + return (FALSE) ; + } + + return (TRUE) ; +} + diff --git a/src/sparse-matrix/umfpack/umf_valid_symbolic.c b/src/sparse-matrix/umfpack/umf_valid_symbolic.c new file mode 100644 index 0000000..b7ddc35 --- /dev/null +++ b/src/sparse-matrix/umfpack/umf_valid_symbolic.c @@ -0,0 +1,49 @@ +/* ========================================================================== */ +/* === UMF_valid_symbolic =================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +#include "umf_internal.h" + +/* Returns TRUE if the Symbolic object is valid, FALSE otherwise. */ +/* The UMFPACK_report_symbolic routine does a more thorough check. */ + +GLOBAL Int UMF_valid_symbolic +( + SymbolicType *Symbolic +) +{ + /* This routine does not check the contents of the individual arrays, so */ + /* it can miss some errors. All it checks for is the presence of the */ + /* arrays, and the Symbolic "valid" entry. */ + + if (!Symbolic) + { + return (FALSE) ; + } + + if (Symbolic->valid != SYMBOLIC_VALID) + { + /* Symbolic does not point to a SymbolicType object */ + return (FALSE) ; + } + + if (!Symbolic->Cperm_init || !Symbolic->Rperm_init || + !Symbolic->Front_npivcol || !Symbolic->Front_1strow || + !Symbolic->Front_leftmostdesc || + !Symbolic->Front_parent || !Symbolic->Chain_start || + !Symbolic->Chain_maxrows || !Symbolic->Chain_maxcols || + Symbolic->n_row <= 0 || Symbolic->n_col <= 0) + { + return (FALSE) ; + } + + return (TRUE) ; +} + diff --git a/src/sparse-matrix/umfpack/umfpack_defaults.c b/src/sparse-matrix/umfpack/umfpack_defaults.c new file mode 100644 index 0000000..57d8433 --- /dev/null +++ b/src/sparse-matrix/umfpack/umfpack_defaults.c @@ -0,0 +1,111 @@ +/* ========================================================================== */ +/* === UMFPACK_defaults ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Sets default control parameters. See umfpack_defaults.h + for details. +*/ + +#include "umf_internal.h" + +GLOBAL void UMFPACK_defaults +( + double Control [UMFPACK_CONTROL] +) +{ + Int i ; + + if (!Control) + { + /* silently return if no Control array */ + return ; + } + + for (i = 0 ; i < UMFPACK_CONTROL ; i++) + { + Control [i] = 0 ; + } + + /* ---------------------------------------------------------------------- */ + /* default control settings: can be modified at run-time */ + /* ---------------------------------------------------------------------- */ + + /* used in UMFPACK_report_* routines: */ + Control [UMFPACK_PRL] = UMFPACK_DEFAULT_PRL ; + + /* used in UMFPACK_*symbolic: */ + Control [UMFPACK_DENSE_ROW] = UMFPACK_DEFAULT_DENSE_ROW ; + Control [UMFPACK_DENSE_COL] = UMFPACK_DEFAULT_DENSE_COL ; + + /* used in UMFPACK_numeric: */ + Control [UMFPACK_PIVOT_TOLERANCE] = UMFPACK_DEFAULT_PIVOT_TOLERANCE ; + Control [UMFPACK_BLOCK_SIZE] = UMFPACK_DEFAULT_BLOCK_SIZE ; + Control [UMFPACK_RELAXED_AMALGAMATION] = + UMFPACK_DEFAULT_RELAXED_AMALGAMATION ; + Control [UMFPACK_RELAXED2_AMALGAMATION] = + UMFPACK_DEFAULT_RELAXED2_AMALGAMATION ; + Control [UMFPACK_RELAXED3_AMALGAMATION] = + UMFPACK_DEFAULT_RELAXED3_AMALGAMATION ; + Control [UMFPACK_ALLOC_INIT] = UMFPACK_DEFAULT_ALLOC_INIT ; + + /* used in UMFPACK_*solve: */ + Control [UMFPACK_IRSTEP] = UMFPACK_DEFAULT_IRSTEP ; + + /* ---------------------------------------------------------------------- */ + /* compile-time settings: cannot be modified at run-time */ + /* ---------------------------------------------------------------------- */ + +#ifdef USE_NO_BLAS + /* use externally-provided BLAS (dgemm, dger, dgemv, zgemm, zgeru, zgemv) */ + Control [UMFPACK_COMPILED_WITH_BLAS] = 1 ; +#else + /* do not use the BLAS - use in-line C code instead */ + Control [UMFPACK_COMPILED_WITH_BLAS] = 0 ; +#endif + +#ifdef MATLAB_MEX_FILE + /* use mxMalloc, mxFree, mxRealloc, and mexPrintf */ + /* use mxAssert if debugging is enabled */ + Control [UMFPACK_COMPILED_FOR_MATLAB] = 1 ; +#else +#ifdef MATHWORKS + /* use internal utMalloc, utFree, utRealloc, and utPrintf routines. */ + /* use utDivideComplex and utFdlibm_hypot for complex version. */ + /* use utAssert if debugging is enabled. */ + Control [UMFPACK_COMPILED_FOR_MATLAB] = 2 ; +#else + /* use ANSI C malloc, free, realloc, and print */ + /* use ANSI C assert if debugging is enabled */ + Control [UMFPACK_COMPILED_FOR_MATLAB] = 0 ; +#endif +#endif + +#ifdef GETRUSAGE + /* uses the non-standard getrusage to get CPU time (Solaris) */ + Control [UMFPACK_COMPILED_WITH_GETRUSAGE] = 1 ; +#else + /* uses the ANSI standard clock routine to get CPU time */ + /* this may wrap around */ + Control [UMFPACK_COMPILED_WITH_GETRUSAGE] = 0 ; +#endif + +#ifndef NDEBUG + /* UMFPACK is compiled in debug mode. */ + /* This is exceedingly slow. */ + PRINTF (("UMFPACK is running in debug mode. This is very slow!\n")) ; + Control [UMFPACK_COMPILED_IN_DEBUG_MODE] = 1 ; +#else + /* UMFPACK is compiled in normal (non-debug) mode */ + Control [UMFPACK_COMPILED_IN_DEBUG_MODE] = 0 ; +#endif + +} + diff --git a/src/sparse-matrix/umfpack/umfpack_free_numeric.c b/src/sparse-matrix/umfpack/umfpack_free_numeric.c new file mode 100644 index 0000000..2e7d339 --- /dev/null +++ b/src/sparse-matrix/umfpack/umfpack_free_numeric.c @@ -0,0 +1,51 @@ +/* ========================================================================== */ +/* === UMFPACK_free_numeric ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* User-callable. Free the entire Numeric object. + See UMFPACK_free_numeric.h for details. +*/ + +#include "umf_internal.h" +#include "umf_free.h" + +GLOBAL void UMFPACK_free_numeric +( + void **NumericHandle +) +{ + + NumericType *Numeric ; + if (!NumericHandle) + { + return ; + } + Numeric = *((NumericType **) NumericHandle) ; + if (!Numeric) + { + return ; + } + + (void) UMF_free ((void *) Numeric->D) ; + (void) UMF_free ((void *) Numeric->Rperm) ; + (void) UMF_free ((void *) Numeric->Cperm) ; + (void) UMF_free ((void *) Numeric->Lpos) ; + (void) UMF_free ((void *) Numeric->Lilen) ; + (void) UMF_free ((void *) Numeric->Lip) ; + (void) UMF_free ((void *) Numeric->Upos) ; + (void) UMF_free ((void *) Numeric->Uilen) ; + (void) UMF_free ((void *) Numeric->Uip) ; + (void) UMF_free ((void *) Numeric->Upattern) ; + (void) UMF_free ((void *) Numeric->Memory) ; + (void) UMF_free ((void *) Numeric) ; + + *NumericHandle = (void *) NULL ; +} + diff --git a/src/sparse-matrix/umfpack/umfpack_free_symbolic.c b/src/sparse-matrix/umfpack/umfpack_free_symbolic.c new file mode 100644 index 0000000..a2389ee --- /dev/null +++ b/src/sparse-matrix/umfpack/umfpack_free_symbolic.c @@ -0,0 +1,50 @@ +/* ========================================================================== */ +/* === UMFPACK_free_symbolic ================================================ */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. See umfpack_free_symbolic.h for details. + All 10 objects comprising the Symbolic object are free'd via UMF_free. +*/ + +#include "umf_internal.h" +#include "umf_free.h" + +GLOBAL void UMFPACK_free_symbolic +( + void **SymbolicHandle +) +{ + + SymbolicType *Symbolic ; + if (!SymbolicHandle) + { + return ; + } + Symbolic = *((SymbolicType **) SymbolicHandle) ; + if (!Symbolic) + { + return ; + } + + (void) UMF_free ((void *) Symbolic->Cperm_init) ; + (void) UMF_free ((void *) Symbolic->Rperm_init) ; + (void) UMF_free ((void *) Symbolic->Front_npivcol) ; + (void) UMF_free ((void *) Symbolic->Front_parent) ; + (void) UMF_free ((void *) Symbolic->Front_1strow) ; + (void) UMF_free ((void *) Symbolic->Front_leftmostdesc) ; + (void) UMF_free ((void *) Symbolic->Chain_start) ; + (void) UMF_free ((void *) Symbolic->Chain_maxrows) ; + (void) UMF_free ((void *) Symbolic->Chain_maxcols) ; + (void) UMF_free ((void *) Symbolic) ; + + *SymbolicHandle = (void *) NULL ; +} + diff --git a/src/sparse-matrix/umfpack/umfpack_get_lunz.c b/src/sparse-matrix/umfpack/umfpack_get_lunz.c new file mode 100644 index 0000000..b7b7ec3 --- /dev/null +++ b/src/sparse-matrix/umfpack/umfpack_get_lunz.c @@ -0,0 +1,57 @@ +/* ========================================================================== */ +/* === UMFPACK_get_lunz ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Determines the number of nonzeros in L and U, and the size + of L and U. +*/ + +#include "umf_internal.h" +#include "umf_valid_numeric.h" + +GLOBAL Int UMFPACK_get_lunz +( + Int *lnz, + Int *unz, + Int *n_row, + Int *n_col, + Int *nz_udiag, + void *NumericHandle +) +{ + NumericType *Numeric ; + + Numeric = (NumericType *) NumericHandle ; + + if (!UMF_valid_numeric (Numeric)) + { + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + if (!lnz || !unz || !n_row || !n_col || !nz_udiag) + { + return (UMFPACK_ERROR_argument_missing) ; + } + + *n_row = Numeric->n_row ; + *n_col = Numeric->n_col ; + + /* number of nz's in L below diagonal, plus the unit diagonal of L */ + *lnz = Numeric->lnz + MIN (Numeric->n_row, Numeric->n_col) ; + + /* number of nz's in U above diagonal, plus nz's on diagaonal of U */ + *unz = Numeric->unz + Numeric->nnzpiv ; + + /* number of nz's on the diagonal */ + *nz_udiag = Numeric->nnzpiv ; + + return (UMFPACK_OK) ; +} + diff --git a/src/sparse-matrix/umfpack/umfpack_get_numeric.c b/src/sparse-matrix/umfpack/umfpack_get_numeric.c new file mode 100644 index 0000000..55b9b87 --- /dev/null +++ b/src/sparse-matrix/umfpack/umfpack_get_numeric.c @@ -0,0 +1,816 @@ +/* ========================================================================== */ +/* === UMFPACK_get_numeric ================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Gets the LU factors and the permutation vectors held in the + Numeric object. L is returned in sparse row form with sorted rows, U is + returned in sparse column form with sorted columns, and P and Q are + returned as permutation vectors. See umfpack_get_numeric.h for a more + detailed description. + + Returns TRUE if successful, FALSE if the Numeric object is invalid or + if out of memory. + + Dynamic memory usage: calls UMF_malloc twice, for a total space of + 2*n integers, and then frees all of it via UMF_free when done. + +*/ + +#include "umf_internal.h" +#include "umf_valid_numeric.h" +#include "umf_malloc.h" +#include "umf_free.h" + +#ifndef NDEBUG +PRIVATE Int init_count ; +#endif + +PRIVATE void get_L +( + Int Lp [ ], + Int Lj [ ], + double Lx [ ], +#ifdef COMPLEX + double Lz [ ], +#endif + NumericType *Numeric, + Int Pattern [ ], + Int Wi [ ] +) ; + +PRIVATE void get_U +( + Int Up [ ], + Int Ui [ ], + double Ux [ ], +#ifdef COMPLEX + double Uz [ ], +#endif + NumericType *Numeric, + Int Pattern [ ], + Int Wi [ ] +) ; + +/* ========================================================================== */ + +GLOBAL Int UMFPACK_get_numeric +( + Int Lp [ ], + Int Lj [ ], + double Lx [ ], +#ifdef COMPLEX + double Lz [ ], +#endif + Int Up [ ], + Int Ui [ ], + double Ux [ ], +#ifdef COMPLEX + double Uz [ ], +#endif + Int P [ ], + Int Q [ ], + double Dx [ ], +#ifdef COMPLEX + double Dz [ ], +#endif + void *NumericHandle +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + NumericType *Numeric ; + Int getL, getU, *Rperm, *Cperm, k, nn, n_row, n_col, *Wi, *Pattern, + n_inner ; + +#ifndef NDEBUG + init_count = UMF_malloc_count ; +#endif + + Wi = (Int *) NULL ; + Pattern = (Int *) NULL ; + + /* ---------------------------------------------------------------------- */ + /* check input parameters */ + /* ---------------------------------------------------------------------- */ + + Numeric = (NumericType *) NumericHandle ; + if (!UMF_valid_numeric (Numeric)) + { + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + + n_row = Numeric->n_row ; + n_col = Numeric->n_col ; + nn = MAX (n_row, n_col) ; + n_inner = MIN (n_row, n_col) ; + + /* ---------------------------------------------------------------------- */ + /* allocate workspace */ + /* ---------------------------------------------------------------------- */ + +#ifdef COMPLEX + getL = Lp && Lj && Lx && Lz ; + getU = Up && Ui && Ux && Uz ; +#else + getL = Lp && Lj && Lx ; + getU = Up && Ui && Ux ; +#endif + + if (getL || getU) + { + Wi = (Int *) UMF_malloc (nn, sizeof (Int)) ; + Pattern = (Int *) UMF_malloc (nn, sizeof (Int)) ; + if (!Wi || !Pattern) + { + (void) UMF_free ((void *) Wi) ; + (void) UMF_free ((void *) Pattern) ; + ASSERT (UMF_malloc_count == init_count) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + ASSERT (UMF_malloc_count == init_count + 2) ; + } + + /* ---------------------------------------------------------------------- */ + /* get contents of Numeric */ + /* ---------------------------------------------------------------------- */ + + if (P) + { + Rperm = Numeric->Rperm ; + for (k = 0 ; k < n_row ; k++) + { + P [k] = Rperm [k] ; + } + } + + if (Q) + { + Cperm = Numeric->Cperm ; + for (k = 0 ; k < n_col ; k++) + { + Q [k] = Cperm [k] ; + } + } + + if (getL) + { + get_L (Lp, Lj, Lx, +#ifdef COMPLEX + Lz, +#endif + Numeric, Pattern, Wi) ; + } + + if (getU) + { + get_U (Up, Ui, Ux, +#ifdef COMPLEX + Uz, +#endif + Numeric, Pattern, Wi) ; + } + + if (Dx) + { + for (k = 0 ; k < n_inner ; k++) + { + Dx [k] = REAL_COMPONENT (Numeric->D [k]) ; + } + } + +#ifdef COMPLEX + if (Dz) + { + for (k = 0 ; k < n_inner ; k++) + { + Dz [k] = IMAG_COMPONENT (Numeric->D [k]) ; + } + } +#endif + + /* ---------------------------------------------------------------------- */ + /* free the workspace */ + /* ---------------------------------------------------------------------- */ + + (void) UMF_free ((void *) Wi) ; + (void) UMF_free ((void *) Pattern) ; + ASSERT (UMF_malloc_count == init_count) ; + + return (UMFPACK_OK) ; +} + + +/* ========================================================================== */ +/* === get_L ================================================================ */ +/* ========================================================================== */ + +/* + The matrix L is stored in the following arrays in the Numeric object: + + Int Lpos [0..npiv] + Int Lip [0..npiv], index into Numeric->Memory + Int Lilen [0..npiv] + Unit *(Numeric->Memory), pointer to memory space holding row indices + and numerical values + + where npiv is the number of pivot entries found. If A is n_row-by-n_col, + then npiv <= MIN (n_row,n_col). + + Let L_k denote the pattern of entries in column k of L (excluding the + diagonal). + + An Lchain is a sequence of columns of L whose nonzero patterns are related. + The start of an Lchain is denoted by a negative value of Lip [k]. + + To obtain L_k: + + (1) If column k starts an Lchain, then L_k is stored in its entirety. + |Lip [k]| is an index into Numeric->Memory for the integer row indices + in L_k. The number of entries in the column is |L_k| = Lilen [k]. + This defines the pattern of the "leading" column of this chain. + Lpos [k] is not used for the first column in the chain. Column zero + is always a leading column. + + (2) If column k does not start an Lchain, then L_k is represented as a + superset of L_k-1. Define Lnew_k such that (L_k-1 - {k} union Lnew_k) + = L_k, where Lnew_k and (L_k-1)-{k} are disjoint. Lnew_k are the + entries in L_k that are not in L_k-1. Lpos [k] holds the position of + pivot row index k in the prior pattern L_k-1 (if it is present), so + that the set subtraction (L_k-1)-{k} can be computed quickly, when + computing the pattern of L_k from L_k-1. The number of new entries in + L_k is stored in Lilen [k] = |Lnew_k|. + + Note that this means we must have the pattern L_k-1 to compute L_k. + + In both cases (1) and (2), we obtain the pattern L_k. + + The numerical values are stored in Numeric->Memory, starting at the index + |Lip [k]| + Lilen [k]. It is stored in the same order as the entries + in L_k, after L_k is obtained from cases (1) or (2), above. + + The advantage of using this "packed" data structure is that it can + dramatically reduce the amount of storage needed for the pattern of L. + The disadvantage is that it can be difficult for the user to access, + and it does not match the sparse matrix data structure used in MATLAB. + Thus, this routine is provided to create a conventional sparse matrix + data structure for L, in sparse-row form. A row-form of L appears to + MATLAB to be a column-oriented from of the transpose of L. If you would + like a column-form of L, then use UMFPACK_transpose (an example of this + is in umfpackmex.c). + +*/ +/* ========================================================================== */ + +PRIVATE void get_L +( + Int Lp [ ], /* of size n_row+1 */ + Int Lj [ ], /* of size lnz, where lnz = Lp [n_row] */ + double Lx [ ], /* of size lnz */ +#ifdef COMPLEX + double Lz [ ], /* of size lnz */ +#endif + NumericType *Numeric, + Int Pattern [ ], /* workspace of size n_row */ + Int Wi [ ] /* workspace of size n_row */ +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int deg, *ip, j, row, n_row, n_col, n_inner, *Lpos, *Lilen, *Lip, p, llen, + lnz2, lp, newLchain, k, pos, npiv ; + Entry *xp, value ; + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + DEBUG4 (("get_L start:\n")) ; + n_row = Numeric->n_row ; + n_col = Numeric->n_col ; + n_inner = MIN (n_row, n_col) ; + npiv = Numeric->npiv ; + Lpos = Numeric->Lpos ; + Lilen = Numeric->Lilen ; + Lip = Numeric->Lip ; + deg = 0 ; + + /* ---------------------------------------------------------------------- */ + /* count the nonzeros in each row of L */ + /* ---------------------------------------------------------------------- */ + + for (row = 0 ; row < n_inner ; row++) + { + /* include the diagonal entry in the row counts */ + Wi [row] = 1 ; + } + for (row = n_inner ; row < n_row ; row++) + { + Wi [row] = 0 ; + } + + /* count the nonzero entries in L */ + for (k = 0 ; k < npiv ; k++) + { + + /* ------------------------------------------------------------------ */ + /* make column of L in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + lp = Lip [k] ; + newLchain = (lp < 0) ; + if (newLchain) + { + lp = -lp ; + deg = 0 ; + DEBUG4 (("start of chain for column of L\n")) ; + } + + /* remove pivot row */ + pos = Lpos [k] ; + if (pos != EMPTY) + { + DEBUG4 ((" k "ID" removing row "ID" at position "ID"\n", + k, Pattern [pos], pos)) ; + ASSERT (!newLchain) ; + ASSERT (deg > 0) ; + ASSERT (pos >= 0 && pos < deg) ; + ASSERT (Pattern [pos] == k) ; + Pattern [pos] = Pattern [--deg] ; + } + + /* concatenate the pattern */ + ip = (Int *) (Numeric->Memory + lp) ; + llen = Lilen [k] ; + for (j = 0 ; j < llen ; j++) + { + row = *ip++ ; + DEBUG4 ((" row "ID" k "ID"\n", row, k)) ; + ASSERT (row > k && row < n_row) ; + Pattern [deg++] = row ; + } + + xp = (Entry *) (Numeric->Memory + lp + UNITS (Int, llen)) ; + + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" row "ID" k "ID" value", Pattern [j], k)) ; + row = Pattern [j] ; + value = *xp++ ; + EDEBUG4 (value) ; + DEBUG4 (("\n")) ; + if (IS_NONZERO (value)) + { + Wi [row]++ ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* construct the final row form of L */ + /* ---------------------------------------------------------------------- */ + + /* create the row pointers */ + lnz2 = 0 ; + for (row = 0 ; row < n_row ; row++) + { + Lp [row] = lnz2 ; + lnz2 += Wi [row] ; + Wi [row] = Lp [row] ; + } + Lp [n_row] = lnz2 ; + ASSERT (Numeric->lnz + n_inner == lnz2) ; + + /* add entries from the rows of L */ + for (k = 0 ; k < npiv ; k++) + { + + /* ------------------------------------------------------------------ */ + /* make column of L in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + lp = Lip [k] ; + newLchain = (lp < 0) ; + if (newLchain) + { + lp = -lp ; + deg = 0 ; + DEBUG4 (("start of chain for column of L\n")) ; + } + + /* remove pivot row */ + pos = Lpos [k] ; + if (pos != EMPTY) + { + DEBUG4 ((" k "ID" removing row "ID" at position "ID"\n", + k, Pattern [pos], pos)) ; + ASSERT (!newLchain) ; + ASSERT (deg > 0) ; + ASSERT (pos >= 0 && pos < deg) ; + ASSERT (Pattern [pos] == k) ; + Pattern [pos] = Pattern [--deg] ; + } + + /* concatenate the pattern */ + ip = (Int *) (Numeric->Memory + lp) ; + llen = Lilen [k] ; + for (j = 0 ; j < llen ; j++) + { + row = *ip++ ; + DEBUG4 ((" row "ID" k "ID"\n", row, k)) ; + ASSERT (row > k) ; + Pattern [deg++] = row ; + } + + xp = (Entry *) (Numeric->Memory + lp + UNITS (Int, llen)) ; + + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" row "ID" k "ID" value", Pattern [j], k)) ; + row = Pattern [j] ; + value = *xp++ ; + EDEBUG4 (value) ; + DEBUG4 (("\n")) ; + if (IS_NONZERO (value)) + { + p = Wi [row]++ ; + Lj [p] = k ; + Lx [p] = REAL_COMPONENT (value) ; +#ifdef COMPLEX + Lz [p] = IMAG_COMPONENT (value) ; +#endif + } + } + } + + /* add all of the diagonal entries (L is unit diagonal) */ + for (row = 0 ; row < n_inner ; row++) + { + p = Wi [row]++ ; + Lj [p] = row ; + Lx [p] = 1. ; +#ifdef COMPLEX + Lz [p] = 0. ; +#endif + ASSERT (Wi [row] == Lp [row+1]) ; + } + +#ifndef NDEBUG + DEBUG6 (("L matrix (stored by rows):")) ; + UMF_dump_col_matrix (Lx, +#ifdef COMPLEX + Lz, +#endif + Lj, Lp, n_inner, n_row, Numeric->lnz+n_inner) ; +#endif + + DEBUG4 (("get_L done:\n")) ; +} + + +/* ========================================================================== */ +/* === get_U ================================================================ */ +/* ========================================================================== */ + +/* + The matrix U is stored in the following arrays in the Numeric object: + + Int Upos [0..npiv] + Int Uip [0..npiv], index into Numeric->Memory + Int Uilen [0..npiv] + Unit *(Numeric->Memory), pointer to memory space holding column indices + and numerical values + + where npiv is the number of pivot entries found. If A is n_row-by-n_col, + then npiv <= MIN (n_row,n_col). + + Let U_k denote the pattern of entries in row k of U (excluding the + diagonal). + + A Uchain is a sequence of columns of U whose nonzero patterns are related. + The start of a Uchain is denoted by a negative value of Uip [k]. + + To obtain U_k-1: + + (1) If row k is the start of a Uchain then Uip [k] is negative and |Uip [k]| + is an index into Numeric->Memory for the integer column indices in + U_k-1. The number of entries in the row is |U_k-1| = Uilen [k]. This + defines the pattern of the "trailing" row of this chain that ends at + row k-1. + + + (2) If row k is not the start of a Uchain, then U_k-1 is a subset of U_k. + The indices in U_k are arranged so that last Uilen [k] entries of + U_k are those indices not in U_k-1. Next, the pivot column index k is + added if it appears in row U_k-1 (it never appears in U_k). Upos [k] + holds the position of pivot column index k in the pattern U_k-1 (if it + is present), so that the set union (U_k-1)+{k} can be computed quickly, + when computing the pattern of U_k-1 from U_k. + + Note that this means we must have the pattern U_k to compute L_k-1. + + In both cases (1) and (2), we obtain the pattern U_k. + + The numerical values are stored in Numeric->Memory. If k is the start of a + Uchain, then the offset is |Uip [k]| plus the size of the space needed to + store the pattern U_k-1. Otherwise, Uip [k] is the offset itself of the + numerical values, since in this case no pattern is stored. + The numerical values are stored in the same order as the entries in U_k, + after U_k is obtained from cases (1) or (2), above. + + The advantage of using this "packed" data structure is that it can + dramatically reduce the amount of storage needed for the pattern of U. + The disadvantage is that it can be difficult for the user to access, + and it does not match the sparse matrix data structure used in MATLAB. + Thus, this routine is provided to create a conventional sparse matrix + data structure for U, in sparse-column form. + +*/ +/* ========================================================================== */ + +PRIVATE void get_U +( + Int Up [ ], /* of size n_col+1 */ + Int Ui [ ], /* of size unz, where unz = Up [n_col] */ + double Ux [ ], /* of size unz */ +#ifdef COMPLEX + double Uz [ ], /* of size unz */ +#endif + NumericType *Numeric, + Int Pattern [ ], /* workspace of size n_col */ + Int Wi [ ] /* workspace of size n_col */ +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int deg, j, *ip, col, *Upos, *Uilen, *Uip, n_col, ulen, + unz2, p, k, up, newUchain, pos, npiv ; + Entry *xp, *D, value ; + +#ifndef NDEBUG + Int nnzpiv = 0 ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + DEBUG4 (("get_U start:\n")) ; + n_col = Numeric->n_col ; + npiv = Numeric->npiv ; + Upos = Numeric->Upos ; + Uilen = Numeric->Uilen ; + Uip = Numeric->Uip ; + D = Numeric->D ; + + /* ---------------------------------------------------------------------- */ + /* count the nonzeros in each column of U */ + /* ---------------------------------------------------------------------- */ + + for (col = 0 ; col < npiv ; col++) + { + /* include the diagonal entry in the column counts */ + DEBUG4 (("D ["ID"] = ", col)) ; + EDEBUG4 (D [col]) ; + Wi [col] = IS_NONZERO (D [col]) ; + DEBUG4 ((" is nonzero: "ID"\n", Wi [col])) ; +#ifndef NDEBUG + nnzpiv += IS_NONZERO (D [col]) ; +#endif + } + DEBUG4 (("nnzpiv "ID" "ID"\n", nnzpiv, Numeric->nnzpiv)) ; + ASSERT (nnzpiv == Numeric->nnzpiv) ; + for (col = npiv ; col < n_col ; col++) + { + /* diagonal entries are zero for structurally singular part */ + Wi [col] = 0 ; + } + + deg = Numeric->ulen ; + if (deg > 0) + { + /* make last pivot row of U (singular matrices only) */ + DEBUG0 (("Last pivot row of U: ulen "ID"\n", deg)) ; + for (j = 0 ; j < deg ; j++) + { + Pattern [j] = Numeric->Upattern [j] ; + DEBUG0 ((" column "ID"\n", Pattern [j])) ; + } + } + + for (k = npiv-1 ; k >= 0 ; k--) + { + + /* ------------------------------------------------------------------ */ + /* use row k of U */ + /* ------------------------------------------------------------------ */ + + up = Uip [k] ; + ulen = Uilen [k] ; + newUchain = (up < 0) ; + if (newUchain) + { + up = -up ; + xp = (Entry *) (Numeric->Memory + up + UNITS (Int, ulen)) ; + } + else + { + xp = (Entry *) (Numeric->Memory + up) ; + } + + for (j = 0 ; j < deg ; j++) + { + DEBUG4 ((" k "ID" col "ID" value\n", k, Pattern [j])) ; + col = Pattern [j] ; + ASSERT (col >= 0 && col < n_col) ; + value = *xp++ ; + EDEBUG4 (value) ; + DEBUG4 (("\n")) ; + if (IS_NONZERO (value)) + { + Wi [col]++ ; + } + } + + /* ------------------------------------------------------------------ */ + /* make row k-1 of U in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + if (newUchain) + { + /* next row is a new Uchain */ + deg = ulen ; + DEBUG4 (("end of chain for row of U "ID" deg "ID"\n", k-1, deg)) ; + ip = (Int *) (Numeric->Memory + up) ; + for (j = 0 ; j < deg ; j++) + { + col = *ip++ ; + DEBUG4 ((" k "ID" col "ID"\n", k-1, col)) ; + ASSERT (k <= col) ; + Pattern [j] = col ; + } + } + else + { + deg -= ulen ; + DEBUG4 (("middle of chain for row of U "ID" deg "ID"\n", k-1, deg)); + ASSERT (deg >= 0) ; + pos = Upos [k] ; + if (pos != EMPTY) + { + /* add the pivot column */ + DEBUG4 (("k "ID" add pivot entry at position "ID"\n", k, pos)) ; + ASSERT (pos >= 0 && pos <= deg) ; + Pattern [deg++] = Pattern [pos] ; + Pattern [pos] = k ; + } + } + } + + /* ---------------------------------------------------------------------- */ + /* construct the final column form of U */ + /* ---------------------------------------------------------------------- */ + + /* create the column pointers */ + unz2 = 0 ; + for (col = 0 ; col < n_col ; col++) + { + Up [col] = unz2 ; + unz2 += Wi [col] ; + } + Up [n_col] = unz2 ; + DEBUG1 (("Numeric->unz "ID" npiv "ID" nnzpiv "ID" unz2 "ID"\n", + Numeric->unz, npiv, Numeric->nnzpiv, unz2)) ; + ASSERT (Numeric->unz + Numeric->nnzpiv == unz2) ; + + for (col = 0 ; col < n_col ; col++) + { + Wi [col] = Up [col+1] ; + } + + /* add all of the diagonal entries */ + for (col = 0 ; col < npiv ; col++) + { + if (IS_NONZERO (D [col])) + { + p = --(Wi [col]) ; + Ui [p] = col ; + Ux [p] = REAL_COMPONENT (D [col]) ; +#ifdef COMPLEX + Uz [p] = IMAG_COMPONENT (D [col]) ; +#endif + } + } + + /* add all the entries from the rows of U */ + + deg = Numeric->ulen ; + if (deg > 0) + { + /* make last pivot row of U (singular matrices only) */ + for (j = 0 ; j < deg ; j++) + { + Pattern [j] = Numeric->Upattern [j] ; + } + } + + for (k = npiv-1 ; k >= 0 ; k--) + { + + /* ------------------------------------------------------------------ */ + /* use row k of U */ + /* ------------------------------------------------------------------ */ + + up = Uip [k] ; + ulen = Uilen [k] ; + newUchain = (up < 0) ; + if (newUchain) + { + up = -up ; + xp = (Entry *) (Numeric->Memory + up + UNITS (Int, ulen)) ; + } + else + { + xp = (Entry *) (Numeric->Memory + up) ; + } + + xp += deg ; + for (j = deg-1 ; j >= 0 ; j--) + { + DEBUG4 ((" k "ID" col "ID" value", k, Pattern [j])) ; + col = Pattern [j] ; + ASSERT (col >= 0 && col < n_col) ; + value = *(--xp) ; + EDEBUG4 (value) ; + DEBUG4 (("\n")) ; + if (IS_NONZERO (value)) + { + p = --(Wi [col]) ; + Ui [p] = k ; + Ux [p] = REAL_COMPONENT (value) ; +#ifdef COMPLEX + Uz [p] = IMAG_COMPONENT (value) ; +#endif + } + } + + /* ------------------------------------------------------------------ */ + /* make row k-1 of U in Pattern [0..deg-1] */ + /* ------------------------------------------------------------------ */ + + if (newUchain) + { + /* next row is a new Uchain */ + deg = ulen ; + DEBUG4 (("end of chain for row of U "ID" deg "ID"\n", k-1, deg)) ; + ip = (Int *) (Numeric->Memory + up) ; + for (j = 0 ; j < deg ; j++) + { + col = *ip++ ; + DEBUG4 ((" k "ID" col "ID"\n", k-1, col)) ; + ASSERT (k <= col) ; + Pattern [j] = col ; + } + } + else + { + deg -= ulen ; + DEBUG4 (("middle of chain for row of U "ID" deg "ID"\n", k-1, deg)); + ASSERT (deg >= 0) ; + pos = Upos [k] ; + if (pos != EMPTY) + { + /* add the pivot column */ + DEBUG4 (("k "ID" add pivot entry at position "ID"\n", k, pos)) ; + ASSERT (pos >= 0 && pos <= deg) ; + Pattern [deg++] = Pattern [pos] ; + Pattern [pos] = k ; + } + } + } + +#ifndef NDEBUG + DEBUG6 (("U matrix:")) ; + UMF_dump_col_matrix (Ux, +#ifdef COMPLEX + Uz, +#endif + Ui, Up, Numeric->n_row, n_col, Numeric->unz + Numeric->nnzpiv) ; +#endif + +} + diff --git a/src/sparse-matrix/umfpack/umfpack_get_symbolic.c b/src/sparse-matrix/umfpack/umfpack_get_symbolic.c new file mode 100644 index 0000000..fb1d251 --- /dev/null +++ b/src/sparse-matrix/umfpack/umfpack_get_symbolic.c @@ -0,0 +1,172 @@ +/* ========================================================================== */ +/* === UMFPACK_get_symbolic ================================================= */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Gets the symbolic information held in the Symbolic object. + See umfpack_get_symbolic.h for a more detailed description. +*/ + +#include "umf_internal.h" +#include "umf_valid_symbolic.h" + +/* ========================================================================== */ + +GLOBAL Int UMFPACK_get_symbolic +( + Int *p_n_row, + Int *p_n_col, + Int *p_nz, + Int *p_nfr, + Int *p_nchains, + Int Ptree [ ], + Int Qtree [ ], + Int Front_npivcol [ ], + Int Front_parent [ ], + Int Front_1strow [ ], + Int Front_leftmostdesc [ ], + Int Chain_start [ ], + Int Chain_maxrows [ ], + Int Chain_maxcols [ ], + void *SymbolicHandle +) +{ + SymbolicType *Symbolic ; + Int k, n_row, n_col, nfr, nchains, *p ; + + /* ---------------------------------------------------------------------- */ + /* check inputs */ + /* ---------------------------------------------------------------------- */ + + Symbolic = (SymbolicType *) SymbolicHandle ; + if (!UMF_valid_symbolic (Symbolic)) + { + return (UMFPACK_ERROR_invalid_Symbolic_object) ; + } + + /* ---------------------------------------------------------------------- */ + /* get contents of Symbolic */ + /* ---------------------------------------------------------------------- */ + + n_row = Symbolic->n_row ; + n_col = Symbolic->n_col ; + nfr = Symbolic->nfr ; + nchains = Symbolic->nchains ; + + if (p_n_row) + { + *p_n_row = n_row ; + } + + if (p_n_col) + { + *p_n_col = n_col ; + } + + if (p_nz) + { + *p_nz = Symbolic->nz ; + } + + if (p_nfr) + { + *p_nfr = nfr ; + } + + if (p_nchains) + { + *p_nchains = nchains ; + } + + if (Ptree) + { + p = Symbolic->Rperm_init ; + for (k = 0 ; k < n_row ; k++) + { + Ptree [k] = p [k] ; + } + } + + if (Qtree) + { + p = Symbolic->Cperm_init ; + for (k = 0 ; k < n_col ; k++) + { + Qtree [k] = p [k] ; + } + } + + if (Front_npivcol) + { + p = Symbolic->Front_npivcol ; + for (k = 0 ; k <= nfr ; k++) + { + Front_npivcol [k] = p [k] ; + } + } + + if (Front_parent) + { + p = Symbolic->Front_parent ; + for (k = 0 ; k <= nfr ; k++) + { + Front_parent [k] = p [k] ; + } + } + + if (Front_1strow) + { + p = Symbolic->Front_1strow ; + for (k = 0 ; k <= nfr ; k++) + { + Front_1strow [k] = p [k] ; + } + } + + if (Front_leftmostdesc) + { + p = Symbolic->Front_leftmostdesc ; + for (k = 0 ; k <= nfr ; k++) + { + Front_leftmostdesc [k] = p [k] ; + } + } + + if (Chain_start) + { + p = Symbolic->Chain_start ; + for (k = 0 ; k <= nchains ; k++) + { + Chain_start [k] = p [k] ; + } + } + + if (Chain_maxrows) + { + p = Symbolic->Chain_maxrows ; + for (k = 0 ; k < nchains ; k++) + { + Chain_maxrows [k] = p [k] ; + } + Chain_maxrows [nchains] = 0 ; + } + + if (Chain_maxcols) + { + p = Symbolic->Chain_maxcols ; + for (k = 0 ; k < nchains ; k++) + { + Chain_maxcols [k] = p [k] ; + } + Chain_maxcols [nchains] = 0 ; + } + + return (UMFPACK_OK) ; +} diff --git a/src/sparse-matrix/umfpack/umfpack_numeric.c b/src/sparse-matrix/umfpack/umfpack_numeric.c new file mode 100644 index 0000000..c2f43c3 --- /dev/null +++ b/src/sparse-matrix/umfpack/umfpack_numeric.c @@ -0,0 +1,736 @@ +/* ========================================================================== */ +/* === UMFPACK_numeric ====================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Factorizes A into its LU factors, given a symbolic + pre-analysis computed by UMFPACK_symbolic. See umfpack_numeric.h for a + description. + + Dynamic memory usage: UMFPACK_numeric makes the heaviest usage of memory + space of all UMFPACK routines. Here is an outline of how it uses memory: + + 1) calls UMF_malloc 14 times, to obtain temporary workspace of size f + Entry's and 2*(n_row+1) + 2*(n_col+1) + (n_col+n_inner+1) + + (nn+1) + 3*(c+1) + 2*(r+1) + max(r,c) + (nfr+1) integers + where f is the size of the working array used for the frontal + matrices, r is the maximum number of rows in the working array, + c is the maximum number of columns in the working array (f is + less than or equal to r*c, and typically f=r*c), n_inner is + min (n_row,n_col), nn is max (n_row,n_col), and nfr is the number + of frontal matrices. For a square matrix, this is f Entry's and + about 7n + 3c + 2r + max(r,c) + nfr integers. + + 2) calls UMF_malloc 10 times, for a total space of sizeof (NumericType) + bytes, 4*(n_row+1) + 4*(n_row+1) integers, and (n_inner+1) Entry's. + sizeof (NumericType) is a small constant. This space is part of the + permanent Numeric object (which holds the LU factors) and is not + freed on return via UMF_free unless an error occurs. + + 3) calls UMF_malloc once, to allocate the variable-sized part of the + Numeric object, whose size in Units is the larger of: + (Control [UMFPACK_ALLOC_INIT]) * (the approximate upper bound + computed by UMFPACK_symbolic), and the minimum required to start the + numerical factorization. The default value of + Control [UMFPACK_ALLOC_INIT] is 1.0. This request is reduced if it + fails. This object is not freed on return via UMF_free unless + an error occurs. + + 4) During numerical factorization (inside UMF_kernel), the variable-size + block of memory is increased in size via a call to UMF_realloc if + it is found to be too small. This is rare with the default control + setting of 1.0 (I've never observed it to happen, but it + theoretically could happen). During factorization, this block holds + the pattern and values of L and U at the top end, and the elements + (contibution blocks) at the bottom end. + + For a square nonsingular matrix, the peak memory usage at this point + is roughly: + + r*c + n Entry's + 15n + 3c + 2r + max(r,c) + nfr integers + plus peak size of the variable-sized part of the Numeric object + plus the size of the Symbolic object (2n to 9n integers) + + where r is the maximum number of rows in the working array used + to hold the current frontal matrix, and c is the maximum number of + columns in the working array. + + The peak value of the variable-sized object is estimated in + UMFPACK_*symbolic (Info [UMFPACK_VARIABLE_PEAK_ESTIMATE]). + The size of the Symbolic object is in Info [UMFPACK_SYMBOLIC_SIZE], + and is between 2*n and 9*n integers. + + 5) After numerical factorization all of the objects allocated in step + (1) are freed via UMF_free, except that one object of size n_col+1 + is kept if there are nonzeros in the last pivot row. + + 6) The variable-sized block is reduced to hold just L and U, via a call + to UMF_realloc, since the frontal matrices are no longer needed. + + 7) This leaves a total of 11 or 12 objects allocated by UMF_malloc that + form the LU factorization. These remain if UMFPACK_numeric was + successful. Otherwise, they are all freed via UMF_free. + The final size of the Numeric object for a square nonsingular matrix + is roughly: + + n Entry's + 8n integers + plus final size of the variable-sized part of the Numeric object + + Dynamic memory usage of UMFPACK_free_numeric: + + 1) It frees, via UMF_free, all 11 or 12 objects allocated for the + Numeric object, in a prior call to UMFPACK_numeric. + +*/ + +/* ========================================================================== */ + +#include "umf_internal.h" +#include "umf_valid_symbolic.h" +#include "umf_set_stats.h" +#include "umf_kernel.h" +#include "umf_malloc.h" +#include "umf_free.h" +#include "umf_realloc.h" + +#ifndef NDEBUG +PRIVATE Int init_count ; +#endif + +PRIVATE Int work_alloc +( + WorkType *Work +) ; + +PRIVATE void free_work +( + WorkType *Work +) ; + +PRIVATE Int numeric_alloc +( + NumericType **NumericHandle, + SymbolicType *Symbolic, + double alloc_init +) ; + +PRIVATE void error +( + NumericType **Numeric, + WorkType *Work +) ; + + +/* ========================================================================== */ + +GLOBAL Int UMFPACK_numeric +( + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + void *SymbolicHandle, + void **NumericHandle, + const double Control [UMFPACK_CONTROL], + double User_Info [UMFPACK_INFO] +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + NumericType *Numeric ; + SymbolicType *Symbolic ; + WorkType WorkSpace, *Work ; + Int n_row, n_col, n_inner, newsize, i, status, *inew, npiv, ulen ; + Unit *mnew ; + double Info2 [UMFPACK_INFO], *Info, alloc_init, relax, relpt, tstart, tend, + relax2, relax3 ; + + /* ---------------------------------------------------------------------- */ + /* get the amount of time used by the process so far */ + /* ---------------------------------------------------------------------- */ + + tstart = umfpack_timer ( ) ; + + /* ---------------------------------------------------------------------- */ + /* initialize and check inputs */ + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + init_count = UMF_malloc_count ; +#endif + + if (Control) + { + /* use the Control array passed to us by the caller; look for NaN's */ + + if (SCALAR_IS_NAN (Control [UMFPACK_PIVOT_TOLERANCE])) + { + relpt = UMFPACK_DEFAULT_PIVOT_TOLERANCE ; + } + else + { + relpt = Control [UMFPACK_PIVOT_TOLERANCE] ; + } + + if (SCALAR_IS_NAN (Control [UMFPACK_RELAXED_AMALGAMATION])) + { + relax = UMFPACK_DEFAULT_RELAXED_AMALGAMATION ; + } + else + { + relax = Control [UMFPACK_RELAXED_AMALGAMATION] ; + } + + if (SCALAR_IS_NAN (Control [UMFPACK_RELAXED2_AMALGAMATION])) + { + relax2 = UMFPACK_DEFAULT_RELAXED2_AMALGAMATION ; + } + else + { + relax2 = Control [UMFPACK_RELAXED2_AMALGAMATION] ; + } + + if (SCALAR_IS_NAN (Control [UMFPACK_RELAXED3_AMALGAMATION])) + { + relax3 = UMFPACK_DEFAULT_RELAXED3_AMALGAMATION ; + } + else + { + relax3 = Control [UMFPACK_RELAXED3_AMALGAMATION] ; + } + + if (SCALAR_IS_NAN (Control [UMFPACK_ALLOC_INIT])) + { + alloc_init = UMFPACK_DEFAULT_ALLOC_INIT ; + } + else + { + alloc_init = Control [UMFPACK_ALLOC_INIT] ; + } + + } + else + { + /* no Control passed - use defaults instead */ + relpt = UMFPACK_DEFAULT_PIVOT_TOLERANCE ; + relax = UMFPACK_DEFAULT_RELAXED_AMALGAMATION ; + relax2 = UMFPACK_DEFAULT_RELAXED2_AMALGAMATION ; + relax3 = UMFPACK_DEFAULT_RELAXED3_AMALGAMATION ; + alloc_init = UMFPACK_DEFAULT_ALLOC_INIT ; + } + + relpt = MAX (0.0, MIN (relpt, 1.0)) ; + relax = MAX (0.0, relax) ; + relax2 = MAX (0.0, relax2) ; + relax3 = MAX (0.0, relax3) ; + alloc_init = MAX (0.0, alloc_init) ; + + if (User_Info) + { + /* return Info in user's array */ + Info = User_Info ; + for (i = UMFPACK_NUMERIC_SIZE ; i <= UMFPACK_NUMERIC_TIME ; i++) + { + Info [i] = EMPTY ; + } + } + else + { + /* no Info array passed - use local one instead */ + Info = Info2 ; + for (i = 0 ; i < UMFPACK_INFO ; i++) + { + Info [i] = EMPTY ; + } + } + + Symbolic = (SymbolicType *) SymbolicHandle ; + Numeric = (NumericType *) NULL ; + if (!UMF_valid_symbolic (Symbolic)) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_Symbolic_object ; + return (UMFPACK_ERROR_invalid_Symbolic_object) ; + } + + n_row = Symbolic->n_row ; + n_col = Symbolic->n_col ; + n_inner = MIN (n_row, n_col) ; + + Info [UMFPACK_STATUS] = UMFPACK_OK ; + Info [UMFPACK_NROW] = n_row ; + Info [UMFPACK_NCOL] = n_col ; + Info [UMFPACK_SIZE_OF_UNIT] = (double) (sizeof (Unit)) ; + Info [UMFPACK_NUMERIC_DEFRAG] = 0 ; + Info [UMFPACK_NUMERIC_REALLOC] = 0 ; + Info [UMFPACK_NUMERIC_COSTLY_REALLOC] = 0 ; + + if (!Ap || !Ai || !Ax || !NumericHandle +#ifdef COMPLEX + || !Az +#endif + ) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ; + return (UMFPACK_ERROR_argument_missing) ; + } + + Info [UMFPACK_NZ] = Ap [n_col] ; + *NumericHandle = (void *) NULL ; + + /* ---------------------------------------------------------------------- */ + /* allocate the Work object */ + /* ---------------------------------------------------------------------- */ + + Work = &WorkSpace ; + Work->n_row = n_row ; + Work->n_col = n_col ; + Work->nfr = Symbolic->nfr ; + Work->maxfrsize = Symbolic->maxfrsize ; + Work->maxnrows = Symbolic->maxnrows ; + Work->maxncols = Symbolic->maxncols ; + + if (!work_alloc (Work)) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + error (&Numeric, Work) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + ASSERT (UMF_malloc_count == init_count + 14) ; + + /* ---------------------------------------------------------------------- */ + /* allocate Numeric object */ + /* ---------------------------------------------------------------------- */ + + if (!numeric_alloc (&Numeric, Symbolic, alloc_init)) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + error (&Numeric, Work) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + DEBUG0 (("malloc: init_count "ID" UMF_malloc_count "ID"\n", + init_count, UMF_malloc_count)) ; + ASSERT (UMF_malloc_count == init_count + 14 + 11) ; + + /* set control parameters */ + Numeric->relpt = relpt ; + Numeric->relax = relax ; + Numeric->relax2 = relax2 ; + Numeric->relax3 = relax3 ; + Numeric->alloc_init = alloc_init ; + + DEBUG0 (("umf control: relpt %g relax [%g %g %g] init %g inc %g red %g\n", + relpt, relax, relax2, relax3, alloc_init, + UMF_REALLOC_INCREASE, UMF_REALLOC_REDUCTION)) ; + + /* ---------------------------------------------------------------------- */ + /* factorize */ + /* ---------------------------------------------------------------------- */ + + status = UMF_kernel (Ap, Ai, Ax, +#ifdef COMPLEX + Az, +#endif + Numeric, Work, Symbolic) ; + Info [UMFPACK_STATUS] = status ; + Info [UMFPACK_VARIABLE_INIT] = Numeric->init_usage ; + if (status < 0) + { + /* out of memory, or pattern has changed */ + error (&Numeric, Work) ; + return (status) ; + } + DEBUG0 (("malloc: init_count "ID" UMF_malloc_count "ID"\n", + init_count, UMF_malloc_count)) ; + + npiv = Numeric->npiv ; /* = n_inner for nonsingular matrices */ + ulen = Numeric->ulen ; /* = 0 for square nonsingular matrices */ + + /* ---------------------------------------------------------------------- */ + /* free Work object */ + /* ---------------------------------------------------------------------- */ + + DEBUG0 (("malloc: init_count "ID" UMF_malloc_count "ID"\n", + init_count, UMF_malloc_count)) ; + free_work (Work) ; + DEBUG0 (("malloc: init_count "ID" UMF_malloc_count "ID"\n", + init_count, UMF_malloc_count)) ; + DEBUG0 (("Numeric->ulen: "ID"\n", ulen)) ; + ASSERT (UMF_malloc_count == init_count + 11 + (ulen > 0)) ; + + /* ---------------------------------------------------------------------- */ + /* reduce Lpos, Lilen, Lip, Upos, Uilen and Uip to size npiv+1 */ + /* ---------------------------------------------------------------------- */ + + /* This is only needed for rectangular or singular matrices. */ + + if (npiv < n_row) + { + /* reduce Lpos, Uilen, and Uip from size n_row+1 to size npiv */ + inew = (Int *) UMF_realloc (Numeric->Lpos, npiv+1, sizeof (Int)) ; + if (inew) + { + Numeric->Lpos = inew ; + } + inew = (Int *) UMF_realloc (Numeric->Uilen, npiv+1, sizeof (Int)) ; + if (inew) + { + Numeric->Uilen = inew ; + } + inew = (Int *) UMF_realloc (Numeric->Uip, npiv+1, sizeof (Int)) ; + if (inew) + { + Numeric->Uip = inew ; + } + } + + if (npiv < n_col) + { + /* reduce Upos, Lilen, and Lip from size n_col+1 to size npiv */ + inew = (Int *) UMF_realloc (Numeric->Upos, npiv+1, sizeof (Int)) ; + if (inew) + { + Numeric->Upos = inew ; + } + inew = (Int *) UMF_realloc (Numeric->Lilen, npiv+1, sizeof (Int)) ; + if (inew) + { + Numeric->Lilen = inew ; + } + inew = (Int *) UMF_realloc (Numeric->Lip, npiv+1, sizeof (Int)) ; + if (inew) + { + Numeric->Lip = inew ; + } + } + + /* ---------------------------------------------------------------------- */ + /* reduce Numeric->Upattern from size n_col+1 to size ulen+1 */ + /* ---------------------------------------------------------------------- */ + + /* This is only needed for singular matrices, or if n_row < n_col. */ + /* If ulen is zero, the object does not exist. */ + + DEBUG4 (("ulen: "ID" Upattern "ID"\n", ulen, (Int) Numeric->Upattern)) ; + ASSERT (IMPLIES (ulen == 0, Numeric->Upattern == (Int *) NULL)) ; + if (ulen > 0 && ulen < n_col) + { + inew = (Int *) UMF_realloc (Numeric->Upattern, ulen+1, sizeof (Int)) ; + if (inew) + { + Numeric->Upattern = inew ; + } + } + + /* ---------------------------------------------------------------------- */ + /* reduce Numeric->Memory to hold just the LU factors at the head */ + /* ---------------------------------------------------------------------- */ + + newsize = Numeric->ihead ; + if (newsize < Numeric->size) + { + mnew = (Unit *) UMF_realloc (Numeric->Memory, newsize, sizeof (Unit)) ; + if (mnew) + { + /* realloc succeeded (how can it fail since the size is reduced?) */ + Numeric->Memory = mnew ; + Numeric->size = newsize ; + } + } + Numeric->ihead = Numeric->size ; + Numeric->itail = Numeric->ihead ; + Numeric->tail_usage = 0 ; + Numeric->ibig = EMPTY ; + /* UMF_mem_alloc_tail_block can no longer be called (no tail marker) */ + + /* ---------------------------------------------------------------------- */ + /* report the results and return the Numeric object */ + /* ---------------------------------------------------------------------- */ + + UMF_set_stats ( + Info, + Symbolic, + (double) Numeric->max_usage, /* actual peak Numeric->Memory */ + (double) Numeric->size, /* actual final Numeric->Memory */ + Numeric->flops, /* actual "true flops" */ + (double) Numeric->lnz + n_inner, /* actual nz in L */ + (double) Numeric->unz + Numeric->nnzpiv, /* actual nz in U */ + (double) Numeric->maxfrsize, /* actual largest front size */ + (double) ulen, /* actual Numeric->Upattern size */ + (double) npiv, /* actual # pivots found */ + ACTUAL) ; + + Info [UMFPACK_NUMERIC_DEFRAG] = Numeric->ngarbage ; + Info [UMFPACK_NUMERIC_REALLOC] = Numeric->nrealloc ; + Info [UMFPACK_NUMERIC_COSTLY_REALLOC] = Numeric->ncostly ; + Info [UMFPACK_COMPRESSED_PATTERN] = Numeric->isize ; + Info [UMFPACK_LU_ENTRIES] = Numeric->nLentries + Numeric->nUentries + + Numeric->npiv ; + Info [UMFPACK_UDIAG_NZ] = Numeric->nnzpiv ; + + /* estimate of the recipricol of the condition number. */ + if (SCALAR_IS_ZERO (Numeric->min_udiag) + || SCALAR_IS_ZERO (Numeric->max_udiag)) + { + /* rcond is zero if there is any zero on the diagonal, */ + /* even if NaN's are also present. */ + Numeric->rcond = 0.0 ; + } + else + { + /* estimate of the recipricol of the condition number. */ + /* This is NaN if diagonal is zero-free, but has one or more NaN's. */ + Numeric->rcond = Numeric->min_udiag / Numeric->max_udiag ; + } + Info [UMFPACK_RCOND] = Numeric->rcond ; + + if (Numeric->nnzpiv < n_inner + || SCALAR_IS_ZERO (Numeric->rcond) || SCALAR_IS_NAN (Numeric->rcond)) + { + /* there are zeros and/or NaN's on the diagonal of U */ + DEBUG0 (("Warning, matrix is singular in umfpack_numeric\n")) ; + DEBUG0 (("nnzpiv "ID" n_inner "ID" rcond %g\n", Numeric->nnzpiv, + n_inner, Numeric->rcond)) ; + status = UMFPACK_WARNING_singular_matrix ; + Info [UMFPACK_STATUS] = status ; + } + + Numeric->valid = NUMERIC_VALID ; + *NumericHandle = (void *) Numeric ; + + /* Numeric has 11 or 12 objects */ + ASSERT (UMF_malloc_count == init_count + 11 + (ulen > 0)) ; + + /* ---------------------------------------------------------------------- */ + /* get the time used by UMFPACK_numeric */ + /* ---------------------------------------------------------------------- */ + + tend = umfpack_timer ( ) ; + Info [UMFPACK_NUMERIC_TIME] = MAX (0, tend - tstart) ; + + /* return UMFPACK_OK or UMFPACK_WARNING_singular_matrix */ + return (status) ; + +} + + +/* ========================================================================== */ +/* === numeric_alloc ======================================================== */ +/* ========================================================================== */ + +/* Allocate the Numeric object */ + +PRIVATE Int numeric_alloc +( + NumericType **NumericHandle, + SymbolicType *Symbolic, + double alloc_init +) +{ + Int n_row, n_col, n_inner, min_usage, trying ; + NumericType *Numeric ; + double nsize, bsize ; + + ASSERT (Symbolic) ; + + n_row = Symbolic->n_row ; + n_col = Symbolic->n_col ; + n_inner = MIN (n_row, n_col) ; + *NumericHandle = (NumericType *) NULL ; + + /* 1 allocation: accounted for in UMF_set_stats (num_fixed_size) */ + Numeric = (NumericType *) UMF_malloc (1, sizeof (NumericType)) ; + + if (!Numeric) + { + return (FALSE) ; /* out of memory */ + } + Numeric->valid = 0 ; + *NumericHandle = Numeric ; + + /* 9 allocations: accounted for in UMF_set_stats (num_fixed_size) */ + Numeric->D = (Entry *) UMF_malloc (n_inner+1, sizeof (Entry)) ; + Numeric->Rperm = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ; + Numeric->Cperm = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ; + Numeric->Lpos = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ; + Numeric->Lilen = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ; + Numeric->Lip = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ; + Numeric->Upos = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ; + Numeric->Uilen = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ; + Numeric->Uip = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ; + + Numeric->Memory = (Unit *) NULL ; + Numeric->Upattern = (Int *) NULL ; /* used for singular matrices only */ + + if (!Numeric->D || !Numeric->Rperm || !Numeric->Cperm || !Numeric->Upos || + !Numeric->Lpos || !Numeric->Lilen || !Numeric->Uilen || !Numeric->Lip || + !Numeric->Uip) + { + return (FALSE) ; /* out of memory */ + } + + /* ---------------------------------------------------------------------- */ + /* allocate initial Numeric->Memory for LU factors and elements */ + /* ---------------------------------------------------------------------- */ + + nsize = (alloc_init * Symbolic->num_mem_usage_est) * (1.0 + MAX_EPSILON) ; + nsize = ceil (nsize) ; + min_usage = Symbolic->num_mem_init_usage ; + + /* Numeric->Memory must be large enough for UMF_kernel_init */ + /* double relop, but ignore NaN case. */ + nsize = MAX (ceil ((double) min_usage * (1.0 + MAX_EPSILON)), nsize) ; + + /* Numeric->Memory cannot be larger in size than Int_MAX / sizeof(Unit) */ + /* For ILP32 mode: 2GB (nsize cannot be bigger than 256 Mwords) */ + + bsize = ((double) Int_MAX) / sizeof (Unit) - 1 ; + DEBUG0 (("bsize %g\n", bsize)) ; + + nsize = MIN (nsize, bsize) ; /* double relop, but ignore NaN case. */ + + Numeric->size = (Int) nsize ; + + DEBUG0 (("Num init %g usage_est %g numsize "ID" minusage "ID"\n", + alloc_init, Symbolic->num_mem_usage_est, Numeric->size, min_usage)) ; + + /* allocates 1 object: */ + /* keep trying until successful, or memory request is too small */ + trying = TRUE ; + while (trying) + { + Numeric->Memory = (Unit *) UMF_malloc (Numeric->size, sizeof (Unit)) ; + if (Numeric->Memory) + { + DEBUG0 (("Successful Numeric->size: "ID"\n", Numeric->size)) ; + return (TRUE) ; + } + /* too much, reduce the request (but not below the minimum) */ + /* and try again */ + trying = Numeric->size > min_usage ; + Numeric->size = (Int) + (UMF_REALLOC_REDUCTION * ((double) Numeric->size)) ; + Numeric->size = MAX (min_usage, Numeric->size) ; + } + + return (FALSE) ; /* we failed to allocate Numeric->Memory */ +} + + +/* ========================================================================== */ +/* === work_alloc =========================================================== */ +/* ========================================================================== */ + +/* Allocate the Work object. Return TRUE if successful. */ + +PRIVATE Int work_alloc +( + WorkType *Work +) +{ + Int n_row, n_col, nn, n_inner, maxfrsize, maxnrows, maxncols, nfr ; + + n_row = Work->n_row ; + n_col = Work->n_col ; + nn = MAX (n_row, n_col) ; + n_inner = MIN (n_row, n_col) ; + nfr = Work->nfr ; + maxfrsize = Work->maxfrsize ; + maxnrows = Work->maxnrows ; + maxncols = Work->maxncols ; + + /* largest front will be maxnrows-by-maxncols, at most */ + DEBUG1 (("Allocating frontal matrix, size "ID" ("ID"-by-"ID") saved: "ID + "\n", maxfrsize, maxnrows, maxncols, + (maxnrows * maxncols) - maxfrsize)) ; + + /* 13 allocations, freed in free_work: */ + /* accounted for in UMF_set_stats (work_usage) */ + Work->Fx = (Entry *) UMF_malloc (maxfrsize, sizeof (Entry)) ; + Work->Frpos = (Int *) UMF_malloc (n_row + 1, sizeof (Int)) ; + Work->Fcpos = (Int *) UMF_malloc (n_col + 1, sizeof (Int)) ; + Work->Lpattern = (Int *) UMF_malloc (n_row + 1, sizeof (Int)) ; + Work->Wp = (Int *) UMF_malloc (nn + 1, sizeof (Int)) ; + Work->Frows = (Int *) UMF_malloc (maxnrows + 1, sizeof (Int)) ; + Work->Fcols = (Int *) UMF_malloc (maxncols + 1, sizeof (Int)) ; + Work->Wio = (Int *) UMF_malloc (maxncols + 1, sizeof (Int)) ; + Work->Woi = (Int *) UMF_malloc (maxncols + 1, sizeof (Int)) ; + Work->Woo = (Int *) UMF_malloc (MAX (maxnrows, maxncols) + 1, sizeof (Int)); + Work->Wm = (Int *) UMF_malloc (maxnrows + 1, sizeof (Int)) ; + Work->E = (Int *) UMF_malloc (n_col + n_inner + 1, sizeof (Int)) ; + Work->Front_new1strow = (Int *) UMF_malloc (nfr + 1, sizeof (Int)) ; + + /* 1 allocation, may become part of Numeric (if singular or rectangular): */ + Work->Upattern = (Int *) UMF_malloc (n_col + 1, sizeof (Int)) ; + + return (Work->Fx && Work->Frpos + && Work->Fcpos && Work->Lpattern && Work->Upattern + && Work->E && Work->Frows && Work->Fcols && Work->Wio + && Work->Woi && Work->Woo && Work->Wm && Work->Wp + && Work->Front_new1strow) ; +} + + +/* ========================================================================== */ +/* === free_work ============================================================ */ +/* ========================================================================== */ + +PRIVATE void free_work +( + WorkType *Work +) +{ + if (Work) + { + + /* free 13 or 14 objects (Upattern may already be gone) */ + Work->Fx = (Entry *) UMF_free ((void *) Work->Fx) ; + Work->Frpos = (Int *) UMF_free ((void *) Work->Frpos) ; + Work->Fcpos = (Int *) UMF_free ((void *) Work->Fcpos) ; + Work->Lpattern = (Int *) UMF_free ((void *) Work->Lpattern) ; + Work->Upattern = (Int *) UMF_free ((void *) Work->Upattern) ; + Work->Wp = (Int *) UMF_free ((void *) Work->Wp) ; + Work->Frows = (Int *) UMF_free ((void *) Work->Frows) ; + Work->Fcols = (Int *) UMF_free ((void *) Work->Fcols) ; + Work->Wio = (Int *) UMF_free ((void *) Work->Wio) ; + Work->Woi = (Int *) UMF_free ((void *) Work->Woi) ; + Work->Woo = (Int *) UMF_free ((void *) Work->Woo) ; + Work->Wm = (Int *) UMF_free ((void *) Work->Wm) ; + Work->E = (Int *) UMF_free ((void *) Work->E) ; + Work->Front_new1strow = + (Int *) UMF_free ((void *) Work->Front_new1strow) ; + + } +} + + +/* ========================================================================== */ +/* === error ================================================================ */ +/* ========================================================================== */ + +/* Error return from UMFPACK_numeric. Free all allocated memory. */ + +PRIVATE void error +( + NumericType **Numeric, + WorkType *Work +) +{ + free_work (Work) ; + UMFPACK_free_numeric ((void **) Numeric) ; + ASSERT (UMF_malloc_count == init_count) ; +} + diff --git a/src/sparse-matrix/umfpack/umfpack_qsymbolic.c b/src/sparse-matrix/umfpack/umfpack_qsymbolic.c new file mode 100644 index 0000000..061d3ba --- /dev/null +++ b/src/sparse-matrix/umfpack/umfpack_qsymbolic.c @@ -0,0 +1,1347 @@ +/* ========================================================================== */ +/* === UMFPACK_qsymbolic ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Performs a symbolic factorization. + See umfpack_qsymbolic.h and umfpack_symbolic.h for details. + + Dynamic memory usage of UMFPACK_qsymbolic: + + 1) calls UMF_malloc 6 times, for workspace of total size (C + 5*n_col) + integers. The value of C is determined by the macro + UMF_COLAMD_RECOMMENDED. It is roughly + max (2*nz, 3*n_col) + 8*n_col + 6*n_row + n_col + nz/5, or typically + about 2.2*nz + 9*n_col + 6*n_row, where nz is the number of entries in + A. If A is square, this is 2.2*nz + 15*n. + + 2) three more calls to UMF_malloc are made, for a total space of + (n_row+1 + n_col+1) integers + sizeof (SymbolicType). + sizeof (SymbolicType) is a small constant. This space is part of the + Symbolic object and is not freed unless an error occurs. The analysis + (UMF_colamd and/or UMF_analyze) is then performed. + This is about 2n if A is square. + + 3) UMF_malloc is called 7 times, for a total workspace of + (4*(nfr+1)+3*(nchains+1)) integers, where nfr is the total number of + frontal matrices and nchains is the total number of frontal matrix + chains, and where nchains <= nfr <= n_col. This space is part of the + Symbolic object and is not free'd unless an error occurs. This is + between 7 and about 7n integers when A is square. + + Thus, the peak memory usage of UMFPACK_symbolic occurs at this point. + For a square matrix, it is at most about (2.2*nz + 24*n) integers for + temporary workspace, plus between 2*n and 9*n integers for the final + Symbolic object. + + 4) The 6 workspace objects allocated in step (1) are free'd via + UMF_free. The final Symbolic object consists of 10 allocated objects. + Its final total size is lies roughly between 2*n and 9*n for a square + matrix. If an error occurs, all 10 objects are free'd via UMF_free. + + Dynamic memory usage of UMFPACK_free_symbolic: + + 1) All 10 objects comprising the Symbolic object are free'd via UMF_free. + +*/ + +/* ========================================================================== */ + +#include "umf_internal.h" +#include "umf_symbolic_usage.h" +#include "umf_colamd.h" +#include "umf_set_stats.h" +#include "umf_analyze.h" +#include "umf_transpose.h" +#include "umf_is_permutation.h" +#include "umf_kernel_init_usage.h" +#include "umf_malloc.h" +#include "umf_free.h" + +typedef struct /* SWorkType */ +{ + Int *Front_npivcol ; + Int *Front_nrows ; + Int *Front_ncols ; + Int *Front_parent ; + Int *Front_cols ; /* in UMF_colamd only */ + Int *Ci ; + +} SWorkType ; + +PRIVATE void free_work +( + SWorkType *SWork +) ; + +PRIVATE void error +( + SymbolicType **Symbolic, + SWorkType *SWork +) ; + +#define SYM_WORK_USAGE(n_col,Clen) (DUNITS (Int, Clen) + 5*DUNITS (Int, n_col)) + +/* required size of Ci for code that calls UMF_transpose and UMF_analyze below*/ +#define UMF_ANALYZE_CLEN(nz,n_row,n_col,nn) \ + ((n_col) + MAX ((nz),1) + 3*(nn)+1 + (n_col)) + +#ifndef NDEBUG +PRIVATE Int init_count ; +#endif + +/* ========================================================================== */ + +GLOBAL Int UMFPACK_qsymbolic +( + Int n_row, + Int n_col, + const Int Ap [ ], + const Int Ai [ ], + const Int Q [ ], + void **SymbolicHandle, + const double Control [UMFPACK_CONTROL], + double User_Info [UMFPACK_INFO] +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int colamd_ok, i, nz, maxfrsize, s, j, newj, status, maxoldpiv, chain_npiv, + maxnrows, maxncols, nfr, col, nchains, maxrows, maxcols, p, nb, nn, + *Chain_start, *Chain_maxrows, *Chain_maxcols, *Front_npivcol, *Ci, + Clen, colamd_stats [COLAMD_STATS], fpiv, frows, fcols, n_inner, + child, cr, cc, cp, parent, *Link, *Row_degree, row, *Front_parent, + head_usage, tail_usage, max_usage, analyze_compactions, + lf, uf, init_tail_usage, k, do_colamd, do_UMF_analyze, too_large, + fpivcol, fallrows, fallcols, *InFront, *F1, + *Front_1strow, f1rows, kk, *Cperm_init, *Rperm_init, newrow, + *Front_leftmostdesc, Clen_analyze ; + double knobs [COLAMD_KNOBS], flops, f, r, c, tstart, tend, + *Info, Info2 [UMFPACK_INFO], drow, dcol, dusage, dlf, duf, dmax_usage, + dhead_usage, dh, dlnz, dunz, dmaxfrsize, dClen, dClen_analyze ; + Int nempty_row, nempty_col ; + SymbolicType *Symbolic ; + SWorkType SWorkSpace, *SWork ; + +#ifndef NDEBUG + double f2 ; + Int lf2, uf2 ; + UMF_dump_start ( ) ; + init_count = UMF_malloc_count ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get the amount of time used by the process so far */ + /* ---------------------------------------------------------------------- */ + + tstart = umfpack_timer ( ) ; + + /* ---------------------------------------------------------------------- */ + /* check input parameters */ + /* ---------------------------------------------------------------------- */ + + if (Control) + { + /* use the Control array passed to us by the caller. */ + + if (SCALAR_IS_NAN (Control [UMFPACK_DENSE_ROW])) + { + drow = UMFPACK_DEFAULT_DENSE_ROW ; + } + else + { + drow = Control [UMFPACK_DENSE_ROW] ; + } + + if (SCALAR_IS_NAN (Control [UMFPACK_DENSE_COL])) + { + dcol = UMFPACK_DEFAULT_DENSE_COL ; + } + else + { + dcol = Control [UMFPACK_DENSE_COL] ; + } + + if (SCALAR_IS_NAN (Control [UMFPACK_BLOCK_SIZE])) + { + nb = UMFPACK_DEFAULT_BLOCK_SIZE ; + } + else + { + nb = (Int) Control [UMFPACK_BLOCK_SIZE] ; + } + + } + else + { + /* no Control passed - use defaults instead */ + drow = UMFPACK_DEFAULT_DENSE_ROW ; + dcol = UMFPACK_DEFAULT_DENSE_COL ; + nb = UMFPACK_DEFAULT_BLOCK_SIZE ; + } + + nb = MAX (1, nb) ; + DEBUG0 (("nb = "ID"\n", nb)) ; + + if (User_Info) + { + /* return Info in user's array */ + Info = User_Info ; + } + else + { + /* no Info array passed - use local one instead */ + Info = Info2 ; + } + for (i = 0 ; i < UMFPACK_INFO ; i++) + { + Info [i] = EMPTY ; + } + + nn = MAX (n_row, n_col) ; + n_inner = MIN (n_row, n_col) ; + + Info [UMFPACK_STATUS] = UMFPACK_OK ; + Info [UMFPACK_NROW] = n_row ; + Info [UMFPACK_NCOL] = n_col ; + Info [UMFPACK_SIZE_OF_UNIT] = (double) (sizeof (Unit)) ; + Info [UMFPACK_SIZE_OF_INT] = (double) (sizeof (int)) ; + Info [UMFPACK_SIZE_OF_LONG] = (double) (sizeof (long)) ; + Info [UMFPACK_SIZE_OF_POINTER] = (double) (sizeof (void *)) ; + Info [UMFPACK_SIZE_OF_ENTRY] = (double) (sizeof (Entry)) ; + Info [UMFPACK_SYMBOLIC_DEFRAG] = 0 ; + + if (!Ai || !Ap || !SymbolicHandle) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ; + return (UMFPACK_ERROR_argument_missing) ; + } + + *SymbolicHandle = (void *) NULL ; + + if (n_row <= 0 || n_col <= 0) /* n_row, n_col must be > 0 */ + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_n_nonpositive ; + return (UMFPACK_ERROR_n_nonpositive) ; + } + + nz = Ap [n_col] ; + DEBUG0 (("In UMFPACK_symbolic, n_row "ID" n_col "ID" nz "ID"\n", + n_row, n_col, nz)) ; + Info [UMFPACK_NZ] = nz ; + if (nz < 0) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_nz_negative ; + return (UMFPACK_ERROR_nz_negative) ; + } + + if (Q) + { + do_colamd = FALSE ; + } + else + { + do_colamd = TRUE ; + } + + /* ---------------------------------------------------------------------- */ + /* determine amount of memory required for UMFPACK_symbolic */ + /* ---------------------------------------------------------------------- */ + + /* The size of Clen required for UMF_colamd is always larger than */ + /* UMF_analyze, but the max is included here in case that changes in */ + /* future versions. */ + + dClen = UMF_COLAMD_RECOMMENDED ((double) nz, (double) n_row, + (double) n_col) ; + dClen_analyze = UMF_ANALYZE_CLEN ((double) nz, (double) n_row, + (double) n_col, (double) nn) ; + dClen = MAX (dClen, dClen_analyze) ; + too_large = INT_OVERFLOW (dClen * sizeof (Int)) ; + + if (too_large) + { + /* Problem is too large for array indexing (Ci [i]) with an Int i. */ + /* Cannot even analyze the problem to determine upper bounds on */ + /* memory usage. Need to use the long integer version, umfpack_*l_*. */ + Info [UMFPACK_STATUS] = UMFPACK_ERROR_problem_too_large ; + Info [UMFPACK_SYMBOLIC_PEAK_MEMORY] = + SYM_WORK_USAGE (n_col, dClen) + + UMF_symbolic_usage (n_row, n_col, n_col, n_col) ; + return (UMFPACK_ERROR_problem_too_large) ; + } + + Clen = UMF_COLAMD_RECOMMENDED (nz, n_row, n_col) ; + Clen_analyze = UMF_ANALYZE_CLEN (nz, n_row, n_col, nn) ; + Clen = MAX (Clen, Clen_analyze) ; + + /* worst case total memory usage for UMFPACK_symbolic (revised below) */ + Info [UMFPACK_SYMBOLIC_PEAK_MEMORY] = + SYM_WORK_USAGE (n_col, Clen) + + UMF_symbolic_usage (n_row, n_col, n_col, n_col) ; + + /* ---------------------------------------------------------------------- */ + /* allocate workspace */ + /* ---------------------------------------------------------------------- */ + + Symbolic = (SymbolicType *) NULL ; + SWork = &SWorkSpace ; /* used for UMFPACK_symbolic only */ + + /* Note that SWork->Front_* does not include the dummy placeholder front. */ + /* This space is accounted for by the SYM_WORK_USAGE (n_col, Clen) macro. */ + SWork->Ci = (Int *) UMF_malloc (Clen, sizeof (Int)) ; + SWork->Front_npivcol = (Int *) UMF_malloc (n_col, sizeof (Int)) ; + SWork->Front_nrows = (Int *) UMF_malloc (n_col, sizeof (Int)) ; + SWork->Front_ncols = (Int *) UMF_malloc (n_col, sizeof (Int)) ; + SWork->Front_parent = (Int *) UMF_malloc (n_col, sizeof (Int)) ; + SWork->Front_cols = (Int *) UMF_malloc (n_col, sizeof (Int)) ; + + if (!SWork->Ci || !SWork->Front_npivcol || !SWork->Front_nrows || + !SWork->Front_ncols || !SWork->Front_parent || !SWork->Front_cols) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + error (&Symbolic, SWork) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + + /* ---------------------------------------------------------------------- */ + /* allocate the first part of the Symbolic object (header and Cperm_init) */ + /* ---------------------------------------------------------------------- */ + + Symbolic = (SymbolicType *) UMF_malloc (1, sizeof (SymbolicType)) ; + + if (!Symbolic) + { + /* If we fail here, Symbolic is NULL and thus it won't be */ + /* dereferenced by UMFPACK_free_symbolic, as called by error ( ). */ + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + error (&Symbolic, SWork) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + + /* We now know that Symbolic has been allocated */ + Symbolic->valid = 0 ; + Symbolic->Chain_start = (Int *) NULL ; + Symbolic->Chain_maxrows = (Int *) NULL ; + Symbolic->Chain_maxcols = (Int *) NULL ; + Symbolic->Front_npivcol = (Int *) NULL ; + Symbolic->Front_parent = (Int *) NULL ; + Symbolic->Front_1strow = (Int *) NULL ; + Symbolic->Front_leftmostdesc = (Int *) NULL ; + + Symbolic->Cperm_init = (Int *) UMF_malloc (n_col+1, sizeof (Int)) ; + Symbolic->Rperm_init = (Int *) UMF_malloc (n_row+1, sizeof (Int)) ; + Cperm_init = Symbolic->Cperm_init ; + Rperm_init = Symbolic->Rperm_init ; + + if (!Symbolic->Cperm_init || !Symbolic->Rperm_init) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + error (&Symbolic, SWork) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + + DEBUG0 (("(1)Symbolic UMF_malloc_count - init_count = "ID"\n", + UMF_malloc_count - init_count)) ; + ASSERT (UMF_malloc_count == init_count + 9) ; + + Symbolic->n_row = n_row ; + Symbolic->n_col = n_col ; + Symbolic->nz = nz ; + Symbolic->drow = drow ; + Symbolic->dcol = dcol ; + Symbolic->nb = nb ; + + /* ---------------------------------------------------------------------- */ + /* use colamd or input column ordering Q */ + /* ---------------------------------------------------------------------- */ + + if (do_colamd) + { + + /* ------------------------------------------------------------------ */ + /* copy the matrix into colamd workspace (colamd destroys its input) */ + /* ------------------------------------------------------------------ */ + + Ci = SWork->Ci ; + for (i = 0 ; i < nz ; i++) + { + DEBUG5 (("Ai "ID": "ID"\n", i, Ai [i])) ; + Ci [i] = Ai [i] ; + } + + /* load the column pointers into Cperm_init [0..n_col] */ + for (col = 0 ; col <= n_col ; col++) + { + DEBUG5 (("Ap "ID": "ID"\n", col, Ap [col])) ; + Cperm_init [col] = Ap [col] ; + } + + /* ------------------------------------------------------------------ */ + /* set UMF_colamd defaults */ + /* ------------------------------------------------------------------ */ + + UMF_colamd_set_defaults (knobs) ; + knobs [COLAMD_DENSE_ROW] = drow ; + knobs [COLAMD_DENSE_COL] = dcol ; + + /* ------------------------------------------------------------------ */ + /* check input matrix and find the initial column pre-ordering */ + /* ------------------------------------------------------------------ */ + + colamd_ok = UMF_colamd (n_row, n_col, Clen, SWork->Ci, + Cperm_init, knobs, colamd_stats, + SWork->Front_npivcol, + SWork->Front_nrows, + SWork->Front_ncols, + SWork->Front_parent, + /* used in UMF_colamd only: */ SWork->Front_cols, + &nfr) ; + + /* get number of truly empty rows and columns */ + nempty_row = colamd_stats [COLAMD_EMPTY_ROW] ; + nempty_col = colamd_stats [COLAMD_EMPTY_COL] ; + + if (!colamd_ok) + { + /* This will be modified below. It is not (yet) an internal */ + /* error. It is only an internal error if is it not modified */ + /* below (colamd_stats missing). */ + Info [UMFPACK_STATUS] = UMFPACK_ERROR_internal_error ; + } + + s = colamd_stats [COLAMD_STATUS] ; + + if (s == COLAMD_OK) + { + status = UMFPACK_OK ; + } + else if (s == COLAMD_ERROR_jumbled_matrix) + { + status = UMFPACK_ERROR_jumbled_matrix ; + } + else if (s == COLAMD_ERROR_p0_nonzero) + { + status = UMFPACK_ERROR_Ap0_nonzero ; + } + else if (s == COLAMD_ERROR_row_index_out_of_bounds) + { + status = UMFPACK_ERROR_row_index_out_of_bounds ; + } + else if (s == COLAMD_ERROR_col_length_negative) + { + status = UMFPACK_ERROR_col_length_negative ; + } + else + { + /* these errors "cannot" happen */ + /* COLAMD_ERROR_A_too_small */ + /* COLAMD_ERROR_A_not_present */ + /* COLAMD_ERROR_p_not_present */ + /* COLAMD_ERROR_out_of_memory */ + /* COLAMD_ERROR_internal_error */ + /* COLAMD_ERROR_nrow_negative */ + /* COLAMD_ERROR_ncol_negative */ + /* COLAMD_ERROR_nnz_negative */ + status = UMFPACK_ERROR_internal_error ; + } + + Info [UMFPACK_STATUS] = status ; + if (status != UMFPACK_OK) + { + error (&Symbolic, SWork) ; + return (status) ; + } + + Info [UMFPACK_NDENSE_ROW] = colamd_stats [COLAMD_DENSE_ROW] ; + Info [UMFPACK_NEMPTY_ROW] = colamd_stats [COLAMD_EMPTY_ROW] + + colamd_stats [COLAMD_NEWLY_EMPTY_ROW] ; + Info [UMFPACK_NDENSE_COL] = colamd_stats [COLAMD_DENSE_COL] ; + Info [UMFPACK_NEMPTY_COL] = colamd_stats [COLAMD_EMPTY_COL] + + colamd_stats [COLAMD_NEWLY_EMPTY_COL] ; + Info [UMFPACK_SYMBOLIC_DEFRAG] = colamd_stats [COLAMD_DEFRAG_COUNT]; + + /* re-analyze if any "dense" rows or cols ignored by UMF_colamd */ + do_UMF_analyze = + colamd_stats [COLAMD_DENSE_ROW] > 0 || + colamd_stats [COLAMD_DENSE_COL] > 0 ; + +#ifndef NDEBUG + for (col = 0 ; col < n_col ; col++) + { + DEBUG1 (("Cperm_init ["ID"] = "ID"\n", col, Cperm_init[col])); + } + /* make sure colamd returned a valid permutation */ + ASSERT (Cperm_init) ; + ASSERT (UMF_is_permutation (Cperm_init, SWork->Ci, n_col, n_col)) ; +#endif + + } + else + { + + /* ------------------------------------------------------------------ */ + /* do not call colamd - use input Q instead */ + /* ------------------------------------------------------------------ */ + + Int length ; + + /* use Ci as workspace to check input permutation */ + if (!UMF_is_permutation (Q, SWork->Ci, n_col, n_col)) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_permutation ; + error (&Symbolic, SWork) ; + return (UMFPACK_ERROR_invalid_permutation) ; + } + + if (Ap [0] != 0) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_Ap0_nonzero ; + error (&Symbolic, SWork) ; + return (UMFPACK_ERROR_Ap0_nonzero) ; + } + + nempty_col = 0 ; + + /* check Ap input */ + for (j = 0 ; j < n_col ; j++) + { + length = Ap [j+1] - Ap [j] ; + if (length == 0) + { + /* this is an empty column */ + DEBUG1 (("Original empty column: "ID"\n", j)) ; + nempty_col++ ; + } + if (length < 0) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_col_length_negative ; + error (&Symbolic, SWork) ; + return (UMFPACK_ERROR_col_length_negative) ; + } + } + + Info [UMFPACK_NDENSE_ROW] = 0 ; + Info [UMFPACK_NEMPTY_ROW] = 0 ; /* this is fixed below */ + Info [UMFPACK_NDENSE_COL] = 0 ; + Info [UMFPACK_NEMPTY_COL] = nempty_col ; + + if (nempty_col == 0) + { + /* copy the user's input permutation */ + for (k = 0 ; k < n_col ; k++) + { + Cperm_init [k] = Q [k] ; + } + } + else + { + /* partition the user's input permutation */ + + /* move empty columns last */ + k = n_col ; + for (j = n_col-1 ; j >= 0 ; j--) + { + col = Q [j] ; + length = Ap [col+1] - Ap [col] ; + if (length == 0) + { + Cperm_init [--k] = col ; + DEBUG1 (("Moving empty col "ID" last: "ID"\n", col, k)) ; + } + } + ASSERT (n_col-k == nempty_col) ; + + /* move remaining columns first */ + k = 0 ; + for (j = 0 ; j < n_col ; j++) + { + col = Q [j] ; + length = Ap [col+1] - Ap [col] ; + if (length != 0) + { + DEBUG1 (("Non empty col "ID" at: "ID"\n", col, k)) ; + Cperm_init [k++] = col ; + } + } + } + + do_UMF_analyze = TRUE ; + + } + + Cperm_init [n_col] = EMPTY ; /* unused in Cperm_init */ + + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + DEBUG3 (("Cperm_init column permutation:\n")) ; + ASSERT (UMF_is_permutation (Cperm_init, SWork->Ci, n_col, n_col)) ; + for (k = 0 ; k < n_col ; k++) + { + DEBUG3 ((ID"\n", Cperm_init [k])) ; + } + /* ensure that empty columns have been placed last in A (:,Cperm_init) */ + for (newj = 0 ; newj < n_col ; newj++) + { + /* empty columns will be last in A (:, Cperm_init (1:n_col)) */ + j = Cperm_init [newj] ; + ASSERT (IMPLIES (newj >= n_col-nempty_col, Ap [j+1] - Ap [j] == 0)) ; + ASSERT (IMPLIES (newj < n_col-nempty_col, Ap [j+1] - Ap [j] > 0)) ; + } +#endif + + /* ---------------------------------------------------------------------- */ + /* analyze, using the given ordering, or using colamd's ordering */ + /* ---------------------------------------------------------------------- */ + + if (do_UMF_analyze) + { + + Int *W, *Bp, *Bi, *Cperm2, ok, ilast, *P, Clen2, bsize, Clen0 ; + + /* ------------------------------------------------------------------ */ + /* Ci [0 .. Clen-1] holds the following work arrays: + + first Clen0 entries empty space, where Clen0 = + Clen - (nn+1 + 2*nn + n_col) + and Clen0 >= nz + n_col + next nn+1 entries Bp [0..nn] + next nn entries Link [0..nn-1] + next nn entries W [0..nn-1] + last n_col entries Cperm2 [0..n_col-1] + */ + + Ci = SWork->Ci ; + Clen0 = Clen - (nn+1 + 2*nn + n_col) ; + Bp = Ci + Clen0 ; + Link = Bp + (nn+1) ; + W = Link + nn ; + Cperm2 = W + nn ; + ASSERT (Cperm2 + n_col == Ci + Clen) ; + ASSERT (Clen0 >= nz + n_col) ; + + /* ------------------------------------------------------------------ */ + /* P = order that rows will be used in UMF_analyze */ + /* ------------------------------------------------------------------ */ + + /* use W to mark rows, and use Link for row permutation P [ [ */ + for (row = 0 ; row < n_row ; row++) + { + W [row] = FALSE ; + } + P = Link ; + + k = 0 ; + for (newj = 0 ; newj < n_col ; newj++) + { + /* empty columns will be last in A (:,Cperm_init) */ + j = Cperm_init [newj] ; + ilast = -1 ; + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + row = Ai [p] ; + if (row < 0 || row >= n_row) + { + Info [UMFPACK_STATUS] = + UMFPACK_ERROR_row_index_out_of_bounds ; + error (&Symbolic, SWork) ; + return (UMFPACK_ERROR_row_index_out_of_bounds) ; + } + if (row <= ilast) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_jumbled_matrix ; + error (&Symbolic, SWork) ; + return (UMFPACK_ERROR_jumbled_matrix) ; + } + if (!W [row]) + { + /* this row has just been see for the first time */ + W [row] = TRUE ; + P [k++] = row ; + } + ilast = row ; + } + } + + /* If the matrix has truly empty rows, then P will not be */ + /* complete, and visa versa. The matrix is structurally singular. */ + ASSERT (IMPLIES (do_colamd, nempty_row == n_row-k)) ; + nempty_row = n_row - k ; + Info [UMFPACK_NEMPTY_ROW] = nempty_row ; + if (k < n_row) + { + /* complete P by putting empty rows last in their natural order, */ + /* rather than declaring an error (the matrix is singular) */ + for (row = 0 ; row < n_row ; row++) + { + if (!W [row]) + { + /* W [row] = TRUE ; (not required) */ + P [k++] = row ; + } + } + } + + /* contents of W no longer needed ] */ + +#ifndef NDEBUG + DEBUG3 (("Induced row permutation:\n")) ; + ASSERT (k == n_row) ; + ASSERT (UMF_is_permutation (P, W, n_row, n_row)) ; + for (k = 0 ; k < n_row ; k++) + { + DEBUG3 ((ID"\n", P [k])) ; + } +#endif + + /* ------------------------------------------------------------------ */ + /* B = row-form of the pattern of A (P, Cperm_init (1:n_col)) */ + /* ------------------------------------------------------------------ */ + + /* Ci [0 .. Clen-1] holds the following work arrays: + + first Clen2 entries empty space, must be at least >= n_col + next max (nz,1) Bi [0..max (nz,1)-1] + next nn+1 entries Bp [0..nn] + next nn entries Link [0..nn-1] + next nn entries W [0..nn-1] + last n_col entries Cperm2 [0..n_col-1] + + This memory usage is accounted for by the UMF_ANALYZE_CLEN + macro. + */ + + Clen2 = Clen0 ; + bsize = MAX (nz, 1) ; + Clen2 -= bsize ; + Bi = Ci + Clen2 ; + ASSERT (Clen2 >= n_col) ; + + /* skip error test (already done, above). Do pattern only. */ + (void) UMF_transpose (n_row, n_col, Ap, Ai, (double *) NULL, P, + Cperm_init, n_col - nempty_col, Bp, Bi, (double *) NULL, W, FALSE +#ifdef COMPLEX + , (double *) NULL, (double *) NULL, FALSE +#endif + ) ; + + /* contents of P (same as Link) and W not needed */ + /* still need Link and W as work arrays, though ] */ + + ASSERT (Bp [0] == 0) ; + ASSERT (Bp [n_row] == nz) ; + + /* increment Bp to point into Ci, not Bi */ + for (i = 0 ; i <= n_row ; i++) + { + Bp [i] += Clen2 ; + } + ASSERT (Bp [0] == Clen0 - bsize) ; + ASSERT (Bp [n_row] <= Clen0) ; + + /* Ci [0 .. Clen-1] holds the following work arrays: + + first Clen0 entries Ci [0 .. Clen0-1], where the col indices + of B are at the tail end of this part, + and Bp [0] = Clen2 >= n_col. Note + that Clen0 = Clen2 + max (nz,1). + next nn+1 entries Bp [0..nn] + next nn entries Link [0..nn-1] + next nn entries W [0..nn-1] + last n_col entries Cperm2 [0..n_col-1] + */ + + /* ------------------------------------------------------------------ */ + /* analyze */ + /* ------------------------------------------------------------------ */ + + /* only analyze the non-empty part of the matrix */ + ok = UMF_analyze (n_row - nempty_row, n_col - nempty_col, + Ci, Bp, Cperm2, W, Link, + SWork->Front_ncols, SWork->Front_nrows, SWork->Front_npivcol, + SWork->Front_parent, &nfr, &analyze_compactions) ; + if (!ok) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_internal_error ; + error (&Symbolic, SWork) ; + return (UMFPACK_ERROR_internal_error) ; + } + Info [UMFPACK_SYMBOLIC_DEFRAG] += analyze_compactions ; + + /* ------------------------------------------------------------------ */ + /* combine the input permutation and UMF_analyze's permutation */ + /* ------------------------------------------------------------------ */ + + /* Cperm2 is the column etree post-ordering */ + ASSERT (UMF_is_permutation (Cperm2, W, + n_col-nempty_col, n_col-nempty_col)) ; + + /* Note that the empty columns remain at the end of Cperm_init */ + for (k = 0 ; k < n_col - nempty_col ; k++) + { + W [k] = Cperm_init [Cperm2 [k]] ; + } + + for (k = 0 ; k < n_col - nempty_col ; k++) + { + Cperm_init [k] = W [k] ; + } + + ASSERT (UMF_is_permutation (Cperm_init, W, n_col, n_col)) ; + + } + + /* ---------------------------------------------------------------------- */ + /* determine the size of the Symbolic object */ + /* ---------------------------------------------------------------------- */ + + nchains = 0 ; + for (i = 0 ; i < nfr ; i++) + { + if (SWork->Front_parent [i] != i+1) + { + nchains++ ; + } + } + + Symbolic->nchains = nchains ; + Symbolic->nfr = nfr ; + + /* final size of Symbolic object */ + Info [UMFPACK_SYMBOLIC_SIZE] = + UMF_symbolic_usage (n_row, n_col, nchains, nfr) ; + + /* actual peak memory usage for UMFPACK_symbolic (actual nfr, nchains) */ + Info [UMFPACK_SYMBOLIC_PEAK_MEMORY] = + SYM_WORK_USAGE (n_col, Clen) + Info [UMFPACK_SYMBOLIC_SIZE] ; + Symbolic->peak_sym_usage = Info [UMFPACK_SYMBOLIC_PEAK_MEMORY] ; + + DEBUG0 (("Number of fronts: "ID"\n", nfr)) ; + + /* ---------------------------------------------------------------------- */ + /* allocate the second part of the Symbolic object (Front_*, Chain_*) */ + /* ---------------------------------------------------------------------- */ + + /* Note that Symbolic->Front_* does include the dummy placeholder front */ + Symbolic->Front_npivcol = (Int *) UMF_malloc (nfr+1, sizeof (Int)) ; + Symbolic->Front_parent = (Int *) UMF_malloc (nfr+1, sizeof (Int)) ; + Symbolic->Front_1strow = (Int *) UMF_malloc (nfr+1, sizeof (Int)) ; + Symbolic->Front_leftmostdesc = (Int *) UMF_malloc (nfr+1, sizeof (Int)) ; + + Symbolic->Chain_start = (Int *) UMF_malloc (nchains+1, sizeof (Int)) ; + Symbolic->Chain_maxrows = (Int *) UMF_malloc (nchains+1, sizeof (Int)) ; + Symbolic->Chain_maxcols = (Int *) UMF_malloc (nchains+1, sizeof (Int)) ; + + if (!Symbolic->Front_npivcol || !Symbolic->Front_parent || + !Symbolic->Front_1strow || !Symbolic->Front_leftmostdesc || + !Symbolic->Chain_start || !Symbolic->Chain_maxrows || + !Symbolic->Chain_maxcols) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + error (&Symbolic, SWork) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + DEBUG0 (("(2)Symbolic UMF_malloc_count - init_count = "ID"\n", + UMF_malloc_count - init_count)) ; + ASSERT (UMF_malloc_count == init_count + 16) ; + + Front_npivcol = Symbolic->Front_npivcol ; + Front_parent = Symbolic->Front_parent ; + Front_1strow = Symbolic->Front_1strow ; + Front_leftmostdesc = Symbolic->Front_leftmostdesc ; + + Chain_start = Symbolic->Chain_start ; + Chain_maxrows = Symbolic->Chain_maxrows ; + Chain_maxcols = Symbolic->Chain_maxcols ; + + /* ---------------------------------------------------------------------- */ + /* find row degrees and assign rows to fronts */ + /* ---------------------------------------------------------------------- */ + + /* Use SWork->Ci as temporary workspace for Row_degree, InFront, and F1 */ + Row_degree = SWork->Ci ; /* [ of size n_row */ + InFront = Row_degree + n_row ; /* [ of size n_row */ + F1 = InFront + n_row ; /* [ of size nfr+1 */ + ASSERT (Clen >= 2*n_row + nfr+1) ; + for (row = 0 ; row < n_row ; row++) + { + Row_degree [row] = 0 ; + InFront [row] = nfr ; /* empty rows go to dummy front nfr */ + } + + newj = 0 ; + k = 0 ; + for (i = 0 ; i < nfr ; i++) + { + fpivcol = SWork->Front_npivcol [i] ; + DEBUG1 (("Front "ID" k "ID" npivcol "ID" nrows "ID" ncols "ID"\n", + i, k, fpivcol, SWork->Front_nrows [i], SWork->Front_ncols [i])) ; + k += fpivcol ; + + /* copy Front info into Symbolic object from SWork */ + Front_npivcol [i] = fpivcol ; + Front_parent [i] = SWork->Front_parent [i] ; + + f1rows = 0 ; + + /* for all pivot columns in front i */ + for (kk = 0 ; kk < fpivcol ; kk++, newj++) + { + j = Cperm_init [newj] ; + ASSERT (IMPLIES (newj >= n_col-nempty_col, Ap [j+1] - Ap [j] == 0)); + for (p = Ap [j] ; p < Ap [j+1] ; p++) + { + row = Ai [p] ; + if (Row_degree [row] == 0) + { + /* this row belongs to front i */ + DEBUG1 ((" Row "ID" in Front "ID"\n", row, i)) ; + InFront [row] = i ; + f1rows++ ; + } + Row_degree [row]++ ; + } + } + Front_1strow [i] = f1rows ; + } + + /* assign empty columns to dummy placehold front nfr */ + DEBUG1 (("Dummy Cols in Front "ID" :: "ID"\n", nfr, n_col-k)) ; + Front_npivcol [nfr] = n_col - k ; + Front_parent [nfr] = EMPTY ; + + /* ---------------------------------------------------------------------- */ + /* find initial row permutation */ + /* ---------------------------------------------------------------------- */ + + /* determine the first row in each front (in the new row ordering) */ + k = 0 ; + for (i = 0 ; i < nfr ; i++) + { + f1rows = Front_1strow [i] ; + DEBUG1 (("Front "ID" :: npivcol "ID" parent "ID, + i, Front_npivcol [i], Front_parent [i])) ; + DEBUG1 ((" 1st rows in Front "ID" : "ID"\n", i, f1rows)) ; + Front_1strow [i] = k ; + k += f1rows ; + } + + /* assign empty rows to dummy placehold front nfr */ + DEBUG1 (("Rows in Front "ID" (dummy): "ID"\n", nfr, n_row-k)) ; + Front_1strow [nfr] = k ; + DEBUG1 (("nfr "ID" 1strow[nfr] "ID" nrow "ID"\n", nfr, k, n_row)) ; + + for (i = 0 ; i <= nfr ; i++) + { + F1 [i] = Front_1strow [i] ; + } + + for (row = 0 ; row < n_row ; row++) + { + i = InFront [row] ; + newrow = F1 [i]++ ; + Rperm_init [newrow] = row ; + } + Rperm_init [n_row] = EMPTY ; /* unused */ + +#ifndef NDEBUG + for (k = 0 ; k < n_row ; k++) + { + DEBUG2 (("Rperm_init ["ID"] = "ID"\n", k, Rperm_init [k])) ; + } +#endif + + /* ] done using F1 */ + /* ] done using InFront */ + + /* ---------------------------------------------------------------------- */ + /* find the leftmost descendant of each front */ + /* ---------------------------------------------------------------------- */ + + for (i = 0 ; i <= nfr ; i++) + { + Front_leftmostdesc [i] = EMPTY ; + } + + for (i = 0 ; i < nfr ; i++) + { + /* start at i and walk up the tree */ + DEBUG2 (("Walk up front tree from "ID"\n", i)) ; + j = i ; + while (j != EMPTY && Front_leftmostdesc [j] == EMPTY) + { + DEBUG3 ((" Leftmost desc of "ID" is "ID"\n", j, i)) ; + Front_leftmostdesc [j] = i ; + j = Front_parent [j] ; + DEBUG3 ((" go to j = "ID"\n", j)) ; + } + } + + /* ---------------------------------------------------------------------- */ + /* compute memory and flop estimates */ + /* ---------------------------------------------------------------------- */ + + nchains = 0 ; /* number of chains */ + Chain_start [0] = 0 ; /* front 0 starts a new chain */ + maxrows = 1 ; + maxcols = 1 ; + maxfrsize = 1 ; + dmaxfrsize = 1 ; + chain_npiv = 0 ; /* number of pivots in current chain */ + + /* ---------------------------------------------------------------------- */ + /* simulate UMF_kernel_init */ + /* ---------------------------------------------------------------------- */ + + /* Numeric->Memory usage estimate, in Units */ + head_usage = 1 ; /* head marker (see UMF_mem_init_memoryspace) */ + init_tail_usage = 2 ; /* tail marker (see UMF_mem_init_memoryspace) */ + dusage = 3 ; /* head and tail markers */ + dhead_usage = 1 ; + + /* elements and tuples at tail*/ + init_tail_usage += UMF_kernel_init_usage (Ap, Row_degree, n_row, n_col, + &dusage) ; + + /* ] done using Row_degree */ + + ASSERT (UMF_is_permutation (Rperm_init, SWork->Ci, n_row, n_row)) ; + + tail_usage = init_tail_usage ; + DEBUG2 (("tail_usage: "ID" (initial)\n", tail_usage)) ; + max_usage = head_usage + init_tail_usage ; + dmax_usage = dusage ; + + Symbolic->num_mem_init_usage = max_usage ; + + too_large = INT_OVERFLOW (dusage * sizeof (Unit)) ; + if (too_large) + { + /* Initial memory usage, for input matrix only, */ + /* has encountered integer overflow. This is an error */ + /* condition, but keep going to compute other statistics. */ + Info [UMFPACK_VARIABLE_INIT_ESTIMATE] = dusage ; /* too large */ + } + else + { + Info [UMFPACK_VARIABLE_INIT_ESTIMATE] = (double) max_usage ; + } + + /* ---------------------------------------------------------------------- */ + /* simulate UMF_kernel */ + /* ---------------------------------------------------------------------- */ + + /* Use SWork->Ci as temporary workspace for link lists */ + Link = SWork->Ci ; + for (i = 0 ; i < nfr ; i++) + { + Link [i] = EMPTY ; + } + + dlnz = MIN (n_row, n_col) ; /* upper limit of nz in L (incl diag) */ + dunz = dlnz ; /* upper limit of nz in U (incl diag) */ + flops = 0 ; /* flop count upper bound */ + + DEBUG1 (("Umfpack symbolic, fronts: nfr = "ID"\n", nfr)) ; + + for (i = 0 ; i < nfr ; i++) + { + + fpivcol = Front_npivcol [i] ; /* # candidate pivot columns */ + fallrows = SWork->Front_nrows [i] ; /* all rows (not just Schur comp*/ + fallcols = SWork->Front_ncols [i] ; /* all cols (not just Schur comp*/ + parent = Front_parent [i] ; /* parent in column etree */ + + DEBUG1 (("\nFront "ID" fpivcol "ID" fallrows "ID" fallcols "ID"\n", + i, fpivcol, fallrows, fallcols)) ; + + /* determine the max size of the contribution block */ + fpiv = MIN (fpivcol, fallrows) ; /* # pivot rows and cols */ + frows = fallrows - fpiv ; /* max # rows in Schur comp. */ + fcols = fallcols - fpiv ; /* max # cols in Schur comp. */ + + DEBUG1 ((" "ID" : fpiv "ID" frows "ID" fcols "ID" parent "ID"\n", + i, fpiv, frows, fcols, parent)) ; + + /* UMF_analyze can generate 0-by-c sized frontal matrices */ + ASSERT (fpiv >= 0 && fcols >= 0 && frows >= 0) ; + + /* assemble all children of front i in column etree */ + for (child = Link [i] ; child != EMPTY ; child = Link [child]) + { + ASSERT (child >= 0 && child < i) ; + ASSERT (Front_parent [child] == i) ; + /* free the child element */ + cp = MIN (Front_npivcol [child], SWork->Front_nrows [child]); + cr = SWork->Front_nrows [child] - cp ; + cc = SWork->Front_ncols [child] - cp ; + ASSERT (cp >= 0 && cr >= 0 && cc >= 0) ; + tail_usage -= GET_ELEMENT_SIZE (cr, cc) + 1 ; + dusage -= DGET_ELEMENT_SIZE (cr, cc) + 1 ; + /* remove it from tuple lists */ + tail_usage -= (cr + cc) * UNITS (Tuple, 1) ; + dusage -= ((double) cr + (double) cc) * UNITS (Tuple, 1) ; + DEBUG2 (("tail_usage: "ID" (assembled "ID" of size "ID")\n", + tail_usage, child, + GET_ELEMENT_SIZE (cr, cc) + 1 + (cr + cc) * UNITS (Tuple, 1))) ; + } + + /* the flop count excludes the BLAS2 calls during pivot search */ + /* the assembly required to search a column, and assembly between */ + /* frontal matrices. Those are "mushy" flops. */ + /* The flop count computed here is "canonical". */ + + /* factorize the frontal matrix */ + f = (double) fpiv ; /* # of pivots */ + r = (double) frows ; /* # rows in Schur complement */ + c = (double) fcols ; /* # cols in Schur complement */ + flops += DIV_FLOPS * (f*r + (f-1)*f/2) /* scale pivot columns */ + /* f outer products: */ + + MULTSUB_FLOPS * (f*r*c + (r+c)*(f-1)*f/2 + (f-1)*f*(2*f-1)/6) ; + + /* count nonzeros in L and U */ + lf = (fpiv*fpiv-fpiv)/2 + fpiv*frows ; /* nz in L below diagonal */ + uf = (fpiv*fpiv-fpiv)/2 + fpiv*fcols ; /* nz in U above diagonal */ + + /* store the f columns of L and f rows of U */ + head_usage += + UNITS (Entry, lf + uf) /* numerical values (excl diagonal) */ + + UNITS (Int, frows + fcols + fpiv) ; /* indices (compressed) */ + + /* count nonzeros and memory usage in double precision */ + dlf = (f*f-f)/2 + f*r ; /* nz in L below diagonal */ + duf = (f*f-f)/2 + f*c ; /* nz in U above diagonal */ + dlnz += dlf ; + dunz += duf ; + dh = + DUNITS (Entry, dlf + duf) /* numerical values (excl diagonal) */ + + DUNITS (Int, r + c + f) ; /* indices (compressed) */ + dusage += dh ; + dhead_usage += dh ; + + if (parent != EMPTY) + { + /* create new element */ + tail_usage += GET_ELEMENT_SIZE (frows, fcols) + 1 ; + dusage += DGET_ELEMENT_SIZE (frows, fcols) + 1 ; + + /* place new element in tuple lists */ + tail_usage += (frows + fcols) * UNITS (Tuple, 1) ; + dusage += (r + c) * UNITS (Tuple, 1) ; + DEBUG2 (("tail_usage: "ID" (create "ID" of size "ID")\n", + tail_usage, i, + GET_ELEMENT_SIZE (frows, fcols) + 1 + + (frows + fcols) * UNITS (Tuple, 1))) ; + + /* place in link list of parent */ + Link [i] = Link [parent] ; + Link [parent] = i ; + } + + /* keep track of peak Numeric->Memory usage */ + max_usage = MAX (max_usage, head_usage + tail_usage) ; + + /* max_usage may encounter integer overflow, so dmax_usage also kept. */ + /* account for possible roundoff errors in dusage. */ + /* Ignore NaN case. */ + dusage *= (1.0 + MAX_EPSILON) ; + dusage = MAX (dusage, + (double) (head_usage + tail_usage) * (1.0 + MAX_EPSILON)) ; + dmax_usage = MAX (dmax_usage, dusage) ; + dhead_usage *= (1.0 + MAX_EPSILON) ; + dhead_usage = MAX (dhead_usage, + (double) head_usage * (1.0 + MAX_EPSILON)) ; + + /* at most nb or chain_npiv pending pivots from old fronts */ + maxoldpiv = MIN (nb, chain_npiv) ; + maxrows = MAX (maxrows, maxoldpiv + fallrows) ; + maxcols = MAX (maxcols, maxoldpiv + fallcols) ; + + DEBUG1 ((" ID: chain_npiv "ID" fpiv "ID" fallrows "ID, + i, chain_npiv, fpiv, fallrows)) ; + DEBUG1 ((" fallcols "ID" maxoldpiv "ID" maxrows "ID" maxcols "ID"\n", + fallcols, maxoldpiv, maxrows, maxcols)) ; + + chain_npiv += fpiv ; + if (parent != i+1) + { + /* this is the end of a chain */ + Chain_maxrows [nchains] = maxrows ; + Chain_maxcols [nchains] = maxcols ; + /* Ignore NaN case. */ + dmaxfrsize = MAX (dmaxfrsize, (double) maxrows * (double) maxcols) ; + maxfrsize = MAX (maxfrsize, maxrows * maxcols) ; + nchains++ ; + Chain_start [nchains] = i+1 ; + maxrows = 1 ; + maxcols = 1 ; + chain_npiv = 0 ; + } + } + + dhead_usage = ceil (dhead_usage) ; + dmax_usage = ceil (dmax_usage) ; + + tail_usage -= init_tail_usage ; + + /* all tuples and elements are now deallocated */ + DEBUG0 (("final tail_usage: "ID"\n", tail_usage)) ; + ASSERT (tail_usage == 0) ; + + DEBUG1 (("dmaxfrsize %30.20g Int_MAX %30d\n", dmaxfrsize, Int_MAX)) ; + + /* check if the frontal matrix is too big */ + too_large = too_large || INT_OVERFLOW (dmaxfrsize * sizeof (Entry)) ; + + /* ---------------------------------------------------------------------- */ + /* find the biggest frontal matrix, for all chains */ + /* ---------------------------------------------------------------------- */ + + maxnrows = 1 ; + maxncols = 1 ; + for (i = 0 ; i < nchains ; i++) + { + maxnrows = MAX (maxnrows, Chain_maxrows [i]) ; + maxncols = MAX (maxncols, Chain_maxcols [i]) ; + } + + /* information to keep for numeric factorization */ + Symbolic->maxfrsize = maxfrsize ; + Symbolic->maxnrows = maxnrows ; + Symbolic->maxncols = maxncols ; + Symbolic->num_mem_usage_est = dmax_usage ; + Symbolic->num_mem_size_est = dhead_usage ; + + /* ---------------------------------------------------------------------- */ + /* estimate total memory usage in UMFPACK_numeric */ + /* ---------------------------------------------------------------------- */ + + UMF_set_stats ( + Info, + Symbolic, + dmax_usage, /* estimated peak size of Numeric->Memory */ + dhead_usage, /* estimated final size of Numeric->Memory */ + flops, /* estimated "true flops" */ + dlnz, /* estimated nz in L */ + dunz, /* estimated nz in U */ + dmaxfrsize, /* estimated largest front size */ + (double) n_col, /* worst case Numeric->Upattern size */ + (double) n_inner, /* max possible pivots to be found */ + ESTIMATE) ; + + /* ---------------------------------------------------------------------- */ + +#ifndef NDEBUG + for (i = 0 ; i < nchains ; i++) + { + DEBUG2 (("Chain "ID" start "ID" end "ID" maxrows "ID" maxcols "ID"\n", + i, Chain_start [i], Chain_start [i+1] - 1, + Chain_maxrows [i], Chain_maxcols [i])) ; + UMF_dump_chain (Chain_start [i], + SWork->Front_parent, + SWork->Front_npivcol, + SWork->Front_nrows, + SWork->Front_ncols, + nfr) ; + } + fpivcol = 0 ; + for (i = 0 ; i < nfr ; i++) + { + fpivcol = MAX (fpivcol, Front_npivcol [i]) ; + } + DEBUG0 (("Max pivot cols in any front: "ID"\n", fpivcol)) ; + DEBUG1 (("Largest front: maxnrows "ID" maxncols "ID" maxfrsize "ID"\n", + maxnrows, maxncols, maxfrsize)) ; + DEBUG1 (("(savings "ID") nchains "ID"\n", + (maxnrows*maxncols) - maxfrsize, nchains)) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* is the problem too large? */ + /* ---------------------------------------------------------------------- */ + + if (too_large) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_problem_too_large ; + error (&Symbolic, SWork) ; + return (UMFPACK_ERROR_problem_too_large) ; + } + + /* ---------------------------------------------------------------------- */ + /* UMFPACK_symbolic was successful, return the object handle */ + /* ---------------------------------------------------------------------- */ + + Symbolic->valid = SYMBOLIC_VALID ; + *SymbolicHandle = (void *) Symbolic ; + + /* ---------------------------------------------------------------------- */ + /* free workspace */ + /* ---------------------------------------------------------------------- */ + + free_work (SWork) ; + /* Symbolic contains 10 objects */ + DEBUG0 (("(3)Symbolic UMF_malloc_count - init_count = "ID"\n", + UMF_malloc_count - init_count)) ; + ASSERT (UMF_malloc_count == init_count + 10) ; + + /* ---------------------------------------------------------------------- */ + /* get the time used by UMFPACK_*symbolic */ + /* ---------------------------------------------------------------------- */ + + tend = umfpack_timer ( ) ; + Info [UMFPACK_SYMBOLIC_TIME] = MAX (0, tend - tstart) ; + + return (UMFPACK_OK) ; +} + + +/* ========================================================================== */ +/* === free_work ============================================================ */ +/* ========================================================================== */ + +PRIVATE void free_work +( + SWorkType *SWork +) +{ + ASSERT (SWork) ; + + SWork->Ci = (Int *) UMF_free ((void *) SWork->Ci) ; + SWork->Front_npivcol = (Int *) UMF_free ((void *) SWork->Front_npivcol) ; + SWork->Front_nrows = (Int *) UMF_free ((void *) SWork->Front_nrows) ; + SWork->Front_ncols = (Int *) UMF_free ((void *) SWork->Front_ncols) ; + SWork->Front_parent = (Int *) UMF_free ((void *) SWork->Front_parent) ; + SWork->Front_cols = (Int *) UMF_free ((void *) SWork->Front_cols) ; + +} + + +/* ========================================================================== */ +/* === error ================================================================ */ +/* ========================================================================== */ + +/* Error return from UMFPACK_symbolic. Free all allocated memory. */ + +PRIVATE void error +( + SymbolicType **Symbolic, + SWorkType *SWork +) +{ + + free_work (SWork) ; + UMFPACK_free_symbolic ((void **) Symbolic) ; + ASSERT (UMF_malloc_count == init_count) ; +} + diff --git a/src/sparse-matrix/umfpack/umfpack_solve.c b/src/sparse-matrix/umfpack/umfpack_solve.c new file mode 100644 index 0000000..9f949f1 --- /dev/null +++ b/src/sparse-matrix/umfpack/umfpack_solve.c @@ -0,0 +1,258 @@ +/* ========================================================================== */ +/* === UMFPACK_solve ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Solves a linear system using the numerical factorization + computed by UMFPACK_numeric. See umfpack_solve.h for more details. + + For umfpack_*_solve: + Dynamic memory usage: UMFPACK_solve calls UMF_malloc twice, for + workspace of size c*n*sizeof(double) + n*sizeof(Int), where c is + defined below. On return, all of this workspace is free'd via UMF_free. + + For umfpack_*_wsolve: + Pattern is a workspace of size n Integers. The double array W must be + at least of size c*n, where c is defined below. + + If iterative refinement is requested, and Ax=b, A'x=b or A.'x=b is being + solved, and the matrix A is not singular, then c is 5 for the real version + and 10 for the complex version. Otherwise, c is 1 for the real version and + 4 for the complex version. + +*/ + +#include "umf_internal.h" +#include "umf_valid_numeric.h" +#include "umf_solve.h" + +#ifndef WSOLVE +#include "umf_malloc.h" +#include "umf_free.h" +#ifndef NDEBUG +PRIVATE Int init_count ; +#endif +#endif + +GLOBAL Int +#ifdef WSOLVE +UMFPACK_wsolve +#else +UMFPACK_solve +#endif +( + Int sys, + const Int Ap [ ], + const Int Ai [ ], + const double Ax [ ], +#ifdef COMPLEX + const double Az [ ], +#endif + double Xx [ ], +#ifdef COMPLEX + double Xz [ ], +#endif + const double Bx [ ], +#ifdef COMPLEX + const double Bz [ ], +#endif + void *NumericHandle, + const double Control [UMFPACK_CONTROL], + double User_Info [UMFPACK_INFO] +#ifdef WSOLVE + , Int Pattern [ ], + double W [ ] +#endif +) +{ + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + NumericType *Numeric ; + Int n, i, irstep, status ; + double Info2 [UMFPACK_INFO], *Info, tstart, tend ; +#ifndef WSOLVE + Int *Pattern, wsize ; + double *W ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get the amount of time used by the process so far */ + /* ---------------------------------------------------------------------- */ + + tstart = umfpack_timer ( ) ; + +#ifndef WSOLVE +#ifndef NDEBUG + init_count = UMF_malloc_count ; +#endif +#endif + + /* ---------------------------------------------------------------------- */ + /* get parameters */ + /* ---------------------------------------------------------------------- */ + + if (!Control) + { + irstep = UMFPACK_DEFAULT_IRSTEP ; + } + else if (SCALAR_IS_NAN (Control [UMFPACK_IRSTEP])) + { + irstep = UMFPACK_DEFAULT_IRSTEP ; + } + else + { + irstep = (Int) Control [UMFPACK_IRSTEP] ; + } + + if (User_Info) + { + /* return Info in user's array */ + Info = User_Info ; + for (i = UMFPACK_IR_TAKEN ; i <= UMFPACK_SOLVE_TIME ; i++) + { + Info [i] = EMPTY ; + } + } + else + { + /* no Info array passed - use local one instead */ + Info = Info2 ; + for (i = 0 ; i < UMFPACK_INFO ; i++) + { + Info [i] = EMPTY ; + } + } + + Info [UMFPACK_STATUS] = UMFPACK_OK ; + Info [UMFPACK_SOLVE_FLOPS] = 0 ; + + Numeric = (NumericType *) NumericHandle ; + if (!UMF_valid_numeric (Numeric)) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_Numeric_object ; + return (UMFPACK_ERROR_invalid_Numeric_object) ; + } + + Info [UMFPACK_NROW] = Numeric->n_row ; + Info [UMFPACK_NCOL] = Numeric->n_col ; + + if (Numeric->n_row != Numeric->n_col) + { + /* only square systems can be handled */ + Info [UMFPACK_STATUS] = UMFPACK_ERROR_invalid_system ; + return (UMFPACK_ERROR_invalid_system) ; + } + n = Numeric->n_row ; + if (Numeric->nnzpiv < n + || SCALAR_IS_ZERO (Numeric->rcond) || SCALAR_IS_NAN (Numeric->rcond)) + { + /* turn off iterative refinement if A is singular */ + /* or if U has NaN's on the diagonal. */ + irstep = 0 ; + } + + if (!Xx || !Bx +#ifdef COMPLEX + || !Xz || !Bz +#endif + ) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ; + return (UMFPACK_ERROR_argument_missing) ; + } + + if (sys >= UMFPACK_Pt_L) + { + /* no iterative refinement except for nonsingular Ax=b, A'x=b, A.'x=b */ + irstep = 0 ; + } + + /* ---------------------------------------------------------------------- */ + /* allocate or check the workspace */ + /* ---------------------------------------------------------------------- */ + +#ifdef WSOLVE + + if (!W || !Pattern) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_argument_missing ; + return (UMFPACK_ERROR_argument_missing) ; + } + +#else + +#ifdef COMPLEX + if (irstep > 0) + { + wsize = 10*n ; /* W, X, Z, S, Y, B2 */ + } + else + { + wsize = 4*n ; /* W, X */ + } +#else + if (irstep > 0) + { + wsize = 5*n ; /* W, Z, S, Y, B2 */ + } + else + { + wsize = n ; /* W */ + } +#endif + + Pattern = (Int *) UMF_malloc (n, sizeof (Int)) ; + W = (double *) UMF_malloc (wsize, sizeof (double)) ; + if (!W || !Pattern) + { + Info [UMFPACK_STATUS] = UMFPACK_ERROR_out_of_memory ; + (void) UMF_free ((void *) W) ; + (void) UMF_free ((void *) Pattern) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + +#endif /* WSOLVE */ + + /* ---------------------------------------------------------------------- */ + /* solve the system */ + /* ---------------------------------------------------------------------- */ + + status = UMF_solve (sys, Ap, Ai, Ax, Xx, Bx, +#ifdef COMPLEX + Az, Xz, Bz, +#endif + Numeric, irstep, Info, Pattern, W) ; + + /* ---------------------------------------------------------------------- */ + /* free the workspace (if allocated) */ + /* ---------------------------------------------------------------------- */ + +#ifndef WSOLVE + (void) UMF_free ((void *) W) ; + (void) UMF_free ((void *) Pattern) ; + ASSERT (UMF_malloc_count == init_count) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* get the time used by UMFPACK_*solve */ + /* ---------------------------------------------------------------------- */ + + Info [UMFPACK_STATUS] = status ; + if (status >= 0) + { + tend = umfpack_timer ( ) ; + Info [UMFPACK_SOLVE_TIME] = MAX (0, tend - tstart) ; + } + + return (status) ; +} + diff --git a/src/sparse-matrix/umfpack/umfpack_symbolic.c b/src/sparse-matrix/umfpack/umfpack_symbolic.c new file mode 100644 index 0000000..e51a0eb --- /dev/null +++ b/src/sparse-matrix/umfpack/umfpack_symbolic.c @@ -0,0 +1,34 @@ +/* ========================================================================== */ +/* === UMFPACK_symbolic ===================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Performs a symbolic factorization. + See umfpack_symbolic.h for details. +*/ + +#include "umf_internal.h" + +GLOBAL Int UMFPACK_symbolic +( + Int n_row, + Int n_col, + const Int Ap [ ], + const Int Ai [ ], + void **SymbolicHandle, + const double Control [UMFPACK_CONTROL], + double Info [UMFPACK_INFO] +) +{ + Int *Qinit = (Int *) NULL ; + return (UMFPACK_qsymbolic (n_row, n_col, Ap, Ai, Qinit, SymbolicHandle, + Control, Info)) ; +} + diff --git a/src/sparse-matrix/umfpack/umfpack_timer.c b/src/sparse-matrix/umfpack/umfpack_timer.c new file mode 100644 index 0000000..29c9b5d --- /dev/null +++ b/src/sparse-matrix/umfpack/umfpack_timer.c @@ -0,0 +1,70 @@ +/* ========================================================================== */ +/* === umfpack_timer ======================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + User-callable. Returns the time in seconds used by the process. BE + CAREFUL: if you compare the run time of UMFPACK with other sparse matrix + packages, be sure to use the same timer. See umfpack_timer.h for details. +*/ + +#ifdef GETRUSAGE + +/* -------------------------------------------------------------------------- */ +/* use getrusage for accurate process times (and no overflow) */ +/* -------------------------------------------------------------------------- */ + +/* + This works under Solaris, SGI Irix, Linux, IBM RS 6000 (AIX), and Compaq + Alpha. It might work on other Unix systems, too. Includes both the "user + time" and the "system time". The system time is the time spent by the + operating system on behalf of the process, and thus should be charged to + the process. +*/ + +#include <sys/time.h> +#include <sys/resource.h> + +double umfpack_timer ( void ) +{ + struct rusage ru ; + double user_time, sys_time ; + + (void) getrusage (RUSAGE_SELF, &ru) ; + + user_time = + ru.ru_utime.tv_sec /* user time (seconds) */ + + 1e-6 * ru.ru_utime.tv_usec ; /* user time (microseconds) */ + + sys_time = + ru.ru_stime.tv_sec /* system time (seconds) */ + + 1e-6 * ru.ru_stime.tv_usec ; /* system time (microseconds) */ + + return (user_time + sys_time) ; +} + +#else + +/* -------------------------------------------------------------------------- */ +/* Generic ANSI C: use the ANSI clock function */ +/* -------------------------------------------------------------------------- */ + +/* This is portable, but may overflow. On Sun Solaris, when compiling in */ +/* 32-bit mode, the overflow occurs in only 2147 seconds (about 36 minutes). */ + +#include <time.h> + +double umfpack_timer ( void ) +{ + return (((double) (clock ( ))) / ((double) (CLOCKS_PER_SEC))) ; +} + +#endif + diff --git a/src/sparse-matrix/umfpack/umfpack_transpose.c b/src/sparse-matrix/umfpack/umfpack_transpose.c new file mode 100644 index 0000000..db50e36 --- /dev/null +++ b/src/sparse-matrix/umfpack/umfpack_transpose.c @@ -0,0 +1,113 @@ +/* ========================================================================== */ +/* === UMFPACK_transpose ==================================================== */ +/* ========================================================================== */ + +/* -------------------------------------------------------------------------- */ +/* UMFPACK Version 4.0 (Apr 11, 2002), Copyright (c) 2002 by Timothy A. */ +/* Davis. All Rights Reserved. See README for License. */ +/* email: davis@cise.ufl.edu CISE Department, Univ. of Florida. */ +/* web: http://www.cise.ufl.edu/research/sparse/umfpack */ +/* -------------------------------------------------------------------------- */ + +/* + + User callable. Computes a permuted transpose, R = (A (P,Q))' in MATLAB + notation. See umfpack_transpose.h for details. A and R can be rectangular. + The matrix A may be singular. + The complex version can do transpose (') or array transpose (.'). + + Dynamic memory usage: + + A single call to UMF_malloc is made, for a workspace of size + max (n_row,n_col,1) * sizeof(Int). This is then free'd on return, + via UMF_free. + +*/ + +#include "umf_internal.h" +#include "umf_transpose.h" +#include "umf_malloc.h" +#include "umf_free.h" + +#ifndef NDEBUG +PRIVATE Int init_count ; +#endif + +/* ========================================================================== */ + +GLOBAL Int UMFPACK_transpose +( + Int n_row, + 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 */ +#ifdef COMPLEX + const double Az [ ], /* size nz, if present */ +#endif + + 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 n_col, if present */ + + Int Rp [ ], /* size n_row+1 */ + Int Ri [ ], /* size nz */ + double Rx [ ] /* size nz, if present */ +#ifdef COMPLEX + , double Rz [ ] /* size nz, if present */ + , Int do_conjugate /* if true, then to conjugate transpose */ + /* otherwise, do array transpose */ +#endif +) +{ + + /* ---------------------------------------------------------------------- */ + /* local variables */ + /* ---------------------------------------------------------------------- */ + + Int status, *W, nn ; + +#ifndef NDEBUG + init_count = UMF_malloc_count ; + UMF_dump_start ( ) ; +#endif + + /* ---------------------------------------------------------------------- */ + /* allocate workspace */ + /* ---------------------------------------------------------------------- */ + + nn = MAX (n_row, n_col) ; + nn = MAX (nn, 1) ; + W = (Int *) UMF_malloc (nn, sizeof (Int)) ; + if (!W) + { + ASSERT (UMF_malloc_count == init_count) ; + return (UMFPACK_ERROR_out_of_memory) ; + } + ASSERT (UMF_malloc_count == init_count + 1) ; + + /* ---------------------------------------------------------------------- */ + /* C = (A (P,Q))' or (A (P,Q)).' */ + /* ---------------------------------------------------------------------- */ + + status = UMF_transpose (n_row, n_col, Ap, Ai, Ax, P, Q, n_col, Rp, Ri, Rx, + W, TRUE +#ifdef COMPLEX + , Az, Rz, do_conjugate +#endif + ) ; + + /* ---------------------------------------------------------------------- */ + /* free the workspace */ + /* ---------------------------------------------------------------------- */ + + (void) UMF_free ((void *) W) ; + ASSERT (UMF_malloc_count == init_count) ; + + return (status) ; +} + diff --git a/src/sparse-matrix/umfpack/wrap_umf_ltsolve.c b/src/sparse-matrix/umfpack/wrap_umf_ltsolve.c new file mode 100644 index 0000000..e9642bd --- /dev/null +++ b/src/sparse-matrix/umfpack/wrap_umf_ltsolve.c @@ -0,0 +1,2 @@ +#define CONJUGATE_SOLVE +#include "umf_ltsolve.c" diff --git a/src/sparse-matrix/umfpack/wrap_umf_utsolve.c b/src/sparse-matrix/umfpack/wrap_umf_utsolve.c new file mode 100644 index 0000000..37e5c0a --- /dev/null +++ b/src/sparse-matrix/umfpack/wrap_umf_utsolve.c @@ -0,0 +1,2 @@ +#define CONJUGATE_SOLVE +#include "umf_utsolve.c" diff --git a/src/sparse-matrix/umfpack/wrap_umfpack_wsolve.c b/src/sparse-matrix/umfpack/wrap_umfpack_wsolve.c new file mode 100644 index 0000000..c02853a --- /dev/null +++ b/src/sparse-matrix/umfpack/wrap_umfpack_wsolve.c @@ -0,0 +1,2 @@ +#define WSOLVE +#include "umfpack_solve.c" |