Mercurial > hg > octave-nkf
diff liboctave/sparse-base-chol.cc @ 5506:b4cfbb0ec8c4
[project @ 2005-10-23 19:09:32 by dbateman]
author | dbateman |
---|---|
date | Sun, 23 Oct 2005 19:09:33 +0000 |
parents | |
children | e67d027ff4e3 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/liboctave/sparse-base-chol.cc @@ -0,0 +1,304 @@ +/* + +Copyright (C) 2005 David Bateman +Copyright (C) 1998-2005 Andy Adler + +Octave is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +Octave is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. + +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "sparse-base-chol.h" +#include "sparse-util.h" +#include "lo-error.h" +#include "oct-sparse.h" +#include "oct-spparms.h" +#include "quit.h" +#include "SparseType.h" + +// Can't use CHOLMOD_NAME(drop)(0.0, S, cm). It doesn't treat complex matrices +template <class chol_type, class chol_elt, class p_type> +void +sparse_base_chol<chol_type, chol_elt, p_type>::sparse_base_chol_rep::drop_zeros + (const cholmod_sparse* S) +{ + chol_elt sik; + octave_idx_type *Sp, *Si; + chol_elt *Sx; + octave_idx_type pdest, k, ncol, p, pend; + + if (S == NULL) + return; + + Sp = static_cast<octave_idx_type *>(S->p); + Si = static_cast<octave_idx_type *>(S->i); + Sx = static_cast<chol_elt *>(S->x); + pdest = 0; + ncol = S->ncol; + + for (k = 0; k < ncol; k++) + { + p = Sp [k]; + pend = Sp [k+1]; + Sp [k] = pdest; + for (; p < pend; p++) + { + sik = Sx [p]; + if (CHOLMOD_IS_NONZERO (sik)) + { + if (p != pdest) + { + Si [pdest] = Si [p]; + Sx [pdest] = sik; + } + pdest++; + } + } + } + Sp [ncol] = pdest; +} + +template <class chol_type, class chol_elt, class p_type> +octave_idx_type +sparse_base_chol<chol_type, chol_elt, p_type>::sparse_base_chol_rep::init + (const chol_type& a, bool natural) +{ + octave_idx_type info = 0; +#ifdef HAVE_CHOLMOD + octave_idx_type a_nr = a.rows (); + octave_idx_type a_nc = a.cols (); + + if (a_nr != a_nc) + { + (*current_liboctave_error_handler) + ("SparseCHOL requires square matrix"); + return -1; + } + + cholmod_common *cm = &Common; + + // Setup initial parameters + CHOLMOD_NAME(start) (cm); + cm->prefer_zomplex = FALSE; + + double spu = Voctave_sparse_controls.get_key ("spumoni"); + if (spu == 0.) + { + cm->print = -1; + cm->print_function = NULL; + } + else + { + cm->print = (int)spu + 2; + cm->print_function =&SparseCholPrint; + } + + cm->error_handler = &SparseCholError; + cm->complex_divide = CHOLMOD_NAME(divcomplex); + cm->hypotenuse = CHOLMOD_NAME(hypot); + +#ifdef HAVE_METIS + // METIS 4.0.1 uses malloc and free, and will terminate MATLAB if it runs + // out of memory. Use CHOLMOD's memory guard for METIS, which mxMalloc's + // a huge block of memory (and then immediately mxFree's it) before calling + // METIS + cm->metis_memory = 2.0; + +#if defined(METIS_VERSION) +#if (METIS_VERSION >= METIS_VER(4,0,2)) + // METIS 4.0.2 uses function pointers for malloc and free + METIS_malloc = cm->malloc_memory; + METIS_free = cm->free_memory; + // Turn off METIS memory guard. It is not needed, because mxMalloc will + // safely terminate the mexFunction and free any workspace without killing + // all of MATLAB. + cm->metis_memory = 0.0; +#endif +#endif +#endif + + cm->final_asis = FALSE; + cm->final_super = FALSE; + cm->final_ll = TRUE; + cm->final_pack = TRUE; + cm->final_monotonic = TRUE; + cm->final_resymbol = FALSE; + + cholmod_sparse A; + cholmod_sparse *ac = &A; + double dummy; + ac->nrow = a_nr; + ac->ncol = a_nc; + + ac->p = a.cidx(); + ac->i = a.ridx(); + ac->nzmax = a.nonzero(); + ac->packed = TRUE; + ac->sorted = TRUE; + ac->nz = NULL; +#ifdef IDX_TYPE_LONG + ac->itype = CHOLMOD_LONG; +#else + ac->itype = CHOLMOD_INT; +#endif + ac->dtype = CHOLMOD_DOUBLE; + ac->stype = 1; +#ifdef OCTAVE_CHOLMOD_TYPE + ac->xtype = OCTAVE_CHOLMOD_TYPE; +#else + ac->xtype = CHOLMOD_REAL; +#endif + + if (a_nr < 1) + ac->x = &dummy; + else + ac->x = a.data(); + + // use natural ordering if no q output parameter + if (natural) + { + cm->nmethods = 1 ; + cm->method [0].ordering = CHOLMOD_NATURAL ; + cm->postorder = FALSE ; + } + + cholmod_factor *Lfactor; + BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; + Lfactor = CHOLMOD_NAME(analyze) (ac, cm); + CHOLMOD_NAME(factorize) (ac, Lfactor, cm); + cond = CHOLMOD_NAME(rcond) (Lfactor, cm); + END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; + + minor_p = Lfactor->minor; + is_pd = cm->status == CHOLMOD_OK; + info = (is_pd ? 0 : cm->status); + + BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; + Lsparse = CHOLMOD_NAME(factor_to_sparse) (Lfactor, cm); + END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; + + if (minor_p > 0 && minor_p < a_nr) + { + size_t n1 = a_nr + 1; + Lsparse->p = CHOLMOD_NAME(realloc) (minor_p+1, sizeof(octave_idx_type), + Lsparse->p, &n1, cm); + BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; + CHOLMOD_NAME(reallocate_sparse) + (static_cast<octave_idx_type *>(Lsparse->p)[minor_p], Lsparse, cm); + END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; + Lsparse->ncol = minor_p; + } + + drop_zeros (Lsparse); + + if (! natural) + { + perms.resize (a_nr); + for (octave_idx_type i = 0; i < a_nr; i++) + perms(i) = static_cast<octave_idx_type *>(Lfactor->Perm)[i]; + } + + BEGIN_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; + CHOLMOD_NAME(free_factor) (&Lfactor, cm); + CHOLMOD_NAME(finish) (cm); + CHOLMOD_NAME(print_common) (" ", cm); + END_INTERRUPT_IMMEDIATELY_IN_FOREIGN_CODE; +#else + (*current_liboctave_error_handler) + ("Missing CHOLMOD. Sparse cholesky factorization disabled"); +#endif + return info; +} + +template <class chol_type, class chol_elt, class p_type> +chol_type +sparse_base_chol<chol_type, chol_elt, p_type>::L (void) const +{ +#ifdef HAVE_CHOLMOD + cholmod_sparse *m = rep->L(); + octave_idx_type nc = m->ncol; + octave_idx_type nnz = m->nzmax; + chol_type ret (m->nrow, nc, nnz); + for (octave_idx_type j = 0; j < nc+1; j++) + ret.xcidx(j) = static_cast<octave_idx_type *>(m->p)[j]; + for (octave_idx_type i = 0; i < nnz; i++) + { + ret.xridx(i) = static_cast<octave_idx_type *>(m->i)[i]; + ret.xdata(i) = static_cast<chol_elt *>(m->x)[i]; + } + return ret; +#else + return chol_type(); +#endif +} + +template <class chol_type, class chol_elt, class p_type> +p_type +sparse_base_chol<chol_type, chol_elt, p_type>:: +sparse_base_chol_rep::Q (void) const +{ +#ifdef HAVE_CHOLMOD + octave_idx_type n = Lsparse->nrow; + p_type p (n, n, n); + + for (octave_idx_type i = 0; i < n; i++) + { + p.xcidx(i) = i; + p.xridx(i) = static_cast<int>(perms(i)); + p.xdata(i) = 1; + } + p.xcidx(n) = n; + + return p; +#else + return p_type(); +#endif +} + +template <class chol_type, class chol_elt, class p_type> +chol_type +sparse_base_chol<chol_type, chol_elt, p_type>::inverse (void) const +{ + chol_type retval; +#ifdef HAVE_CHOLMOD + cholmod_sparse *m = rep->L(); + octave_idx_type n = m->ncol; + ColumnVector perms = rep->perm(); + chol_type ret; + double rcond2; + octave_idx_type info; + SparseType mattype (SparseType::Upper); + chol_type linv = L().transpose().inverse(mattype, info, rcond2, 1, 0); + + if (perms.length() == n) + { + p_type Qc = Q(); + retval = Qc * linv.transpose() * linv * Qc.transpose(); + } + else + retval = linv.transpose() * linv; +#endif + return retval; +} + +/* +;;; Local Variables: *** +;;; mode: C++ *** +;;; End: *** +*/