view liboctave/UMFPACK/AMD/OCTAVE/amd.cc @ 5164:57077d0ddc8e

[project @ 2005-02-25 19:55:24 by jwe]
author jwe
date Fri, 25 Feb 2005 19:55:28 +0000
parents
children
line wrap: on
line source

/*

Copyright (C) 2004 David Bateman

This program 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.

This program 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, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

In addition to the terms of the GPL, you are permitted to link
this program with any Open Source program, as defined by the
Open Source Initiative (www.opensource.org)

*/

/*

This is the Octave interface to the UMFPACK AMD code, which bore the following
copyright

 AMD Version 1.1 (Jan. 21, 2004), Copyright (c) 2004 by Timothy A. Davis,
 Patrick R. Amestoy, and Iain S. Duff.  See ../README for License.
 email: davis@cise.ufl.edu    CISE Department, Univ. of Florida.
 web: http://www.cise.ufl.edu/research/sparse/amd
 --------------------------------------------------------------------------

    Acknowledgements: This work was supported by the National Science
       Foundation, under grants ASC-9111263, DMS-9223088, and CCR-0203270.

*/

#include <cstdlib>
#include <string>

#include <octave/config.h>
#include <octave/ov.h>
#include <octave/defun-dld.h>
#include <octave/pager.h>
#include <octave/ov-re-mat.h>

#include "ov-re-sparse.h"
#include "ov-cx-sparse.h"

// External AMD functions in C
extern "C" {
#include "amd.h"
}

DEFUN_DLD (amd, args, nargout,
    "-*- texinfo -*-\n\
@deftypefn {Loadable Function} {@var{p} =} amd (@var{s})\n\
@deftypefnx {Loadable Function} {@var{Control} =} amd ()\n\
@deftypefnx {Loadable Function} {[@var{p}, @var{info}] =} amd (@var{s})\n\
\n\
AMD Approximate minimum degree permutation. Returns the approximate\n\
minimum degree permutation vector for the sparse matrix\n\
@code{@var{c} = @var{S} + @var{S}'}. The Cholesky factorization of\n\
@code{@var{c} (@var{p}, @var{p})}, or @code{@var{s} (@var{p}, @var{p})},\n\
tends to be sparser than that of @var{c} or @var{s}.\n\
@var{s} must be square. If @var{s} is full, @code{amd (@var{S})} is\n\
equivalent to  @code{amd (sparse (@var{s}))}.\n\
\n\
@table @asis\n\
@item @var{Control} (1)\n\
If S is n-by-n, then rows/columns with more than\n\
@code{@dfn{max} (16, (@var{Control} (1)) * @dfn{sqrt} (@var{n}))} entries\n\
in @code{@var{s} + @var{S}'} are considered @emph{dense}, and ignored during\n\
ordering.  They are placed last in the output permutation.  The default is\n\
10.0 if @var{Control} is not present.\n\
@item @var{Control} (2)\n\
If nonzero, then aggressive absorption is performed. This is the default if\n\
@var{Control} is not present.\n\
@item @var{Control} (3)\n\
If nonzero, print statistics about the ordering.\n\
@end table\n\
\n\
@table @asis\n\
@item @var{Info} (1)\n\
status (0: ok, -1: out of memory, -2: matrix invalid)\n\
@item @var{Info} (2)\n\
@code{@var{n} = size (@var{a}, 1)}\n\
@item @var{Info} (3)\n\
@code{nnz (A)}\n\
@item @var{Info} (4)\n\
The symmetry of the matrix @var{s} (0.0 means purely unsymmetric, 1.0 means\n\
purely symmetric).  Computed as: @code{@var{b} = tril (@var{s}, -1) +\n\
triu (@var{s}, 1); @var{symmetry} = nnz (@var{b} & @var{b}')\n\
/ nnz (@var{b});}\n\
@item @var{Info} (5)\n\
@code{nnz (diag (@var{s}))}\n\
@item @var{Info} (6)\n\
@dfn{nnz} in @code{@var{s} + @var{s}'}, excluding the diagonal\n\
(= @code{nnz (@var{b} + @var{b}')})\n\
@item @var{Info} (7)\n\
Number of @emph{dense} rows/columns in @code{@var{s} + @var{s}'}\n\
@item @var{Info} (8)\n\
The amount of memory used by AMD, in bytes\n\
@item @var{Info} (9)\n\
The number of memory compactions performed by AMD\n\
@end table\n\
\n\
The following statistics are slight upper bounds because of the\n\
approximate degree in AMD. The bounds are looser if @emph{dense}\n\
rows/columns are ignored during ordering @code{(@var{Info} (7) > 0)}.\n\
The statistics are for a subsequent factorization of the matrix\n\
@code{@var{c} (@var{p},@var{p})}.  The LU factorization statistics assume\n \
no pivoting.\n\
\n\
@table @asis\n\
@item @var{Info} (10)\n\
The number of nonzeros in L, excluding the diagonal\n\
@item @var{Info} (11)\n\
The number of divide operations for LL', LDL', or LU\n\
@item @var{Info (12)}\n\
The number of multiply-subtract pairs for LL' or LDL'\n\
@item @var{Info} (13)\n\
The number of multiply-subtract pairs for LU\n\
@item @var{Info} (14)\n\
The max number of nonzeros in any column of L (incl. diagonal)\n\
@item @var{Info} (15:20)\n\
unused, reserved for future use\n\
@end table\n\
\n\
An assembly tree post-ordering is performed, which is typically the same\n\
as an elimination tree post-ordering.  It is not always identical because\n\
of the approximate degree update used, and because @emph{dense} rows/columns\n\
do not take part in the post-order.  It well-suited for a subsequent\n\
@dfn{chol}, however.  If you require a precise elimination tree\n\
post-ordering, then do:\n\
\n\
@group\n\
   @var{p} = @dfn{amd} (@var{s});\n\
   % skip this if S already symmetric\n\
   @var{c} = spones (@var{s}) + spones (@var{s}');\n\
   [@var{ignore}, @var{q}] = sparsfun ('symetree', @var{c} (@var{p}, @var{p}));\n\
   @var{p} = @var{p} (@var{q});\n\
@end group\n\
\n\
AMD Version 1.1 (Jan. 21, 2004), Copyright @copyright{} 2004 by\n\
Timothy A. Davis, Patrick R. Amestoy, and Iain S. Duff.\n\
\n\
email: davis@@cise.ufl.edu  (CISE Department, Univ. of Florida).\n\
\n\
web: http://www.cise.ufl.edu/research/sparse/amd\n\
\n\
Acknowledgements: This work was supported by the National Science\n\
Foundation, under grants ASC-9111263, DMS-9223088, and CCR-0203270.\n\
@end deftypefn")
{
  int nargin = args.length (); 
  octave_value_list retval; 
  int spumoni = 0;

  if (nargin > 2 || nargout > 2)
    usage ("p = amd (A) or [p, Info] = amd (A, Control)");
  else if (nargin == 0)
    {
      // Get the default control parameter, and return
      NDArray control (dim_vector (AMD_CONTROL, 1));
      double *control_ptr = control.fortran_vec ();
      amd_defaults (control_ptr);
      if (nargout == 0)
	amd_control (control_ptr);
      else
	retval(0) = control;
    }
  else
    {
      NDArray control (dim_vector (AMD_CONTROL, 1));
      double *control_ptr = control.fortran_vec ();
      amd_defaults (control_ptr);

      if (nargin > 1)
	{
	  NDArray control_in = args(1).array_value();

	  if (error_state)
	    {	  
	      error ("amd: could not read control vector");
	      return retval;
	    }

	  dim_vector dv = control_in.dims ();
	  if (dv.length() > 2 || (dv(0) != 1 && dv(1) != 1))
	    {
	      error ("amd: control vector isn't a vector");
	      return retval;
	    }
	  
	  int nc = dv.numel ();
	  control (AMD_DENSE) = (nc > 0 ? control_in (AMD_DENSE) :
				 AMD_DEFAULT_DENSE);
	  control (AMD_AGGRESSIVE) = (nc > 1 ? control_in (AMD_AGGRESSIVE) :
				      AMD_DEFAULT_AGGRESSIVE);
	  spumoni = (nc > 2 ? (control_in (2) != 0) : 0);
	}

      if (spumoni > 0)
	amd_control (control_ptr);

      int *Ap, *Ai;
      int n, m, nz;

      // These are here only so that the C++ destructors don't prematurally
      // remove the underlying data we are interested in
      SparseMatrix sm;
      SparseComplexMatrix scm;
      Matrix mm;
      ComplexMatrix cm;

      if (args(0).class_name () != "sparse" && spumoni > 0)
	octave_stdout << "    input matrix A is full (sparse copy"
		      << " of A will be created)" << std::endl;

      if (args(0).is_complex_type ())
	{
	  scm = args(0).sparse_complex_matrix_value ();
	  Ai = scm.ridx ();
	  Ap = scm.cidx ();
	  m = scm.rows ();
	  n = scm.cols ();
	  nz = scm.nnz ();
	}
      else
	{
	  sm = args(0).sparse_matrix_value ();
	  Ai = sm.ridx ();
	  Ap = sm.cidx ();
	  m = sm.rows ();
	  n = sm.cols ();
	  nz = sm.nnz ();
	}

      if (spumoni > 0)
	octave_stdout << "    input matrix A is " << m << "-by-" << n 
		      << std::endl;

      if (m != n)
	{
	  error ("amd: A must be square");
	  return retval;
	}

      if (spumoni > 0)
	octave_stdout << "    input matrix A has " << nz << 
	  " nonzero entries" << std::endl;

      // allocate workspace for output permutation 
      Array<int> P(n+1);
      int *P_ptr = P.fortran_vec ();
      NDArray info (dim_vector (AMD_INFO, 1));
      double *info_ptr = info.fortran_vec ();
      int result;

      // order the matrix
      result = amd_order (n, Ap, Ai, P_ptr, control_ptr, info_ptr);

      // print results (including return value)
      if (spumoni > 0)
	amd_info (info_ptr);

      // check error conditions
      if (result == AMD_OUT_OF_MEMORY)
	error ("amd: out of memory");
      else if (result == AMD_INVALID)
	error ("amd: input matrix A is corrupted");
      else
	{
	  // copy the outputs to Octave

	  // output permutation, P
	  NDArray perm (dim_vector (1, n));
	  for (int i = 0; i < n; i++)
	    perm (i) = double (P(i) + 1);  // 1-based indexing for Octave

	  retval (0) = perm;

	  // Info
	  if (nargout > 1)
	    retval (1) = info;
	}
    }
  return retval;
}

/*
;;; Local Variables: ***
;;; mode: C++ ***
;;; End: ***
*/