diff src/DLD-FUNCTIONS/colamd.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 234abf4c74dd
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/DLD-FUNCTIONS/colamd.cc	Fri Feb 25 19:55:28 2005 +0000
@@ -0,0 +1,726 @@
+/*
+
+Copyright (C) 2004 David Bateman
+Copyright (C) 1998-2004 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, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+// This is the octave interface to colamd, which bore the copyright given
+// in the help of the functions.
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <cstdlib>
+
+#include <string>
+#include <vector>
+
+#include "ov.h"
+#include "defun-dld.h"
+#include "pager.h"
+#include "ov-re-mat.h"
+
+#include "ov-re-sparse.h"
+#include "ov-cx-sparse.h"
+
+// External COLAMD functions in C
+extern "C" {
+#include "COLAMD/colamd.h"
+}
+
+// The symmetric column elimination tree code take from the Davis LDL code. 
+// Copyright given elsewhere in this file.
+static 
+void symetree (const int *ridx, const int *cidx, int *Parent, int *P, int n)
+{
+  OCTAVE_LOCAL_BUFFER (int, Flag, n);
+  OCTAVE_LOCAL_BUFFER (int, Pinv, (P ? n : 0));
+  if (P)
+    // If P is present then compute Pinv, the inverse of P
+    for (int k = 0 ; k < n ; k++)
+      Pinv [P [k]] = k ;
+
+  for (int k = 0 ; k < n ; k++)
+    {
+      // L(k,:) pattern: all nodes reachable in etree from nz in A(0:k-1,k)
+      Parent [k] = n ;	              // parent of k is not yet known 
+      Flag [k] = k ;		      // mark node k as visited 
+      int kk = (P) ? (P [k]) : (k) ;  // kth original, or permuted, column
+      int p2 = cidx [kk+1] ;
+      for (int p = cidx [kk] ; p < p2 ; p++)
+	{
+	  // A (i,k) is nonzero (original or permuted A)
+	  int i = (Pinv) ? (Pinv [ridx [p]]) : (ridx [p]) ;
+	  if (i < k)
+	    {
+	      // follow path from i to root of etree, stop at flagged node 
+	      for ( ; Flag [i] != k ; i = Parent [i])
+		{
+		  // find parent of i if not yet determined
+		  if (Parent [i] == n)
+		    Parent [i] = k ;
+		  Flag [i] = k ;	// mark i as visited
+		}
+	    }
+	}
+    }
+}
+
+// The elimination tree post-ordering code below is taken from SuperLU
+static inline
+int make_set (int i, int *pp)
+{
+  pp[i] = i;
+  return i;
+}
+
+static inline
+int link (int s, int t, int *pp)
+{
+  pp[s] = t;
+  return t;
+}
+
+static inline
+int find (int i, int *pp)
+{
+  register int p, gp;
+    
+  p = pp[i];
+  gp = pp[p];
+  while (gp != p) {
+    pp[i] = gp;
+    i = gp;
+    p = pp[i];
+    gp = pp[p];
+  }
+  return (p);
+}
+
+static
+int etdfs (int v, int *first_kid, int *next_kid, int *post, int postnum)
+{
+  for (int w = first_kid[v]; w != -1; w = next_kid[w]) {
+    postnum = etdfs (w, first_kid, next_kid, post, postnum);
+  }
+  post[postnum++] = v;
+
+  return postnum;
+}
+
+static
+void TreePostorder(int n, int *parent, int *post)
+{
+  // Allocate storage for working arrays and results
+  OCTAVE_LOCAL_BUFFER (int, first_kid, n+1);
+  OCTAVE_LOCAL_BUFFER (int, next_kid, n+1);
+
+  // Set up structure describing children
+  for (int v = 0; v <= n; first_kid[v++] = -1);
+  for (int v = n-1; v >= 0; v--) 
+    {
+      int dad = parent[v];
+      next_kid[v] = first_kid[dad];
+      first_kid[dad] = v;
+    }
+
+  // Depth-first search from dummy root vertex #n
+  etdfs (n, first_kid, next_kid, post, 0);
+}
+
+static 
+void coletree (const int *ridx, const int *colbeg, int *colend, 
+	       int *parent, int nr, int nc)
+{
+  OCTAVE_LOCAL_BUFFER (int, root, nc);
+  OCTAVE_LOCAL_BUFFER (int, pp, nc);
+  OCTAVE_LOCAL_BUFFER (int, firstcol, nr);
+
+  // Compute firstcol[row] = first nonzero column in row
+  for (int row = 0; row < nr; firstcol[row++] = nc);
+  for (int col = 0; col < nc; col++) 
+    for (int p = colbeg[col]; p < colend[col]; p++) 
+      {
+	int row = ridx[p];
+	if (firstcol[row] > col)
+	  firstcol[row] = col;
+      }
+
+  // Compute etree by Liu's algorithm for symmetric matrices,
+  // except use (firstcol[r],c) in place of an edge (r,c) of A.
+  // Thus each row clique in A'*A is replaced by a star
+  // centered at its first vertex, which has the same fill.
+  for (int col = 0; col < nc; col++) 
+    {
+      int cset = make_set (col, pp);
+      root[cset] = col;
+      parent[col] = nc; 
+      for (int p = colbeg[col]; p < colend[col]; p++) 
+	{
+	  int row = firstcol[ridx[p]];
+	  if (row >= col) 
+	    continue;
+	  int rset = find (row, pp);
+	  int rroot = root[rset];
+	  if (rroot != col) 
+	    {
+	      parent[rroot] = col;
+	      cset = link (cset, rset, pp);
+	      root[cset] = col;
+	    }
+	}
+    }
+}
+
+DEFUN_DLD (colamd, args, nargout,
+    "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{p} =} colamd (@var{s})\n\
+@deftypefnx {Loadable Function} {@var{p} =} colamd (@var{s}, @var{knobs})\n\
+@deftypefnx {Loadable Function} {[@var{p}, @var{stats}] =} colamd (@var{s})\n\
+@deftypefnx {Loadable Function} {[@var{p}, @var{stats}] =} colamd (@var{s}, @var{knobs})\n\
+\n\
+Column approximate minimum degree permutation. @code{@var{p} = colamd\n\
+(@var{s})} returns the column approximate minimum degree permutation\n\
+vector for the sparse matrix @var{s}. For a non-symmetric matrix @var{s},\n\
+@code{@var{s} (:,@var{p})} tends to have sparser LU factors than @var{s}.\n\
+The Cholesky factorization of @code{@var{s} (:,@var{p})' * @var{s}\n\
+(:,@var{p})} also tends to be sparser than that of @code{@var{s}' *\n\
+@var{s}}.\n\
+\n\
+@var{knobs} is an optional two-element input vector. If @var{s} is\n\
+m-by-n, then rows with more than @code{(@var{knobs} (1)) * @var{n}}\n\
+entries are ignored.  Columns with more than @code{(@var{knobs} (2)) *\n\
+@var{m}} entries are removed prior to ordering, and ordered last in the\n\
+output permutation @var{p}. If the knobs parameter is not present, then\n\
+0.5 is used instead, for both @code{@var{knobs} (1)} and\n\
+@code{@var{knobs} (2)}. @code{@var{knobs} (3)} controls the printing of\n\
+statistics and error messages.\n\
+\n\
+@var{stats} is an optional 20-element output vector that provides data\n\
+about the ordering and the validity of the input matrix @var{s}. Ordering\n\
+statistics are in @code{@var{stats} (1:3)}. @code{@var{stats} (1)} and\n\
+@code{@var{stats} (2)} are the number of dense or empty rows and columns\n\
+ignored by COLAMD and @code{@var{stats} (3)} is the number of garbage\n\
+collections performed on the internal data structure used by COLAMD\n\
+(roughly of size @code{2.2 * nnz(@var{s}) + 4 * @var{m} + 7 * @var{n}}\n\
+integers).\n\
+\n\
+Octave built-in functions are intended to generate valid sparse matrices,\n\
+with no duplicate entries, with ascending row indices of the nonzeros\n\
+in each column, with a non-negative number of entries in each column (!)\n\
+and so on.  If a matrix is invalid, then COLAMD may or may not be able\n\
+to continue.  If there are duplicate entries (a row index appears two or\n\
+more times in the same column) or if the row indices in a column are out\n\
+of order, then COLAMD can correct these errors by ignoring the duplicate\n\
+entries and sorting each column of its internal copy of the matrix\n\
+@var{s} (the input matrix @var{s} is not repaired, however). If a matrix\n\
+is invalid in other ways then COLAMD cannot continue, an error message is\n\
+printed, and no output arguments (@var{p} or @var{stats}) are returned.\n\
+COLAMD is thus a simple way to check a sparse matrix to see if it's\n\
+valid.\n\
+\n\
+@code{@var{stats} (4:7)} provide information if COLAMD was able to\n\
+continue. The matrix is OK if @code{@var{stats} (4)} is zero, or 1 if\n\
+invalid. @code{@var{stats} (5)} is the rightmost column index that is\n\
+unsorted or contains duplicate entries, or zero if no such column exists.\n\
+@code{@var{stats} (6)} is the last seen duplicate or out-of-order row\n\
+index in the column index given by @code{@var{stats} (5)}, or zero if no\n\
+such row index exists. @code{@var{stats} (7)} is the number of duplicate\n\
+or out-of-order row indices. @code{@var{stats} (8:20)} is always zero in\n\
+the current version of COLAMD (reserved for future use).\n\
+\n\
+The ordering is followed by a column elimination tree post-ordering.\n\
+\n\
+The authors of the code itself are Stefan I. Larimore and Timothy A.\n\
+Davis (davis@@cise.ufl.edu), University of Florida.  The algorithm was\n\
+developed in collaboration with John Gilbert, Xerox PARC, and Esmond\n\
+Ng, Oak Ridge National Laboratory. (see\n\
+@url{http://www.cise.ufl.edu/research/sparse/colamd})\n\
+@end deftypefn\n\
+@seealso{colperm, symamd}")
+{
+  octave_value_list retval;
+  int nargin = args.length ();
+  int spumoni = 0;
+ 
+  if (nargout < 0 || nargout > 2 || nargin < 0 || nargin > 2)
+    usage ("colamd: incorrect number of input and/or output arguments");
+  else
+    {
+      // Get knobs
+      OCTAVE_LOCAL_BUFFER (double, knobs, COLAMD_KNOBS);
+      colamd_set_defaults (knobs);
+
+      // Check for user-passed knobs
+      if (nargin == 2)
+	{
+	  NDArray User_knobs = args(1).array_value ();
+	  int nel_User_knobs = User_knobs.length ();
+	  
+	  if (nel_User_knobs > 0) 
+	    knobs [COLAMD_DENSE_ROW] = User_knobs (COLAMD_DENSE_ROW);
+	  if (nel_User_knobs > 1) 
+	    knobs [COLAMD_DENSE_COL] = User_knobs (COLAMD_DENSE_COL) ;
+	  if (nel_User_knobs > 2) 
+	    spumoni = (int) User_knobs (2);
+	}
+
+      // print knob settings if spumoni is set
+      if (spumoni > 0)
+	{
+	  octave_stdout << "colamd: dense row fraction: " 
+			<< knobs [COLAMD_DENSE_ROW] << std::endl;
+	  octave_stdout << "colamd: dense col fraction: " 
+			<< knobs [COLAMD_DENSE_COL] << std::endl;
+	}
+      
+      int n_row, n_col, nnz;
+      int *ridx, *cidx;
+      SparseComplexMatrix scm;
+      SparseMatrix sm;
+
+      if (args(0).class_name () == "sparse")
+	{
+	  if (args(0).is_complex_type ())
+	    {
+	      scm = args(0). sparse_complex_matrix_value ();
+	      n_row = scm.rows ();
+	      n_col = scm.cols ();
+	      nnz = scm.nnz ();
+	      ridx = scm.xridx ();
+	      cidx = scm.xcidx ();
+	    }
+	  else
+	    {
+	      sm = args(0).sparse_matrix_value ();
+
+	      n_row = sm.rows ();
+	      n_col = sm.cols ();
+	      nnz = sm.nnz ();
+	      ridx = sm.xridx ();
+	      cidx = sm.xcidx ();
+	    }
+	}
+      else
+	{
+	  if (args(0).is_complex_type ())
+	    sm = SparseMatrix (real (args(0).complex_matrix_value ()));
+	  else
+	    sm = SparseMatrix (args(0).matrix_value ());
+
+	  n_row = sm.rows ();
+	  n_col = sm.cols ();
+	  nnz = sm.nnz ();
+	  ridx = sm.xridx ();
+	  cidx = sm.xcidx ();
+	}
+
+      // Allocate workspace for colamd
+      OCTAVE_LOCAL_BUFFER (int, p, n_col+1);
+      for (int i = 0; i < n_col+1; i++)
+	p[i] = cidx [i];
+
+      int Alen = colamd_recommended (nnz, n_row, n_col);
+      OCTAVE_LOCAL_BUFFER (int, A, Alen);
+      for (int i = 0; i < nnz; i++)
+	A[i] = ridx [i];
+
+      // Order the columns (destroys A)
+      OCTAVE_LOCAL_BUFFER (int, stats, COLAMD_STATS);
+      if (!colamd (n_row, n_col, Alen, A, p, knobs, stats))
+	{
+	  colamd_report (stats) ;
+	  error ("colamd: internal error!");
+	  return retval;
+	}
+
+      // column elimination tree post-ordering (reuse variables)
+      OCTAVE_LOCAL_BUFFER (int, colbeg, n_col + 1);
+      OCTAVE_LOCAL_BUFFER (int, colend, n_col + 1);
+      OCTAVE_LOCAL_BUFFER (int, etree, n_col + 1);
+
+      for (int i = 0; i < n_col; i++)
+	{
+	  colbeg[i] = cidx[p[i]];
+	  colend[i] = cidx[p[i]+1];
+	}
+
+      coletree (ridx, colbeg, colend, etree, n_row, n_col);
+
+      // Calculate the tree post-ordering
+      TreePostorder (n_col, etree, colbeg);
+
+      // return the permutation vector
+      NDArray out_perm (dim_vector (1, n_col));
+      for (int i = 0; i < n_col; i++)
+	out_perm(i) = p [colbeg [i]] + 1;
+
+      retval (0) = out_perm;
+
+      // print stats if spumoni > 0
+      if (spumoni > 0)
+	colamd_report (stats) ;
+
+      // Return the stats vector
+      if (nargout == 2)
+	{
+	  NDArray out_stats (dim_vector (1, COLAMD_STATS));
+	  for (int i = 0 ; i < COLAMD_STATS ; i++)
+	    out_stats (i) = stats [i] ;
+	  retval(1) = out_stats;
+
+	  // fix stats (5) and (6), for 1-based information on 
+	  // jumbled matrix.  note that this correction doesn't 
+	  // occur if symamd returns FALSE
+	  out_stats (COLAMD_INFO1) ++ ; 
+	  out_stats (COLAMD_INFO2) ++ ; 
+	}
+    }
+
+  return retval;
+}
+
+DEFUN_DLD (symamd, args, nargout,
+    "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{p} =} symamd (@var{s})\n\
+@deftypefnx {Loadable Function} {@var{p} =} symamd (@var{s}, @var{knobs})\n\
+@deftypefnx {Loadable Function} {[@var{p}, @var{stats}] =} symamd (@var{s})\n\
+@deftypefnx {Loadable Function} {[@var{p}, @var{stats}] =} symamd (@var{s}, @var{knobs})\n\
+\n\
+For a symmetric positive definite matrix @var{s}, returns the permutation\n\
+vector p such that @code{@var{s} (@var{p}, @var{p})} tends to have a\n\
+sparser Cholesky factor than @var{s}. Sometimes SYMAMD works well for\n\
+symmetric indefinite matrices too. The matrix @var{s} is assumed to be\n\
+symmetric; only the strictly lower triangular part is referenced. @var{s}\n\
+must be square.\n\
+\n\
+@var{knobs} is an optional input argument. If @var{s} is n-by-n, then\n\
+rows and columns with more than @code{@var{knobs} (1) * @var{n}} entries\n\
+are removed prior to ordering, and ordered last in the output permutation\n\
+@var{p}. If the @var{knobs} parameter is not present, then the default of\n\
+0.5 is used instead. @code{@var{knobs} (2)} controls the printing of\n\
+statistics and error messages.\n\
+\n\
+@var{stats} is an optional 20-element output vector that provides data\n\
+about the ordering and the validity of the input matrix @var{s}. Ordering\n\
+statistics are in @code{@var{stats} (1:3)}. @code{@var{stats} (1) =\n\
+@var{stats} (2)} is the number of dense or empty rows and columns\n\
+ignored by SYMAMD and @code{@var{stats} (3)} is the number of garbage\n\
+collections performed on the internal data structure used by SYMAMD\n\
+(roughly of size @code{8.4 * nnz (tril (@var{s}, -1)) + 9 * @var{n}}\n\
+integers).\n\
+\n\
+Octave built-in functions are intended to generate valid sparse matrices,\n\
+with no duplicate entries, with ascending row indices of the nonzeros\n\
+in each column, with a non-negative number of entries in each column (!)\n\
+and so on.  If a matrix is invalid, then SYMAMD may or may not be able\n\
+to continue.  If there are duplicate entries (a row index appears two or\n\
+more times in the same column) or if the row indices in a column are out\n\
+of order, then SYMAMD can correct these errors by ignoring the duplicate\n\
+entries and sorting each column of its internal copy of the matrix S (the\n\
+input matrix S is not repaired, however).  If a matrix is invalid in\n\
+other ways then SYMAMD cannot continue, an error message is printed, and\n\
+no output arguments (@var{p} or @var{stats}) are returned.  SYMAMD is\n\
+thus a simple way to check a sparse matrix to see if it's valid.\n\
+\n\
+@code{@var{stats} (4:7)} provide information if SYMAMD was able to\n\
+continue. The matrix is OK if @code{@var{stats} (4)} is zero, or 1\n\
+if invalid. @code{@var{stats} (5)} is the rightmost column index that\n\
+is unsorted or contains duplicate entries, or zero if no such column\n\
+exists. @code{@var{stats} (6)} is the last seen duplicate or out-of-order\n\
+row index in the column index given by @code{@var{stats} (5)}, or zero\n\
+if no such row index exists. @code{@var{stats} (7)} is the number of\n\
+duplicate or out-of-order row indices. @code{@var{stats} (8:20)} is\n\
+always zero in the current version of SYMAMD (reserved for future use).\n\
+\n\
+The ordering is followed by a column elimination tree post-ordering.\n\
+\n\
+\n\
+The authors of the code itself are Stefan I. Larimore and Timothy A.\n\
+Davis (davis@@cise.ufl.edu), University of Florida.  The algorithm was\n\
+developed in collaboration with John Gilbert, Xerox PARC, and Esmond\n\
+Ng, Oak Ridge National Laboratory. (see\n\
+@url{http://www.cise.ufl.edu/research/sparse/colamd})\n\
+@end deftypefn\n\
+@seealso{colperm, colamd}")
+{
+  octave_value_list retval;
+  int nargin = args.length ();
+  int spumoni = 0;
+ 
+  if (nargout < 0 || nargout > 2 || nargin < 0 || nargin > 2)
+    usage ("symamd: incorrect number of input and/or output arguments");
+  else
+    {
+      // Get knobs
+      OCTAVE_LOCAL_BUFFER (double, knobs, COLAMD_KNOBS);
+      colamd_set_defaults (knobs);
+
+      // Check for user-passed knobs
+      if (nargin == 2)
+	{
+	  NDArray User_knobs = args(1).array_value ();
+	  int nel_User_knobs = User_knobs.length ();
+	  
+	  if (nel_User_knobs > 0) 
+	    knobs [COLAMD_DENSE_ROW] = User_knobs (COLAMD_DENSE_ROW);
+	  if (nel_User_knobs > 1) 
+	    spumoni = (int) User_knobs (1);
+	}
+
+      // print knob settings if spumoni is set
+      if (spumoni > 0)
+	octave_stdout << "symamd: dense row/col fraction: " 
+		      << knobs [COLAMD_DENSE_ROW] << std::endl;
+      
+      int n_row, n_col, nnz;
+      int *ridx, *cidx;
+      SparseMatrix sm;
+      SparseComplexMatrix scm;
+
+      if (args(0).class_name () == "sparse")
+	{
+	  if (args(0).is_complex_type ())
+	    {
+	      scm = args(0).sparse_complex_matrix_value ();
+	      n_row = scm.rows ();
+	      n_col = scm.cols ();
+	      nnz = scm.nnz ();
+	      ridx = scm.xridx ();
+	      cidx = scm.xcidx ();
+	    }
+	  else
+	    {
+	      sm = args(0).sparse_matrix_value ();
+	      n_row = sm.rows ();
+	      n_col = sm.cols ();
+	      nnz = sm.nnz ();
+	      ridx = sm.xridx ();
+	      cidx = sm.xcidx ();
+	    }
+	}
+      else
+	{
+	  if (args(0).is_complex_type ())
+	    sm = SparseMatrix (real (args(0).complex_matrix_value ()));
+	  else
+	    sm = SparseMatrix (args(0).matrix_value ());
+	  
+	  n_row = sm.rows ();
+	  n_col = sm.cols ();
+	  nnz = sm.nnz ();
+	  ridx = sm.xridx ();
+	  cidx = sm.xcidx ();
+	}
+
+      if (n_row != n_col)
+	{
+	  error ("symamd: matrix must be square");
+	  return retval;
+	}
+
+      // Allocate workspace for symamd
+      OCTAVE_LOCAL_BUFFER (int, perm, n_col+1);
+      OCTAVE_LOCAL_BUFFER (int, stats, COLAMD_STATS);
+      if (!symamd (n_col, ridx, cidx, perm, knobs, stats, &calloc, &free))
+	{
+	  symamd_report (stats) ;
+	  error ("symamd: internal error!") ;
+	  return retval;
+	}
+
+      // column elimination tree post-ordering
+      OCTAVE_LOCAL_BUFFER (int, etree, n_col + 1);
+      symetree (ridx, cidx, etree, perm, n_col);
+
+      // Calculate the tree post-ordering
+      OCTAVE_LOCAL_BUFFER (int, post, n_col + 1);
+      TreePostorder (n_col, etree, post);
+
+      // return the permutation vector
+      NDArray out_perm (dim_vector (1, n_col));
+      for (int i = 0; i < n_col; i++)
+	out_perm(i) = perm [post [i]] + 1;
+
+      retval (0) = out_perm;
+
+      // print stats if spumoni > 0
+      if (spumoni > 0)
+	symamd_report (stats) ;
+
+      // Return the stats vector
+      if (nargout == 2)
+	{
+	  NDArray out_stats (dim_vector (1, COLAMD_STATS));
+	  for (int i = 0 ; i < COLAMD_STATS ; i++)
+	    out_stats (i) = stats [i] ;
+	  retval(1) = out_stats;
+
+	  // fix stats (5) and (6), for 1-based information on 
+	  // jumbled matrix.  note that this correction doesn't 
+	  // occur if symamd returns FALSE
+	  out_stats (COLAMD_INFO1) ++ ; 
+	  out_stats (COLAMD_INFO2) ++ ; 
+	}
+    }
+
+  return retval;
+}
+
+DEFUN_DLD (etree, args, nargout,
+    "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{p} =} etree (@var{s})\n\
+@deftypefnx {Loadable Function} {@var{p} =} etree (@var{s}, @var{typ})\n\
+@deftypefnx {Loadable Function} {[@var{p}, @var{q}] =} etree (@var{s}, @var{typ})\n\
+\n\
+Returns the elimination tree for the matrix @var{s}. By default @var{s}\n\
+is assumed to be symmetric and the symmetric elimination tree is\n\
+returned. The argument @var{typ} controls whether a symmetric or\n\
+column elimination tree is returned. Valid values of @var{typ} are\n\
+'sym' or 'col', for symmetric or column elimination tree respectively\n\
+\n\
+Called with a second argument, @dfn{etree} also returns the postorder\n\
+permutations on the tree.\n\
+@end deftypefn")
+{
+  octave_value_list retval;
+  int nargin = args.length ();
+
+  if (nargout < 0 || nargout > 2 || nargin < 0 || nargin > 2)
+    usage ("etree: incorrect number of input and/or output arguments");
+  else
+    {
+      int n_row, n_col, nnz;
+      int *ridx, *cidx;
+      bool is_sym = true;
+      SparseMatrix sm;
+      SparseComplexMatrix scm;
+
+      if (args(0).class_name () == "sparse")
+	{
+	  if (args(0).is_complex_type ())
+	    {
+	      scm = args(0).sparse_complex_matrix_value ();
+	      n_row = scm.rows ();
+	      n_col = scm.cols ();
+	      nnz = scm.nnz ();
+	      ridx = scm.xridx ();
+	      cidx = scm.xcidx ();
+	    }
+	  else
+	    {
+	      sm = args(0).sparse_matrix_value ();
+	      n_row = sm.rows ();
+	      n_col = sm.cols ();
+	      nnz = sm.nnz ();
+	      ridx = sm.xridx ();
+	      cidx = sm.xcidx ();
+	    }
+
+	}
+      else
+	{
+	  error ("etree: must be called with a sparse matrix");
+	  return retval;
+	}
+
+      if (nargin == 2)
+	if (args(1).is_string ())
+	  {
+	    std::string str = args(1).string_value ();
+	    if (str.find("C") == 0 || str.find("c") == 0)
+	      is_sym = false;
+	  }
+	else
+	  {
+	    error ("etree: second argument must be a string");
+	    return retval;
+	  }
+
+      // column elimination tree post-ordering (reuse variables)
+      OCTAVE_LOCAL_BUFFER (int, etree, n_col + 1);
+      
+
+      if (is_sym)
+	{
+	  if (n_row != n_col)
+	    {
+	      error ("etree: matrix is marked as symmetric, but not square");
+	      return retval;
+	    }
+	  symetree (ridx, cidx, etree, NULL, n_col);
+	}
+      else
+	{
+	  OCTAVE_LOCAL_BUFFER (int, colbeg, n_col);
+	  OCTAVE_LOCAL_BUFFER (int, colend, n_col);
+
+	  for (int i = 0; i < n_col; i++)
+	    {
+	      colbeg[i] = cidx[i];
+	      colend[i] = cidx[i+1];
+	    }
+
+	  coletree (ridx, colbeg, colend, etree, n_row, n_col);
+	}
+
+      NDArray tree (dim_vector (1, n_col));
+      for (int i = 0; i < n_col; i++)
+	// We flag a root with n_col while Matlab does it with zero
+	// Convert for matlab compatiable output
+	if (etree[i] == n_col)
+	  tree (i) = 0;
+	else
+	  tree (i) = etree[i] + 1;
+
+      retval (0) = tree;
+
+      if (nargout == 2)
+	{
+	  // Calculate the tree post-ordering
+	  OCTAVE_LOCAL_BUFFER (int, post, n_col + 1);
+	  TreePostorder (n_col, etree, post);
+
+	  NDArray postorder (dim_vector (1, n_col));
+	  for (int i = 0; i < n_col; i++)
+	    postorder (i) = post[i] + 1;
+
+	  retval (1) = postorder;
+	}
+    }
+
+  return retval;
+}
+
+DEFUN_DLD (symbfact, args, nargout,
+    "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {[@var{count}, @var{h}, @var{parent}, @var{post}, @var{r}]} = symbfact (@var{s}, @var{typ})\n\
+\n\
+Performs a symbolic factorization analysis on the sparse matrix @var{s}.\n\
+@end deftypefn")
+{
+  error ("symbfact: not implemented yet");
+  return octave_value ();
+}
+
+/*
+;;; Local Variables: ***
+;;; mode: C++ ***
+;;; End: ***
+*/