Mercurial > jwe > octave
changeset 28029:c28b8ba841fb
move sparse functions from dldfcn to corefcn directory (bug #57459)
Octave is always linked with SuiteSparse libraries to support
functions in liboctave so there is no advantage to dynamically load
the interpreter DEFUN functions that depend on SuiteSparse.
* __eigs__.cc, amd.cc, ccolamd.cc, chol.cc, colamd.cc, dmperm.cc,
qr.cc, symbfact.cc, symrcm.cc: Move from dldfcn to corefcn directory.
Define functions with DEFUN or DEFMETHOD instead of DEFUN_DLD or
DEFMETHOD_DLD.
* libinterp/dldfcn/module-files, libinterp/corefcn/module.mk: Update.
* amd.cc (Famd): Don't mlock function.
* symbfact.cc (Fsymbfact): Don't mlock function.
* variables.cc, type.m, which.m: Update tests.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Wed, 29 Jan 2020 06:30:40 -0500 |
parents | 915b3630eed0 |
children | 9d9e01986105 |
files | libinterp/corefcn/__eigs__.cc libinterp/corefcn/amd.cc libinterp/corefcn/ccolamd.cc libinterp/corefcn/chol.cc libinterp/corefcn/colamd.cc libinterp/corefcn/dmperm.cc libinterp/corefcn/module.mk libinterp/corefcn/qr.cc libinterp/corefcn/symbfact.cc libinterp/corefcn/symrcm.cc libinterp/corefcn/variables.cc libinterp/dldfcn/__eigs__.cc libinterp/dldfcn/amd.cc libinterp/dldfcn/ccolamd.cc libinterp/dldfcn/chol.cc libinterp/dldfcn/colamd.cc libinterp/dldfcn/dmperm.cc libinterp/dldfcn/module-files libinterp/dldfcn/qr.cc libinterp/dldfcn/symbfact.cc libinterp/dldfcn/symrcm.cc scripts/help/type.m scripts/help/which.m |
diffstat | 23 files changed, 6692 insertions(+), 6699 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/__eigs__.cc Wed Jan 29 06:30:40 2020 -0500 @@ -0,0 +1,675 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2005-2020 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// This file is part of Octave. +// +// 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 3 of the License, 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 Octave; see the file COPYING. If not, see +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include <limits> +#include <string> + +#include "Matrix.h" +#include "eigs-base.h" +#include "unwind-prot.h" + +#include "defun.h" +#include "error.h" +#include "errwarn.h" +#include "interpreter-private.h" +#include "oct-map.h" +#include "ov.h" +#include "ovl.h" +#include "pager.h" +#include "parse.h" +#include "variables.h" + +#if defined (HAVE_ARPACK) + +// Global pointer for user defined function. +static octave_value eigs_fcn; + +// Have we warned about imaginary values returned from user function? +static bool warned_imaginary = false; + +// Is this a recursive call? +static int call_depth = 0; + +ColumnVector +eigs_func (const ColumnVector& x, int& eigs_error) +{ + ColumnVector retval; + octave_value_list args; + args(0) = x; + + if (eigs_fcn.is_defined ()) + { + octave_value_list tmp; + + try + { + tmp = octave::feval (eigs_fcn, args, 1); + } + catch (octave::execution_exception& e) + { + err_user_supplied_eval (e, "eigs"); + } + + if (tmp.length () && tmp(0).is_defined ()) + { + if (! warned_imaginary && tmp(0).iscomplex ()) + { + warning ("eigs: ignoring imaginary part returned from user-supplied function"); + warned_imaginary = true; + } + + retval = tmp(0).xvector_value ("eigs: evaluation of user-supplied function failed"); + } + else + { + eigs_error = 1; + err_user_supplied_eval ("eigs"); + } + } + + return retval; +} + +ComplexColumnVector +eigs_complex_func (const ComplexColumnVector& x, int& eigs_error) +{ + ComplexColumnVector retval; + octave_value_list args; + args(0) = x; + + if (eigs_fcn.is_defined ()) + { + octave_value_list tmp; + + try + { + tmp = octave::feval (eigs_fcn, args, 1); + } + catch (octave::execution_exception& e) + { + err_user_supplied_eval (e, "eigs"); + } + + if (tmp.length () && tmp(0).is_defined ()) + { + retval = tmp(0).xcomplex_vector_value ("eigs: evaluation of user-supplied function failed"); + } + else + { + eigs_error = 1; + err_user_supplied_eval ("eigs"); + } + } + + return retval; +} + +#endif + +DEFMETHOD (__eigs__, interp, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {@var{d} =} __eigs__ (@var{A}) +@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{k}) +@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{k}, @var{sigma}) +@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{k}, @var{sigma}, @var{opts}) +@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B}) +@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B}, @var{k}) +@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B}, @var{k}, @var{sigma}) +@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B}, @var{k}, @var{sigma}, @var{opts}) +@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}) +@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B}) +@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{k}) +@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B}, @var{k}) +@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{k}, @var{sigma}) +@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B}, @var{k}, @var{sigma}) +@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{k}, @var{sigma}, @var{opts}) +@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B}, @var{k}, @var{sigma}, @var{opts}) +@deftypefnx {} {[@var{V}, @var{d}] =} __eigs__ (@var{A}, @dots{}) +@deftypefnx {} {[@var{V}, @var{d}] =} __eigs__ (@var{af}, @var{n}, @dots{}) +@deftypefnx {} {[@var{V}, @var{d}, @var{flag}] =} __eigs__ (@var{A}, @dots{}) +@deftypefnx {} {[@var{V}, @var{d}, @var{flag}] =} __eigs__ (@var{af}, @var{n}, @dots{}) +Undocumented internal function. +@end deftypefn */) +{ +#if defined (HAVE_ARPACK) + + int nargin = args.length (); + + if (nargin == 0) + print_usage (); + + octave_value_list retval; + + std::string fcn_name; + octave_idx_type n = 0; + octave_idx_type k = 6; + Complex sigma = 0.0; + double sigmar, sigmai; + bool have_sigma = false; + std::string typ = "LM"; + Matrix amm, bmm, bmt; + ComplexMatrix acm, bcm, bct; + SparseMatrix asmm, bsmm, bsmt; + SparseComplexMatrix ascm, bscm, bsct; + int b_arg = 0; + bool have_b = false; + bool have_a_fun = false; + bool a_is_complex = false; + bool b_is_complex = false; + bool symmetric = false; + bool sym_tested = false; + bool cholB = false; + bool a_is_sparse = false; + bool b_is_sparse = false; + ColumnVector permB; + int arg_offset = 0; + double tol = std::numeric_limits<double>::epsilon (); + int maxit = 300; + int disp = 0; + octave_idx_type p = -1; + ColumnVector resid; + ComplexColumnVector cresid; + octave_idx_type info = 1; + + warned_imaginary = false; + + octave::unwind_protect frame; + + frame.protect_var (call_depth); + call_depth++; + + if (call_depth > 1) + error ("eigs: invalid recursive call"); + + if (args(0).is_function_handle () || args(0).is_inline_function () + || args(0).is_string ()) + { + eigs_fcn = octave::get_function_handle (interp, args(0), "x"); + + if (eigs_fcn.is_undefined ()) + error ("eigs: unknown function"); + + if (nargin < 2) + error ("eigs: incorrect number of arguments"); + + n = args(1).nint_value (); + arg_offset = 1; + have_a_fun = true; + } + else + { + if (args(0).iscomplex ()) + { + if (args(0).issparse ()) + { + ascm = (args(0).sparse_complex_matrix_value ()); + a_is_sparse = true; + } + else + acm = (args(0).complex_matrix_value ()); + a_is_complex = true; + } + else + { + if (args(0).issparse ()) + { + asmm = (args(0).sparse_matrix_value ()); + a_is_sparse = true; + } + else + { + amm = (args(0).matrix_value ()); + } + } + } + + // Note hold off reading B until later to avoid issues of double + // copies of the matrix if B is full/real while A is complex. + if (nargin > 1 + arg_offset + && ! (args(1 + arg_offset).is_real_scalar ())) + { + if (args(1+arg_offset).iscomplex ()) + { + b_arg = 1+arg_offset; + if (args(b_arg).issparse ()) + { + bscm = (args(b_arg).sparse_complex_matrix_value ()); + b_is_sparse = true; + } + else + bcm = (args(b_arg).complex_matrix_value ()); + have_b = true; + b_is_complex = true; + arg_offset++; + } + else + { + b_arg = 1+arg_offset; + if (args(b_arg).issparse ()) + { + bsmm = (args(b_arg).sparse_matrix_value ()); + b_is_sparse = true; + } + else + bmm = (args(b_arg).matrix_value ()); + have_b = true; + arg_offset++; + } + } + + if (nargin > (1+arg_offset)) + k = args(1+arg_offset).nint_value (); + + if (nargin > (2+arg_offset)) + { + if (args(2+arg_offset).is_string ()) + { + typ = args(2+arg_offset).string_value (); + + // Use STL function to convert to upper case + transform (typ.begin (), typ.end (), typ.begin (), toupper); + + sigma = 0.0; + } + else + { + sigma = args(2+arg_offset).xcomplex_value ("eigs: SIGMA must be a scalar or a string"); + + have_sigma = true; + } + } + + sigmar = sigma.real (); + sigmai = sigma.imag (); + + if (nargin > (3+arg_offset)) + { + if (! args(3+arg_offset).isstruct ()) + error ("eigs: OPTS argument must be a structure"); + + octave_scalar_map map = args(3+arg_offset).xscalar_map_value ("eigs: OPTS argument must be a scalar structure"); + + octave_value tmp; + + // issym is ignored for complex matrix inputs + tmp = map.getfield ("issym"); + if (tmp.is_defined ()) + { + if (tmp.numel () != 1) + error ("eigs: OPTS.issym must be a scalar value"); + + symmetric = tmp.xbool_value ("eigs: OPTS.issym must be a logical value"); + sym_tested = true; + } + + // isreal is ignored if A is not a function + if (have_a_fun) + { + tmp = map.getfield ("isreal"); + if (tmp.is_defined ()) + { + if (tmp.numel () != 1) + error ("eigs: OPTS.isreal must be a scalar value"); + + a_is_complex = ! tmp.xbool_value ("eigs: OPTS.isreal must be a logical value"); + } + } + + tmp = map.getfield ("tol"); + if (tmp.is_defined ()) + tol = tmp.double_value (); + + tmp = map.getfield ("maxit"); + if (tmp.is_defined ()) + maxit = tmp.nint_value (); + + tmp = map.getfield ("p"); + if (tmp.is_defined ()) + p = tmp.nint_value (); + + tmp = map.getfield ("v0"); + if (tmp.is_defined ()) + { + if (a_is_complex || b_is_complex) + cresid = ComplexColumnVector (tmp.complex_vector_value ()); + else + resid = ColumnVector (tmp.vector_value ()); + } + + tmp = map.getfield ("disp"); + if (tmp.is_defined ()) + disp = tmp.nint_value (); + + tmp = map.getfield ("cholB"); + if (tmp.is_defined ()) + { + if (tmp.numel () != 1) + error ("eigs: OPTS.cholB must be a scalar value"); + + cholB = tmp.xbool_value ("eigs: OPTS.cholB must be a logical value"); + } + + tmp = map.getfield ("permB"); + if (tmp.is_defined ()) + permB = ColumnVector (tmp.vector_value ()) - 1.0; + } + + if (nargin > (4+arg_offset)) + error ("eigs: incorrect number of arguments"); + + // Test undeclared (no issym) matrix inputs for symmetry + if (! sym_tested && ! have_a_fun) + { + if (a_is_complex) + { + if (a_is_sparse) + symmetric = ascm.ishermitian (); + else + symmetric = acm.ishermitian (); + } + else + { + if (a_is_sparse) + symmetric = asmm.issymmetric (); + else + symmetric = amm.issymmetric (); + } + } + + if (have_b) + { + if (a_is_complex || b_is_complex) + { + if (b_is_sparse) + bscm = args(b_arg).sparse_complex_matrix_value (); + else + bcm = args(b_arg).complex_matrix_value (); + } + else + { + if (b_is_sparse) + bsmm = args(b_arg).sparse_matrix_value (); + else + bmm = args(b_arg).matrix_value (); + } + } + + // Mode 1 for SM mode seems unstable for some reason. + // Use Mode 3 instead, with sigma = 0. + if (! have_sigma && typ == "SM") + have_sigma = true; + + octave_idx_type nconv; + if (a_is_complex || b_is_complex) + { + ComplexMatrix eig_vec; + ComplexColumnVector eig_val; + + if (have_a_fun) + { + if (b_is_sparse) + nconv = EigsComplexNonSymmetricFunc + (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec, + eig_val, bscm, permB, cresid, octave_stdout, tol, + (nargout > 1), cholB, disp, maxit); + else + nconv = EigsComplexNonSymmetricFunc + (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec, + eig_val, bcm, permB, cresid, octave_stdout, tol, + (nargout > 1), cholB, disp, maxit); + } + else if (have_sigma) + { + if (a_is_sparse) + nconv = EigsComplexNonSymmetricMatrixShift + (ascm, sigma, k, p, info, eig_vec, eig_val, bscm, permB, + cresid, octave_stdout, tol, (nargout > 1), cholB, disp, + maxit); + else + nconv = EigsComplexNonSymmetricMatrixShift + (acm, sigma, k, p, info, eig_vec, eig_val, bcm, permB, + cresid, octave_stdout, tol, (nargout > 1), cholB, disp, + maxit); + } + else + { + if (a_is_sparse) + nconv = EigsComplexNonSymmetricMatrix + (ascm, typ, k, p, info, eig_vec, eig_val, bscm, permB, + cresid, octave_stdout, tol, (nargout > 1), cholB, disp, + maxit); + else + nconv = EigsComplexNonSymmetricMatrix + (acm, typ, k, p, info, eig_vec, eig_val, bcm, permB, + cresid, octave_stdout, tol, (nargout > 1), cholB, disp, + maxit); + } + + if (nargout < 2) + { + if (symmetric) + retval(0) = real (eig_val); + else + retval(0) = eig_val; + } + else + { + if (symmetric) + retval = ovl (eig_vec, DiagMatrix (real (eig_val)), double (info)); + else + retval = ovl (eig_vec, ComplexDiagMatrix (eig_val), double (info)); + } + } + else if (sigmai != 0.0) + { + // Promote real problem to a complex one. + ComplexMatrix eig_vec; + ComplexColumnVector eig_val; + + if (have_a_fun) + { + if (b_is_sparse) + nconv = EigsComplexNonSymmetricFunc + (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec, + eig_val, bscm, permB, cresid, octave_stdout, tol, + (nargout > 1), cholB, disp, maxit); + else + nconv = EigsComplexNonSymmetricFunc + (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec, + eig_val, bcm, permB, cresid, octave_stdout, tol, + (nargout > 1), cholB, disp, maxit); + } + else + { + if (a_is_sparse) + nconv = EigsComplexNonSymmetricMatrixShift + (SparseComplexMatrix (asmm), sigma, k, p, info, eig_vec, + eig_val, SparseComplexMatrix (bsmm), permB, cresid, + octave_stdout, tol, (nargout > 1), cholB, disp, maxit); + else + nconv = EigsComplexNonSymmetricMatrixShift + (ComplexMatrix (amm), sigma, k, p, info, eig_vec, + eig_val, ComplexMatrix (bmm), permB, cresid, + octave_stdout, tol, (nargout > 1), cholB, disp, maxit); + } + + if (nargout < 2) + { + if (symmetric) + retval(0) = real (eig_val); + else + retval(0) = eig_val; + } + else + { + if (symmetric) + retval = ovl (eig_vec, DiagMatrix (real (eig_val)), double (info)); + else + retval = ovl (eig_vec, ComplexDiagMatrix (eig_val), double (info)); + } + } + else + { + if (symmetric) + { + Matrix eig_vec; + ColumnVector eig_val; + + if (have_a_fun) + { + if (b_is_sparse) + nconv = EigsRealSymmetricFunc + (eigs_func, n, typ, sigmar, k, p, info, eig_vec, + eig_val, bsmm, permB, resid, octave_stdout, tol, + (nargout > 1), cholB, disp, maxit); + else + nconv = EigsRealSymmetricFunc + (eigs_func, n, typ, sigmar, k, p, info, eig_vec, + eig_val, bmm, permB, resid, octave_stdout, tol, + (nargout > 1), cholB, disp, maxit); + } + else if (have_sigma) + { + if (a_is_sparse) + nconv = EigsRealSymmetricMatrixShift + (asmm, sigmar, k, p, info, eig_vec, eig_val, bsmm, + permB, resid, octave_stdout, tol, (nargout > 1), + cholB, disp, maxit); + else + nconv = EigsRealSymmetricMatrixShift + (amm, sigmar, k, p, info, eig_vec, eig_val, bmm, + permB, resid, octave_stdout, tol, (nargout > 1), + cholB, disp, maxit); + } + else + { + if (a_is_sparse) + nconv = EigsRealSymmetricMatrix + (asmm, typ, k, p, info, eig_vec, eig_val, bsmm, + permB, resid, octave_stdout, tol, (nargout > 1), + cholB, disp, maxit); + else + nconv = EigsRealSymmetricMatrix + (amm, typ, k, p, info, eig_vec, eig_val, bmm, permB, + resid, octave_stdout, tol, (nargout > 1), cholB, + disp, maxit); + } + + if (nargout < 2) + retval(0) = eig_val; + else + retval = ovl (eig_vec, DiagMatrix (eig_val), double (info)); + } + else + { + ComplexMatrix eig_vec; + ComplexColumnVector eig_val; + + if (have_a_fun) + { + if (b_is_sparse) + nconv = EigsRealNonSymmetricFunc + (eigs_func, n, typ, sigmar, k, p, info, eig_vec, + eig_val, bsmm, permB, resid, octave_stdout, tol, + (nargout > 1), cholB, disp, maxit); + else + nconv = EigsRealNonSymmetricFunc + (eigs_func, n, typ, sigmar, k, p, info, eig_vec, + eig_val, bmm, permB, resid, octave_stdout, tol, + (nargout > 1), cholB, disp, maxit); + } + else if (have_sigma) + { + if (a_is_sparse) + nconv = EigsRealNonSymmetricMatrixShift + (asmm, sigmar, k, p, info, eig_vec, eig_val, bsmm, + permB, resid, octave_stdout, tol, (nargout > 1), + cholB, disp, maxit); + else + nconv = EigsRealNonSymmetricMatrixShift + (amm, sigmar, k, p, info, eig_vec, eig_val, bmm, + permB, resid, octave_stdout, tol, (nargout > 1), + cholB, disp, maxit); + } + else + { + if (a_is_sparse) + nconv = EigsRealNonSymmetricMatrix + (asmm, typ, k, p, info, eig_vec, eig_val, bsmm, + permB, resid, octave_stdout, tol, (nargout > 1), + cholB, disp, maxit); + else + nconv = EigsRealNonSymmetricMatrix + (amm, typ, k, p, info, eig_vec, eig_val, bmm, permB, + resid, octave_stdout, tol, (nargout > 1), cholB, + disp, maxit); + } + + if (nargout < 2) + retval(0) = eig_val; + else + retval = ovl (eig_vec, ComplexDiagMatrix (eig_val), double (info)); + } + } + + if (nconv <= 0) + warning_with_id ("Octave:eigs:UnconvergedEigenvalues", + "eigs: None of the %" OCTAVE_IDX_TYPE_FORMAT + " requested eigenvalues converged", k); + else if (nconv < k) + warning_with_id ("Octave:eigs:UnconvergedEigenvalues", + "eigs: Only %" OCTAVE_IDX_TYPE_FORMAT + " of the %" OCTAVE_IDX_TYPE_FORMAT + " requested eigenvalues converged", + nconv, k); + + if (! fcn_name.empty ()) + { + octave::symbol_table& symtab = interp.get_symbol_table (); + + symtab.clear_function (fcn_name); + } + + return retval; + +#else + + octave_unused_parameter (interp); + octave_unused_parameter (args); + octave_unused_parameter (nargout); + + err_disabled_feature ("eigs", "ARPACK"); + +#endif +} + +/* +## No test needed for internal helper function. +%!assert (1) +*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/amd.cc Wed Jan 29 06:30:40 2020 -0500 @@ -0,0 +1,202 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2008-2020 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// This file is part of Octave. +// +// 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 3 of the License, 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 Octave; see the file COPYING. If not, see +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + +// This is the octave interface to amd, which bore the copyright given +// in the help of the functions. + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include <cstdlib> + +#include "CSparse.h" +#include "Sparse.h" +#include "dMatrix.h" +#include "oct-locbuf.h" +#include "oct-sparse.h" + +#include "defun.h" +#include "error.h" +#include "errwarn.h" +#include "oct-map.h" +#include "ov.h" +#include "ovl.h" +#include "parse.h" + +DEFUN (amd, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {@var{p} =} amd (@var{S}) +@deftypefnx {} {@var{p} =} amd (@var{S}, @var{opts}) + +Return the approximate minimum degree permutation of a matrix. + +This is a permutation such that the Cholesky@tie{}factorization of +@code{@var{S} (@var{p}, @var{p})} tends to be sparser than the +Cholesky@tie{}factorization of @var{S} itself. @code{amd} is typically +faster than @code{symamd} but serves a similar purpose. + +The optional parameter @var{opts} is a structure that controls the behavior +of @code{amd}. The fields of the structure are + +@table @asis +@item @var{opts}.dense +Determines what @code{amd} considers to be a dense row or column of the +input matrix. Rows or columns with more than @code{max (16, (dense * +sqrt (@var{n})))} entries, where @var{n} is the order of the matrix @var{S}, +are ignored by @code{amd} during the calculation of the permutation. +The value of dense must be a positive scalar and the default value is 10.0 + +@item @var{opts}.aggressive +If this value is a nonzero scalar, then @code{amd} performs aggressive +absorption. The default is not to perform aggressive absorption. +@end table + +The author of the code itself is Timothy A. Davis +(see @url{http://faculty.cse.tamu.edu/davis/suitesparse.html}). +@seealso{symamd, colamd} +@end deftypefn */) +{ +#if defined (HAVE_AMD) + + int nargin = args.length (); + + if (nargin < 1 || nargin > 2) + print_usage (); + + octave_idx_type n_row, n_col; + const octave::suitesparse_integer *ridx, *cidx; + SparseMatrix sm; + SparseComplexMatrix scm; + + if (args(0).issparse ()) + { + if (args(0).iscomplex ()) + { + scm = args(0).sparse_complex_matrix_value (); + n_row = scm.rows (); + n_col = scm.cols (); + ridx = octave::to_suitesparse_intptr (scm.xridx ()); + cidx = octave::to_suitesparse_intptr (scm.xcidx ()); + } + else + { + sm = args(0).sparse_matrix_value (); + n_row = sm.rows (); + n_col = sm.cols (); + ridx = octave::to_suitesparse_intptr (sm.xridx ()); + cidx = octave::to_suitesparse_intptr (sm.xcidx ()); + } + } + else + { + if (args(0).iscomplex ()) + sm = SparseMatrix (real (args(0).complex_matrix_value ())); + else + sm = SparseMatrix (args(0).matrix_value ()); + + n_row = sm.rows (); + n_col = sm.cols (); + ridx = octave::to_suitesparse_intptr (sm.xridx ()); + cidx = octave::to_suitesparse_intptr (sm.xcidx ()); + } + + if (n_row != n_col) + err_square_matrix_required ("amd", "S"); + + OCTAVE_LOCAL_BUFFER (double, Control, AMD_CONTROL); + AMD_NAME (_defaults) (Control); + if (nargin > 1) + { + octave_scalar_map arg1 = args(1).xscalar_map_value ("amd: OPTS argument must be a scalar structure"); + + octave_value tmp; + + tmp = arg1.getfield ("dense"); + if (tmp.is_defined ()) + Control[AMD_DENSE] = tmp.double_value (); + + tmp = arg1.getfield ("aggressive"); + if (tmp.is_defined ()) + Control[AMD_AGGRESSIVE] = tmp.double_value (); + } + + OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, P, n_col); + Matrix xinfo (AMD_INFO, 1); + double *Info = xinfo.fortran_vec (); + + // FIXME: how can we manage the memory allocation of amd + // in a cleaner manner? + SUITESPARSE_ASSIGN_FPTR (malloc_func, amd_malloc, malloc); + SUITESPARSE_ASSIGN_FPTR (free_func, amd_free, free); + SUITESPARSE_ASSIGN_FPTR (calloc_func, amd_calloc, calloc); + SUITESPARSE_ASSIGN_FPTR (realloc_func, amd_realloc, realloc); + SUITESPARSE_ASSIGN_FPTR (printf_func, amd_printf, printf); + + octave_idx_type result = AMD_NAME (_order) (n_col, cidx, ridx, P, Control, + Info); + + if (result == AMD_OUT_OF_MEMORY) + error ("amd: out of memory"); + else if (result == AMD_INVALID) + error ("amd: matrix S is corrupted"); + + Matrix Pout (1, n_col); + for (octave_idx_type i = 0; i < n_col; i++) + Pout.xelem (i) = P[i] + 1; + + if (nargout > 1) + return ovl (Pout, xinfo); + else + return ovl (Pout); + +#else + + octave_unused_parameter (args); + octave_unused_parameter (nargout); + + err_disabled_feature ("amd", "AMD"); + +#endif +} + +/* +%!shared A, A2, opts +%! A = ones (20, 30); +%! A2 = ones (30, 30); + +%!testif HAVE_AMD +%! assert(amd (A2), [1:30]); +%! opts.dense = 25; +%! assert(amd (A2, opts), [1:30]); +%! opts.aggressive = 1; +%! assert(amd (A2, opts), [1:30]); + +%!testif HAVE_AMD +%! assert (amd ([]), zeros (1,0)) + +%!error <S must be a square matrix|was unavailable or disabled> amd (A) +%!error amd (A2, 2) +*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/ccolamd.cc Wed Jan 29 06:30:40 2020 -0500 @@ -0,0 +1,583 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2005-2020 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// This file is part of Octave. +// +// 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 3 of the License, 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 Octave; see the file COPYING. If not, see +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + +// This is the octave interface to ccolamd, which bore the copyright given +// in the help of the functions. + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include <cstdlib> + +#include "CSparse.h" +#include "Sparse.h" +#include "dNDArray.h" +#include "oct-locbuf.h" +#include "oct-sparse.h" + +#include "defun.h" +#include "error.h" +#include "errwarn.h" +#include "ov.h" +#include "pager.h" + +DEFUN (ccolamd, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {@var{p} =} ccolamd (@var{S}) +@deftypefnx {} {@var{p} =} ccolamd (@var{S}, @var{knobs}) +@deftypefnx {} {@var{p} =} ccolamd (@var{S}, @var{knobs}, @var{cmember}) +@deftypefnx {} {[@var{p}, @var{stats}] =} ccolamd (@dots{}) + +Constrained column approximate minimum degree permutation. + +@code{@var{p} = ccolamd (@var{S})} returns the column approximate minimum +degree permutation vector for the sparse matrix @var{S}. For a +non-symmetric matrix @var{S}, @code{@var{S}(:, @var{p})} tends to have +sparser LU@tie{}factors than @var{S}. +@code{chol (@var{S}(:, @var{p})' * @var{S}(:, @var{p}))} also tends to be +sparser than @code{chol (@var{S}' * @var{S})}. +@code{@var{p} = ccolamd (@var{S}, 1)} optimizes the ordering for +@code{lu (@var{S}(:, @var{p}))}. The ordering is followed by a column +elimination tree post-ordering. + +@var{knobs} is an optional 1-element to 5-element input vector, with a +default value of @code{[0 10 10 1 0]} if not present or empty. Entries not +present are set to their defaults. + +@table @code +@item @var{knobs}(1) +if nonzero, the ordering is optimized for @code{lu (S(:, p))}. It will be a +poor ordering for @code{chol (@var{S}(:, @var{p})' * @var{S}(:, @var{p}))}. +This is the most important knob for ccolamd. + +@item @var{knobs}(2) +if @var{S} is m-by-n, rows with more than +@code{max (16, @var{knobs}(2) * sqrt (n))} entries are ignored. + +@item @var{knobs}(3) +columns with more than +@code{max (16, @var{knobs}(3) * sqrt (min (@var{m}, @var{n})))} entries are +ignored and ordered last in the output permutation +(subject to the cmember constraints). + +@item @var{knobs}(4) +if nonzero, aggressive absorption is performed. + +@item @var{knobs}(5) +if nonzero, statistics and knobs are printed. + +@end table + +@var{cmember} is an optional vector of length @math{n}. It defines the +constraints on the column ordering. If @code{@var{cmember}(j) = @var{c}}, +then column @var{j} is in constraint set @var{c} (@var{c} must be in the +range 1 to n). In the output permutation @var{p}, all columns in set 1 +appear first, followed by all columns in set 2, and so on. +@code{@var{cmember} = ones (1,n)} if not present or empty. +@code{ccolamd (@var{S}, [], 1 : n)} returns @code{1 : n} + +@code{@var{p} = ccolamd (@var{S})} is about the same as +@code{@var{p} = colamd (@var{S})}. @var{knobs} and its default values +differ. @code{colamd} always does aggressive absorption, and it finds an +ordering suitable for both @code{lu (@var{S}(:, @var{p}))} and @code{chol +(@var{S}(:, @var{p})' * @var{S}(:, @var{p}))}; it cannot optimize its +ordering for @code{lu (@var{S}(:, @var{p}))} to the extent that +@code{ccolamd (@var{S}, 1)} can. + +@var{stats} is an optional 20-element output vector that provides data +about the ordering and the validity of the input matrix @var{S}. Ordering +statistics are in @code{@var{stats}(1 : 3)}. @code{@var{stats}(1)} and +@code{@var{stats}(2)} are the number of dense or empty rows and columns +ignored by @sc{ccolamd} and @code{@var{stats}(3)} is the number of garbage +collections performed on the internal data structure used by @sc{ccolamd} +(roughly of size @code{2.2 * nnz (@var{S}) + 4 * @var{m} + 7 * @var{n}} +integers). + +@code{@var{stats}(4 : 7)} provide information if CCOLAMD was able to +continue. The matrix is OK if @code{@var{stats}(4)} is zero, or 1 if +invalid. @code{@var{stats}(5)} is the rightmost column index that is +unsorted or contains duplicate entries, or zero if no such column exists. +@code{@var{stats}(6)} is the last seen duplicate or out-of-order row +index in the column index given by @code{@var{stats}(5)}, or zero if no +such row index exists. @code{@var{stats}(7)} is the number of duplicate +or out-of-order row indices. @code{@var{stats}(8 : 20)} is always zero in +the current version of @sc{ccolamd} (reserved for future use). + +The authors of the code itself are @nospell{S. Larimore, T. Davis} and +@nospell{S. Rajamanickam} in collaboration with @nospell{J. Bilbert and E. Ng}. +Supported by the National Science Foundation +@nospell{(DMS-9504974, DMS-9803599, CCR-0203270)}, and a grant from +@nospell{Sandia} National Lab. +See @url{http://faculty.cse.tamu.edu/davis/suitesparse.html} for ccolamd, +csymamd, amd, colamd, symamd, and other related orderings. +@seealso{colamd, csymamd} +@end deftypefn */) +{ +#if defined (HAVE_CCOLAMD) + + int nargin = args.length (); + + if (nargin < 1 || nargin > 3) + print_usage (); + + octave_value_list retval (nargout == 2 ? 2 : 1); + int spumoni = 0; + + // Get knobs + static_assert (CCOLAMD_KNOBS <= 40, + "ccolamd: # of CCOLAMD_KNOBS exceeded. Please report this to bugs.octave.org"); + double knob_storage[CCOLAMD_KNOBS]; + double *knobs = &knob_storage[0]; + CCOLAMD_NAME (_set_defaults) (knobs); + + // Check for user-passed knobs + if (nargin > 1) + { + NDArray User_knobs = args(1).array_value (); + int nel_User_knobs = User_knobs.numel (); + + if (nel_User_knobs > 0) + knobs[CCOLAMD_LU] = (User_knobs(0) != 0); + if (nel_User_knobs > 1) + knobs[CCOLAMD_DENSE_ROW] = User_knobs(1); + if (nel_User_knobs > 2) + knobs[CCOLAMD_DENSE_COL] = User_knobs(2); + if (nel_User_knobs > 3) + knobs[CCOLAMD_AGGRESSIVE] = (User_knobs(3) != 0); + if (nel_User_knobs > 4) + spumoni = (User_knobs(4) != 0); + + // print knob settings if spumoni is set + if (spumoni) + { + octave_stdout << "\nccolamd version " << CCOLAMD_MAIN_VERSION << '.' + << CCOLAMD_SUB_VERSION << ", " << CCOLAMD_DATE + << ":\nknobs(1): " << User_knobs(0) << ", order for "; + if (knobs[CCOLAMD_LU] != 0) + octave_stdout << "lu (A)\n"; + else + octave_stdout << "chol (A'*A)\n"; + + if (knobs[CCOLAMD_DENSE_ROW] >= 0) + octave_stdout << "knobs(2): " << User_knobs(1) + << ", rows with > max (16," + << knobs[CCOLAMD_DENSE_ROW] + << "*sqrt (size(A,2)))" + << " entries removed\n"; + else + octave_stdout << "knobs(2): " << User_knobs(1) + << ", no dense rows removed\n"; + + if (knobs[CCOLAMD_DENSE_COL] >= 0) + octave_stdout << "knobs(3): " << User_knobs(2) + << ", cols with > max (16," + << knobs[CCOLAMD_DENSE_COL] << "*sqrt (size(A)))" + << " entries removed\n"; + else + octave_stdout << "knobs(3): " << User_knobs(2) + << ", no dense columns removed\n"; + + if (knobs[CCOLAMD_AGGRESSIVE] != 0) + octave_stdout << "knobs(4): " << User_knobs(3) + << ", aggressive absorption: yes"; + else + octave_stdout << "knobs(4): " << User_knobs(3) + << ", aggressive absorption: no"; + + octave_stdout << "knobs(5): " << User_knobs(4) + << ", statistics and knobs printed\n"; + } + } + + octave_idx_type n_row, n_col, nnz; + octave_idx_type *ridx, *cidx; + SparseComplexMatrix scm; + SparseMatrix sm; + + if (args(0).issparse ()) + { + if (args(0).iscomplex ()) + { + 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).iscomplex ()) + 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 ccolamd + OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, p, n_col+1); + for (octave_idx_type i = 0; i < n_col+1; i++) + p[i] = cidx[i]; + + octave_idx_type Alen = CCOLAMD_NAME (_recommended) (nnz, n_row, n_col); + OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, A, Alen); + for (octave_idx_type i = 0; i < nnz; i++) + A[i] = ridx[i]; + + static_assert (CCOLAMD_STATS <= 40, + "ccolamd: # of CCOLAMD_STATS exceeded. Please report this to bugs.octave.org"); + octave::suitesparse_integer stats_storage[CCOLAMD_STATS]; + octave::suitesparse_integer *stats = &stats_storage[0]; + + if (nargin > 2) + { + NDArray in_cmember = args(2).array_value (); + octave_idx_type cslen = in_cmember.numel (); + OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, cmember, cslen); + for (octave_idx_type i = 0; i < cslen; i++) + // convert cmember from 1-based to 0-based + cmember[i] = static_cast<octave::suitesparse_integer>(in_cmember(i) - 1); + + if (cslen != n_col) + error ("ccolamd: CMEMBER must be of length equal to #cols of A"); + + // Order the columns (destroys A) + if (! CCOLAMD_NAME () (n_row, n_col, Alen, A, p, knobs, stats,cmember)) + { + CCOLAMD_NAME (_report) (stats); + + error ("ccolamd: internal error!"); + } + } + else + { + // Order the columns (destroys A) + if (! CCOLAMD_NAME () (n_row, n_col, Alen, A, p, knobs, stats, nullptr)) + { + CCOLAMD_NAME (_report) (stats); + + error ("ccolamd: internal error!"); + } + } + + // return the permutation vector + NDArray out_perm (dim_vector (1, n_col)); + for (octave_idx_type i = 0; i < n_col; i++) + out_perm(i) = p[i] + 1; + + retval(0) = out_perm; + + // print stats if spumoni > 0 + if (spumoni > 0) + CCOLAMD_NAME (_report) (stats); + + // Return the stats vector + if (nargout == 2) + { + NDArray out_stats (dim_vector (1, CCOLAMD_STATS)); + for (octave_idx_type i = 0 ; i < CCOLAMD_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(CCOLAMD_INFO1)++; + out_stats(CCOLAMD_INFO2)++; + } + + return retval; + +#else + + octave_unused_parameter (args); + octave_unused_parameter (nargout); + + err_disabled_feature ("ccolamd", "CCOLAMD"); + +#endif +} + +DEFUN (csymamd, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {@var{p} =} csymamd (@var{S}) +@deftypefnx {} {@var{p} =} csymamd (@var{S}, @var{knobs}) +@deftypefnx {} {@var{p} =} csymamd (@var{S}, @var{knobs}, @var{cmember}) +@deftypefnx {} {[@var{p}, @var{stats}] =} csymamd (@dots{}) + +For a symmetric positive definite matrix @var{S}, return the permutation +vector @var{p} such that @code{@var{S}(@var{p},@var{p})} tends to have a +sparser Cholesky@tie{}factor than @var{S}. + +Sometimes @code{csymamd} works well for symmetric indefinite matrices too. +The matrix @var{S} is assumed to be symmetric; only the strictly lower +triangular part is referenced. @var{S} must be square. The ordering is +followed by an elimination tree post-ordering. + +@var{knobs} is an optional 1-element to 3-element input vector, with a +default value of @code{[10 1 0]}. Entries not present are set to their +defaults. + +@table @code +@item @var{knobs}(1) +If @var{S} is n-by-n, then rows and columns with more than +@code{max(16,@var{knobs}(1)*sqrt(n))} entries are ignored, and ordered +last in the output permutation (subject to the cmember constraints). + +@item @var{knobs}(2) +If nonzero, aggressive absorption is performed. + +@item @var{knobs}(3) +If nonzero, statistics and knobs are printed. + +@end table + +@var{cmember} is an optional vector of length n. It defines the constraints +on the ordering. If @code{@var{cmember}(j) = @var{S}}, then row/column j is +in constraint set @var{c} (@var{c} must be in the range 1 to n). In the +output permutation @var{p}, rows/columns in set 1 appear first, followed +by all rows/columns in set 2, and so on. @code{@var{cmember} = ones (1,n)} +if not present or empty. @code{csymamd (@var{S},[],1:n)} returns +@code{1:n}. + +@code{@var{p} = csymamd (@var{S})} is about the same as +@code{@var{p} = symamd (@var{S})}. @var{knobs} and its default values +differ. + +@code{@var{stats}(4:7)} provide information if CCOLAMD was able to +continue. The matrix is OK if @code{@var{stats}(4)} is zero, or 1 if +invalid. @code{@var{stats}(5)} is the rightmost column index that is +unsorted or contains duplicate entries, or zero if no such column exists. +@code{@var{stats}(6)} is the last seen duplicate or out-of-order row +index in the column index given by @code{@var{stats}(5)}, or zero if no +such row index exists. @code{@var{stats}(7)} is the number of duplicate +or out-of-order row indices. @code{@var{stats}(8:20)} is always zero in +the current version of @sc{ccolamd} (reserved for future use). + +The authors of the code itself are @nospell{S. Larimore, T. Davis} and +@nospell{S. Rajamanickam} in collaboration with @nospell{J. Bilbert and E. Ng}. +Supported by the National Science Foundation +@nospell{(DMS-9504974, DMS-9803599, CCR-0203270)}, and a grant from +@nospell{Sandia} National Lab. +See @url{http://faculty.cse.tamu.edu/davis/suitesparse.html} for ccolamd, +colamd, csymamd, amd, colamd, symamd, and other related orderings. +@seealso{symamd, ccolamd} +@end deftypefn */) +{ +#if defined (HAVE_CCOLAMD) + + int nargin = args.length (); + + if (nargin < 1 || nargin > 3) + print_usage (); + + octave_value_list retval (nargout == 2 ? 2 : 1); + int spumoni = 0; + + // Get knobs + static_assert (CCOLAMD_KNOBS <= 40, + "csymamd: # of CCOLAMD_KNOBS exceeded. Please report this to bugs.octave.org"); + double knob_storage[CCOLAMD_KNOBS]; + double *knobs = &knob_storage[0]; + CCOLAMD_NAME (_set_defaults) (knobs); + + // Check for user-passed knobs + if (nargin > 1) + { + NDArray User_knobs = args(1).array_value (); + int nel_User_knobs = User_knobs.numel (); + + if (nel_User_knobs > 0) + knobs[CCOLAMD_DENSE_ROW] = User_knobs(0); + if (nel_User_knobs > 1) + knobs[CCOLAMD_AGGRESSIVE] = User_knobs(1); + if (nel_User_knobs > 2) + spumoni = static_cast<int> (User_knobs(2)); + + // print knob settings if spumoni is set + if (spumoni) + { + octave_stdout << "\ncsymamd version " << CCOLAMD_MAIN_VERSION + << '.' << CCOLAMD_SUB_VERSION + << ", " << CCOLAMD_DATE << "\n"; + + if (knobs[CCOLAMD_DENSE_ROW] >= 0) + octave_stdout << "knobs(1): " << User_knobs(0) + << ", rows/cols with > max (16," + << knobs[CCOLAMD_DENSE_ROW] + << "*sqrt (size(A,2)))" + << " entries removed\n"; + else + octave_stdout << "knobs(1): " << User_knobs(0) + << ", no dense rows/cols removed\n"; + + if (knobs[CCOLAMD_AGGRESSIVE] != 0) + octave_stdout << "knobs(2): " << User_knobs(1) + << ", aggressive absorption: yes"; + else + octave_stdout << "knobs(2): " << User_knobs(1) + << ", aggressive absorption: no"; + + octave_stdout << "knobs(3): " << User_knobs(2) + << ", statistics and knobs printed\n"; + } + } + + octave_idx_type n_row, n_col; + octave_idx_type *ridx, *cidx; + SparseMatrix sm; + SparseComplexMatrix scm; + + if (args(0).issparse ()) + { + if (args(0).iscomplex ()) + { + scm = args(0).sparse_complex_matrix_value (); + n_row = scm.rows (); + n_col = scm.cols (); + ridx = scm.xridx (); + cidx = scm.xcidx (); + } + else + { + sm = args(0).sparse_matrix_value (); + n_row = sm.rows (); + n_col = sm.cols (); + ridx = sm.xridx (); + cidx = sm.xcidx (); + } + } + else + { + if (args(0).iscomplex ()) + sm = SparseMatrix (real (args(0).complex_matrix_value ())); + else + sm = SparseMatrix (args(0).matrix_value ()); + + n_row = sm.rows (); + n_col = sm.cols (); + ridx = sm.xridx (); + cidx = sm.xcidx (); + } + + if (n_row != n_col) + err_square_matrix_required ("csymamd", "S"); + + // Allocate workspace for symamd + OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, perm, n_col+1); + static_assert (CCOLAMD_STATS <= 40, + "csymamd: # of CCOLAMD_STATS exceeded. Please report this to bugs.octave.org"); + octave::suitesparse_integer stats_storage[CCOLAMD_STATS]; + octave::suitesparse_integer *stats = &stats_storage[0]; + + if (nargin > 2) + { + NDArray in_cmember = args(2).array_value (); + octave_idx_type cslen = in_cmember.numel (); + OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, cmember, cslen); + for (octave_idx_type i = 0; i < cslen; i++) + // convert cmember from 1-based to 0-based + cmember[i] = static_cast<octave_idx_type> (in_cmember(i) - 1); + + if (cslen != n_col) + error ("csymamd: CMEMBER must be of length equal to #cols of A"); + + if (! CSYMAMD_NAME () (n_col, + octave::to_suitesparse_intptr (ridx), + octave::to_suitesparse_intptr (cidx), + perm, knobs, stats, &calloc, &free, cmember, -1)) + { + CSYMAMD_NAME (_report)(stats); + + error ("csymamd: internal error!"); + } + } + else + { + if (! CSYMAMD_NAME () (n_col, + octave::to_suitesparse_intptr (ridx), + octave::to_suitesparse_intptr (cidx), + perm, knobs, stats, &calloc, &free, nullptr, -1)) + { + CSYMAMD_NAME (_report)(stats); + + error ("csymamd: internal error!"); + } + } + + // return the permutation vector + NDArray out_perm (dim_vector (1, n_col)); + for (octave_idx_type i = 0; i < n_col; i++) + out_perm(i) = perm[i] + 1; + + retval(0) = out_perm; + + // print stats if spumoni > 0 + if (spumoni > 0) + CSYMAMD_NAME (_report)(stats); + + // Return the stats vector + if (nargout == 2) + { + NDArray out_stats (dim_vector (1, CCOLAMD_STATS)); + for (octave_idx_type i = 0 ; i < CCOLAMD_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(CCOLAMD_INFO1)++; + out_stats(CCOLAMD_INFO2)++; + } + + return retval; + +#else + + octave_unused_parameter (args); + octave_unused_parameter (nargout); + + err_disabled_feature ("csymamd", "CCOLAMD"); + +#endif +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/chol.cc Wed Jan 29 06:30:40 2020 -0500 @@ -0,0 +1,1331 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 1996-2020 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// This file is part of Octave. +// +// 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 3 of the License, 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 Octave; see the file COPYING. If not, see +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include <string> + +#include "Matrix.h" +#include "chol.h" +#include "oct-string.h" +#include "sparse-chol.h" +#include "sparse-util.h" + +#include "defun.h" +#include "error.h" +#include "errwarn.h" +#include "ov.h" +#include "ovl.h" + +template <typename CHOLT> +static octave_value +get_chol (const CHOLT& fact) +{ + return octave_value (fact.chol_matrix()); +} + +template <typename CHOLT> +static octave_value +get_chol_r (const CHOLT& fact) +{ + return octave_value (fact.chol_matrix (), + MatrixType (MatrixType::Upper)); +} + +template <typename CHOLT> +static octave_value +get_chol_l (const CHOLT& fact) +{ + return octave_value (fact.chol_matrix ().transpose (), + MatrixType (MatrixType::Lower)); +} + +DEFUN (chol, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {@var{R} =} chol (@var{A}) +@deftypefnx {} {[@var{R}, @var{p}] =} chol (@var{A}) +@deftypefnx {} {[@var{R}, @var{p}, @var{Q}] =} chol (@var{A}) +@deftypefnx {} {[@var{R}, @var{p}, @var{Q}] =} chol (@var{A}, "vector") +@deftypefnx {} {[@var{L}, @dots{}] =} chol (@dots{}, "lower") +@deftypefnx {} {[@var{R}, @dots{}] =} chol (@dots{}, "upper") +@cindex Cholesky factorization +Compute the upper Cholesky@tie{}factor, @var{R}, of the real symmetric +or complex Hermitian positive definite matrix @var{A}. + +The upper Cholesky@tie{}factor @var{R} is computed by using the upper +triangular part of matrix @var{A} and is defined by +@tex +$ R^T R = A $. +@end tex +@ifnottex + +@example +@var{R}' * @var{R} = @var{A}. +@end example + +@end ifnottex + +Calling @code{chol} using the optional @qcode{"upper"} flag has the +same behavior. In contrast, using the optional @qcode{"lower"} flag, +@code{chol} returns the lower triangular factorization, computed by using +the lower triangular part of matrix @var{A}, such that +@tex +$ L L^T = A $. +@end tex +@ifnottex + +@example +@var{L} * @var{L}' = @var{A}. +@end example + +@end ifnottex + +Called with one output argument @code{chol} fails if matrix @var{A} is +not positive definite. Note that if matrix @var{A} is not real symmetric +or complex Hermitian then the lower triangular part is considered to be +the (complex conjugate) transpose of the upper triangular part, or vice +versa, given the @qcode{"lower"} flag. + +Called with two or more output arguments @var{p} flags whether the matrix +@var{A} was positive definite and @code{chol} does not fail. A zero value +of @var{p} indicates that matrix @var{A} is positive definite and @var{R} +gives the factorization. Otherwise, @var{p} will have a positive value. + +If called with three output arguments matrix @var{A} must be sparse and +a sparsity preserving row/column permutation is applied to matrix @var{A} +prior to the factorization. That is @var{R} is the factorization of +@code{@var{A}(@var{Q},@var{Q})} such that +@tex +$ R^T R = Q^T A Q$. +@end tex +@ifnottex + +@example +@var{R}' * @var{R} = @var{Q}' * @var{A} * @var{Q}. +@end example + +@end ifnottex + +The sparsity preserving permutation is generally returned as a matrix. +However, given the optional flag @qcode{"vector"}, @var{Q} will be +returned as a vector such that +@tex +$ R^T R = A (Q, Q)$. +@end tex +@ifnottex + +@example +@var{R}' * @var{R} = @var{A}(@var{Q}, @var{Q}). +@end example + +@end ifnottex + +In general the lower triangular factorization is significantly faster for +sparse matrices. +@seealso{hess, lu, qr, qz, schur, svd, ichol, cholinv, chol2inv, cholupdate, cholinsert, choldelete, cholshift} +@end deftypefn */) +{ + int nargin = args.length (); + + if (nargin < 1 || nargin > 3 || nargout > 3) + print_usage (); + if (nargout > 2 && ! args(0).issparse ()) + error ("chol: using three output arguments, matrix A must be sparse"); + + bool LLt = false; + bool vecout = false; + + int n = 1; + while (n < nargin) + { + std::string tmp = args(n++).xstring_value ("chol: optional arguments must be strings"); + + if (octave::string::strcmpi (tmp, "vector")) + vecout = true; + else if (octave::string::strcmpi (tmp, "lower")) + LLt = true; + else if (octave::string::strcmpi (tmp, "upper")) + LLt = false; + else + error (R"(chol: optional argument must be one of "vector", "lower", or "upper")"); + } + + octave_value_list retval; + octave_value arg = args(0); + + if (arg.isempty ()) + return ovl (Matrix ()); + + if (arg.issparse ()) + { + octave_idx_type info; + bool natural = (nargout != 3); + bool force = nargout > 1; + + if (arg.isreal ()) + { + SparseMatrix m = arg.sparse_matrix_value (); + + octave::math::sparse_chol<SparseMatrix> fact (m, info, natural, force); + + if (nargout == 3) + { + if (vecout) + retval(2) = fact.perm (); + else + retval(2) = fact.Q (); + } + + if (nargout >= 2 || info == 0) + { + retval(1) = info; + if (LLt) + retval(0) = fact.L (); + else + retval(0) = fact.R (); + } + else + error ("chol: input matrix must be positive definite"); + } + else if (arg.iscomplex ()) + { + SparseComplexMatrix m = arg.sparse_complex_matrix_value (); + + octave::math::sparse_chol<SparseComplexMatrix> fact (m, info, natural, force); + + if (nargout == 3) + { + if (vecout) + retval(2) = fact.perm (); + else + retval(2) = fact.Q (); + } + + if (nargout >= 2 || info == 0) + { + retval(1) = info; + if (LLt) + retval(0) = fact.L (); + else + retval(0) = fact.R (); + } + else + error ("chol: input matrix must be positive definite"); + } + else + err_wrong_type_arg ("chol", arg); + } + else if (arg.is_single_type ()) + { + if (vecout) + error (R"(chol: A must be sparse for the "vector" option)"); + if (arg.isreal ()) + { + FloatMatrix m = arg.float_matrix_value (); + + octave_idx_type info; + + octave::math::chol<FloatMatrix> fact (m, info, LLt != true); + + if (nargout == 2 || info == 0) + retval = ovl (get_chol (fact), info); + else + error ("chol: input matrix must be positive definite"); + } + else if (arg.iscomplex ()) + { + FloatComplexMatrix m = arg.float_complex_matrix_value (); + + octave_idx_type info; + + octave::math::chol<FloatComplexMatrix> fact (m, info, LLt != true); + + if (nargout == 2 || info == 0) + retval = ovl (get_chol (fact), info); + else + error ("chol: input matrix must be positive definite"); + } + else + err_wrong_type_arg ("chol", arg); + } + else + { + if (vecout) + error (R"(chol: A must be sparse for the "vector" option)"); + if (arg.isreal ()) + { + Matrix m = arg.matrix_value (); + + octave_idx_type info; + + octave::math::chol<Matrix> fact (m, info, LLt != true); + + if (nargout == 2 || info == 0) + retval = ovl (get_chol (fact), info); + else + error ("chol: input matrix must be positive definite"); + } + else if (arg.iscomplex ()) + { + ComplexMatrix m = arg.complex_matrix_value (); + + octave_idx_type info; + + octave::math::chol<ComplexMatrix> fact (m, info, LLt != true); + + if (nargout == 2 || info == 0) + retval = ovl (get_chol (fact), info); + else + error ("chol: input matrix must be positive definite"); + } + else + err_wrong_type_arg ("chol", arg); + } + + return retval; +} + +/* +%!assert (chol ([2, 1; 1, 1]), [sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)], sqrt (eps)) +%!assert (chol (single ([2, 1; 1, 1])), single ([sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)]), sqrt (eps ("single"))) + +%!assert (chol ([2, 1; 1, 1], "upper"), [sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)], +%! sqrt (eps)) +%!assert (chol ([2, 1; 1, 1], "lower"), [sqrt(2), 0; 1/sqrt(2), 1/sqrt(2)], +%! sqrt (eps)) + +%!assert (chol ([2, 1; 1, 1], "lower"), chol ([2, 1; 1, 1], "LoweR")) +%!assert (chol ([2, 1; 1, 1], "upper"), chol ([2, 1; 1, 1], "Upper")) + +## Check the "vector" option which only affects the 3rd argument and +## is only valid for sparse input. +%!testif HAVE_CHOLMOD +%! a = sparse ([2 1; 1 1]); +%! r = sparse ([sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)]); +%! [rd, pd, qd] = chol (a); +%! [rv, pv, qv] = chol (a, "vector"); +%! assert (r, rd, eps) +%! assert (r, rv, eps) +%! assert (pd, 0) +%! assert (pd, pv) +%! assert (qd, sparse (eye (2))) +%! assert (qv, [1 2]) +%! +%! [rv, pv, qv] = chol (a, "Vector"); # check case sensitivity +%! assert (r, rv, eps) +%! assert (pd, pv) +%! assert (qv, [1 2]) + +%!testif HAVE_CHOLMOD <*42587> +%! A = sparse ([1 0 8;0 1 8;8 8 1]); +%! [Q, p] = chol (A); +%! assert (p != 0); + +%!error chol () +%!error <matrix must be positive definite> chol ([1, 2; 3, 4]) +%!error <requires square matrix> chol ([1, 2; 3, 4; 5, 6]) +%!error <optional arguments must be strings> chol (1, 2) +%!error <optional argument must be one of "vector", "lower"> chol (1, "foobar") +%!error <matrix A must be sparse> [L,p,Q] = chol ([1, 2; 3, 4]) +%!error <A must be sparse> [L, p] = chol ([1, 2; 3, 4], "vector") +*/ + +DEFUN (cholinv, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {} cholinv (@var{A}) +Compute the inverse of the symmetric positive definite matrix @var{A} using +the Cholesky@tie{}factorization. +@seealso{chol, chol2inv, inv} +@end deftypefn */) +{ + if (args.length () != 1) + print_usage (); + + octave_value retval; + octave_value arg = args(0); + + octave_idx_type nr = arg.rows (); + octave_idx_type nc = arg.columns (); + + if (nr == 0 || nc == 0) + retval = Matrix (); + else + { + if (arg.issparse ()) + { + octave_idx_type info; + + if (arg.isreal ()) + { + SparseMatrix m = arg.sparse_matrix_value (); + + octave::math::sparse_chol<SparseMatrix> chol (m, info); + + if (info == 0) + retval = chol.inverse (); + else + error ("cholinv: A must be positive definite"); + } + else if (arg.iscomplex ()) + { + SparseComplexMatrix m = arg.sparse_complex_matrix_value (); + + octave::math::sparse_chol<SparseComplexMatrix> chol (m, info); + + if (info == 0) + retval = chol.inverse (); + else + error ("cholinv: A must be positive definite"); + } + else + err_wrong_type_arg ("cholinv", arg); + } + else if (arg.is_single_type ()) + { + if (arg.isreal ()) + { + FloatMatrix m = arg.float_matrix_value (); + + octave_idx_type info; + octave::math::chol<FloatMatrix> chol (m, info); + if (info == 0) + retval = chol.inverse (); + else + error ("cholinv: A must be positive definite"); + } + else if (arg.iscomplex ()) + { + FloatComplexMatrix m = arg.float_complex_matrix_value (); + + octave_idx_type info; + octave::math::chol<FloatComplexMatrix> chol (m, info); + if (info == 0) + retval = chol.inverse (); + else + error ("cholinv: A must be positive definite"); + } + else + err_wrong_type_arg ("chol", arg); + } + else + { + if (arg.isreal ()) + { + Matrix m = arg.matrix_value (); + + octave_idx_type info; + octave::math::chol<Matrix> chol (m, info); + if (info == 0) + retval = chol.inverse (); + else + error ("cholinv: A must be positive definite"); + } + else if (arg.iscomplex ()) + { + ComplexMatrix m = arg.complex_matrix_value (); + + octave_idx_type info; + octave::math::chol<ComplexMatrix> chol (m, info); + if (info == 0) + retval = chol.inverse (); + else + error ("cholinv: A must be positive definite"); + } + else + err_wrong_type_arg ("chol", arg); + } + } + + return retval; +} + +/* +%!shared A, Ainv +%! A = [2,0.2;0.2,1]; +%! Ainv = inv (A); +%!test +%! Ainv1 = cholinv (A); +%! assert (norm (Ainv-Ainv1), 0, 1e-10); +%!testif HAVE_CHOLMOD +%! Ainv2 = inv (sparse (A)); +%! assert (norm (Ainv-Ainv2), 0, 1e-10); +%!testif HAVE_CHOLMOD +%! Ainv3 = cholinv (sparse (A)); +%! assert (norm (Ainv-Ainv3), 0, 1e-10); +*/ + +DEFUN (chol2inv, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {} chol2inv (@var{U}) +Invert a symmetric, positive definite square matrix from its Cholesky +decomposition, @var{U}. + +Note that @var{U} should be an upper-triangular matrix with positive +diagonal elements. @code{chol2inv (@var{U})} provides +@code{inv (@var{U}'*@var{U})} but it is much faster than using @code{inv}. +@seealso{chol, cholinv, inv} +@end deftypefn */) +{ + if (args.length () != 1) + print_usage (); + + octave_value retval; + + octave_value arg = args(0); + + octave_idx_type nr = arg.rows (); + octave_idx_type nc = arg.columns (); + + if (nr == 0 || nc == 0) + retval = Matrix (); + else + { + if (arg.issparse ()) + { + if (arg.isreal ()) + { + SparseMatrix r = arg.sparse_matrix_value (); + + retval = octave::math::chol2inv (r); + } + else if (arg.iscomplex ()) + { + SparseComplexMatrix r = arg.sparse_complex_matrix_value (); + + retval = octave::math::chol2inv (r); + } + else + err_wrong_type_arg ("chol2inv", arg); + } + else if (arg.is_single_type ()) + { + if (arg.isreal ()) + { + FloatMatrix r = arg.float_matrix_value (); + + retval = octave::math::chol2inv (r); + } + else if (arg.iscomplex ()) + { + FloatComplexMatrix r = arg.float_complex_matrix_value (); + + retval = octave::math::chol2inv (r); + } + else + err_wrong_type_arg ("chol2inv", arg); + + } + else + { + if (arg.isreal ()) + { + Matrix r = arg.matrix_value (); + + retval = octave::math::chol2inv (r); + } + else if (arg.iscomplex ()) + { + ComplexMatrix r = arg.complex_matrix_value (); + + retval = octave::math::chol2inv (r); + } + else + err_wrong_type_arg ("chol2inv", arg); + } + } + + return retval; +} + +/* + +## Test for bug #36437 +%!function sparse_chol2inv (T, tol) +%! iT = inv (T); +%! ciT = chol2inv (chol (T)); +%! assert (ciT, iT, tol); +%! assert (chol2inv (chol ( full (T))), ciT, tol*2); +%!endfunction + +%!testif HAVE_CHOLMOD +%! A = gallery ("poisson", 3); +%! sparse_chol2inv (A, eps); + +%!testif HAVE_CHOLMOD +%! n = 10; +%! B = spdiags (ones (n, 1) * [1 2 1], [-1 0 1], n, n); +%! sparse_chol2inv (B, eps*100); + +%!testif HAVE_CHOLMOD +%! C = gallery("tridiag", 5); +%! sparse_chol2inv (C, eps*10); + +%!testif HAVE_CHOLMOD +%! D = gallery("wathen", 1, 1); +%! sparse_chol2inv (D, eps*10^4); + +*/ + +DEFUN (cholupdate, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {[@var{R1}, @var{info}] =} cholupdate (@var{R}, @var{u}, @var{op}) +Update or downdate a Cholesky@tie{}factorization. + +Given an upper triangular matrix @var{R} and a column vector @var{u}, +attempt to determine another upper triangular matrix @var{R1} such that + +@itemize @bullet +@item +@var{R1}'*@var{R1} = @var{R}'*@var{R} + @var{u}*@var{u}' +if @var{op} is @qcode{"+"} + +@item +@var{R1}'*@var{R1} = @var{R}'*@var{R} - @var{u}*@var{u}' +if @var{op} is @qcode{"-"} +@end itemize + +If @var{op} is @qcode{"-"}, @var{info} is set to + +@itemize +@item 0 if the downdate was successful, + +@item 1 if @var{R}'*@var{R} - @var{u}*@var{u}' is not positive definite, + +@item 2 if @var{R} is singular. +@end itemize + +If @var{info} is not present, an error message is printed in cases 1 and 2. +@seealso{chol, cholinsert, choldelete, cholshift} +@end deftypefn */) +{ + int nargin = args.length (); + + if (nargin < 2 || nargin > 3) + print_usage (); + + octave_value argr = args(0); + octave_value argu = args(1); + + if (! argr.isnumeric () || ! argu.isnumeric () + || (nargin > 2 && ! args(2).is_string ())) + print_usage (); + + octave_value_list retval (nargout == 2 ? 2 : 1); + + octave_idx_type n = argr.rows (); + + std::string op = (nargin < 3) ? "+" : args(2).string_value (); + + bool down = (op == "-"); + + if (! down && op != "+") + error (R"(cholupdate: OP must be "+" or "-")"); + + if (argr.columns () != n || argu.rows () != n || argu.columns () != 1) + error ("cholupdate: dimension mismatch between R and U"); + + int err = 0; + if (argr.is_single_type () || argu.is_single_type ()) + { + if (argr.isreal () && argu.isreal ()) + { + // real case + FloatMatrix R = argr.float_matrix_value (); + FloatColumnVector u = argu.float_column_vector_value (); + + octave::math::chol<FloatMatrix> fact; + fact.set (R); + + if (down) + err = fact.downdate (u); + else + fact.update (u); + + retval = ovl (get_chol_r (fact)); + } + else + { + // complex case + FloatComplexMatrix R = argr.float_complex_matrix_value (); + FloatComplexColumnVector u + = argu.float_complex_column_vector_value (); + + octave::math::chol<FloatComplexMatrix> fact; + fact.set (R); + + if (down) + err = fact.downdate (u); + else + fact.update (u); + + retval = ovl (get_chol_r (fact)); + } + } + else + { + if (argr.isreal () && argu.isreal ()) + { + // real case + Matrix R = argr.matrix_value (); + ColumnVector u = argu.column_vector_value (); + + octave::math::chol<Matrix> fact; + fact.set (R); + + if (down) + err = fact.downdate (u); + else + fact.update (u); + + retval = ovl (get_chol_r (fact)); + } + else + { + // complex case + ComplexMatrix R = argr.complex_matrix_value (); + ComplexColumnVector u = argu.complex_column_vector_value (); + + octave::math::chol<ComplexMatrix> fact; + fact.set (R); + + if (down) + err = fact.downdate (u); + else + fact.update (u); + + retval = ovl (get_chol_r (fact)); + } + } + + if (nargout > 1) + retval(1) = err; + else if (err == 1) + error ("cholupdate: downdate violates positiveness"); + else if (err == 2) + error ("cholupdate: singular matrix"); + + return retval; +} + +/* +%!shared A, u, Ac, uc +%! A = [ 0.436997 -0.131721 0.124120 -0.061673 ; +%! -0.131721 0.738529 0.019851 -0.140295 ; +%! 0.124120 0.019851 0.354879 -0.059472 ; +%! -0.061673 -0.140295 -0.059472 0.600939 ]; +%! +%! u = [ 0.98950 ; +%! 0.39844 ; +%! 0.63484 ; +%! 0.13351 ]; +%! Ac = [ 0.5585528 + 0.0000000i -0.1662088 - 0.0315341i 0.0107873 + 0.0236411i -0.0276775 - 0.0186073i ; +%! -0.1662088 + 0.0315341i 0.6760061 + 0.0000000i 0.0011452 - 0.0475528i 0.0145967 + 0.0247641i ; +%! 0.0107873 - 0.0236411i 0.0011452 + 0.0475528i 0.6263149 - 0.0000000i -0.1585837 - 0.0719763i ; +%! -0.0276775 + 0.0186073i 0.0145967 - 0.0247641i -0.1585837 + 0.0719763i 0.6034234 - 0.0000000i ]; +%! +%! uc = [ 0.54267 + 0.91519i ; +%! 0.99647 + 0.43141i ; +%! 0.83760 + 0.68977i ; +%! 0.39160 + 0.90378i ]; + +%!test +%! R = chol (A); +%! R1 = cholupdate (R, u); +%! assert (norm (triu (R1)-R1, Inf), 0); +%! assert (norm (R1'*R1 - R'*R - u*u', Inf) < 1e1*eps); +%! +%! R1 = cholupdate (R1, u, "-"); +%! assert (norm (triu (R1)-R1, Inf), 0); +%! assert (norm (R1 - R, Inf) < 1e1*eps); + +%!test +%! R = chol (Ac); +%! R1 = cholupdate (R, uc); +%! assert (norm (triu (R1)-R1, Inf), 0); +%! assert (norm (R1'*R1 - R'*R - uc*uc', Inf) < 1e1*eps); +%! +%! R1 = cholupdate (R1, uc, "-"); +%! assert (norm (triu (R1)-R1, Inf), 0); +%! assert (norm (R1 - R, Inf) < 1e1*eps); + +%!test +%! R = chol (single (A)); +%! R1 = cholupdate (R, single (u)); +%! assert (norm (triu (R1)-R1, Inf), single (0)); +%! assert (norm (R1'*R1 - R'*R - single (u*u'), Inf) < 1e1*eps ("single")); +%! +%! R1 = cholupdate (R1, single (u), "-"); +%! assert (norm (triu (R1)-R1, Inf), single (0)); +%! assert (norm (R1 - R, Inf) < 2e1*eps ("single")); + +%!test +%! R = chol (single (Ac)); +%! R1 = cholupdate (R, single (uc)); +%! assert (norm (triu (R1)-R1, Inf), single (0)); +%! assert (norm (R1'*R1 - R'*R - single (uc*uc'), Inf) < 1e1*eps ("single")); +%! +%! R1 = cholupdate (R1, single (uc), "-"); +%! assert (norm (triu (R1)-R1, Inf), single (0)); +%! assert (norm (R1 - R, Inf) < 2e1*eps ("single")); +*/ + +DEFUN (cholinsert, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {@var{R1} =} cholinsert (@var{R}, @var{j}, @var{u}) +@deftypefnx {} {[@var{R1}, @var{info}] =} cholinsert (@var{R}, @var{j}, @var{u}) +Update a Cholesky factorization given a row or column to insert in the +original factored matrix. + +Given a Cholesky@tie{}factorization of a real symmetric or complex Hermitian +positive definite matrix @w{@var{A} = @var{R}'*@var{R}}, @var{R}@tie{}upper +triangular, return the Cholesky@tie{}factorization of +@var{A1}, where @w{A1(p,p) = A}, @w{A1(:,j) = A1(j,:)' = u} and +@w{p = [1:j-1,j+1:n+1]}. @w{u(j)} should be positive. + +On return, @var{info} is set to + +@itemize +@item 0 if the insertion was successful, + +@item 1 if @var{A1} is not positive definite, + +@item 2 if @var{R} is singular. +@end itemize + +If @var{info} is not present, an error message is printed in cases 1 and 2. +@seealso{chol, cholupdate, choldelete, cholshift} +@end deftypefn */) +{ + if (args.length () != 3) + print_usage (); + + octave_value argr = args(0); + octave_value argj = args(1); + octave_value argu = args(2); + + if (! argr.isnumeric () || ! argu.isnumeric () + || ! argj.is_real_scalar ()) + print_usage (); + + octave_idx_type n = argr.rows (); + octave_idx_type j = argj.scalar_value (); + + if (argr.columns () != n || argu.rows () != n+1 || argu.columns () != 1) + error ("cholinsert: dimension mismatch between R and U"); + + if (j < 1 || j > n+1) + error ("cholinsert: index J out of range"); + + octave_value_list retval (nargout == 2 ? 2 : 1); + + int err = 0; + if (argr.is_single_type () || argu.is_single_type ()) + { + if (argr.isreal () && argu.isreal ()) + { + // real case + FloatMatrix R = argr.float_matrix_value (); + FloatColumnVector u = argu.float_column_vector_value (); + + octave::math::chol<FloatMatrix> fact; + fact.set (R); + err = fact.insert_sym (u, j-1); + + retval = ovl (get_chol_r (fact)); + } + else + { + // complex case + FloatComplexMatrix R = argr.float_complex_matrix_value (); + FloatComplexColumnVector u + = argu.float_complex_column_vector_value (); + + octave::math::chol<FloatComplexMatrix> fact; + fact.set (R); + err = fact.insert_sym (u, j-1); + + retval = ovl (get_chol_r (fact)); + } + } + else + { + if (argr.isreal () && argu.isreal ()) + { + // real case + Matrix R = argr.matrix_value (); + ColumnVector u = argu.column_vector_value (); + + octave::math::chol<Matrix> fact; + fact.set (R); + err = fact.insert_sym (u, j-1); + + retval = ovl (get_chol_r (fact)); + } + else + { + // complex case + ComplexMatrix R = argr.complex_matrix_value (); + ComplexColumnVector u = argu.complex_column_vector_value (); + + octave::math::chol<ComplexMatrix> fact; + fact.set (R); + err = fact.insert_sym (u, j-1); + + retval = ovl (get_chol_r (fact)); + } + } + + if (nargout > 1) + retval(1) = err; + else if (err == 1) + error ("cholinsert: insertion violates positiveness"); + else if (err == 2) + error ("cholinsert: singular matrix"); + else if (err == 3) + error ("cholinsert: diagonal element must be real"); + + return retval; +} + +/* +%!test +%! u2 = [ 0.35080 ; +%! 0.63930 ; +%! 3.31057 ; +%! -0.13825 ; +%! 0.45266 ]; +%! +%! R = chol (A); +%! +%! j = 3; p = [1:j-1, j+1:5]; +%! R1 = cholinsert (R, j, u2); +%! A1 = R1'*R1; +%! +%! assert (norm (triu (R1)-R1, Inf), 0); +%! assert (norm (A1(p,p) - A, Inf) < 1e1*eps); + +%!test +%! u2 = [ 0.35080 + 0.04298i; +%! 0.63930 + 0.23778i; +%! 3.31057 + 0.00000i; +%! -0.13825 + 0.19879i; +%! 0.45266 + 0.50020i]; +%! +%! R = chol (Ac); +%! +%! j = 3; p = [1:j-1, j+1:5]; +%! R1 = cholinsert (R, j, u2); +%! A1 = R1'*R1; +%! +%! assert (norm (triu (R1)-R1, Inf), 0); +%! assert (norm (A1(p,p) - Ac, Inf) < 1e1*eps); + +%!test +%! u2 = single ([ 0.35080 ; +%! 0.63930 ; +%! 3.31057 ; +%! -0.13825 ; +%! 0.45266 ]); +%! +%! R = chol (single (A)); +%! +%! j = 3; p = [1:j-1, j+1:5]; +%! R1 = cholinsert (R, j, u2); +%! A1 = R1'*R1; +%! +%! assert (norm (triu (R1)-R1, Inf), single (0)); +%! assert (norm (A1(p,p) - A, Inf) < 1e1*eps ("single")); + +%!test +%! u2 = single ([ 0.35080 + 0.04298i; +%! 0.63930 + 0.23778i; +%! 3.31057 + 0.00000i; +%! -0.13825 + 0.19879i; +%! 0.45266 + 0.50020i]); +%! +%! R = chol (single (Ac)); +%! +%! j = 3; p = [1:j-1, j+1:5]; +%! R1 = cholinsert (R, j, u2); +%! A1 = R1'*R1; +%! +%! assert (norm (triu (R1)-R1, Inf), single (0)); +%! assert (norm (A1(p,p) - single (Ac), Inf) < 2e1*eps ("single")); + +%!test +%! cu = chol (triu (A), "upper"); +%! cl = chol (tril (A), "lower"); +%! assert (cu, cl', eps); + +%!test +%! cca = chol (Ac); +%! +%! ccal = chol (Ac, "lower"); +%! ccal2 = chol (tril (Ac), "lower"); +%! +%! ccau = chol (Ac, "upper"); +%! ccau2 = chol (triu (Ac), "upper"); +%! +%! assert (cca'*cca, Ac, eps); +%! assert (ccau'*ccau, Ac, eps); +%! assert (ccau2'*ccau2, Ac, eps); +%! +%! assert (cca, ccal', eps); +%! assert (cca, ccau, eps); +%! assert (cca, ccal2', eps); +%! assert (cca, ccau2, eps); + +%!test +%! cca = chol (single (Ac)); +%! +%! ccal = chol (single (Ac), "lower"); +%! ccal2 = chol (tril (single (Ac)), "lower"); +%! +%! ccau = chol (single (Ac), "upper"); +%! ccau2 = chol (triu (single (Ac)), "upper"); +%! +%! assert (cca'*cca, single (Ac), eps ("single")); +%! assert (ccau'*ccau, single (Ac), eps ("single")); +%! assert (ccau2'*ccau2, single (Ac), eps ("single")); +%! +%! assert (cca, ccal', eps ("single")); +%! assert (cca, ccau, eps ("single")); +%! assert (cca, ccal2', eps ("single")); +%! assert (cca, ccau2, eps ("single")); + +%!test +%! a = [12, 2, 3, 4; +%! 2, 14, 5, 3; +%! 3, 5, 16, 6; +%! 4, 3, 6, 16]; +%! +%! b = [0, 1, 2, 3; +%! -1, 0, 1, 2; +%! -2, -1, 0, 1; +%! -3, -2, -1, 0]; +%! +%! ca = a + i*b; +%! +%! cca = chol (ca); +%! +%! ccal = chol (ca, "lower"); +%! ccal2 = chol (tril (ca), "lower"); +%! +%! ccau = chol (ca, "upper"); +%! ccau2 = chol (triu (ca), "upper"); +%! +%! assert (cca'*cca, ca, 16*eps); +%! assert (ccau'*ccau, ca, 16*eps); +%! assert (ccau2'*ccau2, ca, 16*eps); +%! +%! assert (cca, ccal', 16*eps); +%! assert (cca, ccau, 16*eps); +%! assert (cca, ccal2', 16*eps); +%! assert (cca, ccau2, 16*eps); +*/ + +DEFUN (choldelete, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{R1} =} choldelete (@var{R}, @var{j}) +Update a Cholesky factorization given a row or column to delete from the +original factored matrix. + +Given a Cholesky@tie{}factorization of a real symmetric or complex Hermitian +positive definite matrix @w{@var{A} = @var{R}'*@var{R}}, @var{R}@tie{}upper +triangular, return the Cholesky@tie{}factorization of @w{A(p,p)}, where +@w{p = [1:j-1,j+1:n+1]}. +@seealso{chol, cholupdate, cholinsert, cholshift} +@end deftypefn */) +{ + if (args.length () != 2) + print_usage (); + + octave_value argr = args(0); + octave_value argj = args(1); + + if (! argr.isnumeric () || ! argj.is_real_scalar ()) + print_usage (); + + octave_idx_type n = argr.rows (); + octave_idx_type j = argj.scalar_value (); + + if (argr.columns () != n) + err_square_matrix_required ("choldelete", "R"); + + if (j < 0 && j > n) + error ("choldelete: index J out of range"); + + octave_value_list retval; + + if (argr.is_single_type ()) + { + if (argr.isreal ()) + { + // real case + FloatMatrix R = argr.float_matrix_value (); + + octave::math::chol<FloatMatrix> fact; + fact.set (R); + fact.delete_sym (j-1); + + retval = ovl (get_chol_r (fact)); + } + else + { + // complex case + FloatComplexMatrix R = argr.float_complex_matrix_value (); + + octave::math::chol<FloatComplexMatrix> fact; + fact.set (R); + fact.delete_sym (j-1); + + retval = ovl (get_chol_r (fact)); + } + } + else + { + if (argr.isreal ()) + { + // real case + Matrix R = argr.matrix_value (); + + octave::math::chol<Matrix> fact; + fact.set (R); + fact.delete_sym (j-1); + + retval = ovl (get_chol_r (fact)); + } + else + { + // complex case + ComplexMatrix R = argr.complex_matrix_value (); + + octave::math::chol<ComplexMatrix> fact; + fact.set (R); + fact.delete_sym (j-1); + + retval = ovl (get_chol_r (fact)); + } + } + + return retval; +} + +/* +%!test +%! R = chol (A); +%! +%! j = 3; p = [1:j-1,j+1:4]; +%! R1 = choldelete (R, j); +%! +%! assert (norm (triu (R1)-R1, Inf), 0); +%! assert (norm (R1'*R1 - A(p,p), Inf) < 1e1*eps); + +%!test +%! R = chol (Ac); +%! +%! j = 3; p = [1:j-1,j+1:4]; +%! R1 = choldelete (R, j); +%! +%! assert (norm (triu (R1)-R1, Inf), 0); +%! assert (norm (R1'*R1 - Ac(p,p), Inf) < 1e1*eps); + +%!test +%! R = chol (single (A)); +%! +%! j = 3; p = [1:j-1,j+1:4]; +%! R1 = choldelete (R, j); +%! +%! assert (norm (triu (R1)-R1, Inf), single (0)); +%! assert (norm (R1'*R1 - single (A(p,p)), Inf) < 1e1*eps ("single")); + +%!test +%! R = chol (single (Ac)); +%! +%! j = 3; p = [1:j-1,j+1:4]; +%! R1 = choldelete (R,j); +%! +%! assert (norm (triu (R1)-R1, Inf), single (0)); +%! assert (norm (R1'*R1 - single (Ac(p,p)), Inf) < 1e1*eps ("single")); +*/ + +DEFUN (cholshift, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{R1} =} cholshift (@var{R}, @var{i}, @var{j}) +Update a Cholesky factorization given a range of columns to shift in the +original factored matrix. + +Given a Cholesky@tie{}factorization of a real symmetric or complex Hermitian +positive definite matrix @w{@var{A} = @var{R}'*@var{R}}, @var{R}@tie{}upper +triangular, return the Cholesky@tie{}factorization of +@w{@var{A}(p,p)}, where @w{p} is the permutation @* +@code{p = [1:i-1, shift(i:j, 1), j+1:n]} if @w{@var{i} < @var{j}} @* + or @* +@code{p = [1:j-1, shift(j:i,-1), i+1:n]} if @w{@var{j} < @var{i}}. @* + +@seealso{chol, cholupdate, cholinsert, choldelete} +@end deftypefn */) +{ + if (args.length () != 3) + print_usage (); + + octave_value argr = args(0); + octave_value argi = args(1); + octave_value argj = args(2); + + if (! argr.isnumeric () || ! argi.is_real_scalar () + || ! argj.is_real_scalar ()) + print_usage (); + + octave_idx_type n = argr.rows (); + octave_idx_type i = argi.scalar_value (); + octave_idx_type j = argj.scalar_value (); + + if (argr.columns () != n) + err_square_matrix_required ("cholshift", "R"); + + if (j < 0 || j > n+1 || i < 0 || i > n+1) + error ("cholshift: index I or J is out of range"); + + octave_value_list retval; + + if (argr.is_single_type () && argi.is_single_type () + && argj.is_single_type ()) + { + if (argr.isreal ()) + { + // real case + FloatMatrix R = argr.float_matrix_value (); + + octave::math::chol<FloatMatrix> fact; + fact.set (R); + fact.shift_sym (i-1, j-1); + + retval = ovl (get_chol_r (fact)); + } + else + { + // complex case + FloatComplexMatrix R = argr.float_complex_matrix_value (); + + octave::math::chol<FloatComplexMatrix> fact; + fact.set (R); + fact.shift_sym (i-1, j-1); + + retval = ovl (get_chol_r (fact)); + } + } + else + { + if (argr.isreal ()) + { + // real case + Matrix R = argr.matrix_value (); + + octave::math::chol<Matrix> fact; + fact.set (R); + fact.shift_sym (i-1, j-1); + + retval = ovl (get_chol_r (fact)); + } + else + { + // complex case + ComplexMatrix R = argr.complex_matrix_value (); + + octave::math::chol<ComplexMatrix> fact; + fact.set (R); + fact.shift_sym (i-1, j-1); + + retval = ovl (get_chol_r (fact)); + } + } + + return retval; +} + +/* +%!test +%! R = chol (A); +%! +%! i = 1; j = 3; p = [1:i-1, shift(i:j,-1), j+1:4]; +%! R1 = cholshift (R, i, j); +%! +%! assert (norm (triu (R1)-R1, Inf), 0); +%! assert (norm (R1'*R1 - A(p,p), Inf) < 1e1*eps); +%! +%! j = 1; i = 3; p = [1:j-1, shift(j:i,+1), i+1:4]; +%! R1 = cholshift (R, i, j); +%! +%! assert (norm (triu (R1) - R1, Inf), 0); +%! assert (norm (R1'*R1 - A(p,p), Inf) < 1e1*eps); + +%!test +%! R = chol (Ac); +%! +%! i = 1; j = 3; p = [1:i-1, shift(i:j,-1), j+1:4]; +%! R1 = cholshift (R, i, j); +%! +%! assert (norm (triu (R1)-R1, Inf), 0); +%! assert (norm (R1'*R1 - Ac(p,p), Inf) < 1e1*eps); +%! +%! j = 1; i = 3; p = [1:j-1, shift(j:i,+1), i+1:4]; +%! R1 = cholshift (R, i, j); +%! +%! assert (norm (triu (R1)-R1, Inf), 0); +%! assert (norm (R1'*R1 - Ac(p,p), Inf) < 1e1*eps); + +%!test +%! R = chol (single (A)); +%! +%! i = 1; j = 3; p = [1:i-1, shift(i:j,-1), j+1:4]; +%! R1 = cholshift (R, i, j); +%! +%! assert (norm (triu (R1)-R1, Inf), 0); +%! assert (norm (R1'*R1 - single (A(p,p)), Inf) < 1e1*eps ("single")); +%! +%! j = 1; i = 3; p = [1:j-1, shift(j:i,+1), i+1:4]; +%! R1 = cholshift (R, i, j); +%! +%! assert (norm (triu (R1)-R1, Inf), 0); +%! assert (norm (R1'*R1 - single (A(p,p)), Inf) < 1e1*eps ("single")); + +%!test +%! R = chol (single (Ac)); +%! +%! i = 1; j = 3; p = [1:i-1, shift(i:j,-1), j+1:4]; +%! R1 = cholshift (R, i, j); +%! +%! assert (norm (triu (R1)-R1, Inf), 0); +%! assert (norm (R1'*R1 - single (Ac(p,p)), Inf) < 1e1*eps ("single")); +%! +%! j = 1; i = 3; p = [1:j-1, shift(j:i,+1), i+1:4]; +%! R1 = cholshift (R, i, j); +%! +%! assert (norm (triu (R1)-R1, Inf), 0); +%! assert (norm (R1'*R1 - single (Ac(p,p)), Inf) < 1e1*eps ("single")); +*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/colamd.cc Wed Jan 29 06:30:40 2020 -0500 @@ -0,0 +1,774 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 1998-2020 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// This file is part of Octave. +// +// 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 3 of the License, 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 Octave; see the file COPYING. If not, see +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + +// This is the octave interface to colamd, which bore the copyright given +// in the help of the functions. + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include <cstdlib> + +#include <string> + +#include "CSparse.h" +#include "dNDArray.h" +#include "dSparse.h" +#include "oct-locbuf.h" +#include "oct-sparse.h" + +#include "defun.h" +#include "error.h" +#include "errwarn.h" +#include "ovl.h" +#include "pager.h" + +// The symmetric column elimination tree code take from the Davis LDL code. +// Copyright given elsewhere in this file. +static void +symetree (const octave_idx_type *ridx, const octave_idx_type *cidx, + octave_idx_type *Parent, octave_idx_type *P, octave_idx_type n) +{ + OCTAVE_LOCAL_BUFFER (octave_idx_type, Flag, n); + OCTAVE_LOCAL_BUFFER (octave_idx_type, Pinv, (P ? n : 0)); + if (P) + // If P is present then compute Pinv, the inverse of P + for (octave_idx_type k = 0 ; k < n ; k++) + Pinv[P[k]] = k; + + for (octave_idx_type 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 + octave_idx_type kk = (P ? P[k] : k); // kth original, or permuted, column + octave_idx_type p2 = cidx[kk+1]; + for (octave_idx_type p = cidx[kk] ; p < p2 ; p++) + { + // A (i,k) is nonzero (original or permuted A) + octave_idx_type i = (P ? 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 octave_idx_type +make_set (octave_idx_type i, octave_idx_type *pp) +{ + pp[i] = i; + return i; +} + +static inline octave_idx_type +link (octave_idx_type s, octave_idx_type t, octave_idx_type *pp) +{ + pp[s] = t; + return t; +} + +static inline octave_idx_type +find (octave_idx_type i, octave_idx_type *pp) +{ + octave_idx_type p = pp[i]; + octave_idx_type gp = pp[p]; + + while (gp != p) + { + pp[i] = gp; + i = gp; + p = pp[i]; + gp = pp[p]; + } + + return p; +} + +static octave_idx_type +etdfs (octave_idx_type v, octave_idx_type *first_kid, + octave_idx_type *next_kid, octave_idx_type *post, + octave_idx_type postnum) +{ + for (octave_idx_type 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 +tree_postorder (octave_idx_type n, octave_idx_type *parent, + octave_idx_type *post) +{ + // Allocate storage for working arrays and results + OCTAVE_LOCAL_BUFFER (octave_idx_type, first_kid, n+1); + OCTAVE_LOCAL_BUFFER (octave_idx_type, next_kid, n+1); + + // Set up structure describing children + for (octave_idx_type v = 0; v <= n; first_kid[v++] = -1) + ; // do nothing + + for (octave_idx_type v = n-1; v >= 0; v--) + { + octave_idx_type 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 octave_idx_type *ridx, const octave_idx_type *colbeg, + octave_idx_type *colend, octave_idx_type *parent, + octave_idx_type nr, octave_idx_type nc) +{ + OCTAVE_LOCAL_BUFFER (octave_idx_type, root, nc); + OCTAVE_LOCAL_BUFFER (octave_idx_type, pp, nc); + OCTAVE_LOCAL_BUFFER (octave_idx_type, firstcol, nr); + + // Compute firstcol[row] = first nonzero column in row + for (octave_idx_type row = 0; row < nr; firstcol[row++] = nc) + ; // do nothing + + for (octave_idx_type col = 0; col < nc; col++) + for (octave_idx_type p = colbeg[col]; p < colend[col]; p++) + { + octave_idx_type 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 (octave_idx_type col = 0; col < nc; col++) + { + octave_idx_type cset = make_set (col, pp); + root[cset] = col; + parent[col] = nc; + for (octave_idx_type p = colbeg[col]; p < colend[col]; p++) + { + octave_idx_type row = firstcol[ridx[p]]; + if (row >= col) + continue; + octave_idx_type rset = find (row, pp); + octave_idx_type rroot = root[rset]; + if (rroot != col) + { + parent[rroot] = col; + cset = link (cset, rset, pp); + root[cset] = col; + } + } + } +} + +DEFUN (colamd, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {@var{p} =} colamd (@var{S}) +@deftypefnx {} {@var{p} =} colamd (@var{S}, @var{knobs}) +@deftypefnx {} {[@var{p}, @var{stats}] =} colamd (@var{S}) +@deftypefnx {} {[@var{p}, @var{stats}] =} colamd (@var{S}, @var{knobs}) + +Compute the column approximate minimum degree permutation. + +@code{@var{p} = colamd (@var{S})} returns the column approximate minimum +degree permutation vector for the sparse matrix @var{S}. For a +non-symmetric matrix @var{S}, @code{@var{S}(:,@var{p})} tends to have +sparser LU@tie{}factors than @var{S}. The Cholesky@tie{}factorization of +@code{@var{S}(:,@var{p})' * @var{S}(:,@var{p})} also tends to be sparser +than that of @code{@var{S}' * @var{S}}. + +@var{knobs} is an optional one- to three-element input vector. If @var{S} +is m-by-n, then rows with more than @code{max(16,@var{knobs}(1)*sqrt(n))} +entries are ignored. Columns with more than +@code{max (16,@var{knobs}(2)*sqrt(min(m,n)))} entries are removed prior to +ordering, and ordered last in the output permutation @var{p}. Only +completely dense rows or columns are removed if @code{@var{knobs}(1)} and +@code{@var{knobs}(2)} are < 0, respectively. If @code{@var{knobs}(3)} is +nonzero, @var{stats} and @var{knobs} are printed. The default is +@code{@var{knobs} = [10 10 0]}. Note that @var{knobs} differs from earlier +versions of colamd. + +@var{stats} is an optional 20-element output vector that provides data +about the ordering and the validity of the input matrix @var{S}. Ordering +statistics are in @code{@var{stats}(1:3)}. @code{@var{stats}(1)} and +@code{@var{stats}(2)} are the number of dense or empty rows and columns +ignored by @sc{colamd} and @code{@var{stats}(3)} is the number of garbage +collections performed on the internal data structure used by @sc{colamd} +(roughly of size @code{2.2 * nnz(@var{S}) + 4 * @var{m} + 7 * @var{n}} +integers). + +Octave built-in functions are intended to generate valid sparse matrices, +with no duplicate entries, with ascending row indices of the nonzeros +in each column, with a non-negative number of entries in each column (!) +and so on. If a matrix is invalid, then @sc{colamd} may or may not be able +to continue. If there are duplicate entries (a row index appears two or +more times in the same column) or if the row indices in a column are out +of order, then @sc{colamd} can correct these errors by ignoring the +duplicate entries and sorting each column of its internal copy of the +matrix @var{S} (the input matrix @var{S} is not repaired, however). If a +matrix is invalid in other ways then @sc{colamd} cannot continue, an error +message is printed, and no output arguments (@var{p} or @var{stats}) are +returned. +@sc{colamd} is thus a simple way to check a sparse matrix to see if it's +valid. + +@code{@var{stats}(4:7)} provide information if @sc{colamd} was able to +continue. The matrix is OK if @code{@var{stats}(4)} is zero, or 1 if +invalid. @code{@var{stats}(5)} is the rightmost column index that is +unsorted or contains duplicate entries, or zero if no such column exists. +@code{@var{stats}(6)} is the last seen duplicate or out-of-order row +index in the column index given by @code{@var{stats}(5)}, or zero if no +such row index exists. @code{@var{stats}(7)} is the number of duplicate +or out-of-order row indices. @code{@var{stats}(8:20)} is always zero in +the current version of @sc{colamd} (reserved for future use). + +The ordering is followed by a column elimination tree post-ordering. + +The authors of the code itself are @nospell{Stefan I. Larimore} and +@nospell{Timothy A. Davis}. The algorithm was developed in collaboration with +@nospell{John Gilbert}, Xerox PARC, and @nospell{Esmond Ng}, Oak Ridge National +Laboratory. (see @url{http://faculty.cse.tamu.edu/davis/suitesparse.html}) +@seealso{colperm, symamd, ccolamd} +@end deftypefn */) +{ +#if defined (HAVE_COLAMD) + + int nargin = args.length (); + + if (nargin < 1 || nargin > 2) + print_usage (); + + octave_value_list retval (nargout == 2 ? 2 : 1); + int spumoni = 0; + + // Get knobs + static_assert (COLAMD_KNOBS <= 40, + "colamd: # of COLAMD_KNOBS exceeded. Please report this to bugs.octave.org"); + double knob_storage[COLAMD_KNOBS]; + double *knobs = &knob_storage[0]; + COLAMD_NAME (_set_defaults) (knobs); + + // Check for user-passed knobs + if (nargin == 2) + { + NDArray User_knobs = args(1).array_value (); + int nel_User_knobs = User_knobs.numel (); + + if (nel_User_knobs > 0) + knobs[COLAMD_DENSE_ROW] = User_knobs(0); + if (nel_User_knobs > 1) + knobs[COLAMD_DENSE_COL] = User_knobs(1); + if (nel_User_knobs > 2) + spumoni = static_cast<int> (User_knobs(2)); + + // print knob settings if spumoni is set + if (spumoni) + { + + octave_stdout << "\ncolamd version " << COLAMD_MAIN_VERSION + << '.' << COLAMD_SUB_VERSION + << ", " << COLAMD_DATE << ":\n"; + + if (knobs[COLAMD_DENSE_ROW] >= 0) + octave_stdout << "knobs(1): " << User_knobs (0) + << ", rows with > max (16," + << knobs[COLAMD_DENSE_ROW] << "*sqrt (size(A,2)))" + << " entries removed\n"; + else + octave_stdout << "knobs(1): " << User_knobs (0) + << ", only completely dense rows removed\n"; + + if (knobs[COLAMD_DENSE_COL] >= 0) + octave_stdout << "knobs(2): " << User_knobs (1) + << ", cols with > max (16," + << knobs[COLAMD_DENSE_COL] << "*sqrt (size(A)))" + << " entries removed\n"; + else + octave_stdout << "knobs(2): " << User_knobs (1) + << ", only completely dense columns removed\n"; + + octave_stdout << "knobs(3): " << User_knobs (2) + << ", statistics and knobs printed\n"; + + } + } + + octave_idx_type n_row, n_col, nnz; + octave_idx_type *ridx, *cidx; + SparseComplexMatrix scm; + SparseMatrix sm; + + if (args(0).issparse ()) + { + if (args(0).iscomplex ()) + { + 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).iscomplex ()) + 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 (octave::suitesparse_integer, p, n_col+1); + for (octave_idx_type i = 0; i < n_col+1; i++) + p[i] = cidx[i]; + + octave_idx_type Alen = COLAMD_NAME (_recommended) (nnz, n_row, n_col); + OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, A, Alen); + for (octave_idx_type i = 0; i < nnz; i++) + A[i] = ridx[i]; + + // Order the columns (destroys A) + static_assert (COLAMD_STATS <= 40, + "colamd: # of COLAMD_STATS exceeded. Please report this to bugs.octave.org"); + octave::suitesparse_integer stats_storage[COLAMD_STATS]; + octave::suitesparse_integer *stats = &stats_storage[0]; + if (! COLAMD_NAME () (n_row, n_col, Alen, A, p, knobs, stats)) + { + COLAMD_NAME (_report)(stats); + + error ("colamd: internal error!"); + } + + // column elimination tree post-ordering (reuse variables) + OCTAVE_LOCAL_BUFFER (octave_idx_type, colbeg, n_col + 1); + OCTAVE_LOCAL_BUFFER (octave_idx_type, colend, n_col + 1); + OCTAVE_LOCAL_BUFFER (octave_idx_type, etree, n_col + 1); + + for (octave_idx_type 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 + tree_postorder (n_col, etree, colbeg); + + // return the permutation vector + NDArray out_perm (dim_vector (1, n_col)); + for (octave_idx_type 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_NAME (_report)(stats); + + // Return the stats vector + if (nargout == 2) + { + NDArray out_stats (dim_vector (1, COLAMD_STATS)); + for (octave_idx_type 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; + +#else + + octave_unused_parameter (args); + octave_unused_parameter (nargout); + + err_disabled_feature ("colamd", "COLAMD"); + +#endif +} + +DEFUN (symamd, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {@var{p} =} symamd (@var{S}) +@deftypefnx {} {@var{p} =} symamd (@var{S}, @var{knobs}) +@deftypefnx {} {[@var{p}, @var{stats}] =} symamd (@var{S}) +@deftypefnx {} {[@var{p}, @var{stats}] =} symamd (@var{S}, @var{knobs}) + +For a symmetric positive definite matrix @var{S}, returns the permutation +vector p such that @code{@var{S}(@var{p}, @var{p})} tends to have a +sparser Cholesky@tie{}factor than @var{S}. + +Sometimes @code{symamd} works well for symmetric indefinite matrices too. +The matrix @var{S} is assumed to be symmetric; only the strictly lower +triangular part is referenced. @var{S} must be square. + +@var{knobs} is an optional one- to two-element input vector. If @var{S} is +n-by-n, then rows and columns with more than +@code{max (16,@var{knobs}(1)*sqrt(n))} entries are removed prior to +ordering, and ordered last in the output permutation @var{p}. No +rows/columns are removed if @code{@var{knobs}(1) < 0}. If +@code{@var{knobs}(2)} is nonzero, @var{stats} and @var{knobs} are +printed. The default is @code{@var{knobs} = [10 0]}. Note that +@var{knobs} differs from earlier versions of @code{symamd}. + +@var{stats} is an optional 20-element output vector that provides data +about the ordering and the validity of the input matrix @var{S}. Ordering +statistics are in @code{@var{stats}(1:3)}. +@code{@var{stats}(1) = @var{stats}(2)} is the number of dense or empty rows +and columns ignored by SYMAMD and @code{@var{stats}(3)} is the number of +garbage collections performed on the internal data structure used by SYMAMD +(roughly of size @code{8.4 * nnz (tril (@var{S}, -1)) + 9 * @var{n}} +integers). + +Octave built-in functions are intended to generate valid sparse matrices, +with no duplicate entries, with ascending row indices of the nonzeros +in each column, with a non-negative number of entries in each column (!) +and so on. If a matrix is invalid, then SYMAMD may or may not be able +to continue. If there are duplicate entries (a row index appears two or +more times in the same column) or if the row indices in a column are out +of order, then SYMAMD can correct these errors by ignoring the duplicate +entries and sorting each column of its internal copy of the matrix S (the +input matrix S is not repaired, however). If a matrix is invalid in +other ways then SYMAMD cannot continue, an error message is printed, and +no output arguments (@var{p} or @var{stats}) are returned. SYMAMD is +thus a simple way to check a sparse matrix to see if it's valid. + +@code{@var{stats}(4:7)} provide information if SYMAMD was able to +continue. The matrix is OK if @code{@var{stats} (4)} is zero, or 1 +if invalid. @code{@var{stats}(5)} is the rightmost column index that +is unsorted or contains duplicate entries, or zero if no such column +exists. @code{@var{stats}(6)} is the last seen duplicate or out-of-order +row index in the column index given by @code{@var{stats}(5)}, or zero +if no such row index exists. @code{@var{stats}(7)} is the number of +duplicate or out-of-order row indices. @code{@var{stats}(8:20)} is +always zero in the current version of SYMAMD (reserved for future use). + +The ordering is followed by a column elimination tree post-ordering. + +The authors of the code itself are @nospell{Stefan I. Larimore} and +@nospell{Timothy A. Davis}. The algorithm was developed in collaboration with +@nospell{John Gilbert}, Xerox PARC, and @nospell{Esmond Ng}, Oak Ridge National +Laboratory. (see @url{http://faculty.cse.tamu.edu/davis/suitesparse.html}) +@seealso{colperm, colamd} +@end deftypefn */) +{ +#if defined (HAVE_COLAMD) + + int nargin = args.length (); + + if (nargin < 1 || nargin > 2) + print_usage (); + + octave_value_list retval (nargin == 2 ? 2 : 1); + int spumoni = 0; + + // Get knobs + static_assert (COLAMD_KNOBS <= 40, + "symamd: # of COLAMD_KNOBS exceeded. Please report this to bugs.octave.org"); + double knob_storage[COLAMD_KNOBS]; + double *knobs = &knob_storage[0]; + COLAMD_NAME (_set_defaults) (knobs); + + // Check for user-passed knobs + if (nargin == 2) + { + NDArray User_knobs = args(1).array_value (); + int nel_User_knobs = User_knobs.numel (); + + if (nel_User_knobs > 0) + knobs[COLAMD_DENSE_ROW] = User_knobs(COLAMD_DENSE_ROW); + if (nel_User_knobs > 1) + spumoni = static_cast<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; + + octave_idx_type n_row, n_col; + octave_idx_type *ridx, *cidx; + SparseMatrix sm; + SparseComplexMatrix scm; + + if (args(0).issparse ()) + { + if (args(0).iscomplex ()) + { + scm = args(0).sparse_complex_matrix_value (); + n_row = scm.rows (); + n_col = scm.cols (); + ridx = scm.xridx (); + cidx = scm.xcidx (); + } + else + { + sm = args(0).sparse_matrix_value (); + n_row = sm.rows (); + n_col = sm.cols (); + ridx = sm.xridx (); + cidx = sm.xcidx (); + } + } + else + { + if (args(0).iscomplex ()) + sm = SparseMatrix (real (args(0).complex_matrix_value ())); + else + sm = SparseMatrix (args(0).matrix_value ()); + + n_row = sm.rows (); + n_col = sm.cols (); + ridx = sm.xridx (); + cidx = sm.xcidx (); + } + + if (n_row != n_col) + err_square_matrix_required ("symamd", "S"); + + // Allocate workspace for symamd + OCTAVE_LOCAL_BUFFER (octave_idx_type, perm, n_col+1); + static_assert (COLAMD_STATS <= 40, + "symamd: # of COLAMD_STATS exceeded. Please report this to bugs.octave.org"); + octave::suitesparse_integer stats_storage[COLAMD_STATS]; + octave::suitesparse_integer *stats = &stats_storage[0]; + if (! SYMAMD_NAME () (n_col, octave::to_suitesparse_intptr (ridx), + octave::to_suitesparse_intptr (cidx), + octave::to_suitesparse_intptr (perm), + knobs, stats, &calloc, &free)) + { + SYMAMD_NAME (_report)(stats); + + error ("symamd: internal error!"); + } + + // column elimination tree post-ordering + OCTAVE_LOCAL_BUFFER (octave_idx_type, etree, n_col + 1); + symetree (ridx, cidx, etree, perm, n_col); + + // Calculate the tree post-ordering + OCTAVE_LOCAL_BUFFER (octave_idx_type, post, n_col + 1); + tree_postorder (n_col, etree, post); + + // return the permutation vector + NDArray out_perm (dim_vector (1, n_col)); + for (octave_idx_type 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_NAME (_report)(stats); + + // Return the stats vector + if (nargout == 2) + { + NDArray out_stats (dim_vector (1, COLAMD_STATS)); + for (octave_idx_type 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; + +#else + + octave_unused_parameter (args); + octave_unused_parameter (nargout); + + err_disabled_feature ("symamd", "COLAMD"); + +#endif +} + +DEFUN (etree, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {@var{p} =} etree (@var{S}) +@deftypefnx {} {@var{p} =} etree (@var{S}, @var{typ}) +@deftypefnx {} {[@var{p}, @var{q}] =} etree (@var{S}, @var{typ}) + +Return the elimination tree for the matrix @var{S}. + +By default @var{S} is assumed to be symmetric and the symmetric elimination +tree is returned. The argument @var{typ} controls whether a symmetric or +column elimination tree is returned. Valid values of @var{typ} are +@qcode{"sym"} or @qcode{"col"}, for symmetric or column elimination tree +respectively. + +Called with a second argument, @code{etree} also returns the postorder +permutations on the tree. +@end deftypefn */) +{ + int nargin = args.length (); + + if (nargin < 1 || nargin > 2) + print_usage (); + + octave_value_list retval (nargout == 2 ? 2 : 1); + + octave_idx_type n_row = 0; + octave_idx_type n_col = 0; + octave_idx_type *ridx = nullptr; + octave_idx_type *cidx = nullptr; + + if (! args(0).issparse ()) + error ("etree: S must be a sparse matrix"); + + if (args(0).iscomplex ()) + { + SparseComplexMatrix scm = args(0).sparse_complex_matrix_value (); + + n_row = scm.rows (); + n_col = scm.cols (); + ridx = scm.xridx (); + cidx = scm.xcidx (); + } + else + { + SparseMatrix sm = args(0).sparse_matrix_value (); + + n_row = sm.rows (); + n_col = sm.cols (); + ridx = sm.xridx (); + cidx = sm.xcidx (); + } + + bool is_sym = true; + + if (nargin == 2) + { + std::string str = args(1).xstring_value ("etree: TYP must be a string"); + if (str.find ('C') == 0 || str.find ('c') == 0) + is_sym = false; + } + + // column elimination tree post-ordering (reuse variables) + OCTAVE_LOCAL_BUFFER (octave_idx_type, etree, n_col + 1); + + if (is_sym) + { + if (n_row != n_col) + error ("etree: S is marked as symmetric, but is not square"); + + symetree (ridx, cidx, etree, nullptr, n_col); + } + else + { + OCTAVE_LOCAL_BUFFER (octave_idx_type, colbeg, n_col); + OCTAVE_LOCAL_BUFFER (octave_idx_type, colend, n_col); + + for (octave_idx_type 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 (octave_idx_type i = 0; i < n_col; i++) + // We flag a root with n_col while Matlab does it with zero + // Convert for matlab compatible 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 (octave_idx_type, post, n_col + 1); + tree_postorder (n_col, etree, post); + + NDArray postorder (dim_vector (1, n_col)); + for (octave_idx_type i = 0; i < n_col; i++) + postorder(i) = post[i] + 1; + + retval(1) = postorder; + } + + return retval; +} + +/* +%!assert (etree (speye (2)), [0, 0]); +%!assert (etree (gallery ("poisson", 16)), [2:256, 0]); + +%!error etree () +%!error etree (1, 2, 3) +%!error <S must be a sparse matrix> etree ([1, 2; 3, 4]) +%!error <TYP must be a string> etree (speye (2), 3) +%!error <is not square> etree (sprand (2, 4, .25)) +*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/dmperm.cc Wed Jan 29 06:30:40 2020 -0500 @@ -0,0 +1,215 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 1998-2020 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// This file is part of Octave. +// +// 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 3 of the License, 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 Octave; see the file COPYING. If not, see +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include "CSparse.h" +#include "dRowVector.h" +#include "dSparse.h" +#include "oct-sparse.h" + +#include "defun.h" +#include "errwarn.h" +#include "ov.h" +#include "ovl.h" +#include "utils.h" + +#if defined (OCTAVE_ENABLE_64) +# define CXSPARSE_NAME(name) cs_dl ## name +#else +# define CXSPARSE_NAME(name) cs_di ## name +#endif + +#if defined (HAVE_CXSPARSE) + +static RowVector +put_int (octave::suitesparse_integer *p, octave_idx_type n) +{ + RowVector ret (n); + for (octave_idx_type i = 0; i < n; i++) + ret.xelem (i) = p[i] + 1; + return ret; +} + +static octave_value_list +dmperm_internal (bool rank, const octave_value arg, int nargout) +{ + octave_value_list retval; + octave_idx_type nr = arg.rows (); + octave_idx_type nc = arg.columns (); + SparseMatrix m; + SparseComplexMatrix cm; + CXSPARSE_NAME () csm; + csm.m = nr; + csm.n = nc; + csm.x = nullptr; + csm.nz = -1; + + if (arg.isreal ()) + { + m = arg.sparse_matrix_value (); + csm.nzmax = m.nnz (); + csm.p = octave::to_suitesparse_intptr (m.xcidx ()); + csm.i = octave::to_suitesparse_intptr (m.xridx ()); + } + else + { + cm = arg.sparse_complex_matrix_value (); + csm.nzmax = cm.nnz (); + csm.p = octave::to_suitesparse_intptr (cm.xcidx ()); + csm.i = octave::to_suitesparse_intptr (cm.xridx ()); + } + + if (nargout <= 1 || rank) + { + octave::suitesparse_integer *jmatch = CXSPARSE_NAME (_maxtrans) (&csm, 0); + if (rank) + { + octave_idx_type r = 0; + for (octave_idx_type i = 0; i < nc; i++) + if (jmatch[nr+i] >= 0) + r++; + retval(0) = static_cast<double> (r); + } + else + retval(0) = put_int (jmatch + nr, nc); + CXSPARSE_NAME (_free) (jmatch); + } + else + { + CXSPARSE_NAME (d) *dm = CXSPARSE_NAME(_dmperm) (&csm, 0); + + //retval(5) = put_int (dm->rr, 5); + //retval(4) = put_int (dm->cc, 5); + retval = ovl (put_int (dm->p, nr), put_int (dm->q, nc), + put_int (dm->r, dm->nb+1), put_int (dm->s, dm->nb+1)); + + CXSPARSE_NAME (_dfree) (dm); + } + + return retval; +} + +#endif + +DEFUN (dmperm, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {@var{p} =} dmperm (@var{S}) +@deftypefnx {} {[@var{p}, @var{q}, @var{r}, @var{S}] =} dmperm (@var{S}) + +@cindex @nospell{Dulmage-Mendelsohn} decomposition +Perform a @nospell{Dulmage-Mendelsohn} permutation of the sparse matrix +@var{S}. + +With a single output argument @code{dmperm} performs the row permutations +@var{p} such that @code{@var{S}(@var{p},:)} has no zero elements on the +diagonal. + +Called with two or more output arguments, returns the row and column +permutations, such that @code{@var{S}(@var{p}, @var{q})} is in block +triangular form. The values of @var{r} and @var{S} define the boundaries +of the blocks. If @var{S} is square then @code{@var{r} == @var{S}}. + +The method used is described in: @nospell{A. Pothen & C.-J. Fan.} +@cite{Computing the Block Triangular Form of a Sparse Matrix}. +@nospell{ACM} Trans.@: Math.@: Software, 16(4):303--324, 1990. +@seealso{colamd, ccolamd} +@end deftypefn */) +{ +#if defined (HAVE_CXSPARSE) + + if (args.length () != 1) + print_usage (); + + return dmperm_internal (false, args(0), nargout); + +#else + + octave_unused_parameter (args); + octave_unused_parameter (nargout); + + err_disabled_feature ("dmperm", "CXSparse"); + +#endif +} + +/* +%!testif HAVE_CXSPARSE +%! n = 20; +%! a = speye (n,n); +%! a = a(randperm (n),:); +%! assert (a(dmperm (a),:), speye (n)); + +%!testif HAVE_CXSPARSE +%! n = 20; +%! d = 0.2; +%! a = tril (sprandn (n,n,d), -1) + speye (n,n); +%! a = a(randperm (n), randperm (n)); +%! [p,q,r,s] = dmperm (a); +%! assert (tril (a(p,q), -1), sparse (n, n)); +*/ + +DEFUN (sprank, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {@var{p} =} sprank (@var{S}) +@cindex structural rank + +Calculate the structural rank of the sparse matrix @var{S}. + +Note that only the structure of the matrix is used in this calculation based +on a @nospell{Dulmage-Mendelsohn} permutation to block triangular form. As +such the numerical rank of the matrix @var{S} is bounded by +@code{sprank (@var{S}) >= rank (@var{S})}. Ignoring floating point errors +@code{sprank (@var{S}) == rank (@var{S})}. +@seealso{dmperm} +@end deftypefn */) +{ +#if defined (HAVE_CXSPARSE) + + if (args.length () != 1) + print_usage (); + + return dmperm_internal (true, args(0), nargout); + +#else + + octave_unused_parameter (args); + octave_unused_parameter (nargout); + + err_disabled_feature ("sprank", "CXSparse"); + +#endif +} + +/* +%!testif HAVE_CXSPARSE +%! assert (sprank (speye (20)), 20); +%!testif HAVE_CXSPARSE +%! assert (sprank ([1,0,2,0;2,0,4,0]), 2); + +%!error sprank (1,2) +*/
--- a/libinterp/corefcn/module.mk Sat Jan 25 15:26:07 2020 +0100 +++ b/libinterp/corefcn/module.mk Wed Jan 29 06:30:40 2020 -0500 @@ -113,6 +113,7 @@ %reldir%/__betainc__.cc \ %reldir%/__contourc__.cc \ %reldir%/__dsearchn__.cc \ + %reldir%/__eigs__.cc \ %reldir%/__expint__.cc \ %reldir%/__ftp__.cc \ %reldir%/__gammainc__.cc \ @@ -122,16 +123,20 @@ %reldir%/__magick_read__.cc \ %reldir%/__pchip_deriv__.cc \ %reldir%/__qp__.cc \ + %reldir%/amd.cc \ %reldir%/balance.cc \ %reldir%/besselj.cc \ %reldir%/bitfcns.cc \ %reldir%/bsxfun.cc \ %reldir%/c-file-ptr-stream.cc \ %reldir%/call-stack.cc \ + %reldir%/ccolamd.cc \ %reldir%/cdisplay.c \ %reldir%/cellfun.cc \ + %reldir%/chol.cc \ + %reldir%/coct-hdf5-types.c \ + %reldir%/colamd.cc \ %reldir%/colloc.cc \ - %reldir%/coct-hdf5-types.c \ %reldir%/conv2.cc \ %reldir%/daspk.cc \ %reldir%/dasrt.cc \ @@ -144,6 +149,7 @@ %reldir%/dirfns.cc \ %reldir%/display.cc \ %reldir%/dlmread.cc \ + %reldir%/dmperm.cc \ %reldir%/dot.cc \ %reldir%/dynamic-ld.cc \ %reldir%/eig.cc \ @@ -178,9 +184,9 @@ %reldir%/hex2num.cc \ %reldir%/hook-fcn.cc \ %reldir%/input.cc \ - %reldir%/inv.cc \ %reldir%/interpreter-private.cc \ %reldir%/interpreter.cc \ + %reldir%/inv.cc \ %reldir%/kron.cc \ %reldir%/load-path.cc \ %reldir%/load-save.cc \ @@ -190,8 +196,8 @@ %reldir%/ls-mat-ascii.cc \ %reldir%/ls-mat4.cc \ %reldir%/ls-mat5.cc \ + %reldir%/ls-oct-binary.cc \ %reldir%/ls-oct-text.cc \ - %reldir%/ls-oct-binary.cc \ %reldir%/ls-utils.cc \ %reldir%/lsode.cc \ %reldir%/lu.cc \ @@ -221,6 +227,7 @@ %reldir%/pr-output.cc \ %reldir%/procstream.cc \ %reldir%/psi.cc \ + %reldir%/qr.cc \ %reldir%/quad.cc \ %reldir%/quadcc.cc \ %reldir%/qz.cc \ @@ -242,21 +249,23 @@ %reldir%/sub2ind.cc \ %reldir%/svd.cc \ %reldir%/sylvester.cc \ + %reldir%/symbfact.cc \ %reldir%/syminfo.cc \ + %reldir%/symrcm.cc \ %reldir%/symrec.cc \ %reldir%/symscope.cc \ %reldir%/symtab.cc \ %reldir%/syscalls.cc \ %reldir%/sysdep.cc \ - %reldir%/time.cc \ %reldir%/text-engine.cc \ %reldir%/text-renderer.cc \ + %reldir%/time.cc \ %reldir%/toplev.cc \ %reldir%/tril.cc \ %reldir%/tsearch.cc \ %reldir%/typecast.cc \ + %reldir%/url-handle-manager.cc \ %reldir%/urlwrite.cc \ - %reldir%/url-handle-manager.cc \ %reldir%/utils.cc \ %reldir%/variables.cc \ %reldir%/xdiv.cc \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/qr.cc Wed Jan 29 06:30:40 2020 -0500 @@ -0,0 +1,1751 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 1996-2020 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// This file is part of Octave. +// +// 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 3 of the License, 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 Octave; see the file COPYING. If not, see +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include <string> + +#include "MArray.h" +#include "Matrix.h" +#include "qr.h" +#include "qrp.h" +#include "sparse-qr.h" + +#include "defun.h" +#include "error.h" +#include "errwarn.h" +#include "ov.h" +#include "ovl.h" + +/* +## Restore all rand* "state" values +%!function restore_rand_states (state) +%! rand ("state", state.rand); +%! randn ("state", state.randn); +%!endfunction + +%!shared old_state, restore_state +%! ## Save and restore the states of both random number generators that are +%! ## tested by the unit tests in this file. +%! old_state.rand = rand ("state"); +%! old_state.randn = randn ("state"); +%! restore_state = onCleanup (@() restore_rand_states (old_state)); +*/ + +template <typename MT> +static octave_value +get_qr_r (const octave::math::qr<MT>& fact) +{ + MT R = fact.R (); + if (R.issquare () && fact.regular ()) + return octave_value (R, MatrixType (MatrixType::Upper)); + else + return R; +} + +template <typename T> +static typename octave::math::qr<T>::type +qr_type (int nargout, bool economy) +{ + if (nargout == 0 || nargout == 1) + return octave::math::qr<T>::raw; + else if (economy) + return octave::math::qr<T>::economy; + else + return octave::math::qr<T>::std; +} + +// [Q, R] = qr (X): form Q unitary and R upper triangular such +// that Q * R = X +// +// [Q, R] = qr (X, 0): form the economy decomposition such that if X is +// m by n then only the first n columns of Q are +// computed. +// +// [Q, R, P] = qr (X): form QRP factorization of X where +// P is a permutation matrix such that +// A * P = Q * R +// +// [Q, R, P] = qr (X, 0): form the economy decomposition with +// permutation vector P such that Q * R = X(:, P) +// +// qr (X) alone returns the output of the LAPACK routine dgeqrf, such +// that R = triu (qr (X)) + +DEFUN (qr, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {[@var{Q}, @var{R}] =} qr (@var{A}) +@deftypefnx {} {[@var{Q}, @var{R}, @var{P}] =} qr (@var{A}) # non-sparse A +@deftypefnx {} {@var{X} =} qr (@var{A}) # non-sparse A +@deftypefnx {} {@var{R} =} qr (@var{A}) # sparse A +@deftypefnx {} {[@var{C}, @var{R}] =} qr (@var{A}, @var{B}) +@deftypefnx {} {[@dots{}] =} qr (@dots{}, 0) +@deftypefnx {} {[@dots{}] =} qr (@dots{}, "vector") +@deftypefnx {} {[@dots{}] =} qr (@dots{}, "matrix") +@cindex QR factorization +Compute the QR@tie{}factorization of @var{A}, using standard @sc{lapack} +subroutines. + +The QR@tie{}factorization is +@tex +$QR = A$ where $Q$ is an orthogonal matrix and $R$ is upper triangular. +@end tex +@ifnottex + +@example +@var{Q} * @var{R} = @var{A} +@end example + +@noindent +where @var{Q} is an orthogonal matrix and @var{R} is upper triangular. +@end ifnottex + +For example, given the matrix @code{@var{A} = [1, 2; 3, 4]}, + +@example +[@var{Q}, @var{R}] = qr (@var{A}) +@end example + +@noindent +returns + +@example +@group +@var{Q} = + + -0.31623 -0.94868 + -0.94868 0.31623 + +@var{R} = + + -3.16228 -4.42719 + 0.00000 -0.63246 +@end group +@end example + +@noindent +which multiplied together return the original matrix + +@example +@group +@var{Q} * @var{R} + @result{} + 1.0000 2.0000 + 3.0000 4.0000 +@end group +@end example + +If just a single return value is requested then it is either @var{R}, if +@var{A} is sparse, or @var{X}, such that @code{@var{R} = triu (@var{X})} if +@var{A} is full. (Note: unlike most commands, the single return value is not +the first return value when multiple values are requested.) + +If the matrix @var{A} is full, and a third output @var{P} is requested, then +@code{qr} calculates the permuted QR@tie{}factorization +@tex +$QR = AP$ where $Q$ is an orthogonal matrix, $R$ is upper triangular, and $P$ +is a permutation matrix. +@end tex +@ifnottex + +@example +@var{Q} * @var{R} = @var{A} * @var{P} +@end example + +@noindent +where @var{Q} is an orthogonal matrix, @var{R} is upper triangular, and +@var{P} is a permutation matrix. +@end ifnottex + +The permuted QR@tie{}factorization has the additional property that the +diagonal entries of @var{R} are ordered by decreasing magnitude. In other +words, @code{abs (diag (@var{R}))} will be ordered from largest to smallest. + +For example, given the matrix @code{@var{A} = [1, 2; 3, 4]}, + +@example +[@var{Q}, @var{R}, @var{P}] = qr (@var{A}) +@end example + +@noindent +returns + +@example +@group +@var{Q} = + + -0.44721 -0.89443 + -0.89443 0.44721 + +@var{R} = + + -4.47214 -3.13050 + 0.00000 0.44721 + +@var{P} = + + 0 1 + 1 0 +@end group +@end example + +If the input matrix @var{A} is sparse then the sparse QR@tie{}factorization +is computed using @sc{CSparse}. Because the matrix @var{Q} is, in general, a +full matrix, it is recommended to request only one return value @var{R}. In +that case, the computation avoids the construction of @var{Q} and returns +@var{R} such that @code{@var{R} = chol (@var{A}' * @var{A})}. + +If an additional matrix @var{B} is supplied and two return values are +requested, then @code{qr} returns @var{C}, where +@code{@var{C} = @var{Q}' * @var{B}}. This allows the least squares +approximation of @code{@var{A} \ @var{B}} to be calculated as + +@example +@group +[@var{C}, @var{R}] = qr (@var{A}, @var{B}) +@var{x} = @var{R} \ @var{C} +@end group +@end example + +If the final argument is the string @qcode{"vector"} then @var{P} is a +permutation vector (of the columns of @var{A}) instead of a permutation matrix. +In this case, the defining relationship is + +@example +@var{Q} * @var{R} = @var{A}(:, @var{P}) +@end example + +The default, however, is to return a permutation matrix and this may be +explicitly specified by using a final argument of @qcode{"matrix"}. + +If the final argument is the scalar 0 an @qcode{"economy"} factorization is +returned. When the original matrix @var{A} has size MxN and M > N then the +@qcode{"economy"} factorization will calculate just N rows in @var{R} and N +columns in @var{Q} and omit the zeros in @var{R}. If M @leq{} N there is no +difference between the economy and standard factorizations. When calculating +an @qcode{"economy"} factorization the output @var{P} is always a vector +rather than a matrix. + +Background: The QR factorization has applications in the solution of least +squares problems +@tex +$$ +\min_x \left\Vert A x - b \right\Vert_2 +$$ +@end tex +@ifnottex + +@example +min norm (A*x - b) +@end example + +@end ifnottex +for overdetermined systems of equations (i.e., +@tex +$A$ +@end tex +@ifnottex +@var{A} +@end ifnottex +is a tall, thin matrix). + +The permuted QR@tie{}factorization +@code{[@var{Q}, @var{R}, @var{P}] = qr (@var{A})} allows the construction of an +orthogonal basis of @code{span (A)}. + +@seealso{chol, hess, lu, qz, schur, svd, qrupdate, qrinsert, qrdelete, qrshift} +@end deftypefn */) +{ + int nargin = args.length (); + + if (nargin < 1 || nargin > 3) + print_usage (); + + octave_value_list retval; + + octave_value arg = args(0); + + bool economy = false; + bool is_cmplx = false; + bool have_b = 0; + bool vector_p = 0; + + if (arg.iscomplex ()) + is_cmplx = true; + if (nargin > 1) + { + have_b = true; + if (args(nargin-1).is_scalar_type ()) + { + int val = args(nargin-1).int_value (); + if (val == 0) + { + economy = true; + have_b = (nargin > 2); + } + else if (nargin == 3) // argument 3 should be 0 or a string + print_usage (); + } + else if (args(nargin-1).is_string ()) + { + std::string str = args(nargin-1).string_value (); + if (str == "vector") + vector_p = true; + else if (str != "matrix") + error ("qr: type for P must be 'matrix' or 'vector', not %s", + str.c_str ()); + have_b = (nargin > 2); + } + else if (! args(nargin-1).is_matrix_type ()) + err_wrong_type_arg ("qr", args(nargin-1)); + else if (nargin == 3) // should be caught by is_scalar_type or is_string + print_usage (); + + if (have_b && args(1).iscomplex ()) + is_cmplx = true; + } + + if (arg.issparse ()) + { + if (nargout > 2) + error ("qr: Permutation output is not supported for sparse input"); + + if (is_cmplx) + { + octave::math::sparse_qr<SparseComplexMatrix> q (arg.sparse_complex_matrix_value ()); + + if (have_b) + { + retval = ovl (q.C (args(1).complex_matrix_value ()), + q.R (economy)); + if (arg.rows () < arg.columns ()) + warning ("qr: non minimum norm solution for under-determined " + "problem %" OCTAVE_IDX_TYPE_FORMAT + "x%" OCTAVE_IDX_TYPE_FORMAT, + arg.rows (), arg.columns ()); + } + else if (nargout > 1) + retval = ovl (q.Q (), q.R (economy)); + else + retval = ovl (q.R (economy)); + } + else + { + octave::math::sparse_qr<SparseMatrix> q (arg.sparse_matrix_value ()); + + if (have_b) + { + retval = ovl (q.C (args(1).matrix_value ()), q.R (economy)); + if (arg.rows () < arg.columns ()) + warning ("qr: non minimum norm solution for under-determined " + "problem %" OCTAVE_IDX_TYPE_FORMAT + "x%" OCTAVE_IDX_TYPE_FORMAT, + arg.rows (), arg.columns ()); + } + else if (nargout > 1) + retval = ovl (q.Q (), q.R (economy)); + else + retval = ovl (q.R (economy)); + } + } + else + { + if (arg.is_single_type ()) + { + if (arg.isreal ()) + { + octave::math::qr<FloatMatrix>::type type + = qr_type<FloatMatrix> (nargout, economy); + + FloatMatrix m = arg.float_matrix_value (); + + switch (nargout) + { + case 0: + case 1: + { + octave::math::qr<FloatMatrix> fact (m, type); + retval = ovl (fact.R ()); + } + break; + + case 2: + { + octave::math::qr<FloatMatrix> fact (m, type); + retval = ovl (fact.Q (), get_qr_r (fact)); + if (have_b) + { + if (is_cmplx) + retval(0) = fact.Q ().transpose () + * args(1).float_complex_matrix_value (); + else + retval(0) = fact.Q ().transpose () + * args(1).float_matrix_value (); + } + } + break; + + default: + { + octave::math::qrp<FloatMatrix> fact (m, type); + + if (economy || vector_p) + retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ()); + else + retval = ovl (fact.Q (), get_qr_r (fact), fact.P ()); + } + break; + } + } + else if (arg.iscomplex ()) + { + octave::math::qr<FloatComplexMatrix>::type type + = qr_type<FloatComplexMatrix> (nargout, economy); + + FloatComplexMatrix m = arg.float_complex_matrix_value (); + + switch (nargout) + { + case 0: + case 1: + { + octave::math::qr<FloatComplexMatrix> fact (m, type); + retval = ovl (fact.R ()); + } + break; + + case 2: + { + octave::math::qr<FloatComplexMatrix> fact (m, type); + retval = ovl (fact.Q (), get_qr_r (fact)); + if (have_b) + retval (0) = conj (fact.Q ().transpose ()) + * args(1).float_complex_matrix_value (); + } + break; + + default: + { + octave::math::qrp<FloatComplexMatrix> fact (m, type); + if (economy || vector_p) + retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ()); + else + retval = ovl (fact.Q (), get_qr_r (fact), fact.P ()); + } + break; + } + } + } + else + { + if (arg.isreal ()) + { + octave::math::qr<Matrix>::type type + = qr_type<Matrix> (nargout, economy); + + Matrix m = arg.matrix_value (); + + switch (nargout) + { + case 0: + case 1: + { + octave::math::qr<Matrix> fact (m, type); + retval = ovl (fact.R ()); + } + break; + + case 2: + { + octave::math::qr<Matrix> fact (m, type); + retval = ovl (fact.Q (), get_qr_r (fact)); + if (have_b) + { + if (is_cmplx) + retval(0) = fact.Q ().transpose () + * args(1).complex_matrix_value (); + else + retval(0) = fact.Q ().transpose () + * args(1).matrix_value (); + } + } + break; + + default: + { + octave::math::qrp<Matrix> fact (m, type); + if (economy || vector_p) + retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ()); + else + retval = ovl (fact.Q (), get_qr_r (fact), fact.P ()); + } + break; + } + } + else if (arg.iscomplex ()) + { + octave::math::qr<ComplexMatrix>::type type + = qr_type<ComplexMatrix> (nargout, economy); + + ComplexMatrix m = arg.complex_matrix_value (); + + switch (nargout) + { + case 0: + case 1: + { + octave::math::qr<ComplexMatrix> fact (m, type); + retval = ovl (fact.R ()); + } + break; + + case 2: + { + octave::math::qr<ComplexMatrix> fact (m, type); + retval = ovl (fact.Q (), get_qr_r (fact)); + if (have_b) + retval (0) = conj (fact.Q ().transpose ()) + * args(1).complex_matrix_value (); + } + break; + + default: + { + octave::math::qrp<ComplexMatrix> fact (m, type); + if (economy || vector_p) + retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ()); + else + retval = ovl (fact.Q (), get_qr_r (fact), fact.P ()); + } + break; + } + } + else + err_wrong_type_arg ("qr", arg); + } + } + + return retval; +} + +/* +%!test +%! a = [0, 2, 1; 2, 1, 2]; +%! +%! [q, r] = qr (a); +%! [qe, re] = qr (a, 0); +%! +%! assert (q * r, a, sqrt (eps)); +%! assert (qe * re, a, sqrt (eps)); + +%!test +%! a = [0, 2, 1; 2, 1, 2]; +%! +%! [q, r] = qr (a); +%! [qe, re] = qr (a, 0); +%! +%! assert (q * r, a, sqrt (eps)); +%! assert (qe * re, a, sqrt (eps)); + +%!test +%! a = [0, 2, 1; 2, 1, 2]; +%! +%! [q, r, p] = qr (a); # FIXME: not giving right dimensions. +%! [qe, re, pe] = qr (a, 0); +%! +%! assert (q * r, a * p, sqrt (eps)); +%! assert (qe * re, a(:, pe), sqrt (eps)); + +%!test +%! a = [0, 2; 2, 1; 1, 2]; +%! +%! [q, r] = qr (a); +%! [qe, re] = qr (a, 0); +%! +%! assert (q * r, a, sqrt (eps)); +%! assert (qe * re, a, sqrt (eps)); + +%!test +%! a = [0, 2; 2, 1; 1, 2]; +%! +%! [q, r, p] = qr (a); +%! [qe, re, pe] = qr (a, 0); +%! +%! assert (q * r, a * p, sqrt (eps)); +%! assert (qe * re, a(:, pe), sqrt (eps)); + +%!test +%! a = [0, 2, 1; 2, 1, 2; 3, 1, 2]; +%! b = [1, 3, 2; 1, 1, 0; 3, 0, 2]; +%! +%! [q, r] = qr (a); +%! [c, re] = qr (a, b); +%! +%! assert (r, re, sqrt (eps)); +%! assert (q'*b, c, sqrt (eps)); + +%!test +%! a = [0, 2, i; 2, 1, 2; 3, 1, 2]; +%! b = [1, 3, 2; 1, i, 0; 3, 0, 2]; +%! +%! [q, r] = qr (a); +%! [c, re] = qr (a, b); +%! +%! assert (r, re, sqrt (eps)); +%! assert (q'*b, c, sqrt (eps)); + +%!test +%! a = [0, 2, i; 2, 1, 2; 3, 1, 2]; +%! b = [1, 3, 2; 1, 1, 0; 3, 0, 2]; +%! +%! [q, r] = qr (a); +%! [c, re] = qr (a, b); +%! +%! assert (r, re, sqrt (eps)); +%! assert (q'*b, c, sqrt (eps)); + +%!test +%! a = [0, 2, 1; 2, 1, 2; 3, 1, 2]; +%! b = [1, 3, 2; 1, i, 0; 3, 0, 2]; +%! +%! [q, r] = qr (a); +%! [c, re] = qr (a, b); +%! +%! assert (r, re, sqrt (eps)); +%! assert (q'*b, c, sqrt (eps)); + +%!test +%! assert (qr (zeros (0, 0)), zeros (0, 0)) +%! assert (qr (zeros (1, 0)), zeros (1, 0)) +%! assert (qr (zeros (0, 1)), zeros (0, 1)) + +%!error qr () +%!error qr ([1, 2; 3, 4], 0, 2) + +%!function retval = __testqr (q, r, a, p) +%! tol = 100*eps (class (q)); +%! retval = 0; +%! if (nargin == 3) +%! n1 = norm (q*r - a); +%! n2 = norm (q'*q - eye (columns (q))); +%! retval = (n1 < tol && n2 < tol); +%! else +%! n1 = norm (q'*q - eye (columns (q))); +%! retval = (n1 < tol); +%! if (isvector (p)) +%! n2 = norm (q*r - a(:,p)); +%! retval = (retval && n2 < tol); +%! else +%! n2 = norm (q*r - a*p); +%! retval = (retval && n2 < tol); +%! endif +%! endif +%!endfunction + +%!test +%! t = ones (24, 1); +%! j = 1; +%! +%! if (false) # eliminate big matrix tests +%! a = rand (5000, 20); +%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); +%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); +%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); +%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a', p); +%! +%! a = a+1i*eps; +%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); +%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); +%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); +%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a', p); +%! endif +%! +%! a = [ ones(1,15); sqrt(eps)*eye(15) ]; +%! [q,r] = qr (a); t(j++) = __testqr (q, r, a); +%! [q,r] = qr (a'); t(j++) = __testqr (q, r, a'); +%! [q,r,p] = qr (a); t(j++) = __testqr (q, r, a, p); +%! [q,r,p] = qr (a'); t(j++) = __testqr (q, r, a', p); +%! +%! a = a+1i*eps; +%! [q,r] = qr (a); t(j++) = __testqr (q, r, a); +%! [q,r] = qr (a'); t(j++) = __testqr (q, r, a'); +%! [q,r,p] = qr (a); t(j++) = __testqr (q, r, a, p); +%! [q,r,p] = qr (a'); t(j++) = __testqr (q, r, a', p); +%! +%! a = [ ones(1,15); sqrt(eps)*eye(15) ]; +%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); +%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); +%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); +%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a', p); +%! +%! a = a+1i*eps; +%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); +%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); +%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); +%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a', p); +%! +%! a = [ 611 196 -192 407 -8 -52 -49 29 +%! 196 899 113 -192 -71 -43 -8 -44 +%! -192 113 899 196 61 49 8 52 +%! 407 -192 196 611 8 44 59 -23 +%! -8 -71 61 8 411 -599 208 208 +%! -52 -43 49 44 -599 411 208 208 +%! -49 -8 8 59 208 208 99 -911 +%! 29 -44 52 -23 208 208 -911 99 ]; +%! [q,r] = qr (a); +%! +%! assert (all (t) && norm (q*r - a) < 5000*eps); + +%!test +%! a = single ([0, 2, 1; 2, 1, 2]); +%! +%! [q, r] = qr (a); +%! [qe, re] = qr (a, 0); +%! +%! assert (q * r, a, sqrt (eps ("single"))); +%! assert (qe * re, a, sqrt (eps ("single"))); + +%!test +%! a = single ([0, 2, 1; 2, 1, 2]); +%! +%! [q, r, p] = qr (a); # FIXME: not giving right dimensions. +%! [qe, re, pe] = qr (a, 0); +%! +%! assert (q * r, a * p, sqrt (eps ("single"))); +%! assert (qe * re, a(:, pe), sqrt (eps ("single"))); + +%!test +%! a = single ([0, 2; 2, 1; 1, 2]); +%! +%! [q, r] = qr (a); +%! [qe, re] = qr (a, 0); +%! +%! assert (q * r, a, sqrt (eps ("single"))); +%! assert (qe * re, a, sqrt (eps ("single"))); + +%!test +%! a = single ([0, 2; 2, 1; 1, 2]); +%! +%! [q, r, p] = qr (a); +%! [qe, re, pe] = qr (a, 0); +%! +%! assert (q * r, a * p, sqrt (eps ("single"))); +%! assert (qe * re, a(:, pe), sqrt (eps ("single"))); + +%!test +%! a = single([0, 2, 1; 2, 1, 2; 3, 1, 2]); +%! b = single([1, 3, 2; 1, 1, 0; 3, 0, 2]); +%! +%! [q, r] = qr (a); +%! [c, re] = qr (a, b); +%! +%! assert (r, re, sqrt (eps ("single"))); +%! assert (q'*b, c, sqrt (eps ("single"))); + +%!test +%! a = single([0, 2, i; 2, 1, 2; 3, 1, 2]); +%! b = single([1, 3, 2; 1, i, 0; 3, 0, 2]); +%! +%! [q, r] = qr (a); +%! [c, re] = qr (a, b); +%! +%! assert (r, re, sqrt (eps ("single"))); +%! assert (q'*b, c, sqrt (eps ("single"))); + +%!test +%! a = single([0, 2, i; 2, 1, 2; 3, 1, 2]); +%! b = single([1, 3, 2; 1, 1, 0; 3, 0, 2]); +%! +%! [q, r] = qr (a); +%! [c, re] = qr (a, b); +%! +%! assert (r, re, sqrt (eps)); +%! assert (q'*b, c, sqrt (eps)); + +%!test +%! a = single([0, 2, 1; 2, 1, 2; 3, 1, 2]); +%! b = single([1, 3, 2; 1, i, 0; 3, 0, 2]); +%! +%! [q, r] = qr (a); +%! [c, re] = qr (a, b); +%! +%! assert (r, re, sqrt (eps ("single"))); +%! assert (q'*b, c, sqrt (eps ("single"))); + +%!error qr () +%!error qr ([1, 2; 3, 4], 0, 2) + +%!test +%! t = ones (24, 1); +%! j = 1; +%! +%! if (false) # eliminate big matrix tests +%! a = rand (5000,20); +%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); +%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); +%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); +%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a', p); +%! +%! a = a+1i*eps ("single"); +%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); +%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); +%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); +%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a', p); +%! endif +%! +%! a = [ ones(1,15); sqrt(eps("single"))*eye(15) ]; +%! [q,r] = qr (a); t(j++) = __testqr (q, r, a); +%! [q,r] = qr (a'); t(j++) = __testqr (q, r, a'); +%! [q,r,p] = qr (a); t(j++) = __testqr (q, r, a, p); +%! [q,r,p] = qr (a'); t(j++) = __testqr (q, r, a', p); +%! +%! a = a+1i*eps ("single"); +%! [q,r] = qr (a); t(j++) = __testqr (q, r, a); +%! [q,r] = qr (a'); t(j++) = __testqr (q, r, a'); +%! [q,r,p] = qr (a); t(j++) = __testqr (q, r, a, p); +%! [q,r,p] = qr (a'); t(j++) = __testqr (q, r, a', p); +%! +%! a = [ ones(1,15); sqrt(eps("single"))*eye(15) ]; +%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); +%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); +%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); +%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a', p); +%! +%! a = a+1i*eps ("single"); +%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); +%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); +%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); +%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a',p); +%! +%! a = [ 611 196 -192 407 -8 -52 -49 29 +%! 196 899 113 -192 -71 -43 -8 -44 +%! -192 113 899 196 61 49 8 52 +%! 407 -192 196 611 8 44 59 -23 +%! -8 -71 61 8 411 -599 208 208 +%! -52 -43 49 44 -599 411 208 208 +%! -49 -8 8 59 208 208 99 -911 +%! 29 -44 52 -23 208 208 -911 99 ]; +%! [q,r] = qr (a); +%! +%! assert (all (t) && norm (q*r-a) < 5000*eps ("single")); + +## The deactivated tests below can't be tested till rectangular back-subs is +## implemented for sparse matrices. + +%!testif HAVE_CXSPARSE +%! n = 20; d = 0.2; +%! ## initialize generators to make behavior reproducible +%! rand ("state", 42); +%! randn ("state", 42); +%! a = sprandn (n,n,d) + speye (n,n); +%! r = qr (a); +%! assert (r'*r, a'*a, 1e-10); + +%!testif HAVE_COLAMD +%! n = 20; d = 0.2; +%! ## initialize generators to make behavior reproducible +%! rand ("state", 42); +%! randn ("state", 42); +%! a = sprandn (n,n,d) + speye (n,n); +%! q = symamd (a); +%! a = a(q,q); +%! r = qr (a); +%! assert (r'*r, a'*a, 1e-10); + +%!testif HAVE_CXSPARSE +%! n = 20; d = 0.2; +%! ## initialize generators to make behavior reproducible +%! rand ("state", 42); +%! randn ("state", 42); +%! a = sprandn (n,n,d) + speye (n,n); +%! [c,r] = qr (a, ones (n,1)); +%! assert (r\c, full (a)\ones (n,1), 10e-10); + +%!testif HAVE_CXSPARSE +%! n = 20; d = 0.2; +%! ## initialize generators to make behavior reproducible +%! rand ("state", 42); +%! randn ("state", 42); +%! a = sprandn (n,n,d) + speye (n,n); +%! b = randn (n,2); +%! [c,r] = qr (a, b); +%! assert (r\c, full (a)\b, 10e-10); + +## Test under-determined systems!! +%!#testif HAVE_CXSPARSE +%! n = 20; d = 0.2; +%! ## initialize generators to make behavior reproducible +%! rand ("state", 42); +%! randn ("state", 42); +%! a = sprandn (n,n+1,d) + speye (n,n+1); +%! b = randn (n,2); +%! [c,r] = qr (a, b); +%! assert (r\c, full (a)\b, 10e-10); + +%!testif HAVE_CXSPARSE +%! n = 20; d = 0.2; +%! ## initialize generators to make behavior reproducible +%! rand ("state", 42); +%! randn ("state", 42); +%! a = 1i*sprandn (n,n,d) + speye (n,n); +%! r = qr (a); +%! assert (r'*r,a'*a,1e-10); + +%!testif HAVE_COLAMD +%! n = 20; d = 0.2; +%! ## initialize generators to make behavior reproducible +%! rand ("state", 42); +%! randn ("state", 42); +%! a = 1i*sprandn (n,n,d) + speye (n,n); +%! q = symamd (a); +%! a = a(q,q); +%! r = qr (a); +%! assert (r'*r, a'*a, 1e-10); + +%!testif HAVE_CXSPARSE +%! n = 20; d = 0.2; +%! ## initialize generators to make behavior reproducible +%! rand ("state", 42); +%! randn ("state", 42); +%! a = 1i*sprandn (n,n,d) + speye (n,n); +%! [c,r] = qr (a, ones (n,1)); +%! assert (r\c, full (a)\ones (n,1), 10e-10); + +%!testif HAVE_CXSPARSE +%! n = 20; d = 0.2; +%! ## initialize generators to make behavior reproducible +%! rand ("state", 42); +%! randn ("state", 42); +%! a = 1i*sprandn (n,n,d) + speye (n,n); +%! b = randn (n,2); +%! [c,r] = qr (a, b); +%! assert (r\c, full (a)\b, 10e-10); + +## Test under-determined systems!! +%!#testif HAVE_CXSPARSE +%! n = 20; d = 0.2; +%! ## initialize generators to make behavior reproducible +%! rand ("state", 42); +%! randn ("state", 42); +%! a = 1i*sprandn (n,n+1,d) + speye (n,n+1); +%! b = randn (n,2); +%! [c,r] = qr (a, b); +%! assert (r\c, full (a)\b, 10e-10); + +*/ + +static +bool check_qr_dims (const octave_value& q, const octave_value& r, + bool allow_ecf = false) +{ + octave_idx_type m = q.rows (); + octave_idx_type k = r.rows (); + octave_idx_type n = r.columns (); + return ((q.ndims () == 2 && r.ndims () == 2 && k == q.columns ()) + && (m == k || (allow_ecf && k == n && k < m))); +} + +static +bool check_index (const octave_value& i, bool vector_allowed = false) +{ + return ((i.isreal () || i.isinteger ()) + && (i.is_scalar_type () || vector_allowed)); +} + +DEFUN (qrupdate, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {[@var{Q1}, @var{R1}] =} qrupdate (@var{Q}, @var{R}, @var{u}, @var{v}) +Update a QR factorization given update vectors or matrices. + +Given a QR@tie{}factorization of a real or complex matrix +@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and +@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization of +@w{@var{A} + @var{u}*@var{v}'}, where @var{u} and @var{v} are column vectors +(rank-1 update) or matrices with equal number of columns +(rank-k update). Notice that the latter case is done as a sequence of +rank-1 updates; thus, for k large enough, it will be both faster and more +accurate to recompute the factorization from scratch. + +The QR@tie{}factorization supplied may be either full (Q is square) or +economized (R is square). + +@seealso{qr, qrinsert, qrdelete, qrshift} +@end deftypefn */) +{ + octave_value_list retval; + + if (args.length () != 4) + print_usage (); + + octave_value argq = args(0); + octave_value argr = args(1); + octave_value argu = args(2); + octave_value argv = args(3); + + if (! argq.isnumeric () || ! argr.isnumeric () + || ! argu.isnumeric () || ! argv.isnumeric ()) + print_usage (); + + if (! check_qr_dims (argq, argr, true)) + error ("qrupdate: Q and R dimensions don't match"); + + if (argq.isreal () && argr.isreal () && argu.isreal () + && argv.isreal ()) + { + // all real case + if (argq.is_single_type () || argr.is_single_type () + || argu.is_single_type () || argv.is_single_type ()) + { + FloatMatrix Q = argq.float_matrix_value (); + FloatMatrix R = argr.float_matrix_value (); + FloatMatrix u = argu.float_matrix_value (); + FloatMatrix v = argv.float_matrix_value (); + + octave::math::qr<FloatMatrix> fact (Q, R); + fact.update (u, v); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + else + { + Matrix Q = argq.matrix_value (); + Matrix R = argr.matrix_value (); + Matrix u = argu.matrix_value (); + Matrix v = argv.matrix_value (); + + octave::math::qr<Matrix> fact (Q, R); + fact.update (u, v); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + } + else + { + // complex case + if (argq.is_single_type () || argr.is_single_type () + || argu.is_single_type () || argv.is_single_type ()) + { + FloatComplexMatrix Q = argq.float_complex_matrix_value (); + FloatComplexMatrix R = argr.float_complex_matrix_value (); + FloatComplexMatrix u = argu.float_complex_matrix_value (); + FloatComplexMatrix v = argv.float_complex_matrix_value (); + + octave::math::qr<FloatComplexMatrix> fact (Q, R); + fact.update (u, v); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + else + { + ComplexMatrix Q = argq.complex_matrix_value (); + ComplexMatrix R = argr.complex_matrix_value (); + ComplexMatrix u = argu.complex_matrix_value (); + ComplexMatrix v = argv.complex_matrix_value (); + + octave::math::qr<ComplexMatrix> fact (Q, R); + fact.update (u, v); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + } + + return retval; +} + +/* +%!shared A, u, v, Ac, uc, vc +%! A = [0.091364 0.613038 0.999083; +%! 0.594638 0.425302 0.603537; +%! 0.383594 0.291238 0.085574; +%! 0.265712 0.268003 0.238409; +%! 0.669966 0.743851 0.445057 ]; +%! +%! u = [0.85082; +%! 0.76426; +%! 0.42883; +%! 0.53010; +%! 0.80683 ]; +%! +%! v = [0.98810; +%! 0.24295; +%! 0.43167 ]; +%! +%! Ac = [0.620405 + 0.956953i 0.480013 + 0.048806i 0.402627 + 0.338171i; +%! 0.589077 + 0.658457i 0.013205 + 0.279323i 0.229284 + 0.721929i; +%! 0.092758 + 0.345687i 0.928679 + 0.241052i 0.764536 + 0.832406i; +%! 0.912098 + 0.721024i 0.049018 + 0.269452i 0.730029 + 0.796517i; +%! 0.112849 + 0.603871i 0.486352 + 0.142337i 0.355646 + 0.151496i ]; +%! +%! uc = [0.20351 + 0.05401i; +%! 0.13141 + 0.43708i; +%! 0.29808 + 0.08789i; +%! 0.69821 + 0.38844i; +%! 0.74871 + 0.25821i ]; +%! +%! vc = [0.85839 + 0.29468i; +%! 0.20820 + 0.93090i; +%! 0.86184 + 0.34689i ]; +%! + +%!test +%! [Q,R] = qr (A); +%! [Q,R] = qrupdate (Q, R, u, v); +%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps); +%! assert (norm (vec (triu (R)-R), Inf) == 0); +%! assert (norm (vec (Q*R - A - u*v'), Inf) < norm (A)*1e1*eps); +%! +%!test +%! [Q,R] = qr (Ac); +%! [Q,R] = qrupdate (Q, R, uc, vc); +%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps); +%! assert (norm (vec (triu (R)-R), Inf) == 0); +%! assert (norm (vec (Q*R - Ac - uc*vc'), Inf) < norm (Ac)*1e1*eps); + +%!test +%! [Q,R] = qr (single (A)); +%! [Q,R] = qrupdate (Q, R, single (u), single (v)); +%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single")); +%! assert (norm (vec (triu (R)-R), Inf) == 0); +%! assert (norm (vec (Q*R - single (A) - single (u)*single (v)'), Inf) < norm (single (A))*1e1*eps ("single")); +%! +%!test +%! [Q,R] = qr (single (Ac)); +%! [Q,R] = qrupdate (Q, R, single (uc), single (vc)); +%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single")); +%! assert (norm (vec (triu (R)-R), Inf) == 0); +%! assert (norm (vec (Q*R - single (Ac) - single (uc)*single (vc)'), Inf) < norm (single (Ac))*1e1*eps ("single")); +*/ + +DEFUN (qrinsert, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {[@var{Q1}, @var{R1}] =} qrinsert (@var{Q}, @var{R}, @var{j}, @var{x}, @var{orient}) +Update a QR factorization given a row or column to insert in the original +factored matrix. + + +Given a QR@tie{}factorization of a real or complex matrix +@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and +@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization of +@w{[A(:,1:j-1) x A(:,j:n)]}, where @var{u} is a column vector to be inserted +into @var{A} (if @var{orient} is @qcode{"col"}), or the +QR@tie{}factorization of @w{[A(1:j-1,:);x;A(:,j:n)]}, where @var{x} is a row +vector to be inserted into @var{A} (if @var{orient} is @qcode{"row"}). + +The default value of @var{orient} is @qcode{"col"}. If @var{orient} is +@qcode{"col"}, @var{u} may be a matrix and @var{j} an index vector +resulting in the QR@tie{}factorization of a matrix @var{B} such that +@w{B(:,@var{j})} gives @var{u} and @w{B(:,@var{j}) = []} gives @var{A}. +Notice that the latter case is done as a sequence of k insertions; +thus, for k large enough, it will be both faster and more accurate to +recompute the factorization from scratch. + +If @var{orient} is @qcode{"col"}, the QR@tie{}factorization supplied may +be either full (Q is square) or economized (R is square). + +If @var{orient} is @qcode{"row"}, full factorization is needed. +@seealso{qr, qrupdate, qrdelete, qrshift} +@end deftypefn */) +{ + int nargin = args.length (); + + if (nargin < 4 || nargin > 5) + print_usage (); + + octave_value argq = args(0); + octave_value argr = args(1); + octave_value argj = args(2); + octave_value argx = args(3); + + if (! argq.isnumeric () || ! argr.isnumeric () + || ! argx.isnumeric () + || (nargin > 4 && ! args(4).is_string ())) + print_usage (); + + std::string orient = (nargin < 5) ? "col" : args(4).string_value (); + bool col = (orient == "col"); + + if (! col && orient != "row") + error (R"(qrinsert: ORIENT must be "col" or "row")"); + + if (! check_qr_dims (argq, argr, col) || (! col && argx.rows () != 1)) + error ("qrinsert: dimension mismatch"); + + if (! check_index (argj, col)) + error ("qrinsert: invalid index J"); + + octave_value_list retval; + + MArray<octave_idx_type> j = argj.octave_idx_type_vector_value (); + + octave_idx_type one = 1; + + if (argq.isreal () && argr.isreal () && argx.isreal ()) + { + // real case + if (argq.is_single_type () || argr.is_single_type () + || argx.is_single_type ()) + { + FloatMatrix Q = argq.float_matrix_value (); + FloatMatrix R = argr.float_matrix_value (); + FloatMatrix x = argx.float_matrix_value (); + + octave::math::qr<FloatMatrix> fact (Q, R); + + if (col) + fact.insert_col (x, j-one); + else + fact.insert_row (x.row (0), j(0)-one); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + else + { + Matrix Q = argq.matrix_value (); + Matrix R = argr.matrix_value (); + Matrix x = argx.matrix_value (); + + octave::math::qr<Matrix> fact (Q, R); + + if (col) + fact.insert_col (x, j-one); + else + fact.insert_row (x.row (0), j(0)-one); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + } + else + { + // complex case + if (argq.is_single_type () || argr.is_single_type () + || argx.is_single_type ()) + { + FloatComplexMatrix Q = argq.float_complex_matrix_value (); + FloatComplexMatrix R = argr.float_complex_matrix_value (); + FloatComplexMatrix x = argx.float_complex_matrix_value (); + + octave::math::qr<FloatComplexMatrix> fact (Q, R); + + if (col) + fact.insert_col (x, j-one); + else + fact.insert_row (x.row (0), j(0)-one); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + else + { + ComplexMatrix Q = argq.complex_matrix_value (); + ComplexMatrix R = argr.complex_matrix_value (); + ComplexMatrix x = argx.complex_matrix_value (); + + octave::math::qr<ComplexMatrix> fact (Q, R); + + if (col) + fact.insert_col (x, j-one); + else + fact.insert_row (x.row (0), j(0)-one); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + } + + return retval; +} + +/* +%!test +%! [Q,R] = qr (A); +%! [Q,R] = qrinsert (Q, R, 3, u); +%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - [A(:,1:2) u A(:,3)]), Inf) < norm (A)*1e1*eps); +%!test +%! [Q,R] = qr (Ac); +%! [Q,R] = qrinsert (Q, R, 3, uc); +%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - [Ac(:,1:2) uc Ac(:,3)]), Inf) < norm (Ac)*1e1*eps); +%!test +%! x = [0.85082 0.76426 0.42883 ]; +%! +%! [Q,R] = qr (A); +%! [Q,R] = qrinsert (Q, R, 3, x, "row"); +%! assert (norm (vec (Q'*Q - eye (6)), Inf) < 1e1*eps); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - [A(1:2,:);x;A(3:5,:)]), Inf) < norm (A)*1e1*eps); +%!test +%! x = [0.20351 + 0.05401i 0.13141 + 0.43708i 0.29808 + 0.08789i ]; +%! +%! [Q,R] = qr (Ac); +%! [Q,R] = qrinsert (Q, R, 3, x, "row"); +%! assert (norm (vec (Q'*Q - eye (6)), Inf) < 1e1*eps); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - [Ac(1:2,:);x;Ac(3:5,:)]), Inf) < norm (Ac)*1e1*eps); + +%!test +%! [Q,R] = qr (single (A)); +%! [Q,R] = qrinsert (Q, R, 3, single (u)); +%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single")); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - single ([A(:,1:2) u A(:,3)])), Inf) < norm (single (A))*1e1*eps ("single")); +%!test +%! [Q,R] = qr (single (Ac)); +%! [Q,R] = qrinsert (Q, R, 3, single (uc)); +%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single")); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - single ([Ac(:,1:2) uc Ac(:,3)])), Inf) < norm (single (Ac))*1e1*eps ("single")); +%!test +%! x = single ([0.85082 0.76426 0.42883 ]); +%! +%! [Q,R] = qr (single (A)); +%! [Q,R] = qrinsert (Q, R, 3, x, "row"); +%! assert (norm (vec (Q'*Q - eye (6,"single")), Inf) < 1e1*eps ("single")); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - single ([A(1:2,:);x;A(3:5,:)])), Inf) < norm (single (A))*1e1*eps ("single")); +%!test +%! x = single ([0.20351 + 0.05401i 0.13141 + 0.43708i 0.29808 + 0.08789i ]); +%! +%! [Q,R] = qr (single (Ac)); +%! [Q,R] = qrinsert (Q, R, 3, x, "row"); +%! assert (norm (vec (Q'*Q - eye (6,"single")), Inf) < 1e1*eps ("single")); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - single ([Ac(1:2,:);x;Ac(3:5,:)])), Inf) < norm (single (Ac))*1e1*eps ("single")); +*/ + +DEFUN (qrdelete, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {[@var{Q1}, @var{R1}] =} qrdelete (@var{Q}, @var{R}, @var{j}, @var{orient}) +Update a QR factorization given a row or column to delete from the original +factored matrix. + +Given a QR@tie{}factorization of a real or complex matrix +@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and +@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization of +@w{[A(:,1:j-1), U, A(:,j:n)]}, +where @var{u} is a column vector to be inserted into @var{A} +(if @var{orient} is @qcode{"col"}), +or the QR@tie{}factorization of @w{[A(1:j-1,:);X;A(:,j:n)]}, +where @var{x} is a row @var{orient} is @qcode{"row"}). +The default value of @var{orient} is @qcode{"col"}. + +If @var{orient} is @qcode{"col"}, @var{j} may be an index vector +resulting in the QR@tie{}factorization of a matrix @var{B} such that +@w{A(:,@var{j}) = []} gives @var{B}. Notice that the latter case is done as +a sequence of k deletions; thus, for k large enough, it will be both faster +and more accurate to recompute the factorization from scratch. + +If @var{orient} is @qcode{"col"}, the QR@tie{}factorization supplied may +be either full (Q is square) or economized (R is square). + +If @var{orient} is @qcode{"row"}, full factorization is needed. +@seealso{qr, qrupdate, qrinsert, qrshift} +@end deftypefn */) +{ + int nargin = args.length (); + + if (nargin < 3 || nargin > 4) + print_usage (); + + octave_value argq = args(0); + octave_value argr = args(1); + octave_value argj = args(2); + + if (! argq.isnumeric () || ! argr.isnumeric () + || (nargin > 3 && ! args(3).is_string ())) + print_usage (); + + std::string orient = (nargin < 4) ? "col" : args(3).string_value (); + bool col = orient == "col"; + + if (! col && orient != "row") + error (R"(qrdelete: ORIENT must be "col" or "row")"); + + if (! check_qr_dims (argq, argr, col)) + error ("qrdelete: dimension mismatch"); + + MArray<octave_idx_type> j = argj.octave_idx_type_vector_value (); + if (! check_index (argj, col)) + error ("qrdelete: invalid index J"); + + octave_value_list retval; + + octave_idx_type one = 1; + + if (argq.isreal () && argr.isreal ()) + { + // real case + if (argq.is_single_type () || argr.is_single_type ()) + { + FloatMatrix Q = argq.float_matrix_value (); + FloatMatrix R = argr.float_matrix_value (); + + octave::math::qr<FloatMatrix> fact (Q, R); + + if (col) + fact.delete_col (j-one); + else + fact.delete_row (j(0)-one); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + else + { + Matrix Q = argq.matrix_value (); + Matrix R = argr.matrix_value (); + + octave::math::qr<Matrix> fact (Q, R); + + if (col) + fact.delete_col (j-one); + else + fact.delete_row (j(0)-one); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + } + else + { + // complex case + if (argq.is_single_type () || argr.is_single_type ()) + { + FloatComplexMatrix Q = argq.float_complex_matrix_value (); + FloatComplexMatrix R = argr.float_complex_matrix_value (); + + octave::math::qr<FloatComplexMatrix> fact (Q, R); + + if (col) + fact.delete_col (j-one); + else + fact.delete_row (j(0)-one); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + else + { + ComplexMatrix Q = argq.complex_matrix_value (); + ComplexMatrix R = argr.complex_matrix_value (); + + octave::math::qr<ComplexMatrix> fact (Q, R); + + if (col) + fact.delete_col (j-one); + else + fact.delete_row (j(0)-one); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + } + + return retval; +} + +/* +%!test +%! AA = [0.091364 0.613038 0.027504 0.999083; +%! 0.594638 0.425302 0.562834 0.603537; +%! 0.383594 0.291238 0.742073 0.085574; +%! 0.265712 0.268003 0.783553 0.238409; +%! 0.669966 0.743851 0.457255 0.445057 ]; +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrdelete (Q, R, 3); +%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 16*eps); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps); +%! +%!test +%! AA = [0.364554 + 0.993117i 0.669818 + 0.510234i 0.426568 + 0.041337i 0.847051 + 0.233291i; +%! 0.049600 + 0.242783i 0.448946 + 0.484022i 0.141155 + 0.074420i 0.446746 + 0.392706i; +%! 0.581922 + 0.657416i 0.581460 + 0.030016i 0.219909 + 0.447288i 0.201144 + 0.069132i; +%! 0.694986 + 0.000571i 0.682327 + 0.841712i 0.807537 + 0.166086i 0.192767 + 0.358098i; +%! 0.945002 + 0.066788i 0.350492 + 0.642638i 0.579629 + 0.048102i 0.600170 + 0.636938i ] * I; +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrdelete (Q, R, 3); +%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 16*eps); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps); +%! +%!test +%! AA = [0.091364 0.613038 0.027504 0.999083; +%! 0.594638 0.425302 0.562834 0.603537; +%! 0.383594 0.291238 0.742073 0.085574; +%! 0.265712 0.268003 0.783553 0.238409; +%! 0.669966 0.743851 0.457255 0.445057 ]; +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrdelete (Q, R, 3, "row"); +%! assert (norm (vec (Q'*Q - eye (4)), Inf) < 1e1*eps); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps); +%! +%!test +%! AA = [0.364554 + 0.993117i 0.669818 + 0.510234i 0.426568 + 0.041337i 0.847051 + 0.233291i; +%! 0.049600 + 0.242783i 0.448946 + 0.484022i 0.141155 + 0.074420i 0.446746 + 0.392706i; +%! 0.581922 + 0.657416i 0.581460 + 0.030016i 0.219909 + 0.447288i 0.201144 + 0.069132i; +%! 0.694986 + 0.000571i 0.682327 + 0.841712i 0.807537 + 0.166086i 0.192767 + 0.358098i; +%! 0.945002 + 0.066788i 0.350492 + 0.642638i 0.579629 + 0.048102i 0.600170 + 0.636938i ] * I; +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrdelete (Q, R, 3, "row"); +%! assert (norm (vec (Q'*Q - eye (4)), Inf) < 1e1*eps); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps); + +%!test +%! AA = single ([0.091364 0.613038 0.027504 0.999083; +%! 0.594638 0.425302 0.562834 0.603537; +%! 0.383594 0.291238 0.742073 0.085574; +%! 0.265712 0.268003 0.783553 0.238409; +%! 0.669966 0.743851 0.457255 0.445057 ]); +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrdelete (Q, R, 3); +%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single")); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps ("single")); +%! +%!test +%! AA = single ([0.364554 + 0.993117i 0.669818 + 0.510234i 0.426568 + 0.041337i 0.847051 + 0.233291i; +%! 0.049600 + 0.242783i 0.448946 + 0.484022i 0.141155 + 0.074420i 0.446746 + 0.392706i; +%! 0.581922 + 0.657416i 0.581460 + 0.030016i 0.219909 + 0.447288i 0.201144 + 0.069132i; +%! 0.694986 + 0.000571i 0.682327 + 0.841712i 0.807537 + 0.166086i 0.192767 + 0.358098i; +%! 0.945002 + 0.066788i 0.350492 + 0.642638i 0.579629 + 0.048102i 0.600170 + 0.636938i ]) * I; +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrdelete (Q, R, 3); +%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single")); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps ("single")); +%! +%!test +%! AA = single ([0.091364 0.613038 0.027504 0.999083; +%! 0.594638 0.425302 0.562834 0.603537; +%! 0.383594 0.291238 0.742073 0.085574; +%! 0.265712 0.268003 0.783553 0.238409; +%! 0.669966 0.743851 0.457255 0.445057 ]); +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrdelete (Q, R, 3, "row"); +%! assert (norm (vec (Q'*Q - eye (4,"single")), Inf) < 1.5e1*eps ("single")); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps ("single")); +%!testif HAVE_QRUPDATE +%! ## Same test as above but with more precicision +%! AA = single ([0.091364 0.613038 0.027504 0.999083; +%! 0.594638 0.425302 0.562834 0.603537; +%! 0.383594 0.291238 0.742073 0.085574; +%! 0.265712 0.268003 0.783553 0.238409; +%! 0.669966 0.743851 0.457255 0.445057 ]); +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrdelete (Q, R, 3, "row"); +%! assert (norm (vec (Q'*Q - eye (4,"single")), Inf) < 1e1*eps ("single")); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps ("single")); +%! +%!test +%! AA = single ([0.364554 + 0.993117i 0.669818 + 0.510234i 0.426568 + 0.041337i 0.847051 + 0.233291i; +%! 0.049600 + 0.242783i 0.448946 + 0.484022i 0.141155 + 0.074420i 0.446746 + 0.392706i; +%! 0.581922 + 0.657416i 0.581460 + 0.030016i 0.219909 + 0.447288i 0.201144 + 0.069132i; +%! 0.694986 + 0.000571i 0.682327 + 0.841712i 0.807537 + 0.166086i 0.192767 + 0.358098i; +%! 0.945002 + 0.066788i 0.350492 + 0.642638i 0.579629 + 0.048102i 0.600170 + 0.636938i ]) * I; +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrdelete (Q, R, 3, "row"); +%! assert (norm (vec (Q'*Q - eye (4,"single")), Inf) < 1e1*eps ("single")); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps ("single")); +*/ + +DEFUN (qrshift, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {[@var{Q1}, @var{R1}] =} qrshift (@var{Q}, @var{R}, @var{i}, @var{j}) +Update a QR factorization given a range of columns to shift in the original +factored matrix. + +Given a QR@tie{}factorization of a real or complex matrix +@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and +@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization +of @w{@var{A}(:,p)}, where @w{p} is the permutation @* +@code{p = [1:i-1, shift(i:j, 1), j+1:n]} if @w{@var{i} < @var{j}} @* + or @* +@code{p = [1:j-1, shift(j:i,-1), i+1:n]} if @w{@var{j} < @var{i}}. @* + +@seealso{qr, qrupdate, qrinsert, qrdelete} +@end deftypefn */) +{ + if (args.length () != 4) + print_usage (); + + octave_value argq = args(0); + octave_value argr = args(1); + octave_value argi = args(2); + octave_value argj = args(3); + + if (! argq.isnumeric () || ! argr.isnumeric ()) + print_usage (); + + if (! check_qr_dims (argq, argr, true)) + error ("qrshift: dimensions mismatch"); + + octave_idx_type i = argi.idx_type_value (); + octave_idx_type j = argj.idx_type_value (); + + if (! check_index (argi) || ! check_index (argj)) + error ("qrshift: invalid index I or J"); + + octave_value_list retval; + + if (argq.isreal () && argr.isreal ()) + { + // all real case + if (argq.is_single_type () + && argr.is_single_type ()) + { + FloatMatrix Q = argq.float_matrix_value (); + FloatMatrix R = argr.float_matrix_value (); + + octave::math::qr<FloatMatrix> fact (Q, R); + fact.shift_cols (i-1, j-1); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + else + { + Matrix Q = argq.matrix_value (); + Matrix R = argr.matrix_value (); + + octave::math::qr<Matrix> fact (Q, R); + fact.shift_cols (i-1, j-1); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + } + else + { + // complex case + if (argq.is_single_type () + && argr.is_single_type ()) + { + FloatComplexMatrix Q = argq.float_complex_matrix_value (); + FloatComplexMatrix R = argr.float_complex_matrix_value (); + + octave::math::qr<FloatComplexMatrix> fact (Q, R); + fact.shift_cols (i-1, j-1); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + else + { + ComplexMatrix Q = argq.complex_matrix_value (); + ComplexMatrix R = argr.complex_matrix_value (); + + octave::math::qr<ComplexMatrix> fact (Q, R); + fact.shift_cols (i-1, j-1); + + retval = ovl (fact.Q (), get_qr_r (fact)); + } + } + + return retval; +} + +/* +%!test +%! AA = A.'; +%! i = 2; j = 4; p = [1:i-1, shift(i:j,-1), j+1:5]; +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrshift (Q, R, i, j); +%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps); +%! +%! j = 2; i = 4; p = [1:j-1, shift(j:i,+1), i+1:5]; +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrshift (Q, R, i, j); +%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps); +%! +%!test +%! AA = Ac.'; +%! i = 2; j = 4; p = [1:i-1, shift(i:j,-1), j+1:5]; +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrshift (Q, R, i, j); +%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps); +%! +%! j = 2; i = 4; p = [1:j-1, shift(j:i,+1), i+1:5]; +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrshift (Q, R, i, j); +%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps); + +%!test +%! AA = single (A).'; +%! i = 2; j = 4; p = [1:i-1, shift(i:j,-1), j+1:5]; +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrshift (Q, R, i, j); +%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single")); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single")); +%! +%! j = 2; i = 4; p = [1:j-1, shift(j:i,+1), i+1:5]; +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrshift (Q, R, i, j); +%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single")); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single")); +%! +%!test +%! AA = single (Ac).'; +%! i = 2; j = 4; p = [1:i-1, shift(i:j,-1), j+1:5]; +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrshift (Q, R, i, j); +%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single")); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single")); +%! +%! j = 2; i = 4; p = [1:j-1, shift(j:i,+1), i+1:5]; +%! +%! [Q,R] = qr (AA); +%! [Q,R] = qrshift (Q, R, i, j); +%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single")); +%! assert (norm (vec (triu (R) - R), Inf) == 0); +%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single")); +*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/symbfact.cc Wed Jan 29 06:30:40 2020 -0500 @@ -0,0 +1,428 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 1998-2020 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// This file is part of Octave. +// +// 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 3 of the License, 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 Octave; see the file COPYING. If not, see +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include <cmath> + +#include <algorithm> +#include <string> + +#include "CSparse.h" +#include "boolSparse.h" +#include "dColVector.h" +#include "dSparse.h" +#include "oct-locbuf.h" +#include "oct-sparse.h" +#include "oct-spparms.h" +#include "sparse-util.h" + +#include "defun.h" +#include "error.h" +#include "errwarn.h" +#include "ovl.h" +#include "parse.h" +#include "utils.h" + +DEFUN (symbfact, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {[@var{count}, @var{h}, @var{parent}, @var{post}, @var{R}] =} symbfact (@var{S}) +@deftypefnx {} {[@dots{}] =} symbfact (@var{S}, @var{typ}) +@deftypefnx {} {[@dots{}] =} symbfact (@var{S}, @var{typ}, @var{mode}) + +Perform a symbolic factorization analysis of the sparse matrix @var{S}. + +The input variables are + +@table @var +@item S +@var{S} is a real or complex sparse matrix. + +@item typ +Is the type of the factorization and can be one of + +@table @asis +@item @qcode{"sym"} (default) +Factorize @var{S}. Assumes @var{S} is symmetric and uses the upper +triangular portion of the matrix. + +@item @qcode{"col"} +Factorize @tcode{@var{S}' * @var{S}}. + +@item @qcode{"row"} +Factorize @tcode{@var{S} * @var{S}'}. + +@item @qcode{"lo"} +Factorize @tcode{@var{S}'}. Assumes @var{S} is symmetric and uses the lower +triangular portion of the matrix. +@end table + +@item mode +When @var{mode} is unspecified return the Cholesky@tie{}factorization for +@var{R}. If @var{mode} is @qcode{"lower"} or @qcode{"L"} then return +the conjugate transpose @tcode{@var{R}'} which is a lower triangular factor. +The conjugate transpose version is faster and uses less memory, but still +returns the same values for all other outputs: @var{count}, @var{h}, +@var{parent}, and @var{post}. +@end table + +The output variables are: + +@table @var +@item count +The row counts of the Cholesky@tie{}factorization as determined by +@var{typ}. The computational difficulty of performing the true +factorization using @code{chol} is @code{sum (@var{count} .^ 2)}. + +@item h +The height of the elimination tree. + +@item parent +The elimination tree itself. + +@item post +A sparse boolean matrix whose structure is that of the +Cholesky@tie{}factorization as determined by @var{typ}. +@end table +@seealso{chol, etree, treelayout} +@end deftypefn */) +{ +#if defined (HAVE_CHOLMOD) + + int nargin = args.length (); + + if (nargin < 1 || nargin > 3) + print_usage (); + + octave_value_list retval; + + double dummy; + cholmod_sparse Astore; + cholmod_sparse *A = &Astore; + A->packed = true; + A->sorted = true; + A->nz = nullptr; +#if defined (OCTAVE_ENABLE_64) + A->itype = CHOLMOD_LONG; +#else + A->itype = CHOLMOD_INT; +#endif + A->dtype = CHOLMOD_DOUBLE; + A->stype = 1; + A->x = &dummy; + + if (args(0).isreal ()) + { + const SparseMatrix a = args(0).sparse_matrix_value (); + A->nrow = a.rows (); + A->ncol = a.cols (); + A->p = a.cidx (); + A->i = a.ridx (); + A->nzmax = a.nnz (); + A->xtype = CHOLMOD_REAL; + + if (a.rows () > 0 && a.cols () > 0) + A->x = a.data (); + } + else if (args(0).iscomplex ()) + { + const SparseComplexMatrix a = args(0).sparse_complex_matrix_value (); + A->nrow = a.rows (); + A->ncol = a.cols (); + A->p = a.cidx (); + A->i = a.ridx (); + A->nzmax = a.nnz (); + A->xtype = CHOLMOD_COMPLEX; + + if (a.rows () > 0 && a.cols () > 0) + A->x = a.data (); + } + else + err_wrong_type_arg ("symbfact", args(0)); + + bool coletree = false; + octave_idx_type n = A->nrow; + + if (nargin > 1) + { + std::string str = args(1).xstring_value ("TYP must be a string"); + // FIXME: The input validation could be improved to use strncmp + char ch; + ch = tolower (str[0]); + if (ch == 'r') // 'row' + A->stype = 0; + else if (ch == 'c') // 'col' + { + n = A->ncol; + coletree = true; + A->stype = 0; + } + else if (ch == 's') // 'sym' (default) + A->stype = 1; + else if (ch == 'l') // 'lo' + A->stype = -1; + else + error (R"(symbfact: unrecognized TYP "%s")", str.c_str ()); + } + + if (nargin == 3) + { + std::string str = args(2).xstring_value ("MODE must be a string"); + // FIXME: The input validation could be improved to use strncmp + char ch; + ch = toupper (str[0]); + if (ch != 'L') + error (R"(symbfact: unrecognized MODE "%s")", str.c_str ()); + } + + if (A->stype && A->nrow != A->ncol) + err_square_matrix_required ("symbfact", "S"); + + OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, Parent, n); + OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, Post, n); + OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, ColCount, n); + OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, First, n); + OCTAVE_LOCAL_BUFFER (octave_idx_type, Level, n); + + cholmod_common Common; + cholmod_common *cm = &Common; + CHOLMOD_NAME(start) (cm); + + double spu = octave_sparse_params::get_key ("spumoni"); + if (spu == 0.0) + { + cm->print = -1; + SUITESPARSE_ASSIGN_FPTR (printf_func, cm->print_function, nullptr); + } + else + { + cm->print = static_cast<int> (spu) + 2; + SUITESPARSE_ASSIGN_FPTR (printf_func, cm->print_function, &SparseCholPrint); + } + + cm->error_handler = &SparseCholError; + SUITESPARSE_ASSIGN_FPTR2 (divcomplex_func, cm->complex_divide, divcomplex); + SUITESPARSE_ASSIGN_FPTR2 (hypot_func, cm->hypotenuse, hypot); + + cholmod_sparse *F = CHOLMOD_NAME(transpose) (A, 0, cm); + cholmod_sparse *Aup, *Alo; + + if (A->stype == 1 || coletree) + { + Aup = A; + Alo = F; + } + else + { + Aup = F; + Alo = A; + } + + CHOLMOD_NAME(etree) (Aup, Parent, cm); + + ColumnVector tmp (n); // Declaration must precede any goto cleanup. + std::string err_msg; + + if (cm->status < CHOLMOD_OK) + { + err_msg = "symbfact: matrix corrupted"; + goto cleanup; + } + + if (CHOLMOD_NAME(postorder) (Parent, n, nullptr, Post, cm) != n) + { + err_msg = "symbfact: postorder failed"; + goto cleanup; + } + + CHOLMOD_NAME(rowcolcounts) (Alo, nullptr, 0, Parent, Post, nullptr, ColCount, + First, octave::to_suitesparse_intptr (Level), cm); + + if (cm->status < CHOLMOD_OK) + { + err_msg = "symbfact: matrix corrupted"; + goto cleanup; + } + + if (nargout > 4) + { + cholmod_sparse *A1, *A2; + + if (A->stype == 1) + { + A1 = A; + A2 = nullptr; + } + else if (A->stype == -1) + { + A1 = F; + A2 = nullptr; + } + else if (coletree) + { + A1 = F; + A2 = A; + } + else + { + A1 = A; + A2 = F; + } + + // count the total number of entries in L + octave_idx_type lnz = 0; + for (octave_idx_type j = 0 ; j < n ; j++) + lnz += ColCount[j]; + + // allocate the output matrix L (pattern-only) + SparseBoolMatrix L (dim_vector (n, n), lnz); + + // initialize column pointers + lnz = 0; + for (octave_idx_type j = 0 ; j < n ; j++) + { + L.xcidx(j) = lnz; + lnz += ColCount[j]; + } + L.xcidx(n) = lnz; + + // create a copy of the column pointers + octave::suitesparse_integer *W = First; + for (octave_idx_type j = 0 ; j < n ; j++) + W[j] = L.xcidx (j); + + // get workspace for computing one row of L + cholmod_sparse *R + = CHOLMOD_NAME(allocate_sparse) (n, 1, n, false, true, + 0, CHOLMOD_PATTERN, cm); + octave_idx_type *Rp = static_cast<octave_idx_type *> (R->p); + octave_idx_type *Ri = static_cast<octave_idx_type *> (R->i); + + // compute L one row at a time + for (octave_idx_type k = 0 ; k < n ; k++) + { + // get the kth row of L and store in the columns of L + CHOLMOD_NAME(row_subtree) (A1, A2, k, Parent, R, cm); + for (octave_idx_type p = 0 ; p < Rp[1] ; p++) + L.xridx (W[Ri[p]]++) = k; + + // add the diagonal entry + L.xridx (W[k]++) = k; + } + + // free workspace + CHOLMOD_NAME(free_sparse) (&R, cm); + + // fill L with one's + std::fill_n (L.xdata (), lnz, true); + + // transpose L to get R, or leave as is + if (nargin < 3) + L = L.transpose (); + + retval(4) = L; + } + + if (nargout > 3) + { + for (octave_idx_type i = 0; i < n; i++) + tmp(i) = Post[i] + 1; + retval(3) = tmp; + } + + if (nargout > 2) + { + for (octave_idx_type i = 0; i < n; i++) + tmp(i) = Parent[i] + 1; + retval(2) = tmp; + } + + if (nargout > 1) + { + // compute the elimination tree height + octave_idx_type height = 0; + for (int i = 0 ; i < n ; i++) + height = std::max (height, Level[i]); + height++; + retval(1) = static_cast<double> (height); + } + + for (octave_idx_type i = 0; i < n; i++) + tmp(i) = ColCount[i]; + retval(0) = tmp; + +cleanup: + CHOLMOD_NAME(free_sparse) (&F, cm); + CHOLMOD_NAME(finish) (cm); + + if (! err_msg.empty ()) + error ("%s", err_msg.c_str ()); + + return retval; + +#else + + octave_unused_parameter (args); + octave_unused_parameter (nargout); + + err_disabled_feature ("symbfact", "CHOLMOD"); + +#endif +} + +/* +%!testif HAVE_CHOLMOD +%! A = sparse (magic (3)); +%! [count, h, parent, post, r] = symbfact (A); +%! assert (count, [3; 2; 1]); +%! assert (h, 3); +%! assert (parent, [2; 3; 0]); +%! assert (r, sparse (triu (true (3)))); + +%!testif HAVE_CHOLMOD +%! ## Test MODE "lower" +%! A = sparse (magic (3)); +%! [~, ~, ~, ~, l] = symbfact (A, "sym", "lower"); +%! assert (l, sparse (tril (true (3)))); + +%!testif HAVE_CHOLMOD <*42587> +%! ## singular matrix +%! A = sparse ([1 0 8;0 1 8;8 8 1]); +%! [count, h, parent, post, r] = symbfact (A); + +## Test input validation +%!testif HAVE_CHOLMOD +%! fail ("symbfact ()"); +%! fail ("symbfact (1,2,3,4)"); +%! fail ("symbfact ({1})", "wrong type argument 'cell'"); +%! fail ("symbfact (sparse (1), {1})", "TYP must be a string"); +%! fail ("symbfact (sparse (1), 'foobar')", 'unrecognized TYP "foobar"'); +%! fail ("symbfact (sparse (1), 'sym', {'L'})", "MODE must be a string"); +%! fail ('symbfact (sparse (1), "sym", "foobar")', 'unrecognized MODE "foobar"'); +%! fail ("symbfact (sparse ([1, 2; 3, 4; 5, 6]))", "S must be a square matrix"); + +*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/symrcm.cc Wed Jan 29 06:30:40 2020 -0500 @@ -0,0 +1,705 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2007-2020 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or <https://octave.org/copyright/>. +// +// This file is part of Octave. +// +// 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 3 of the License, 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 Octave; see the file COPYING. If not, see +// <https://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////// + +/* +An implementation of the Reverse Cuthill-McKee algorithm (symrcm) + +The implementation of this algorithm is based in the descriptions found in + +@INPROCEEDINGS{, + author = {E. Cuthill and J. McKee}, + title = {Reducing the Bandwidth of Sparse Symmetric Matrices}, + booktitle = {Proceedings of the 24th ACM National Conference}, + publisher = {Brandon Press}, + pages = {157 -- 172}, + location = {New Jersey}, + year = {1969} +} + +@BOOK{, + author = {Alan George and Joseph W. H. Liu}, + title = {Computer Solution of Large Sparse Positive Definite Systems}, + publisher = {Prentice Hall Series in Computational Mathematics}, + ISBN = {0-13-165274-5}, + year = {1981} +} + +The algorithm represents a heuristic approach to the NP-complete minimum +bandwidth problem. + +Written by Michael Weitzel <michael.weitzel@@uni-siegen.de> + <weitzel@@ldknet.org> +*/ + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include <algorithm> + +#include "CSparse.h" +#include "boolNDArray.h" +#include "dNDArray.h" +#include "dSparse.h" +#include "oct-locbuf.h" +#include "oct-sparse.h" +#include "quit.h" + +#include "defun.h" +#include "errwarn.h" +#include "ov.h" +#include "ovl.h" + +// A node struct for the Cuthill-McKee algorithm +struct CMK_Node +{ + // the node's id (matrix row index) + octave_idx_type id; + // the node's degree + octave_idx_type deg; + // minimal distance to the root of the spanning tree + octave_idx_type dist; +}; + +// A simple queue. +// Queues Q have a fixed maximum size N (rows,cols of the matrix) and are +// stored in an array. qh and qt point to queue head and tail. + +// Enqueue operation (adds a node "o" at the tail) + +inline static void +Q_enq (CMK_Node *Q, octave_idx_type N, octave_idx_type& qt, const CMK_Node& o) +{ + Q[qt] = o; + qt = (qt + 1) % (N + 1); +} + +// Dequeue operation (removes a node from the head) + +inline static CMK_Node +Q_deq (CMK_Node * Q, octave_idx_type N, octave_idx_type& qh) +{ + CMK_Node r = Q[qh]; + qh = (qh + 1) % (N + 1); + return r; +} + +// Predicate (queue empty) +#define Q_empty(Q, N, qh, qt) ((qh) == (qt)) + +// A simple, array-based binary heap (used as a priority queue for nodes) + +// the left descendant of entry i +#define LEFT(i) (((i) << 1) + 1) // = (2*(i)+1) +// the right descendant of entry i +#define RIGHT(i) (((i) << 1) + 2) // = (2*(i)+2) +// the parent of entry i +#define PARENT(i) (((i) - 1) >> 1) // = floor(((i)-1)/2) + +// Builds a min-heap (the root contains the smallest element). A is an array +// with the graph's nodes, i is a starting position, size is the length of A. + +static void +H_heapify_min (CMK_Node *A, octave_idx_type i, octave_idx_type size) +{ + octave_idx_type j = i; + for (;;) + { + octave_idx_type l = LEFT(j); + octave_idx_type r = RIGHT(j); + + octave_idx_type smallest; + if (l < size && A[l].deg < A[j].deg) + smallest = l; + else + smallest = j; + + if (r < size && A[r].deg < A[smallest].deg) + smallest = r; + + if (smallest != j) + { + std::swap (A[j], A[smallest]); + j = smallest; + } + else + break; + } +} + +// Heap operation insert. Running time is O(log(n)) + +static void +H_insert (CMK_Node *H, octave_idx_type& h, const CMK_Node& o) +{ + octave_idx_type i = h++; + + H[i] = o; + + if (i == 0) + return; + do + { + octave_idx_type p = PARENT(i); + if (H[i].deg < H[p].deg) + { + std::swap (H[i], H[p]); + + i = p; + } + else + break; + } + while (i > 0); +} + +// Heap operation remove-min. Removes the smallest element in O(1) and +// reorganizes the heap optionally in O(log(n)) + +inline static CMK_Node +H_remove_min (CMK_Node *H, octave_idx_type& h, int reorg/*=1*/) +{ + CMK_Node r = H[0]; + H[0] = H[--h]; + if (reorg) + H_heapify_min (H, 0, h); + return r; +} + +// Predicate (heap empty) +#define H_empty(H, h) ((h) == 0) + +// Helper function for the Cuthill-McKee algorithm. Tries to determine a +// pseudo-peripheral node of the graph as starting node. + +static octave_idx_type +find_starting_node (octave_idx_type N, const octave_idx_type *ridx, + const octave_idx_type *cidx, const octave_idx_type *ridx2, + const octave_idx_type *cidx2, octave_idx_type *D, + octave_idx_type start) +{ + CMK_Node w; + + OCTAVE_LOCAL_BUFFER (CMK_Node, Q, N+1); + boolNDArray btmp (dim_vector (1, N), false); + bool *visit = btmp.fortran_vec (); + + octave_idx_type qh = 0; + octave_idx_type qt = 0; + CMK_Node x; + x.id = start; + x.deg = D[start]; + x.dist = 0; + Q_enq (Q, N, qt, x); + visit[start] = true; + + // distance level + octave_idx_type level = 0; + // current largest "eccentricity" + octave_idx_type max_dist = 0; + + for (;;) + { + while (! Q_empty (Q, N, qh, qt)) + { + CMK_Node v = Q_deq (Q, N, qh); + + if (v.dist > x.dist || (v.id != x.id && v.deg > x.deg)) + x = v; + + octave_idx_type i = v.id; + + // add all unvisited neighbors to the queue + octave_idx_type j1 = cidx[i]; + octave_idx_type j2 = cidx2[i]; + while (j1 < cidx[i+1] || j2 < cidx2[i+1]) + { + octave_quit (); + + if (j1 == cidx[i+1]) + { + octave_idx_type r2 = ridx2[j2++]; + if (! visit[r2]) + { + // the distance of node j is dist(i)+1 + w.id = r2; + w.deg = D[r2]; + w.dist = v.dist+1; + Q_enq (Q, N, qt, w); + visit[r2] = true; + + if (w.dist > level) + level = w.dist; + } + } + else if (j2 == cidx2[i+1]) + { + octave_idx_type r1 = ridx[j1++]; + if (! visit[r1]) + { + // the distance of node j is dist(i)+1 + w.id = r1; + w.deg = D[r1]; + w.dist = v.dist+1; + Q_enq (Q, N, qt, w); + visit[r1] = true; + + if (w.dist > level) + level = w.dist; + } + } + else + { + octave_idx_type r1 = ridx[j1]; + octave_idx_type r2 = ridx2[j2]; + if (r1 <= r2) + { + if (! visit[r1]) + { + w.id = r1; + w.deg = D[r1]; + w.dist = v.dist+1; + Q_enq (Q, N, qt, w); + visit[r1] = true; + + if (w.dist > level) + level = w.dist; + } + j1++; + if (r1 == r2) + j2++; + } + else + { + if (! visit[r2]) + { + w.id = r2; + w.deg = D[r2]; + w.dist = v.dist+1; + Q_enq (Q, N, qt, w); + visit[r2] = true; + + if (w.dist > level) + level = w.dist; + } + j2++; + } + } + } + } // finish of BFS + + if (max_dist < x.dist) + { + max_dist = x.dist; + + for (octave_idx_type i = 0; i < N; i++) + visit[i] = false; + + visit[x.id] = true; + x.dist = 0; + qt = qh = 0; + Q_enq (Q, N, qt, x); + } + else + break; + } + return x.id; +} + +// Calculates the node's degrees. This means counting the nonzero elements +// in the symmetric matrix' rows. This works for non-symmetric matrices +// as well. + +static octave_idx_type +calc_degrees (octave_idx_type N, const octave_idx_type *ridx, + const octave_idx_type *cidx, octave_idx_type *D) +{ + octave_idx_type max_deg = 0; + + for (octave_idx_type i = 0; i < N; i++) + D[i] = 0; + + for (octave_idx_type j = 0; j < N; j++) + { + for (octave_idx_type i = cidx[j]; i < cidx[j+1]; i++) + { + octave_quit (); + + octave_idx_type k = ridx[i]; + // there is a nonzero element (k,j) + D[k]++; + if (D[k] > max_deg) + max_deg = D[k]; + // if there is no element (j,k) there is one in + // the symmetric matrix: + if (k != j) + { + bool found = false; + for (octave_idx_type l = cidx[k]; l < cidx[k + 1]; l++) + { + octave_quit (); + + if (ridx[l] == j) + { + found = true; + break; + } + else if (ridx[l] > j) + break; + } + + if (! found) + { + // A(j,k) == 0 + D[j]++; + if (D[j] > max_deg) + max_deg = D[j]; + } + } + } + } + return max_deg; +} + +// Transpose of the structure of a square sparse matrix + +static void +transpose (octave_idx_type N, const octave_idx_type *ridx, + const octave_idx_type *cidx, octave_idx_type *ridx2, + octave_idx_type *cidx2) +{ + octave_idx_type nz = cidx[N]; + + OCTAVE_LOCAL_BUFFER (octave_idx_type, w, N + 1); + for (octave_idx_type i = 0; i < N; i++) + w[i] = 0; + for (octave_idx_type i = 0; i < nz; i++) + w[ridx[i]]++; + nz = 0; + for (octave_idx_type i = 0; i < N; i++) + { + octave_quit (); + + cidx2[i] = nz; + nz += w[i]; + w[i] = cidx2[i]; + } + cidx2[N] = nz; + w[N] = nz; + + for (octave_idx_type j = 0; j < N; j++) + for (octave_idx_type k = cidx[j]; k < cidx[j + 1]; k++) + { + octave_quit (); + + octave_idx_type q = w[ridx[k]]++; + ridx2[q] = j; + } +} + +// An implementation of the Cuthill-McKee algorithm. +DEFUN (symrcm, args, , + doc: /* -*- texinfo -*- +@deftypefn {} {@var{p} =} symrcm (@var{S}) +Return the symmetric reverse @nospell{Cuthill-McKee} permutation of @var{S}. + +@var{p} is a permutation vector such that +@code{@var{S}(@var{p}, @var{p})} tends to have its diagonal elements closer +to the diagonal than @var{S}. This is a good preordering for LU or +Cholesky@tie{}factorization of matrices that come from ``long, skinny'' +problems. It works for both symmetric and asymmetric @var{S}. + +The algorithm represents a heuristic approach to the NP-complete bandwidth +minimization problem. The implementation is based in the descriptions found +in + +@nospell{E. Cuthill, J. McKee}. +@cite{Reducing the Bandwidth of Sparse Symmetric Matrices}. +Proceedings of the 24th @nospell{ACM} National Conference, +157--172 1969, Brandon Press, New Jersey. + +@nospell{A. George, J.W.H. Liu}. @cite{Computer Solution of Large Sparse +Positive Definite Systems}, Prentice Hall Series in Computational +Mathematics, ISBN 0-13-165274-5, 1981. + +@seealso{colperm, colamd, symamd} +@end deftypefn */) +{ + if (args.length () != 1) + print_usage (); + + octave_value arg = args(0); + + // the parameter of the matrix is converted into a sparse matrix + //(if necessary) + octave_idx_type *cidx; + octave_idx_type *ridx; + SparseMatrix Ar; + SparseComplexMatrix Ac; + + if (arg.isreal ()) + { + Ar = arg.sparse_matrix_value (); + // Note cidx/ridx are const, so use xridx and xcidx... + cidx = Ar.xcidx (); + ridx = Ar.xridx (); + } + else + { + Ac = arg.sparse_complex_matrix_value (); + cidx = Ac.xcidx (); + ridx = Ac.xridx (); + } + + octave_idx_type nr = arg.rows (); + octave_idx_type nc = arg.columns (); + + if (nr != nc) + err_square_matrix_required ("symrcm", "S"); + + if (nr == 0 && nc == 0) + return ovl (NDArray (dim_vector (1, 0))); + + // sizes of the heaps + octave_idx_type s = 0; + + // head- and tail-indices for the queue + octave_idx_type qt = 0; + octave_idx_type qh = 0; + CMK_Node v, w; + // dimension of the matrix + octave_idx_type N = nr; + + OCTAVE_LOCAL_BUFFER (octave_idx_type, cidx2, N + 1); + OCTAVE_LOCAL_BUFFER (octave_idx_type, ridx2, cidx[N]); + transpose (N, ridx, cidx, ridx2, cidx2); + + // the permutation vector + NDArray P (dim_vector (1, N)); + + // compute the node degrees + OCTAVE_LOCAL_BUFFER (octave_idx_type, D, N); + octave_idx_type max_deg = calc_degrees (N, ridx, cidx, D); + + // if none of the nodes has a degree > 0 (a matrix of zeros) + // the return value corresponds to the identity permutation + if (max_deg == 0) + { + for (octave_idx_type i = 0; i < N; i++) + P(i) = i; + + return ovl (P); + } + + // a heap for the a node's neighbors. The number of neighbors is + // limited by the maximum degree max_deg: + OCTAVE_LOCAL_BUFFER (CMK_Node, S, max_deg); + + // a queue for the BFS. The array is always one element larger than + // the number of entries that are stored. + OCTAVE_LOCAL_BUFFER (CMK_Node, Q, N+1); + + // a counter (for building the permutation) + octave_idx_type c = -1; + + // upper bound for the bandwidth (=quality of solution) + // initialize the bandwidth of the graph with 0. B contains the + // the maximum of the theoretical lower limits of the subgraphs + // bandwidths. + octave_idx_type B = 0; + + // mark all nodes as unvisited; with the exception of the nodes + // that have degree==0 and build a CC of the graph. + + boolNDArray btmp (dim_vector (1, N), false); + bool *visit = btmp.fortran_vec (); + + do + { + // locate an unvisited starting node of the graph + octave_idx_type i; + for (i = 0; i < N; i++) + if (! visit[i]) + break; + + // locate a probably better starting node + v.id = find_starting_node (N, ridx, cidx, ridx2, cidx2, D, i); + + // mark the node as visited and enqueue it (a starting node + // for the BFS). Since the node will be a root of a spanning + // tree, its dist is 0. + v.deg = D[v.id]; + v.dist = 0; + visit[v.id] = true; + Q_enq (Q, N, qt, v); + + // lower bound for the bandwidth of a subgraph + // keep a "level" in the spanning tree (= min. distance to the + // root) for determining the bandwidth of the computed + // permutation P + octave_idx_type Bsub = 0; + // min. dist. to the root is 0 + octave_idx_type level = 0; + // the root is the first/only node on level 0 + octave_idx_type level_N = 1; + + while (! Q_empty (Q, N, qh, qt)) + { + v = Q_deq (Q, N, qh); + i = v.id; + + c++; + + // for computing the inverse permutation P where + // A(inv(P),inv(P)) or P'*A*P is banded + // P(i) = c; + + // for computing permutation P where + // A(P(i),P(j)) or P*A*P' is banded + P(c) = i; + + // put all unvisited neighbors j of node i on the heap + s = 0; + octave_idx_type j1 = cidx[i]; + octave_idx_type j2 = cidx2[i]; + + octave_quit (); + + while (j1 < cidx[i+1] || j2 < cidx2[i+1]) + { + octave_quit (); + + if (j1 == cidx[i+1]) + { + octave_idx_type r2 = ridx2[j2++]; + if (! visit[r2]) + { + // the distance of node j is dist(i)+1 + w.id = r2; + w.deg = D[r2]; + w.dist = v.dist+1; + H_insert (S, s, w); + visit[r2] = true; + } + } + else if (j2 == cidx2[i+1]) + { + octave_idx_type r1 = ridx[j1++]; + if (! visit[r1]) + { + w.id = r1; + w.deg = D[r1]; + w.dist = v.dist+1; + H_insert (S, s, w); + visit[r1] = true; + } + } + else + { + octave_idx_type r1 = ridx[j1]; + octave_idx_type r2 = ridx2[j2]; + if (r1 <= r2) + { + if (! visit[r1]) + { + w.id = r1; + w.deg = D[r1]; + w.dist = v.dist+1; + H_insert (S, s, w); + visit[r1] = true; + } + j1++; + if (r1 == r2) + j2++; + } + else + { + if (! visit[r2]) + { + w.id = r2; + w.deg = D[r2]; + w.dist = v.dist+1; + H_insert (S, s, w); + visit[r2] = true; + } + j2++; + } + } + } + + // add the neighbors to the queue (sorted by node degree) + while (! H_empty (S, s)) + { + octave_quit (); + + // locate a neighbor of i with minimal degree in O(log(N)) + v = H_remove_min (S, s, 1); + + // entered the BFS a new level? + if (v.dist > level) + { + // adjustment of bandwidth: + // "[...] the minimum bandwidth that + // can be obtained [...] is the + // maximum number of nodes per level" + if (Bsub < level_N) + Bsub = level_N; + + level = v.dist; + // v is the first node on the new level + level_N = 1; + } + else + { + // there is no new level but another node on + // this level: + level_N++; + } + + // enqueue v in O(1) + Q_enq (Q, N, qt, v); + } + + // synchronize the bandwidth with level_N once again: + if (Bsub < level_N) + Bsub = level_N; + } + // finish of BFS. If there are still unvisited nodes in the graph + // then it is split into CCs. The computed bandwidth is the maximum + // of all subgraphs. Update: + if (Bsub > B) + B = Bsub; + } + // are there any nodes left? + while (c+1 < N); + + // compute the reverse-ordering + s = N / 2 - 1; + for (octave_idx_type i = 0, j = N - 1; i <= s; i++, j--) + std::swap (P.elem (i), P.elem (j)); + + // increment all indices, since Octave is not C + return ovl (P+1); +}
--- a/libinterp/corefcn/variables.cc Sat Jan 25 15:26:07 2020 +0100 +++ b/libinterp/corefcn/variables.cc Wed Jan 29 06:30:40 2020 -0500 @@ -492,11 +492,10 @@ %! end_unwind_protect %! assert (exist (fullfile (pwd (), "%nonexistentfile%"), "file"), 0); -%!testif HAVE_CHOLMOD -%! assert (exist ("chol"), 3); -%! assert (exist ("chol.oct"), 3); -%! assert (exist ("chol", "file"), 3); -%! assert (exist ("chol", "builtin"), 0); +%!assert (exist ("fftw"), 3); +%!assert (exist ("fftw.oct"), 3); +%!assert (exist ("fftw", "file"), 3); +%!assert (exist ("fftw", "builtin"), 0); %!assert (exist ("sin"), 5) %!assert (exist ("sin", "builtin"), 5)
--- a/libinterp/dldfcn/__eigs__.cc Sat Jan 25 15:26:07 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,675 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2005-2020 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or <https://octave.org/copyright/>. -// -// This file is part of Octave. -// -// 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 3 of the License, 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 Octave; see the file COPYING. If not, see -// <https://www.gnu.org/licenses/>. -// -//////////////////////////////////////////////////////////////////////// - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include <limits> -#include <string> - -#include "Matrix.h" -#include "eigs-base.h" -#include "unwind-prot.h" - -#include "defun-dld.h" -#include "error.h" -#include "errwarn.h" -#include "interpreter-private.h" -#include "oct-map.h" -#include "ov.h" -#include "ovl.h" -#include "pager.h" -#include "parse.h" -#include "variables.h" - -#if defined (HAVE_ARPACK) - -// Global pointer for user defined function. -static octave_value eigs_fcn; - -// Have we warned about imaginary values returned from user function? -static bool warned_imaginary = false; - -// Is this a recursive call? -static int call_depth = 0; - -ColumnVector -eigs_func (const ColumnVector& x, int& eigs_error) -{ - ColumnVector retval; - octave_value_list args; - args(0) = x; - - if (eigs_fcn.is_defined ()) - { - octave_value_list tmp; - - try - { - tmp = octave::feval (eigs_fcn, args, 1); - } - catch (octave::execution_exception& e) - { - err_user_supplied_eval (e, "eigs"); - } - - if (tmp.length () && tmp(0).is_defined ()) - { - if (! warned_imaginary && tmp(0).iscomplex ()) - { - warning ("eigs: ignoring imaginary part returned from user-supplied function"); - warned_imaginary = true; - } - - retval = tmp(0).xvector_value ("eigs: evaluation of user-supplied function failed"); - } - else - { - eigs_error = 1; - err_user_supplied_eval ("eigs"); - } - } - - return retval; -} - -ComplexColumnVector -eigs_complex_func (const ComplexColumnVector& x, int& eigs_error) -{ - ComplexColumnVector retval; - octave_value_list args; - args(0) = x; - - if (eigs_fcn.is_defined ()) - { - octave_value_list tmp; - - try - { - tmp = octave::feval (eigs_fcn, args, 1); - } - catch (octave::execution_exception& e) - { - err_user_supplied_eval (e, "eigs"); - } - - if (tmp.length () && tmp(0).is_defined ()) - { - retval = tmp(0).xcomplex_vector_value ("eigs: evaluation of user-supplied function failed"); - } - else - { - eigs_error = 1; - err_user_supplied_eval ("eigs"); - } - } - - return retval; -} - -#endif - -DEFMETHOD_DLD (__eigs__, interp, args, nargout, - doc: /* -*- texinfo -*- -@deftypefn {} {@var{d} =} __eigs__ (@var{A}) -@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{k}) -@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{k}, @var{sigma}) -@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{k}, @var{sigma}, @var{opts}) -@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B}) -@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B}, @var{k}) -@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B}, @var{k}, @var{sigma}) -@deftypefnx {} {@var{d} =} __eigs__ (@var{A}, @var{B}, @var{k}, @var{sigma}, @var{opts}) -@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}) -@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B}) -@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{k}) -@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B}, @var{k}) -@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{k}, @var{sigma}) -@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B}, @var{k}, @var{sigma}) -@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{k}, @var{sigma}, @var{opts}) -@deftypefnx {} {@var{d} =} __eigs__ (@var{af}, @var{n}, @var{B}, @var{k}, @var{sigma}, @var{opts}) -@deftypefnx {} {[@var{V}, @var{d}] =} __eigs__ (@var{A}, @dots{}) -@deftypefnx {} {[@var{V}, @var{d}] =} __eigs__ (@var{af}, @var{n}, @dots{}) -@deftypefnx {} {[@var{V}, @var{d}, @var{flag}] =} __eigs__ (@var{A}, @dots{}) -@deftypefnx {} {[@var{V}, @var{d}, @var{flag}] =} __eigs__ (@var{af}, @var{n}, @dots{}) -Undocumented internal function. -@end deftypefn */) -{ -#if defined (HAVE_ARPACK) - - int nargin = args.length (); - - if (nargin == 0) - print_usage (); - - octave_value_list retval; - - std::string fcn_name; - octave_idx_type n = 0; - octave_idx_type k = 6; - Complex sigma = 0.0; - double sigmar, sigmai; - bool have_sigma = false; - std::string typ = "LM"; - Matrix amm, bmm, bmt; - ComplexMatrix acm, bcm, bct; - SparseMatrix asmm, bsmm, bsmt; - SparseComplexMatrix ascm, bscm, bsct; - int b_arg = 0; - bool have_b = false; - bool have_a_fun = false; - bool a_is_complex = false; - bool b_is_complex = false; - bool symmetric = false; - bool sym_tested = false; - bool cholB = false; - bool a_is_sparse = false; - bool b_is_sparse = false; - ColumnVector permB; - int arg_offset = 0; - double tol = std::numeric_limits<double>::epsilon (); - int maxit = 300; - int disp = 0; - octave_idx_type p = -1; - ColumnVector resid; - ComplexColumnVector cresid; - octave_idx_type info = 1; - - warned_imaginary = false; - - octave::unwind_protect frame; - - frame.protect_var (call_depth); - call_depth++; - - if (call_depth > 1) - error ("eigs: invalid recursive call"); - - if (args(0).is_function_handle () || args(0).is_inline_function () - || args(0).is_string ()) - { - eigs_fcn = octave::get_function_handle (interp, args(0), "x"); - - if (eigs_fcn.is_undefined ()) - error ("eigs: unknown function"); - - if (nargin < 2) - error ("eigs: incorrect number of arguments"); - - n = args(1).nint_value (); - arg_offset = 1; - have_a_fun = true; - } - else - { - if (args(0).iscomplex ()) - { - if (args(0).issparse ()) - { - ascm = (args(0).sparse_complex_matrix_value ()); - a_is_sparse = true; - } - else - acm = (args(0).complex_matrix_value ()); - a_is_complex = true; - } - else - { - if (args(0).issparse ()) - { - asmm = (args(0).sparse_matrix_value ()); - a_is_sparse = true; - } - else - { - amm = (args(0).matrix_value ()); - } - } - } - - // Note hold off reading B until later to avoid issues of double - // copies of the matrix if B is full/real while A is complex. - if (nargin > 1 + arg_offset - && ! (args(1 + arg_offset).is_real_scalar ())) - { - if (args(1+arg_offset).iscomplex ()) - { - b_arg = 1+arg_offset; - if (args(b_arg).issparse ()) - { - bscm = (args(b_arg).sparse_complex_matrix_value ()); - b_is_sparse = true; - } - else - bcm = (args(b_arg).complex_matrix_value ()); - have_b = true; - b_is_complex = true; - arg_offset++; - } - else - { - b_arg = 1+arg_offset; - if (args(b_arg).issparse ()) - { - bsmm = (args(b_arg).sparse_matrix_value ()); - b_is_sparse = true; - } - else - bmm = (args(b_arg).matrix_value ()); - have_b = true; - arg_offset++; - } - } - - if (nargin > (1+arg_offset)) - k = args(1+arg_offset).nint_value (); - - if (nargin > (2+arg_offset)) - { - if (args(2+arg_offset).is_string ()) - { - typ = args(2+arg_offset).string_value (); - - // Use STL function to convert to upper case - transform (typ.begin (), typ.end (), typ.begin (), toupper); - - sigma = 0.0; - } - else - { - sigma = args(2+arg_offset).xcomplex_value ("eigs: SIGMA must be a scalar or a string"); - - have_sigma = true; - } - } - - sigmar = sigma.real (); - sigmai = sigma.imag (); - - if (nargin > (3+arg_offset)) - { - if (! args(3+arg_offset).isstruct ()) - error ("eigs: OPTS argument must be a structure"); - - octave_scalar_map map = args(3+arg_offset).xscalar_map_value ("eigs: OPTS argument must be a scalar structure"); - - octave_value tmp; - - // issym is ignored for complex matrix inputs - tmp = map.getfield ("issym"); - if (tmp.is_defined ()) - { - if (tmp.numel () != 1) - error ("eigs: OPTS.issym must be a scalar value"); - - symmetric = tmp.xbool_value ("eigs: OPTS.issym must be a logical value"); - sym_tested = true; - } - - // isreal is ignored if A is not a function - if (have_a_fun) - { - tmp = map.getfield ("isreal"); - if (tmp.is_defined ()) - { - if (tmp.numel () != 1) - error ("eigs: OPTS.isreal must be a scalar value"); - - a_is_complex = ! tmp.xbool_value ("eigs: OPTS.isreal must be a logical value"); - } - } - - tmp = map.getfield ("tol"); - if (tmp.is_defined ()) - tol = tmp.double_value (); - - tmp = map.getfield ("maxit"); - if (tmp.is_defined ()) - maxit = tmp.nint_value (); - - tmp = map.getfield ("p"); - if (tmp.is_defined ()) - p = tmp.nint_value (); - - tmp = map.getfield ("v0"); - if (tmp.is_defined ()) - { - if (a_is_complex || b_is_complex) - cresid = ComplexColumnVector (tmp.complex_vector_value ()); - else - resid = ColumnVector (tmp.vector_value ()); - } - - tmp = map.getfield ("disp"); - if (tmp.is_defined ()) - disp = tmp.nint_value (); - - tmp = map.getfield ("cholB"); - if (tmp.is_defined ()) - { - if (tmp.numel () != 1) - error ("eigs: OPTS.cholB must be a scalar value"); - - cholB = tmp.xbool_value ("eigs: OPTS.cholB must be a logical value"); - } - - tmp = map.getfield ("permB"); - if (tmp.is_defined ()) - permB = ColumnVector (tmp.vector_value ()) - 1.0; - } - - if (nargin > (4+arg_offset)) - error ("eigs: incorrect number of arguments"); - - // Test undeclared (no issym) matrix inputs for symmetry - if (! sym_tested && ! have_a_fun) - { - if (a_is_complex) - { - if (a_is_sparse) - symmetric = ascm.ishermitian (); - else - symmetric = acm.ishermitian (); - } - else - { - if (a_is_sparse) - symmetric = asmm.issymmetric (); - else - symmetric = amm.issymmetric (); - } - } - - if (have_b) - { - if (a_is_complex || b_is_complex) - { - if (b_is_sparse) - bscm = args(b_arg).sparse_complex_matrix_value (); - else - bcm = args(b_arg).complex_matrix_value (); - } - else - { - if (b_is_sparse) - bsmm = args(b_arg).sparse_matrix_value (); - else - bmm = args(b_arg).matrix_value (); - } - } - - // Mode 1 for SM mode seems unstable for some reason. - // Use Mode 3 instead, with sigma = 0. - if (! have_sigma && typ == "SM") - have_sigma = true; - - octave_idx_type nconv; - if (a_is_complex || b_is_complex) - { - ComplexMatrix eig_vec; - ComplexColumnVector eig_val; - - if (have_a_fun) - { - if (b_is_sparse) - nconv = EigsComplexNonSymmetricFunc - (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec, - eig_val, bscm, permB, cresid, octave_stdout, tol, - (nargout > 1), cholB, disp, maxit); - else - nconv = EigsComplexNonSymmetricFunc - (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec, - eig_val, bcm, permB, cresid, octave_stdout, tol, - (nargout > 1), cholB, disp, maxit); - } - else if (have_sigma) - { - if (a_is_sparse) - nconv = EigsComplexNonSymmetricMatrixShift - (ascm, sigma, k, p, info, eig_vec, eig_val, bscm, permB, - cresid, octave_stdout, tol, (nargout > 1), cholB, disp, - maxit); - else - nconv = EigsComplexNonSymmetricMatrixShift - (acm, sigma, k, p, info, eig_vec, eig_val, bcm, permB, - cresid, octave_stdout, tol, (nargout > 1), cholB, disp, - maxit); - } - else - { - if (a_is_sparse) - nconv = EigsComplexNonSymmetricMatrix - (ascm, typ, k, p, info, eig_vec, eig_val, bscm, permB, - cresid, octave_stdout, tol, (nargout > 1), cholB, disp, - maxit); - else - nconv = EigsComplexNonSymmetricMatrix - (acm, typ, k, p, info, eig_vec, eig_val, bcm, permB, - cresid, octave_stdout, tol, (nargout > 1), cholB, disp, - maxit); - } - - if (nargout < 2) - { - if (symmetric) - retval(0) = real (eig_val); - else - retval(0) = eig_val; - } - else - { - if (symmetric) - retval = ovl (eig_vec, DiagMatrix (real (eig_val)), double (info)); - else - retval = ovl (eig_vec, ComplexDiagMatrix (eig_val), double (info)); - } - } - else if (sigmai != 0.0) - { - // Promote real problem to a complex one. - ComplexMatrix eig_vec; - ComplexColumnVector eig_val; - - if (have_a_fun) - { - if (b_is_sparse) - nconv = EigsComplexNonSymmetricFunc - (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec, - eig_val, bscm, permB, cresid, octave_stdout, tol, - (nargout > 1), cholB, disp, maxit); - else - nconv = EigsComplexNonSymmetricFunc - (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec, - eig_val, bcm, permB, cresid, octave_stdout, tol, - (nargout > 1), cholB, disp, maxit); - } - else - { - if (a_is_sparse) - nconv = EigsComplexNonSymmetricMatrixShift - (SparseComplexMatrix (asmm), sigma, k, p, info, eig_vec, - eig_val, SparseComplexMatrix (bsmm), permB, cresid, - octave_stdout, tol, (nargout > 1), cholB, disp, maxit); - else - nconv = EigsComplexNonSymmetricMatrixShift - (ComplexMatrix (amm), sigma, k, p, info, eig_vec, - eig_val, ComplexMatrix (bmm), permB, cresid, - octave_stdout, tol, (nargout > 1), cholB, disp, maxit); - } - - if (nargout < 2) - { - if (symmetric) - retval(0) = real (eig_val); - else - retval(0) = eig_val; - } - else - { - if (symmetric) - retval = ovl (eig_vec, DiagMatrix (real (eig_val)), double (info)); - else - retval = ovl (eig_vec, ComplexDiagMatrix (eig_val), double (info)); - } - } - else - { - if (symmetric) - { - Matrix eig_vec; - ColumnVector eig_val; - - if (have_a_fun) - { - if (b_is_sparse) - nconv = EigsRealSymmetricFunc - (eigs_func, n, typ, sigmar, k, p, info, eig_vec, - eig_val, bsmm, permB, resid, octave_stdout, tol, - (nargout > 1), cholB, disp, maxit); - else - nconv = EigsRealSymmetricFunc - (eigs_func, n, typ, sigmar, k, p, info, eig_vec, - eig_val, bmm, permB, resid, octave_stdout, tol, - (nargout > 1), cholB, disp, maxit); - } - else if (have_sigma) - { - if (a_is_sparse) - nconv = EigsRealSymmetricMatrixShift - (asmm, sigmar, k, p, info, eig_vec, eig_val, bsmm, - permB, resid, octave_stdout, tol, (nargout > 1), - cholB, disp, maxit); - else - nconv = EigsRealSymmetricMatrixShift - (amm, sigmar, k, p, info, eig_vec, eig_val, bmm, - permB, resid, octave_stdout, tol, (nargout > 1), - cholB, disp, maxit); - } - else - { - if (a_is_sparse) - nconv = EigsRealSymmetricMatrix - (asmm, typ, k, p, info, eig_vec, eig_val, bsmm, - permB, resid, octave_stdout, tol, (nargout > 1), - cholB, disp, maxit); - else - nconv = EigsRealSymmetricMatrix - (amm, typ, k, p, info, eig_vec, eig_val, bmm, permB, - resid, octave_stdout, tol, (nargout > 1), cholB, - disp, maxit); - } - - if (nargout < 2) - retval(0) = eig_val; - else - retval = ovl (eig_vec, DiagMatrix (eig_val), double (info)); - } - else - { - ComplexMatrix eig_vec; - ComplexColumnVector eig_val; - - if (have_a_fun) - { - if (b_is_sparse) - nconv = EigsRealNonSymmetricFunc - (eigs_func, n, typ, sigmar, k, p, info, eig_vec, - eig_val, bsmm, permB, resid, octave_stdout, tol, - (nargout > 1), cholB, disp, maxit); - else - nconv = EigsRealNonSymmetricFunc - (eigs_func, n, typ, sigmar, k, p, info, eig_vec, - eig_val, bmm, permB, resid, octave_stdout, tol, - (nargout > 1), cholB, disp, maxit); - } - else if (have_sigma) - { - if (a_is_sparse) - nconv = EigsRealNonSymmetricMatrixShift - (asmm, sigmar, k, p, info, eig_vec, eig_val, bsmm, - permB, resid, octave_stdout, tol, (nargout > 1), - cholB, disp, maxit); - else - nconv = EigsRealNonSymmetricMatrixShift - (amm, sigmar, k, p, info, eig_vec, eig_val, bmm, - permB, resid, octave_stdout, tol, (nargout > 1), - cholB, disp, maxit); - } - else - { - if (a_is_sparse) - nconv = EigsRealNonSymmetricMatrix - (asmm, typ, k, p, info, eig_vec, eig_val, bsmm, - permB, resid, octave_stdout, tol, (nargout > 1), - cholB, disp, maxit); - else - nconv = EigsRealNonSymmetricMatrix - (amm, typ, k, p, info, eig_vec, eig_val, bmm, permB, - resid, octave_stdout, tol, (nargout > 1), cholB, - disp, maxit); - } - - if (nargout < 2) - retval(0) = eig_val; - else - retval = ovl (eig_vec, ComplexDiagMatrix (eig_val), double (info)); - } - } - - if (nconv <= 0) - warning_with_id ("Octave:eigs:UnconvergedEigenvalues", - "eigs: None of the %" OCTAVE_IDX_TYPE_FORMAT - " requested eigenvalues converged", k); - else if (nconv < k) - warning_with_id ("Octave:eigs:UnconvergedEigenvalues", - "eigs: Only %" OCTAVE_IDX_TYPE_FORMAT - " of the %" OCTAVE_IDX_TYPE_FORMAT - " requested eigenvalues converged", - nconv, k); - - if (! fcn_name.empty ()) - { - octave::symbol_table& symtab = interp.get_symbol_table (); - - symtab.clear_function (fcn_name); - } - - return retval; - -#else - - octave_unused_parameter (interp); - octave_unused_parameter (args); - octave_unused_parameter (nargout); - - err_disabled_feature ("eigs", "ARPACK"); - -#endif -} - -/* -## No test needed for internal helper function. -%!assert (1) -*/
--- a/libinterp/dldfcn/amd.cc Sat Jan 25 15:26:07 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,205 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2008-2020 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or <https://octave.org/copyright/>. -// -// This file is part of Octave. -// -// 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 3 of the License, 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 Octave; see the file COPYING. If not, see -// <https://www.gnu.org/licenses/>. -// -//////////////////////////////////////////////////////////////////////// - -// This is the octave interface to amd, which bore the copyright given -// in the help of the functions. - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include <cstdlib> - -#include "CSparse.h" -#include "Sparse.h" -#include "dMatrix.h" -#include "oct-locbuf.h" -#include "oct-sparse.h" - -#include "defun-dld.h" -#include "error.h" -#include "errwarn.h" -#include "oct-map.h" -#include "ov.h" -#include "ovl.h" -#include "parse.h" - -DEFMETHOD_DLD (amd, interp, args, nargout, - doc: /* -*- texinfo -*- -@deftypefn {} {@var{p} =} amd (@var{S}) -@deftypefnx {} {@var{p} =} amd (@var{S}, @var{opts}) - -Return the approximate minimum degree permutation of a matrix. - -This is a permutation such that the Cholesky@tie{}factorization of -@code{@var{S} (@var{p}, @var{p})} tends to be sparser than the -Cholesky@tie{}factorization of @var{S} itself. @code{amd} is typically -faster than @code{symamd} but serves a similar purpose. - -The optional parameter @var{opts} is a structure that controls the behavior -of @code{amd}. The fields of the structure are - -@table @asis -@item @var{opts}.dense -Determines what @code{amd} considers to be a dense row or column of the -input matrix. Rows or columns with more than @code{max (16, (dense * -sqrt (@var{n})))} entries, where @var{n} is the order of the matrix @var{S}, -are ignored by @code{amd} during the calculation of the permutation. -The value of dense must be a positive scalar and the default value is 10.0 - -@item @var{opts}.aggressive -If this value is a nonzero scalar, then @code{amd} performs aggressive -absorption. The default is not to perform aggressive absorption. -@end table - -The author of the code itself is Timothy A. Davis -(see @url{http://faculty.cse.tamu.edu/davis/suitesparse.html}). -@seealso{symamd, colamd} -@end deftypefn */) -{ -#if defined (HAVE_AMD) - - int nargin = args.length (); - - if (nargin < 1 || nargin > 2) - print_usage (); - - octave_idx_type n_row, n_col; - const octave::suitesparse_integer *ridx, *cidx; - SparseMatrix sm; - SparseComplexMatrix scm; - - if (args(0).issparse ()) - { - if (args(0).iscomplex ()) - { - scm = args(0).sparse_complex_matrix_value (); - n_row = scm.rows (); - n_col = scm.cols (); - ridx = octave::to_suitesparse_intptr (scm.xridx ()); - cidx = octave::to_suitesparse_intptr (scm.xcidx ()); - } - else - { - sm = args(0).sparse_matrix_value (); - n_row = sm.rows (); - n_col = sm.cols (); - ridx = octave::to_suitesparse_intptr (sm.xridx ()); - cidx = octave::to_suitesparse_intptr (sm.xcidx ()); - } - } - else - { - if (args(0).iscomplex ()) - sm = SparseMatrix (real (args(0).complex_matrix_value ())); - else - sm = SparseMatrix (args(0).matrix_value ()); - - n_row = sm.rows (); - n_col = sm.cols (); - ridx = octave::to_suitesparse_intptr (sm.xridx ()); - cidx = octave::to_suitesparse_intptr (sm.xcidx ()); - } - - if (n_row != n_col) - err_square_matrix_required ("amd", "S"); - - OCTAVE_LOCAL_BUFFER (double, Control, AMD_CONTROL); - AMD_NAME (_defaults) (Control); - if (nargin > 1) - { - octave_scalar_map arg1 = args(1).xscalar_map_value ("amd: OPTS argument must be a scalar structure"); - - octave_value tmp; - - tmp = arg1.getfield ("dense"); - if (tmp.is_defined ()) - Control[AMD_DENSE] = tmp.double_value (); - - tmp = arg1.getfield ("aggressive"); - if (tmp.is_defined ()) - Control[AMD_AGGRESSIVE] = tmp.double_value (); - } - - OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, P, n_col); - Matrix xinfo (AMD_INFO, 1); - double *Info = xinfo.fortran_vec (); - - // Lock the function to not loose the SuiteSparse_config structure - interp.mlock (); - - // FIXME: how can we manage the memory allocation of amd - // in a cleaner manner? - SUITESPARSE_ASSIGN_FPTR (malloc_func, amd_malloc, malloc); - SUITESPARSE_ASSIGN_FPTR (free_func, amd_free, free); - SUITESPARSE_ASSIGN_FPTR (calloc_func, amd_calloc, calloc); - SUITESPARSE_ASSIGN_FPTR (realloc_func, amd_realloc, realloc); - SUITESPARSE_ASSIGN_FPTR (printf_func, amd_printf, printf); - - octave_idx_type result = AMD_NAME (_order) (n_col, cidx, ridx, P, Control, - Info); - - if (result == AMD_OUT_OF_MEMORY) - error ("amd: out of memory"); - else if (result == AMD_INVALID) - error ("amd: matrix S is corrupted"); - - Matrix Pout (1, n_col); - for (octave_idx_type i = 0; i < n_col; i++) - Pout.xelem (i) = P[i] + 1; - - if (nargout > 1) - return ovl (Pout, xinfo); - else - return ovl (Pout); - -#else - - octave_unused_parameter (args); - octave_unused_parameter (nargout); - - err_disabled_feature ("amd", "AMD"); - -#endif -} - -/* -%!shared A, A2, opts -%! A = ones (20, 30); -%! A2 = ones (30, 30); - -%!testif HAVE_AMD -%! assert(amd (A2), [1:30]); -%! opts.dense = 25; -%! assert(amd (A2, opts), [1:30]); -%! opts.aggressive = 1; -%! assert(amd (A2, opts), [1:30]); - -%!testif HAVE_AMD -%! assert (amd ([]), zeros (1,0)) - -%!error <S must be a square matrix|was unavailable or disabled> amd (A) -%!error amd (A2, 2) -*/
--- a/libinterp/dldfcn/ccolamd.cc Sat Jan 25 15:26:07 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,583 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2005-2020 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or <https://octave.org/copyright/>. -// -// This file is part of Octave. -// -// 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 3 of the License, 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 Octave; see the file COPYING. If not, see -// <https://www.gnu.org/licenses/>. -// -//////////////////////////////////////////////////////////////////////// - -// This is the octave interface to ccolamd, which bore the copyright given -// in the help of the functions. - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include <cstdlib> - -#include "CSparse.h" -#include "Sparse.h" -#include "dNDArray.h" -#include "oct-locbuf.h" -#include "oct-sparse.h" - -#include "defun-dld.h" -#include "error.h" -#include "errwarn.h" -#include "ov.h" -#include "pager.h" - -DEFUN_DLD (ccolamd, args, nargout, - doc: /* -*- texinfo -*- -@deftypefn {} {@var{p} =} ccolamd (@var{S}) -@deftypefnx {} {@var{p} =} ccolamd (@var{S}, @var{knobs}) -@deftypefnx {} {@var{p} =} ccolamd (@var{S}, @var{knobs}, @var{cmember}) -@deftypefnx {} {[@var{p}, @var{stats}] =} ccolamd (@dots{}) - -Constrained column approximate minimum degree permutation. - -@code{@var{p} = ccolamd (@var{S})} returns the column approximate minimum -degree permutation vector for the sparse matrix @var{S}. For a -non-symmetric matrix @var{S}, @code{@var{S}(:, @var{p})} tends to have -sparser LU@tie{}factors than @var{S}. -@code{chol (@var{S}(:, @var{p})' * @var{S}(:, @var{p}))} also tends to be -sparser than @code{chol (@var{S}' * @var{S})}. -@code{@var{p} = ccolamd (@var{S}, 1)} optimizes the ordering for -@code{lu (@var{S}(:, @var{p}))}. The ordering is followed by a column -elimination tree post-ordering. - -@var{knobs} is an optional 1-element to 5-element input vector, with a -default value of @code{[0 10 10 1 0]} if not present or empty. Entries not -present are set to their defaults. - -@table @code -@item @var{knobs}(1) -if nonzero, the ordering is optimized for @code{lu (S(:, p))}. It will be a -poor ordering for @code{chol (@var{S}(:, @var{p})' * @var{S}(:, @var{p}))}. -This is the most important knob for ccolamd. - -@item @var{knobs}(2) -if @var{S} is m-by-n, rows with more than -@code{max (16, @var{knobs}(2) * sqrt (n))} entries are ignored. - -@item @var{knobs}(3) -columns with more than -@code{max (16, @var{knobs}(3) * sqrt (min (@var{m}, @var{n})))} entries are -ignored and ordered last in the output permutation -(subject to the cmember constraints). - -@item @var{knobs}(4) -if nonzero, aggressive absorption is performed. - -@item @var{knobs}(5) -if nonzero, statistics and knobs are printed. - -@end table - -@var{cmember} is an optional vector of length @math{n}. It defines the -constraints on the column ordering. If @code{@var{cmember}(j) = @var{c}}, -then column @var{j} is in constraint set @var{c} (@var{c} must be in the -range 1 to n). In the output permutation @var{p}, all columns in set 1 -appear first, followed by all columns in set 2, and so on. -@code{@var{cmember} = ones (1,n)} if not present or empty. -@code{ccolamd (@var{S}, [], 1 : n)} returns @code{1 : n} - -@code{@var{p} = ccolamd (@var{S})} is about the same as -@code{@var{p} = colamd (@var{S})}. @var{knobs} and its default values -differ. @code{colamd} always does aggressive absorption, and it finds an -ordering suitable for both @code{lu (@var{S}(:, @var{p}))} and @code{chol -(@var{S}(:, @var{p})' * @var{S}(:, @var{p}))}; it cannot optimize its -ordering for @code{lu (@var{S}(:, @var{p}))} to the extent that -@code{ccolamd (@var{S}, 1)} can. - -@var{stats} is an optional 20-element output vector that provides data -about the ordering and the validity of the input matrix @var{S}. Ordering -statistics are in @code{@var{stats}(1 : 3)}. @code{@var{stats}(1)} and -@code{@var{stats}(2)} are the number of dense or empty rows and columns -ignored by @sc{ccolamd} and @code{@var{stats}(3)} is the number of garbage -collections performed on the internal data structure used by @sc{ccolamd} -(roughly of size @code{2.2 * nnz (@var{S}) + 4 * @var{m} + 7 * @var{n}} -integers). - -@code{@var{stats}(4 : 7)} provide information if CCOLAMD was able to -continue. The matrix is OK if @code{@var{stats}(4)} is zero, or 1 if -invalid. @code{@var{stats}(5)} is the rightmost column index that is -unsorted or contains duplicate entries, or zero if no such column exists. -@code{@var{stats}(6)} is the last seen duplicate or out-of-order row -index in the column index given by @code{@var{stats}(5)}, or zero if no -such row index exists. @code{@var{stats}(7)} is the number of duplicate -or out-of-order row indices. @code{@var{stats}(8 : 20)} is always zero in -the current version of @sc{ccolamd} (reserved for future use). - -The authors of the code itself are @nospell{S. Larimore, T. Davis} and -@nospell{S. Rajamanickam} in collaboration with @nospell{J. Bilbert and E. Ng}. -Supported by the National Science Foundation -@nospell{(DMS-9504974, DMS-9803599, CCR-0203270)}, and a grant from -@nospell{Sandia} National Lab. -See @url{http://faculty.cse.tamu.edu/davis/suitesparse.html} for ccolamd, -csymamd, amd, colamd, symamd, and other related orderings. -@seealso{colamd, csymamd} -@end deftypefn */) -{ -#if defined (HAVE_CCOLAMD) - - int nargin = args.length (); - - if (nargin < 1 || nargin > 3) - print_usage (); - - octave_value_list retval (nargout == 2 ? 2 : 1); - int spumoni = 0; - - // Get knobs - static_assert (CCOLAMD_KNOBS <= 40, - "ccolamd: # of CCOLAMD_KNOBS exceeded. Please report this to bugs.octave.org"); - double knob_storage[CCOLAMD_KNOBS]; - double *knobs = &knob_storage[0]; - CCOLAMD_NAME (_set_defaults) (knobs); - - // Check for user-passed knobs - if (nargin > 1) - { - NDArray User_knobs = args(1).array_value (); - int nel_User_knobs = User_knobs.numel (); - - if (nel_User_knobs > 0) - knobs[CCOLAMD_LU] = (User_knobs(0) != 0); - if (nel_User_knobs > 1) - knobs[CCOLAMD_DENSE_ROW] = User_knobs(1); - if (nel_User_knobs > 2) - knobs[CCOLAMD_DENSE_COL] = User_knobs(2); - if (nel_User_knobs > 3) - knobs[CCOLAMD_AGGRESSIVE] = (User_knobs(3) != 0); - if (nel_User_knobs > 4) - spumoni = (User_knobs(4) != 0); - - // print knob settings if spumoni is set - if (spumoni) - { - octave_stdout << "\nccolamd version " << CCOLAMD_MAIN_VERSION << '.' - << CCOLAMD_SUB_VERSION << ", " << CCOLAMD_DATE - << ":\nknobs(1): " << User_knobs(0) << ", order for "; - if (knobs[CCOLAMD_LU] != 0) - octave_stdout << "lu (A)\n"; - else - octave_stdout << "chol (A'*A)\n"; - - if (knobs[CCOLAMD_DENSE_ROW] >= 0) - octave_stdout << "knobs(2): " << User_knobs(1) - << ", rows with > max (16," - << knobs[CCOLAMD_DENSE_ROW] - << "*sqrt (size(A,2)))" - << " entries removed\n"; - else - octave_stdout << "knobs(2): " << User_knobs(1) - << ", no dense rows removed\n"; - - if (knobs[CCOLAMD_DENSE_COL] >= 0) - octave_stdout << "knobs(3): " << User_knobs(2) - << ", cols with > max (16," - << knobs[CCOLAMD_DENSE_COL] << "*sqrt (size(A)))" - << " entries removed\n"; - else - octave_stdout << "knobs(3): " << User_knobs(2) - << ", no dense columns removed\n"; - - if (knobs[CCOLAMD_AGGRESSIVE] != 0) - octave_stdout << "knobs(4): " << User_knobs(3) - << ", aggressive absorption: yes"; - else - octave_stdout << "knobs(4): " << User_knobs(3) - << ", aggressive absorption: no"; - - octave_stdout << "knobs(5): " << User_knobs(4) - << ", statistics and knobs printed\n"; - } - } - - octave_idx_type n_row, n_col, nnz; - octave_idx_type *ridx, *cidx; - SparseComplexMatrix scm; - SparseMatrix sm; - - if (args(0).issparse ()) - { - if (args(0).iscomplex ()) - { - 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).iscomplex ()) - 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 ccolamd - OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, p, n_col+1); - for (octave_idx_type i = 0; i < n_col+1; i++) - p[i] = cidx[i]; - - octave_idx_type Alen = CCOLAMD_NAME (_recommended) (nnz, n_row, n_col); - OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, A, Alen); - for (octave_idx_type i = 0; i < nnz; i++) - A[i] = ridx[i]; - - static_assert (CCOLAMD_STATS <= 40, - "ccolamd: # of CCOLAMD_STATS exceeded. Please report this to bugs.octave.org"); - octave::suitesparse_integer stats_storage[CCOLAMD_STATS]; - octave::suitesparse_integer *stats = &stats_storage[0]; - - if (nargin > 2) - { - NDArray in_cmember = args(2).array_value (); - octave_idx_type cslen = in_cmember.numel (); - OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, cmember, cslen); - for (octave_idx_type i = 0; i < cslen; i++) - // convert cmember from 1-based to 0-based - cmember[i] = static_cast<octave::suitesparse_integer>(in_cmember(i) - 1); - - if (cslen != n_col) - error ("ccolamd: CMEMBER must be of length equal to #cols of A"); - - // Order the columns (destroys A) - if (! CCOLAMD_NAME () (n_row, n_col, Alen, A, p, knobs, stats,cmember)) - { - CCOLAMD_NAME (_report) (stats); - - error ("ccolamd: internal error!"); - } - } - else - { - // Order the columns (destroys A) - if (! CCOLAMD_NAME () (n_row, n_col, Alen, A, p, knobs, stats, nullptr)) - { - CCOLAMD_NAME (_report) (stats); - - error ("ccolamd: internal error!"); - } - } - - // return the permutation vector - NDArray out_perm (dim_vector (1, n_col)); - for (octave_idx_type i = 0; i < n_col; i++) - out_perm(i) = p[i] + 1; - - retval(0) = out_perm; - - // print stats if spumoni > 0 - if (spumoni > 0) - CCOLAMD_NAME (_report) (stats); - - // Return the stats vector - if (nargout == 2) - { - NDArray out_stats (dim_vector (1, CCOLAMD_STATS)); - for (octave_idx_type i = 0 ; i < CCOLAMD_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(CCOLAMD_INFO1)++; - out_stats(CCOLAMD_INFO2)++; - } - - return retval; - -#else - - octave_unused_parameter (args); - octave_unused_parameter (nargout); - - err_disabled_feature ("ccolamd", "CCOLAMD"); - -#endif -} - -DEFUN_DLD (csymamd, args, nargout, - doc: /* -*- texinfo -*- -@deftypefn {} {@var{p} =} csymamd (@var{S}) -@deftypefnx {} {@var{p} =} csymamd (@var{S}, @var{knobs}) -@deftypefnx {} {@var{p} =} csymamd (@var{S}, @var{knobs}, @var{cmember}) -@deftypefnx {} {[@var{p}, @var{stats}] =} csymamd (@dots{}) - -For a symmetric positive definite matrix @var{S}, return the permutation -vector @var{p} such that @code{@var{S}(@var{p},@var{p})} tends to have a -sparser Cholesky@tie{}factor than @var{S}. - -Sometimes @code{csymamd} works well for symmetric indefinite matrices too. -The matrix @var{S} is assumed to be symmetric; only the strictly lower -triangular part is referenced. @var{S} must be square. The ordering is -followed by an elimination tree post-ordering. - -@var{knobs} is an optional 1-element to 3-element input vector, with a -default value of @code{[10 1 0]}. Entries not present are set to their -defaults. - -@table @code -@item @var{knobs}(1) -If @var{S} is n-by-n, then rows and columns with more than -@code{max(16,@var{knobs}(1)*sqrt(n))} entries are ignored, and ordered -last in the output permutation (subject to the cmember constraints). - -@item @var{knobs}(2) -If nonzero, aggressive absorption is performed. - -@item @var{knobs}(3) -If nonzero, statistics and knobs are printed. - -@end table - -@var{cmember} is an optional vector of length n. It defines the constraints -on the ordering. If @code{@var{cmember}(j) = @var{S}}, then row/column j is -in constraint set @var{c} (@var{c} must be in the range 1 to n). In the -output permutation @var{p}, rows/columns in set 1 appear first, followed -by all rows/columns in set 2, and so on. @code{@var{cmember} = ones (1,n)} -if not present or empty. @code{csymamd (@var{S},[],1:n)} returns -@code{1:n}. - -@code{@var{p} = csymamd (@var{S})} is about the same as -@code{@var{p} = symamd (@var{S})}. @var{knobs} and its default values -differ. - -@code{@var{stats}(4:7)} provide information if CCOLAMD was able to -continue. The matrix is OK if @code{@var{stats}(4)} is zero, or 1 if -invalid. @code{@var{stats}(5)} is the rightmost column index that is -unsorted or contains duplicate entries, or zero if no such column exists. -@code{@var{stats}(6)} is the last seen duplicate or out-of-order row -index in the column index given by @code{@var{stats}(5)}, or zero if no -such row index exists. @code{@var{stats}(7)} is the number of duplicate -or out-of-order row indices. @code{@var{stats}(8:20)} is always zero in -the current version of @sc{ccolamd} (reserved for future use). - -The authors of the code itself are @nospell{S. Larimore, T. Davis} and -@nospell{S. Rajamanickam} in collaboration with @nospell{J. Bilbert and E. Ng}. -Supported by the National Science Foundation -@nospell{(DMS-9504974, DMS-9803599, CCR-0203270)}, and a grant from -@nospell{Sandia} National Lab. -See @url{http://faculty.cse.tamu.edu/davis/suitesparse.html} for ccolamd, -colamd, csymamd, amd, colamd, symamd, and other related orderings. -@seealso{symamd, ccolamd} -@end deftypefn */) -{ -#if defined (HAVE_CCOLAMD) - - int nargin = args.length (); - - if (nargin < 1 || nargin > 3) - print_usage (); - - octave_value_list retval (nargout == 2 ? 2 : 1); - int spumoni = 0; - - // Get knobs - static_assert (CCOLAMD_KNOBS <= 40, - "csymamd: # of CCOLAMD_KNOBS exceeded. Please report this to bugs.octave.org"); - double knob_storage[CCOLAMD_KNOBS]; - double *knobs = &knob_storage[0]; - CCOLAMD_NAME (_set_defaults) (knobs); - - // Check for user-passed knobs - if (nargin > 1) - { - NDArray User_knobs = args(1).array_value (); - int nel_User_knobs = User_knobs.numel (); - - if (nel_User_knobs > 0) - knobs[CCOLAMD_DENSE_ROW] = User_knobs(0); - if (nel_User_knobs > 1) - knobs[CCOLAMD_AGGRESSIVE] = User_knobs(1); - if (nel_User_knobs > 2) - spumoni = static_cast<int> (User_knobs(2)); - - // print knob settings if spumoni is set - if (spumoni) - { - octave_stdout << "\ncsymamd version " << CCOLAMD_MAIN_VERSION - << '.' << CCOLAMD_SUB_VERSION - << ", " << CCOLAMD_DATE << "\n"; - - if (knobs[CCOLAMD_DENSE_ROW] >= 0) - octave_stdout << "knobs(1): " << User_knobs(0) - << ", rows/cols with > max (16," - << knobs[CCOLAMD_DENSE_ROW] - << "*sqrt (size(A,2)))" - << " entries removed\n"; - else - octave_stdout << "knobs(1): " << User_knobs(0) - << ", no dense rows/cols removed\n"; - - if (knobs[CCOLAMD_AGGRESSIVE] != 0) - octave_stdout << "knobs(2): " << User_knobs(1) - << ", aggressive absorption: yes"; - else - octave_stdout << "knobs(2): " << User_knobs(1) - << ", aggressive absorption: no"; - - octave_stdout << "knobs(3): " << User_knobs(2) - << ", statistics and knobs printed\n"; - } - } - - octave_idx_type n_row, n_col; - octave_idx_type *ridx, *cidx; - SparseMatrix sm; - SparseComplexMatrix scm; - - if (args(0).issparse ()) - { - if (args(0).iscomplex ()) - { - scm = args(0).sparse_complex_matrix_value (); - n_row = scm.rows (); - n_col = scm.cols (); - ridx = scm.xridx (); - cidx = scm.xcidx (); - } - else - { - sm = args(0).sparse_matrix_value (); - n_row = sm.rows (); - n_col = sm.cols (); - ridx = sm.xridx (); - cidx = sm.xcidx (); - } - } - else - { - if (args(0).iscomplex ()) - sm = SparseMatrix (real (args(0).complex_matrix_value ())); - else - sm = SparseMatrix (args(0).matrix_value ()); - - n_row = sm.rows (); - n_col = sm.cols (); - ridx = sm.xridx (); - cidx = sm.xcidx (); - } - - if (n_row != n_col) - err_square_matrix_required ("csymamd", "S"); - - // Allocate workspace for symamd - OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, perm, n_col+1); - static_assert (CCOLAMD_STATS <= 40, - "csymamd: # of CCOLAMD_STATS exceeded. Please report this to bugs.octave.org"); - octave::suitesparse_integer stats_storage[CCOLAMD_STATS]; - octave::suitesparse_integer *stats = &stats_storage[0]; - - if (nargin > 2) - { - NDArray in_cmember = args(2).array_value (); - octave_idx_type cslen = in_cmember.numel (); - OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, cmember, cslen); - for (octave_idx_type i = 0; i < cslen; i++) - // convert cmember from 1-based to 0-based - cmember[i] = static_cast<octave_idx_type> (in_cmember(i) - 1); - - if (cslen != n_col) - error ("csymamd: CMEMBER must be of length equal to #cols of A"); - - if (! CSYMAMD_NAME () (n_col, - octave::to_suitesparse_intptr (ridx), - octave::to_suitesparse_intptr (cidx), - perm, knobs, stats, &calloc, &free, cmember, -1)) - { - CSYMAMD_NAME (_report)(stats); - - error ("csymamd: internal error!"); - } - } - else - { - if (! CSYMAMD_NAME () (n_col, - octave::to_suitesparse_intptr (ridx), - octave::to_suitesparse_intptr (cidx), - perm, knobs, stats, &calloc, &free, nullptr, -1)) - { - CSYMAMD_NAME (_report)(stats); - - error ("csymamd: internal error!"); - } - } - - // return the permutation vector - NDArray out_perm (dim_vector (1, n_col)); - for (octave_idx_type i = 0; i < n_col; i++) - out_perm(i) = perm[i] + 1; - - retval(0) = out_perm; - - // print stats if spumoni > 0 - if (spumoni > 0) - CSYMAMD_NAME (_report)(stats); - - // Return the stats vector - if (nargout == 2) - { - NDArray out_stats (dim_vector (1, CCOLAMD_STATS)); - for (octave_idx_type i = 0 ; i < CCOLAMD_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(CCOLAMD_INFO1)++; - out_stats(CCOLAMD_INFO2)++; - } - - return retval; - -#else - - octave_unused_parameter (args); - octave_unused_parameter (nargout); - - err_disabled_feature ("csymamd", "CCOLAMD"); - -#endif -}
--- a/libinterp/dldfcn/chol.cc Sat Jan 25 15:26:07 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1331 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 1996-2020 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or <https://octave.org/copyright/>. -// -// This file is part of Octave. -// -// 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 3 of the License, 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 Octave; see the file COPYING. If not, see -// <https://www.gnu.org/licenses/>. -// -//////////////////////////////////////////////////////////////////////// - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include <string> - -#include "Matrix.h" -#include "chol.h" -#include "oct-string.h" -#include "sparse-chol.h" -#include "sparse-util.h" - -#include "defun-dld.h" -#include "error.h" -#include "errwarn.h" -#include "ov.h" -#include "ovl.h" - -template <typename CHOLT> -static octave_value -get_chol (const CHOLT& fact) -{ - return octave_value (fact.chol_matrix()); -} - -template <typename CHOLT> -static octave_value -get_chol_r (const CHOLT& fact) -{ - return octave_value (fact.chol_matrix (), - MatrixType (MatrixType::Upper)); -} - -template <typename CHOLT> -static octave_value -get_chol_l (const CHOLT& fact) -{ - return octave_value (fact.chol_matrix ().transpose (), - MatrixType (MatrixType::Lower)); -} - -DEFUN_DLD (chol, args, nargout, - doc: /* -*- texinfo -*- -@deftypefn {} {@var{R} =} chol (@var{A}) -@deftypefnx {} {[@var{R}, @var{p}] =} chol (@var{A}) -@deftypefnx {} {[@var{R}, @var{p}, @var{Q}] =} chol (@var{A}) -@deftypefnx {} {[@var{R}, @var{p}, @var{Q}] =} chol (@var{A}, "vector") -@deftypefnx {} {[@var{L}, @dots{}] =} chol (@dots{}, "lower") -@deftypefnx {} {[@var{R}, @dots{}] =} chol (@dots{}, "upper") -@cindex Cholesky factorization -Compute the upper Cholesky@tie{}factor, @var{R}, of the real symmetric -or complex Hermitian positive definite matrix @var{A}. - -The upper Cholesky@tie{}factor @var{R} is computed by using the upper -triangular part of matrix @var{A} and is defined by -@tex -$ R^T R = A $. -@end tex -@ifnottex - -@example -@var{R}' * @var{R} = @var{A}. -@end example - -@end ifnottex - -Calling @code{chol} using the optional @qcode{"upper"} flag has the -same behavior. In contrast, using the optional @qcode{"lower"} flag, -@code{chol} returns the lower triangular factorization, computed by using -the lower triangular part of matrix @var{A}, such that -@tex -$ L L^T = A $. -@end tex -@ifnottex - -@example -@var{L} * @var{L}' = @var{A}. -@end example - -@end ifnottex - -Called with one output argument @code{chol} fails if matrix @var{A} is -not positive definite. Note that if matrix @var{A} is not real symmetric -or complex Hermitian then the lower triangular part is considered to be -the (complex conjugate) transpose of the upper triangular part, or vice -versa, given the @qcode{"lower"} flag. - -Called with two or more output arguments @var{p} flags whether the matrix -@var{A} was positive definite and @code{chol} does not fail. A zero value -of @var{p} indicates that matrix @var{A} is positive definite and @var{R} -gives the factorization. Otherwise, @var{p} will have a positive value. - -If called with three output arguments matrix @var{A} must be sparse and -a sparsity preserving row/column permutation is applied to matrix @var{A} -prior to the factorization. That is @var{R} is the factorization of -@code{@var{A}(@var{Q},@var{Q})} such that -@tex -$ R^T R = Q^T A Q$. -@end tex -@ifnottex - -@example -@var{R}' * @var{R} = @var{Q}' * @var{A} * @var{Q}. -@end example - -@end ifnottex - -The sparsity preserving permutation is generally returned as a matrix. -However, given the optional flag @qcode{"vector"}, @var{Q} will be -returned as a vector such that -@tex -$ R^T R = A (Q, Q)$. -@end tex -@ifnottex - -@example -@var{R}' * @var{R} = @var{A}(@var{Q}, @var{Q}). -@end example - -@end ifnottex - -In general the lower triangular factorization is significantly faster for -sparse matrices. -@seealso{hess, lu, qr, qz, schur, svd, ichol, cholinv, chol2inv, cholupdate, cholinsert, choldelete, cholshift} -@end deftypefn */) -{ - int nargin = args.length (); - - if (nargin < 1 || nargin > 3 || nargout > 3) - print_usage (); - if (nargout > 2 && ! args(0).issparse ()) - error ("chol: using three output arguments, matrix A must be sparse"); - - bool LLt = false; - bool vecout = false; - - int n = 1; - while (n < nargin) - { - std::string tmp = args(n++).xstring_value ("chol: optional arguments must be strings"); - - if (octave::string::strcmpi (tmp, "vector")) - vecout = true; - else if (octave::string::strcmpi (tmp, "lower")) - LLt = true; - else if (octave::string::strcmpi (tmp, "upper")) - LLt = false; - else - error (R"(chol: optional argument must be one of "vector", "lower", or "upper")"); - } - - octave_value_list retval; - octave_value arg = args(0); - - if (arg.isempty ()) - return ovl (Matrix ()); - - if (arg.issparse ()) - { - octave_idx_type info; - bool natural = (nargout != 3); - bool force = nargout > 1; - - if (arg.isreal ()) - { - SparseMatrix m = arg.sparse_matrix_value (); - - octave::math::sparse_chol<SparseMatrix> fact (m, info, natural, force); - - if (nargout == 3) - { - if (vecout) - retval(2) = fact.perm (); - else - retval(2) = fact.Q (); - } - - if (nargout >= 2 || info == 0) - { - retval(1) = info; - if (LLt) - retval(0) = fact.L (); - else - retval(0) = fact.R (); - } - else - error ("chol: input matrix must be positive definite"); - } - else if (arg.iscomplex ()) - { - SparseComplexMatrix m = arg.sparse_complex_matrix_value (); - - octave::math::sparse_chol<SparseComplexMatrix> fact (m, info, natural, force); - - if (nargout == 3) - { - if (vecout) - retval(2) = fact.perm (); - else - retval(2) = fact.Q (); - } - - if (nargout >= 2 || info == 0) - { - retval(1) = info; - if (LLt) - retval(0) = fact.L (); - else - retval(0) = fact.R (); - } - else - error ("chol: input matrix must be positive definite"); - } - else - err_wrong_type_arg ("chol", arg); - } - else if (arg.is_single_type ()) - { - if (vecout) - error (R"(chol: A must be sparse for the "vector" option)"); - if (arg.isreal ()) - { - FloatMatrix m = arg.float_matrix_value (); - - octave_idx_type info; - - octave::math::chol<FloatMatrix> fact (m, info, LLt != true); - - if (nargout == 2 || info == 0) - retval = ovl (get_chol (fact), info); - else - error ("chol: input matrix must be positive definite"); - } - else if (arg.iscomplex ()) - { - FloatComplexMatrix m = arg.float_complex_matrix_value (); - - octave_idx_type info; - - octave::math::chol<FloatComplexMatrix> fact (m, info, LLt != true); - - if (nargout == 2 || info == 0) - retval = ovl (get_chol (fact), info); - else - error ("chol: input matrix must be positive definite"); - } - else - err_wrong_type_arg ("chol", arg); - } - else - { - if (vecout) - error (R"(chol: A must be sparse for the "vector" option)"); - if (arg.isreal ()) - { - Matrix m = arg.matrix_value (); - - octave_idx_type info; - - octave::math::chol<Matrix> fact (m, info, LLt != true); - - if (nargout == 2 || info == 0) - retval = ovl (get_chol (fact), info); - else - error ("chol: input matrix must be positive definite"); - } - else if (arg.iscomplex ()) - { - ComplexMatrix m = arg.complex_matrix_value (); - - octave_idx_type info; - - octave::math::chol<ComplexMatrix> fact (m, info, LLt != true); - - if (nargout == 2 || info == 0) - retval = ovl (get_chol (fact), info); - else - error ("chol: input matrix must be positive definite"); - } - else - err_wrong_type_arg ("chol", arg); - } - - return retval; -} - -/* -%!assert (chol ([2, 1; 1, 1]), [sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)], sqrt (eps)) -%!assert (chol (single ([2, 1; 1, 1])), single ([sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)]), sqrt (eps ("single"))) - -%!assert (chol ([2, 1; 1, 1], "upper"), [sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)], -%! sqrt (eps)) -%!assert (chol ([2, 1; 1, 1], "lower"), [sqrt(2), 0; 1/sqrt(2), 1/sqrt(2)], -%! sqrt (eps)) - -%!assert (chol ([2, 1; 1, 1], "lower"), chol ([2, 1; 1, 1], "LoweR")) -%!assert (chol ([2, 1; 1, 1], "upper"), chol ([2, 1; 1, 1], "Upper")) - -## Check the "vector" option which only affects the 3rd argument and -## is only valid for sparse input. -%!testif HAVE_CHOLMOD -%! a = sparse ([2 1; 1 1]); -%! r = sparse ([sqrt(2), 1/sqrt(2); 0, 1/sqrt(2)]); -%! [rd, pd, qd] = chol (a); -%! [rv, pv, qv] = chol (a, "vector"); -%! assert (r, rd, eps) -%! assert (r, rv, eps) -%! assert (pd, 0) -%! assert (pd, pv) -%! assert (qd, sparse (eye (2))) -%! assert (qv, [1 2]) -%! -%! [rv, pv, qv] = chol (a, "Vector"); # check case sensitivity -%! assert (r, rv, eps) -%! assert (pd, pv) -%! assert (qv, [1 2]) - -%!testif HAVE_CHOLMOD <*42587> -%! A = sparse ([1 0 8;0 1 8;8 8 1]); -%! [Q, p] = chol (A); -%! assert (p != 0); - -%!error chol () -%!error <matrix must be positive definite> chol ([1, 2; 3, 4]) -%!error <requires square matrix> chol ([1, 2; 3, 4; 5, 6]) -%!error <optional arguments must be strings> chol (1, 2) -%!error <optional argument must be one of "vector", "lower"> chol (1, "foobar") -%!error <matrix A must be sparse> [L,p,Q] = chol ([1, 2; 3, 4]) -%!error <A must be sparse> [L, p] = chol ([1, 2; 3, 4], "vector") -*/ - -DEFUN_DLD (cholinv, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {} cholinv (@var{A}) -Compute the inverse of the symmetric positive definite matrix @var{A} using -the Cholesky@tie{}factorization. -@seealso{chol, chol2inv, inv} -@end deftypefn */) -{ - if (args.length () != 1) - print_usage (); - - octave_value retval; - octave_value arg = args(0); - - octave_idx_type nr = arg.rows (); - octave_idx_type nc = arg.columns (); - - if (nr == 0 || nc == 0) - retval = Matrix (); - else - { - if (arg.issparse ()) - { - octave_idx_type info; - - if (arg.isreal ()) - { - SparseMatrix m = arg.sparse_matrix_value (); - - octave::math::sparse_chol<SparseMatrix> chol (m, info); - - if (info == 0) - retval = chol.inverse (); - else - error ("cholinv: A must be positive definite"); - } - else if (arg.iscomplex ()) - { - SparseComplexMatrix m = arg.sparse_complex_matrix_value (); - - octave::math::sparse_chol<SparseComplexMatrix> chol (m, info); - - if (info == 0) - retval = chol.inverse (); - else - error ("cholinv: A must be positive definite"); - } - else - err_wrong_type_arg ("cholinv", arg); - } - else if (arg.is_single_type ()) - { - if (arg.isreal ()) - { - FloatMatrix m = arg.float_matrix_value (); - - octave_idx_type info; - octave::math::chol<FloatMatrix> chol (m, info); - if (info == 0) - retval = chol.inverse (); - else - error ("cholinv: A must be positive definite"); - } - else if (arg.iscomplex ()) - { - FloatComplexMatrix m = arg.float_complex_matrix_value (); - - octave_idx_type info; - octave::math::chol<FloatComplexMatrix> chol (m, info); - if (info == 0) - retval = chol.inverse (); - else - error ("cholinv: A must be positive definite"); - } - else - err_wrong_type_arg ("chol", arg); - } - else - { - if (arg.isreal ()) - { - Matrix m = arg.matrix_value (); - - octave_idx_type info; - octave::math::chol<Matrix> chol (m, info); - if (info == 0) - retval = chol.inverse (); - else - error ("cholinv: A must be positive definite"); - } - else if (arg.iscomplex ()) - { - ComplexMatrix m = arg.complex_matrix_value (); - - octave_idx_type info; - octave::math::chol<ComplexMatrix> chol (m, info); - if (info == 0) - retval = chol.inverse (); - else - error ("cholinv: A must be positive definite"); - } - else - err_wrong_type_arg ("chol", arg); - } - } - - return retval; -} - -/* -%!shared A, Ainv -%! A = [2,0.2;0.2,1]; -%! Ainv = inv (A); -%!test -%! Ainv1 = cholinv (A); -%! assert (norm (Ainv-Ainv1), 0, 1e-10); -%!testif HAVE_CHOLMOD -%! Ainv2 = inv (sparse (A)); -%! assert (norm (Ainv-Ainv2), 0, 1e-10); -%!testif HAVE_CHOLMOD -%! Ainv3 = cholinv (sparse (A)); -%! assert (norm (Ainv-Ainv3), 0, 1e-10); -*/ - -DEFUN_DLD (chol2inv, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {} chol2inv (@var{U}) -Invert a symmetric, positive definite square matrix from its Cholesky -decomposition, @var{U}. - -Note that @var{U} should be an upper-triangular matrix with positive -diagonal elements. @code{chol2inv (@var{U})} provides -@code{inv (@var{U}'*@var{U})} but it is much faster than using @code{inv}. -@seealso{chol, cholinv, inv} -@end deftypefn */) -{ - if (args.length () != 1) - print_usage (); - - octave_value retval; - - octave_value arg = args(0); - - octave_idx_type nr = arg.rows (); - octave_idx_type nc = arg.columns (); - - if (nr == 0 || nc == 0) - retval = Matrix (); - else - { - if (arg.issparse ()) - { - if (arg.isreal ()) - { - SparseMatrix r = arg.sparse_matrix_value (); - - retval = octave::math::chol2inv (r); - } - else if (arg.iscomplex ()) - { - SparseComplexMatrix r = arg.sparse_complex_matrix_value (); - - retval = octave::math::chol2inv (r); - } - else - err_wrong_type_arg ("chol2inv", arg); - } - else if (arg.is_single_type ()) - { - if (arg.isreal ()) - { - FloatMatrix r = arg.float_matrix_value (); - - retval = octave::math::chol2inv (r); - } - else if (arg.iscomplex ()) - { - FloatComplexMatrix r = arg.float_complex_matrix_value (); - - retval = octave::math::chol2inv (r); - } - else - err_wrong_type_arg ("chol2inv", arg); - - } - else - { - if (arg.isreal ()) - { - Matrix r = arg.matrix_value (); - - retval = octave::math::chol2inv (r); - } - else if (arg.iscomplex ()) - { - ComplexMatrix r = arg.complex_matrix_value (); - - retval = octave::math::chol2inv (r); - } - else - err_wrong_type_arg ("chol2inv", arg); - } - } - - return retval; -} - -/* - -## Test for bug #36437 -%!function sparse_chol2inv (T, tol) -%! iT = inv (T); -%! ciT = chol2inv (chol (T)); -%! assert (ciT, iT, tol); -%! assert (chol2inv (chol ( full (T))), ciT, tol*2); -%!endfunction - -%!testif HAVE_CHOLMOD -%! A = gallery ("poisson", 3); -%! sparse_chol2inv (A, eps); - -%!testif HAVE_CHOLMOD -%! n = 10; -%! B = spdiags (ones (n, 1) * [1 2 1], [-1 0 1], n, n); -%! sparse_chol2inv (B, eps*100); - -%!testif HAVE_CHOLMOD -%! C = gallery("tridiag", 5); -%! sparse_chol2inv (C, eps*10); - -%!testif HAVE_CHOLMOD -%! D = gallery("wathen", 1, 1); -%! sparse_chol2inv (D, eps*10^4); - -*/ - -DEFUN_DLD (cholupdate, args, nargout, - doc: /* -*- texinfo -*- -@deftypefn {} {[@var{R1}, @var{info}] =} cholupdate (@var{R}, @var{u}, @var{op}) -Update or downdate a Cholesky@tie{}factorization. - -Given an upper triangular matrix @var{R} and a column vector @var{u}, -attempt to determine another upper triangular matrix @var{R1} such that - -@itemize @bullet -@item -@var{R1}'*@var{R1} = @var{R}'*@var{R} + @var{u}*@var{u}' -if @var{op} is @qcode{"+"} - -@item -@var{R1}'*@var{R1} = @var{R}'*@var{R} - @var{u}*@var{u}' -if @var{op} is @qcode{"-"} -@end itemize - -If @var{op} is @qcode{"-"}, @var{info} is set to - -@itemize -@item 0 if the downdate was successful, - -@item 1 if @var{R}'*@var{R} - @var{u}*@var{u}' is not positive definite, - -@item 2 if @var{R} is singular. -@end itemize - -If @var{info} is not present, an error message is printed in cases 1 and 2. -@seealso{chol, cholinsert, choldelete, cholshift} -@end deftypefn */) -{ - int nargin = args.length (); - - if (nargin < 2 || nargin > 3) - print_usage (); - - octave_value argr = args(0); - octave_value argu = args(1); - - if (! argr.isnumeric () || ! argu.isnumeric () - || (nargin > 2 && ! args(2).is_string ())) - print_usage (); - - octave_value_list retval (nargout == 2 ? 2 : 1); - - octave_idx_type n = argr.rows (); - - std::string op = (nargin < 3) ? "+" : args(2).string_value (); - - bool down = (op == "-"); - - if (! down && op != "+") - error (R"(cholupdate: OP must be "+" or "-")"); - - if (argr.columns () != n || argu.rows () != n || argu.columns () != 1) - error ("cholupdate: dimension mismatch between R and U"); - - int err = 0; - if (argr.is_single_type () || argu.is_single_type ()) - { - if (argr.isreal () && argu.isreal ()) - { - // real case - FloatMatrix R = argr.float_matrix_value (); - FloatColumnVector u = argu.float_column_vector_value (); - - octave::math::chol<FloatMatrix> fact; - fact.set (R); - - if (down) - err = fact.downdate (u); - else - fact.update (u); - - retval = ovl (get_chol_r (fact)); - } - else - { - // complex case - FloatComplexMatrix R = argr.float_complex_matrix_value (); - FloatComplexColumnVector u - = argu.float_complex_column_vector_value (); - - octave::math::chol<FloatComplexMatrix> fact; - fact.set (R); - - if (down) - err = fact.downdate (u); - else - fact.update (u); - - retval = ovl (get_chol_r (fact)); - } - } - else - { - if (argr.isreal () && argu.isreal ()) - { - // real case - Matrix R = argr.matrix_value (); - ColumnVector u = argu.column_vector_value (); - - octave::math::chol<Matrix> fact; - fact.set (R); - - if (down) - err = fact.downdate (u); - else - fact.update (u); - - retval = ovl (get_chol_r (fact)); - } - else - { - // complex case - ComplexMatrix R = argr.complex_matrix_value (); - ComplexColumnVector u = argu.complex_column_vector_value (); - - octave::math::chol<ComplexMatrix> fact; - fact.set (R); - - if (down) - err = fact.downdate (u); - else - fact.update (u); - - retval = ovl (get_chol_r (fact)); - } - } - - if (nargout > 1) - retval(1) = err; - else if (err == 1) - error ("cholupdate: downdate violates positiveness"); - else if (err == 2) - error ("cholupdate: singular matrix"); - - return retval; -} - -/* -%!shared A, u, Ac, uc -%! A = [ 0.436997 -0.131721 0.124120 -0.061673 ; -%! -0.131721 0.738529 0.019851 -0.140295 ; -%! 0.124120 0.019851 0.354879 -0.059472 ; -%! -0.061673 -0.140295 -0.059472 0.600939 ]; -%! -%! u = [ 0.98950 ; -%! 0.39844 ; -%! 0.63484 ; -%! 0.13351 ]; -%! Ac = [ 0.5585528 + 0.0000000i -0.1662088 - 0.0315341i 0.0107873 + 0.0236411i -0.0276775 - 0.0186073i ; -%! -0.1662088 + 0.0315341i 0.6760061 + 0.0000000i 0.0011452 - 0.0475528i 0.0145967 + 0.0247641i ; -%! 0.0107873 - 0.0236411i 0.0011452 + 0.0475528i 0.6263149 - 0.0000000i -0.1585837 - 0.0719763i ; -%! -0.0276775 + 0.0186073i 0.0145967 - 0.0247641i -0.1585837 + 0.0719763i 0.6034234 - 0.0000000i ]; -%! -%! uc = [ 0.54267 + 0.91519i ; -%! 0.99647 + 0.43141i ; -%! 0.83760 + 0.68977i ; -%! 0.39160 + 0.90378i ]; - -%!test -%! R = chol (A); -%! R1 = cholupdate (R, u); -%! assert (norm (triu (R1)-R1, Inf), 0); -%! assert (norm (R1'*R1 - R'*R - u*u', Inf) < 1e1*eps); -%! -%! R1 = cholupdate (R1, u, "-"); -%! assert (norm (triu (R1)-R1, Inf), 0); -%! assert (norm (R1 - R, Inf) < 1e1*eps); - -%!test -%! R = chol (Ac); -%! R1 = cholupdate (R, uc); -%! assert (norm (triu (R1)-R1, Inf), 0); -%! assert (norm (R1'*R1 - R'*R - uc*uc', Inf) < 1e1*eps); -%! -%! R1 = cholupdate (R1, uc, "-"); -%! assert (norm (triu (R1)-R1, Inf), 0); -%! assert (norm (R1 - R, Inf) < 1e1*eps); - -%!test -%! R = chol (single (A)); -%! R1 = cholupdate (R, single (u)); -%! assert (norm (triu (R1)-R1, Inf), single (0)); -%! assert (norm (R1'*R1 - R'*R - single (u*u'), Inf) < 1e1*eps ("single")); -%! -%! R1 = cholupdate (R1, single (u), "-"); -%! assert (norm (triu (R1)-R1, Inf), single (0)); -%! assert (norm (R1 - R, Inf) < 2e1*eps ("single")); - -%!test -%! R = chol (single (Ac)); -%! R1 = cholupdate (R, single (uc)); -%! assert (norm (triu (R1)-R1, Inf), single (0)); -%! assert (norm (R1'*R1 - R'*R - single (uc*uc'), Inf) < 1e1*eps ("single")); -%! -%! R1 = cholupdate (R1, single (uc), "-"); -%! assert (norm (triu (R1)-R1, Inf), single (0)); -%! assert (norm (R1 - R, Inf) < 2e1*eps ("single")); -*/ - -DEFUN_DLD (cholinsert, args, nargout, - doc: /* -*- texinfo -*- -@deftypefn {} {@var{R1} =} cholinsert (@var{R}, @var{j}, @var{u}) -@deftypefnx {} {[@var{R1}, @var{info}] =} cholinsert (@var{R}, @var{j}, @var{u}) -Update a Cholesky factorization given a row or column to insert in the -original factored matrix. - -Given a Cholesky@tie{}factorization of a real symmetric or complex Hermitian -positive definite matrix @w{@var{A} = @var{R}'*@var{R}}, @var{R}@tie{}upper -triangular, return the Cholesky@tie{}factorization of -@var{A1}, where @w{A1(p,p) = A}, @w{A1(:,j) = A1(j,:)' = u} and -@w{p = [1:j-1,j+1:n+1]}. @w{u(j)} should be positive. - -On return, @var{info} is set to - -@itemize -@item 0 if the insertion was successful, - -@item 1 if @var{A1} is not positive definite, - -@item 2 if @var{R} is singular. -@end itemize - -If @var{info} is not present, an error message is printed in cases 1 and 2. -@seealso{chol, cholupdate, choldelete, cholshift} -@end deftypefn */) -{ - if (args.length () != 3) - print_usage (); - - octave_value argr = args(0); - octave_value argj = args(1); - octave_value argu = args(2); - - if (! argr.isnumeric () || ! argu.isnumeric () - || ! argj.is_real_scalar ()) - print_usage (); - - octave_idx_type n = argr.rows (); - octave_idx_type j = argj.scalar_value (); - - if (argr.columns () != n || argu.rows () != n+1 || argu.columns () != 1) - error ("cholinsert: dimension mismatch between R and U"); - - if (j < 1 || j > n+1) - error ("cholinsert: index J out of range"); - - octave_value_list retval (nargout == 2 ? 2 : 1); - - int err = 0; - if (argr.is_single_type () || argu.is_single_type ()) - { - if (argr.isreal () && argu.isreal ()) - { - // real case - FloatMatrix R = argr.float_matrix_value (); - FloatColumnVector u = argu.float_column_vector_value (); - - octave::math::chol<FloatMatrix> fact; - fact.set (R); - err = fact.insert_sym (u, j-1); - - retval = ovl (get_chol_r (fact)); - } - else - { - // complex case - FloatComplexMatrix R = argr.float_complex_matrix_value (); - FloatComplexColumnVector u - = argu.float_complex_column_vector_value (); - - octave::math::chol<FloatComplexMatrix> fact; - fact.set (R); - err = fact.insert_sym (u, j-1); - - retval = ovl (get_chol_r (fact)); - } - } - else - { - if (argr.isreal () && argu.isreal ()) - { - // real case - Matrix R = argr.matrix_value (); - ColumnVector u = argu.column_vector_value (); - - octave::math::chol<Matrix> fact; - fact.set (R); - err = fact.insert_sym (u, j-1); - - retval = ovl (get_chol_r (fact)); - } - else - { - // complex case - ComplexMatrix R = argr.complex_matrix_value (); - ComplexColumnVector u = argu.complex_column_vector_value (); - - octave::math::chol<ComplexMatrix> fact; - fact.set (R); - err = fact.insert_sym (u, j-1); - - retval = ovl (get_chol_r (fact)); - } - } - - if (nargout > 1) - retval(1) = err; - else if (err == 1) - error ("cholinsert: insertion violates positiveness"); - else if (err == 2) - error ("cholinsert: singular matrix"); - else if (err == 3) - error ("cholinsert: diagonal element must be real"); - - return retval; -} - -/* -%!test -%! u2 = [ 0.35080 ; -%! 0.63930 ; -%! 3.31057 ; -%! -0.13825 ; -%! 0.45266 ]; -%! -%! R = chol (A); -%! -%! j = 3; p = [1:j-1, j+1:5]; -%! R1 = cholinsert (R, j, u2); -%! A1 = R1'*R1; -%! -%! assert (norm (triu (R1)-R1, Inf), 0); -%! assert (norm (A1(p,p) - A, Inf) < 1e1*eps); - -%!test -%! u2 = [ 0.35080 + 0.04298i; -%! 0.63930 + 0.23778i; -%! 3.31057 + 0.00000i; -%! -0.13825 + 0.19879i; -%! 0.45266 + 0.50020i]; -%! -%! R = chol (Ac); -%! -%! j = 3; p = [1:j-1, j+1:5]; -%! R1 = cholinsert (R, j, u2); -%! A1 = R1'*R1; -%! -%! assert (norm (triu (R1)-R1, Inf), 0); -%! assert (norm (A1(p,p) - Ac, Inf) < 1e1*eps); - -%!test -%! u2 = single ([ 0.35080 ; -%! 0.63930 ; -%! 3.31057 ; -%! -0.13825 ; -%! 0.45266 ]); -%! -%! R = chol (single (A)); -%! -%! j = 3; p = [1:j-1, j+1:5]; -%! R1 = cholinsert (R, j, u2); -%! A1 = R1'*R1; -%! -%! assert (norm (triu (R1)-R1, Inf), single (0)); -%! assert (norm (A1(p,p) - A, Inf) < 1e1*eps ("single")); - -%!test -%! u2 = single ([ 0.35080 + 0.04298i; -%! 0.63930 + 0.23778i; -%! 3.31057 + 0.00000i; -%! -0.13825 + 0.19879i; -%! 0.45266 + 0.50020i]); -%! -%! R = chol (single (Ac)); -%! -%! j = 3; p = [1:j-1, j+1:5]; -%! R1 = cholinsert (R, j, u2); -%! A1 = R1'*R1; -%! -%! assert (norm (triu (R1)-R1, Inf), single (0)); -%! assert (norm (A1(p,p) - single (Ac), Inf) < 2e1*eps ("single")); - -%!test -%! cu = chol (triu (A), "upper"); -%! cl = chol (tril (A), "lower"); -%! assert (cu, cl', eps); - -%!test -%! cca = chol (Ac); -%! -%! ccal = chol (Ac, "lower"); -%! ccal2 = chol (tril (Ac), "lower"); -%! -%! ccau = chol (Ac, "upper"); -%! ccau2 = chol (triu (Ac), "upper"); -%! -%! assert (cca'*cca, Ac, eps); -%! assert (ccau'*ccau, Ac, eps); -%! assert (ccau2'*ccau2, Ac, eps); -%! -%! assert (cca, ccal', eps); -%! assert (cca, ccau, eps); -%! assert (cca, ccal2', eps); -%! assert (cca, ccau2, eps); - -%!test -%! cca = chol (single (Ac)); -%! -%! ccal = chol (single (Ac), "lower"); -%! ccal2 = chol (tril (single (Ac)), "lower"); -%! -%! ccau = chol (single (Ac), "upper"); -%! ccau2 = chol (triu (single (Ac)), "upper"); -%! -%! assert (cca'*cca, single (Ac), eps ("single")); -%! assert (ccau'*ccau, single (Ac), eps ("single")); -%! assert (ccau2'*ccau2, single (Ac), eps ("single")); -%! -%! assert (cca, ccal', eps ("single")); -%! assert (cca, ccau, eps ("single")); -%! assert (cca, ccal2', eps ("single")); -%! assert (cca, ccau2, eps ("single")); - -%!test -%! a = [12, 2, 3, 4; -%! 2, 14, 5, 3; -%! 3, 5, 16, 6; -%! 4, 3, 6, 16]; -%! -%! b = [0, 1, 2, 3; -%! -1, 0, 1, 2; -%! -2, -1, 0, 1; -%! -3, -2, -1, 0]; -%! -%! ca = a + i*b; -%! -%! cca = chol (ca); -%! -%! ccal = chol (ca, "lower"); -%! ccal2 = chol (tril (ca), "lower"); -%! -%! ccau = chol (ca, "upper"); -%! ccau2 = chol (triu (ca), "upper"); -%! -%! assert (cca'*cca, ca, 16*eps); -%! assert (ccau'*ccau, ca, 16*eps); -%! assert (ccau2'*ccau2, ca, 16*eps); -%! -%! assert (cca, ccal', 16*eps); -%! assert (cca, ccau, 16*eps); -%! assert (cca, ccal2', 16*eps); -%! assert (cca, ccau2, 16*eps); -*/ - -DEFUN_DLD (choldelete, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {@var{R1} =} choldelete (@var{R}, @var{j}) -Update a Cholesky factorization given a row or column to delete from the -original factored matrix. - -Given a Cholesky@tie{}factorization of a real symmetric or complex Hermitian -positive definite matrix @w{@var{A} = @var{R}'*@var{R}}, @var{R}@tie{}upper -triangular, return the Cholesky@tie{}factorization of @w{A(p,p)}, where -@w{p = [1:j-1,j+1:n+1]}. -@seealso{chol, cholupdate, cholinsert, cholshift} -@end deftypefn */) -{ - if (args.length () != 2) - print_usage (); - - octave_value argr = args(0); - octave_value argj = args(1); - - if (! argr.isnumeric () || ! argj.is_real_scalar ()) - print_usage (); - - octave_idx_type n = argr.rows (); - octave_idx_type j = argj.scalar_value (); - - if (argr.columns () != n) - err_square_matrix_required ("choldelete", "R"); - - if (j < 0 && j > n) - error ("choldelete: index J out of range"); - - octave_value_list retval; - - if (argr.is_single_type ()) - { - if (argr.isreal ()) - { - // real case - FloatMatrix R = argr.float_matrix_value (); - - octave::math::chol<FloatMatrix> fact; - fact.set (R); - fact.delete_sym (j-1); - - retval = ovl (get_chol_r (fact)); - } - else - { - // complex case - FloatComplexMatrix R = argr.float_complex_matrix_value (); - - octave::math::chol<FloatComplexMatrix> fact; - fact.set (R); - fact.delete_sym (j-1); - - retval = ovl (get_chol_r (fact)); - } - } - else - { - if (argr.isreal ()) - { - // real case - Matrix R = argr.matrix_value (); - - octave::math::chol<Matrix> fact; - fact.set (R); - fact.delete_sym (j-1); - - retval = ovl (get_chol_r (fact)); - } - else - { - // complex case - ComplexMatrix R = argr.complex_matrix_value (); - - octave::math::chol<ComplexMatrix> fact; - fact.set (R); - fact.delete_sym (j-1); - - retval = ovl (get_chol_r (fact)); - } - } - - return retval; -} - -/* -%!test -%! R = chol (A); -%! -%! j = 3; p = [1:j-1,j+1:4]; -%! R1 = choldelete (R, j); -%! -%! assert (norm (triu (R1)-R1, Inf), 0); -%! assert (norm (R1'*R1 - A(p,p), Inf) < 1e1*eps); - -%!test -%! R = chol (Ac); -%! -%! j = 3; p = [1:j-1,j+1:4]; -%! R1 = choldelete (R, j); -%! -%! assert (norm (triu (R1)-R1, Inf), 0); -%! assert (norm (R1'*R1 - Ac(p,p), Inf) < 1e1*eps); - -%!test -%! R = chol (single (A)); -%! -%! j = 3; p = [1:j-1,j+1:4]; -%! R1 = choldelete (R, j); -%! -%! assert (norm (triu (R1)-R1, Inf), single (0)); -%! assert (norm (R1'*R1 - single (A(p,p)), Inf) < 1e1*eps ("single")); - -%!test -%! R = chol (single (Ac)); -%! -%! j = 3; p = [1:j-1,j+1:4]; -%! R1 = choldelete (R,j); -%! -%! assert (norm (triu (R1)-R1, Inf), single (0)); -%! assert (norm (R1'*R1 - single (Ac(p,p)), Inf) < 1e1*eps ("single")); -*/ - -DEFUN_DLD (cholshift, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {@var{R1} =} cholshift (@var{R}, @var{i}, @var{j}) -Update a Cholesky factorization given a range of columns to shift in the -original factored matrix. - -Given a Cholesky@tie{}factorization of a real symmetric or complex Hermitian -positive definite matrix @w{@var{A} = @var{R}'*@var{R}}, @var{R}@tie{}upper -triangular, return the Cholesky@tie{}factorization of -@w{@var{A}(p,p)}, where @w{p} is the permutation @* -@code{p = [1:i-1, shift(i:j, 1), j+1:n]} if @w{@var{i} < @var{j}} @* - or @* -@code{p = [1:j-1, shift(j:i,-1), i+1:n]} if @w{@var{j} < @var{i}}. @* - -@seealso{chol, cholupdate, cholinsert, choldelete} -@end deftypefn */) -{ - if (args.length () != 3) - print_usage (); - - octave_value argr = args(0); - octave_value argi = args(1); - octave_value argj = args(2); - - if (! argr.isnumeric () || ! argi.is_real_scalar () - || ! argj.is_real_scalar ()) - print_usage (); - - octave_idx_type n = argr.rows (); - octave_idx_type i = argi.scalar_value (); - octave_idx_type j = argj.scalar_value (); - - if (argr.columns () != n) - err_square_matrix_required ("cholshift", "R"); - - if (j < 0 || j > n+1 || i < 0 || i > n+1) - error ("cholshift: index I or J is out of range"); - - octave_value_list retval; - - if (argr.is_single_type () && argi.is_single_type () - && argj.is_single_type ()) - { - if (argr.isreal ()) - { - // real case - FloatMatrix R = argr.float_matrix_value (); - - octave::math::chol<FloatMatrix> fact; - fact.set (R); - fact.shift_sym (i-1, j-1); - - retval = ovl (get_chol_r (fact)); - } - else - { - // complex case - FloatComplexMatrix R = argr.float_complex_matrix_value (); - - octave::math::chol<FloatComplexMatrix> fact; - fact.set (R); - fact.shift_sym (i-1, j-1); - - retval = ovl (get_chol_r (fact)); - } - } - else - { - if (argr.isreal ()) - { - // real case - Matrix R = argr.matrix_value (); - - octave::math::chol<Matrix> fact; - fact.set (R); - fact.shift_sym (i-1, j-1); - - retval = ovl (get_chol_r (fact)); - } - else - { - // complex case - ComplexMatrix R = argr.complex_matrix_value (); - - octave::math::chol<ComplexMatrix> fact; - fact.set (R); - fact.shift_sym (i-1, j-1); - - retval = ovl (get_chol_r (fact)); - } - } - - return retval; -} - -/* -%!test -%! R = chol (A); -%! -%! i = 1; j = 3; p = [1:i-1, shift(i:j,-1), j+1:4]; -%! R1 = cholshift (R, i, j); -%! -%! assert (norm (triu (R1)-R1, Inf), 0); -%! assert (norm (R1'*R1 - A(p,p), Inf) < 1e1*eps); -%! -%! j = 1; i = 3; p = [1:j-1, shift(j:i,+1), i+1:4]; -%! R1 = cholshift (R, i, j); -%! -%! assert (norm (triu (R1) - R1, Inf), 0); -%! assert (norm (R1'*R1 - A(p,p), Inf) < 1e1*eps); - -%!test -%! R = chol (Ac); -%! -%! i = 1; j = 3; p = [1:i-1, shift(i:j,-1), j+1:4]; -%! R1 = cholshift (R, i, j); -%! -%! assert (norm (triu (R1)-R1, Inf), 0); -%! assert (norm (R1'*R1 - Ac(p,p), Inf) < 1e1*eps); -%! -%! j = 1; i = 3; p = [1:j-1, shift(j:i,+1), i+1:4]; -%! R1 = cholshift (R, i, j); -%! -%! assert (norm (triu (R1)-R1, Inf), 0); -%! assert (norm (R1'*R1 - Ac(p,p), Inf) < 1e1*eps); - -%!test -%! R = chol (single (A)); -%! -%! i = 1; j = 3; p = [1:i-1, shift(i:j,-1), j+1:4]; -%! R1 = cholshift (R, i, j); -%! -%! assert (norm (triu (R1)-R1, Inf), 0); -%! assert (norm (R1'*R1 - single (A(p,p)), Inf) < 1e1*eps ("single")); -%! -%! j = 1; i = 3; p = [1:j-1, shift(j:i,+1), i+1:4]; -%! R1 = cholshift (R, i, j); -%! -%! assert (norm (triu (R1)-R1, Inf), 0); -%! assert (norm (R1'*R1 - single (A(p,p)), Inf) < 1e1*eps ("single")); - -%!test -%! R = chol (single (Ac)); -%! -%! i = 1; j = 3; p = [1:i-1, shift(i:j,-1), j+1:4]; -%! R1 = cholshift (R, i, j); -%! -%! assert (norm (triu (R1)-R1, Inf), 0); -%! assert (norm (R1'*R1 - single (Ac(p,p)), Inf) < 1e1*eps ("single")); -%! -%! j = 1; i = 3; p = [1:j-1, shift(j:i,+1), i+1:4]; -%! R1 = cholshift (R, i, j); -%! -%! assert (norm (triu (R1)-R1, Inf), 0); -%! assert (norm (R1'*R1 - single (Ac(p,p)), Inf) < 1e1*eps ("single")); -*/
--- a/libinterp/dldfcn/colamd.cc Sat Jan 25 15:26:07 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,774 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 1998-2020 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or <https://octave.org/copyright/>. -// -// This file is part of Octave. -// -// 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 3 of the License, 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 Octave; see the file COPYING. If not, see -// <https://www.gnu.org/licenses/>. -// -//////////////////////////////////////////////////////////////////////// - -// This is the octave interface to colamd, which bore the copyright given -// in the help of the functions. - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include <cstdlib> - -#include <string> - -#include "CSparse.h" -#include "dNDArray.h" -#include "dSparse.h" -#include "oct-locbuf.h" -#include "oct-sparse.h" - -#include "defun-dld.h" -#include "error.h" -#include "errwarn.h" -#include "ovl.h" -#include "pager.h" - -// The symmetric column elimination tree code take from the Davis LDL code. -// Copyright given elsewhere in this file. -static void -symetree (const octave_idx_type *ridx, const octave_idx_type *cidx, - octave_idx_type *Parent, octave_idx_type *P, octave_idx_type n) -{ - OCTAVE_LOCAL_BUFFER (octave_idx_type, Flag, n); - OCTAVE_LOCAL_BUFFER (octave_idx_type, Pinv, (P ? n : 0)); - if (P) - // If P is present then compute Pinv, the inverse of P - for (octave_idx_type k = 0 ; k < n ; k++) - Pinv[P[k]] = k; - - for (octave_idx_type 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 - octave_idx_type kk = (P ? P[k] : k); // kth original, or permuted, column - octave_idx_type p2 = cidx[kk+1]; - for (octave_idx_type p = cidx[kk] ; p < p2 ; p++) - { - // A (i,k) is nonzero (original or permuted A) - octave_idx_type i = (P ? 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 octave_idx_type -make_set (octave_idx_type i, octave_idx_type *pp) -{ - pp[i] = i; - return i; -} - -static inline octave_idx_type -link (octave_idx_type s, octave_idx_type t, octave_idx_type *pp) -{ - pp[s] = t; - return t; -} - -static inline octave_idx_type -find (octave_idx_type i, octave_idx_type *pp) -{ - octave_idx_type p = pp[i]; - octave_idx_type gp = pp[p]; - - while (gp != p) - { - pp[i] = gp; - i = gp; - p = pp[i]; - gp = pp[p]; - } - - return p; -} - -static octave_idx_type -etdfs (octave_idx_type v, octave_idx_type *first_kid, - octave_idx_type *next_kid, octave_idx_type *post, - octave_idx_type postnum) -{ - for (octave_idx_type 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 -tree_postorder (octave_idx_type n, octave_idx_type *parent, - octave_idx_type *post) -{ - // Allocate storage for working arrays and results - OCTAVE_LOCAL_BUFFER (octave_idx_type, first_kid, n+1); - OCTAVE_LOCAL_BUFFER (octave_idx_type, next_kid, n+1); - - // Set up structure describing children - for (octave_idx_type v = 0; v <= n; first_kid[v++] = -1) - ; // do nothing - - for (octave_idx_type v = n-1; v >= 0; v--) - { - octave_idx_type 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 octave_idx_type *ridx, const octave_idx_type *colbeg, - octave_idx_type *colend, octave_idx_type *parent, - octave_idx_type nr, octave_idx_type nc) -{ - OCTAVE_LOCAL_BUFFER (octave_idx_type, root, nc); - OCTAVE_LOCAL_BUFFER (octave_idx_type, pp, nc); - OCTAVE_LOCAL_BUFFER (octave_idx_type, firstcol, nr); - - // Compute firstcol[row] = first nonzero column in row - for (octave_idx_type row = 0; row < nr; firstcol[row++] = nc) - ; // do nothing - - for (octave_idx_type col = 0; col < nc; col++) - for (octave_idx_type p = colbeg[col]; p < colend[col]; p++) - { - octave_idx_type 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 (octave_idx_type col = 0; col < nc; col++) - { - octave_idx_type cset = make_set (col, pp); - root[cset] = col; - parent[col] = nc; - for (octave_idx_type p = colbeg[col]; p < colend[col]; p++) - { - octave_idx_type row = firstcol[ridx[p]]; - if (row >= col) - continue; - octave_idx_type rset = find (row, pp); - octave_idx_type rroot = root[rset]; - if (rroot != col) - { - parent[rroot] = col; - cset = link (cset, rset, pp); - root[cset] = col; - } - } - } -} - -DEFUN_DLD (colamd, args, nargout, - doc: /* -*- texinfo -*- -@deftypefn {} {@var{p} =} colamd (@var{S}) -@deftypefnx {} {@var{p} =} colamd (@var{S}, @var{knobs}) -@deftypefnx {} {[@var{p}, @var{stats}] =} colamd (@var{S}) -@deftypefnx {} {[@var{p}, @var{stats}] =} colamd (@var{S}, @var{knobs}) - -Compute the column approximate minimum degree permutation. - -@code{@var{p} = colamd (@var{S})} returns the column approximate minimum -degree permutation vector for the sparse matrix @var{S}. For a -non-symmetric matrix @var{S}, @code{@var{S}(:,@var{p})} tends to have -sparser LU@tie{}factors than @var{S}. The Cholesky@tie{}factorization of -@code{@var{S}(:,@var{p})' * @var{S}(:,@var{p})} also tends to be sparser -than that of @code{@var{S}' * @var{S}}. - -@var{knobs} is an optional one- to three-element input vector. If @var{S} -is m-by-n, then rows with more than @code{max(16,@var{knobs}(1)*sqrt(n))} -entries are ignored. Columns with more than -@code{max (16,@var{knobs}(2)*sqrt(min(m,n)))} entries are removed prior to -ordering, and ordered last in the output permutation @var{p}. Only -completely dense rows or columns are removed if @code{@var{knobs}(1)} and -@code{@var{knobs}(2)} are < 0, respectively. If @code{@var{knobs}(3)} is -nonzero, @var{stats} and @var{knobs} are printed. The default is -@code{@var{knobs} = [10 10 0]}. Note that @var{knobs} differs from earlier -versions of colamd. - -@var{stats} is an optional 20-element output vector that provides data -about the ordering and the validity of the input matrix @var{S}. Ordering -statistics are in @code{@var{stats}(1:3)}. @code{@var{stats}(1)} and -@code{@var{stats}(2)} are the number of dense or empty rows and columns -ignored by @sc{colamd} and @code{@var{stats}(3)} is the number of garbage -collections performed on the internal data structure used by @sc{colamd} -(roughly of size @code{2.2 * nnz(@var{S}) + 4 * @var{m} + 7 * @var{n}} -integers). - -Octave built-in functions are intended to generate valid sparse matrices, -with no duplicate entries, with ascending row indices of the nonzeros -in each column, with a non-negative number of entries in each column (!) -and so on. If a matrix is invalid, then @sc{colamd} may or may not be able -to continue. If there are duplicate entries (a row index appears two or -more times in the same column) or if the row indices in a column are out -of order, then @sc{colamd} can correct these errors by ignoring the -duplicate entries and sorting each column of its internal copy of the -matrix @var{S} (the input matrix @var{S} is not repaired, however). If a -matrix is invalid in other ways then @sc{colamd} cannot continue, an error -message is printed, and no output arguments (@var{p} or @var{stats}) are -returned. -@sc{colamd} is thus a simple way to check a sparse matrix to see if it's -valid. - -@code{@var{stats}(4:7)} provide information if @sc{colamd} was able to -continue. The matrix is OK if @code{@var{stats}(4)} is zero, or 1 if -invalid. @code{@var{stats}(5)} is the rightmost column index that is -unsorted or contains duplicate entries, or zero if no such column exists. -@code{@var{stats}(6)} is the last seen duplicate or out-of-order row -index in the column index given by @code{@var{stats}(5)}, or zero if no -such row index exists. @code{@var{stats}(7)} is the number of duplicate -or out-of-order row indices. @code{@var{stats}(8:20)} is always zero in -the current version of @sc{colamd} (reserved for future use). - -The ordering is followed by a column elimination tree post-ordering. - -The authors of the code itself are @nospell{Stefan I. Larimore} and -@nospell{Timothy A. Davis}. The algorithm was developed in collaboration with -@nospell{John Gilbert}, Xerox PARC, and @nospell{Esmond Ng}, Oak Ridge National -Laboratory. (see @url{http://faculty.cse.tamu.edu/davis/suitesparse.html}) -@seealso{colperm, symamd, ccolamd} -@end deftypefn */) -{ -#if defined (HAVE_COLAMD) - - int nargin = args.length (); - - if (nargin < 1 || nargin > 2) - print_usage (); - - octave_value_list retval (nargout == 2 ? 2 : 1); - int spumoni = 0; - - // Get knobs - static_assert (COLAMD_KNOBS <= 40, - "colamd: # of COLAMD_KNOBS exceeded. Please report this to bugs.octave.org"); - double knob_storage[COLAMD_KNOBS]; - double *knobs = &knob_storage[0]; - COLAMD_NAME (_set_defaults) (knobs); - - // Check for user-passed knobs - if (nargin == 2) - { - NDArray User_knobs = args(1).array_value (); - int nel_User_knobs = User_knobs.numel (); - - if (nel_User_knobs > 0) - knobs[COLAMD_DENSE_ROW] = User_knobs(0); - if (nel_User_knobs > 1) - knobs[COLAMD_DENSE_COL] = User_knobs(1); - if (nel_User_knobs > 2) - spumoni = static_cast<int> (User_knobs(2)); - - // print knob settings if spumoni is set - if (spumoni) - { - - octave_stdout << "\ncolamd version " << COLAMD_MAIN_VERSION - << '.' << COLAMD_SUB_VERSION - << ", " << COLAMD_DATE << ":\n"; - - if (knobs[COLAMD_DENSE_ROW] >= 0) - octave_stdout << "knobs(1): " << User_knobs (0) - << ", rows with > max (16," - << knobs[COLAMD_DENSE_ROW] << "*sqrt (size(A,2)))" - << " entries removed\n"; - else - octave_stdout << "knobs(1): " << User_knobs (0) - << ", only completely dense rows removed\n"; - - if (knobs[COLAMD_DENSE_COL] >= 0) - octave_stdout << "knobs(2): " << User_knobs (1) - << ", cols with > max (16," - << knobs[COLAMD_DENSE_COL] << "*sqrt (size(A)))" - << " entries removed\n"; - else - octave_stdout << "knobs(2): " << User_knobs (1) - << ", only completely dense columns removed\n"; - - octave_stdout << "knobs(3): " << User_knobs (2) - << ", statistics and knobs printed\n"; - - } - } - - octave_idx_type n_row, n_col, nnz; - octave_idx_type *ridx, *cidx; - SparseComplexMatrix scm; - SparseMatrix sm; - - if (args(0).issparse ()) - { - if (args(0).iscomplex ()) - { - 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).iscomplex ()) - 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 (octave::suitesparse_integer, p, n_col+1); - for (octave_idx_type i = 0; i < n_col+1; i++) - p[i] = cidx[i]; - - octave_idx_type Alen = COLAMD_NAME (_recommended) (nnz, n_row, n_col); - OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, A, Alen); - for (octave_idx_type i = 0; i < nnz; i++) - A[i] = ridx[i]; - - // Order the columns (destroys A) - static_assert (COLAMD_STATS <= 40, - "colamd: # of COLAMD_STATS exceeded. Please report this to bugs.octave.org"); - octave::suitesparse_integer stats_storage[COLAMD_STATS]; - octave::suitesparse_integer *stats = &stats_storage[0]; - if (! COLAMD_NAME () (n_row, n_col, Alen, A, p, knobs, stats)) - { - COLAMD_NAME (_report)(stats); - - error ("colamd: internal error!"); - } - - // column elimination tree post-ordering (reuse variables) - OCTAVE_LOCAL_BUFFER (octave_idx_type, colbeg, n_col + 1); - OCTAVE_LOCAL_BUFFER (octave_idx_type, colend, n_col + 1); - OCTAVE_LOCAL_BUFFER (octave_idx_type, etree, n_col + 1); - - for (octave_idx_type 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 - tree_postorder (n_col, etree, colbeg); - - // return the permutation vector - NDArray out_perm (dim_vector (1, n_col)); - for (octave_idx_type 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_NAME (_report)(stats); - - // Return the stats vector - if (nargout == 2) - { - NDArray out_stats (dim_vector (1, COLAMD_STATS)); - for (octave_idx_type 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; - -#else - - octave_unused_parameter (args); - octave_unused_parameter (nargout); - - err_disabled_feature ("colamd", "COLAMD"); - -#endif -} - -DEFUN_DLD (symamd, args, nargout, - doc: /* -*- texinfo -*- -@deftypefn {} {@var{p} =} symamd (@var{S}) -@deftypefnx {} {@var{p} =} symamd (@var{S}, @var{knobs}) -@deftypefnx {} {[@var{p}, @var{stats}] =} symamd (@var{S}) -@deftypefnx {} {[@var{p}, @var{stats}] =} symamd (@var{S}, @var{knobs}) - -For a symmetric positive definite matrix @var{S}, returns the permutation -vector p such that @code{@var{S}(@var{p}, @var{p})} tends to have a -sparser Cholesky@tie{}factor than @var{S}. - -Sometimes @code{symamd} works well for symmetric indefinite matrices too. -The matrix @var{S} is assumed to be symmetric; only the strictly lower -triangular part is referenced. @var{S} must be square. - -@var{knobs} is an optional one- to two-element input vector. If @var{S} is -n-by-n, then rows and columns with more than -@code{max (16,@var{knobs}(1)*sqrt(n))} entries are removed prior to -ordering, and ordered last in the output permutation @var{p}. No -rows/columns are removed if @code{@var{knobs}(1) < 0}. If -@code{@var{knobs}(2)} is nonzero, @var{stats} and @var{knobs} are -printed. The default is @code{@var{knobs} = [10 0]}. Note that -@var{knobs} differs from earlier versions of @code{symamd}. - -@var{stats} is an optional 20-element output vector that provides data -about the ordering and the validity of the input matrix @var{S}. Ordering -statistics are in @code{@var{stats}(1:3)}. -@code{@var{stats}(1) = @var{stats}(2)} is the number of dense or empty rows -and columns ignored by SYMAMD and @code{@var{stats}(3)} is the number of -garbage collections performed on the internal data structure used by SYMAMD -(roughly of size @code{8.4 * nnz (tril (@var{S}, -1)) + 9 * @var{n}} -integers). - -Octave built-in functions are intended to generate valid sparse matrices, -with no duplicate entries, with ascending row indices of the nonzeros -in each column, with a non-negative number of entries in each column (!) -and so on. If a matrix is invalid, then SYMAMD may or may not be able -to continue. If there are duplicate entries (a row index appears two or -more times in the same column) or if the row indices in a column are out -of order, then SYMAMD can correct these errors by ignoring the duplicate -entries and sorting each column of its internal copy of the matrix S (the -input matrix S is not repaired, however). If a matrix is invalid in -other ways then SYMAMD cannot continue, an error message is printed, and -no output arguments (@var{p} or @var{stats}) are returned. SYMAMD is -thus a simple way to check a sparse matrix to see if it's valid. - -@code{@var{stats}(4:7)} provide information if SYMAMD was able to -continue. The matrix is OK if @code{@var{stats} (4)} is zero, or 1 -if invalid. @code{@var{stats}(5)} is the rightmost column index that -is unsorted or contains duplicate entries, or zero if no such column -exists. @code{@var{stats}(6)} is the last seen duplicate or out-of-order -row index in the column index given by @code{@var{stats}(5)}, or zero -if no such row index exists. @code{@var{stats}(7)} is the number of -duplicate or out-of-order row indices. @code{@var{stats}(8:20)} is -always zero in the current version of SYMAMD (reserved for future use). - -The ordering is followed by a column elimination tree post-ordering. - -The authors of the code itself are @nospell{Stefan I. Larimore} and -@nospell{Timothy A. Davis}. The algorithm was developed in collaboration with -@nospell{John Gilbert}, Xerox PARC, and @nospell{Esmond Ng}, Oak Ridge National -Laboratory. (see @url{http://faculty.cse.tamu.edu/davis/suitesparse.html}) -@seealso{colperm, colamd} -@end deftypefn */) -{ -#if defined (HAVE_COLAMD) - - int nargin = args.length (); - - if (nargin < 1 || nargin > 2) - print_usage (); - - octave_value_list retval (nargin == 2 ? 2 : 1); - int spumoni = 0; - - // Get knobs - static_assert (COLAMD_KNOBS <= 40, - "symamd: # of COLAMD_KNOBS exceeded. Please report this to bugs.octave.org"); - double knob_storage[COLAMD_KNOBS]; - double *knobs = &knob_storage[0]; - COLAMD_NAME (_set_defaults) (knobs); - - // Check for user-passed knobs - if (nargin == 2) - { - NDArray User_knobs = args(1).array_value (); - int nel_User_knobs = User_knobs.numel (); - - if (nel_User_knobs > 0) - knobs[COLAMD_DENSE_ROW] = User_knobs(COLAMD_DENSE_ROW); - if (nel_User_knobs > 1) - spumoni = static_cast<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; - - octave_idx_type n_row, n_col; - octave_idx_type *ridx, *cidx; - SparseMatrix sm; - SparseComplexMatrix scm; - - if (args(0).issparse ()) - { - if (args(0).iscomplex ()) - { - scm = args(0).sparse_complex_matrix_value (); - n_row = scm.rows (); - n_col = scm.cols (); - ridx = scm.xridx (); - cidx = scm.xcidx (); - } - else - { - sm = args(0).sparse_matrix_value (); - n_row = sm.rows (); - n_col = sm.cols (); - ridx = sm.xridx (); - cidx = sm.xcidx (); - } - } - else - { - if (args(0).iscomplex ()) - sm = SparseMatrix (real (args(0).complex_matrix_value ())); - else - sm = SparseMatrix (args(0).matrix_value ()); - - n_row = sm.rows (); - n_col = sm.cols (); - ridx = sm.xridx (); - cidx = sm.xcidx (); - } - - if (n_row != n_col) - err_square_matrix_required ("symamd", "S"); - - // Allocate workspace for symamd - OCTAVE_LOCAL_BUFFER (octave_idx_type, perm, n_col+1); - static_assert (COLAMD_STATS <= 40, - "symamd: # of COLAMD_STATS exceeded. Please report this to bugs.octave.org"); - octave::suitesparse_integer stats_storage[COLAMD_STATS]; - octave::suitesparse_integer *stats = &stats_storage[0]; - if (! SYMAMD_NAME () (n_col, octave::to_suitesparse_intptr (ridx), - octave::to_suitesparse_intptr (cidx), - octave::to_suitesparse_intptr (perm), - knobs, stats, &calloc, &free)) - { - SYMAMD_NAME (_report)(stats); - - error ("symamd: internal error!"); - } - - // column elimination tree post-ordering - OCTAVE_LOCAL_BUFFER (octave_idx_type, etree, n_col + 1); - symetree (ridx, cidx, etree, perm, n_col); - - // Calculate the tree post-ordering - OCTAVE_LOCAL_BUFFER (octave_idx_type, post, n_col + 1); - tree_postorder (n_col, etree, post); - - // return the permutation vector - NDArray out_perm (dim_vector (1, n_col)); - for (octave_idx_type 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_NAME (_report)(stats); - - // Return the stats vector - if (nargout == 2) - { - NDArray out_stats (dim_vector (1, COLAMD_STATS)); - for (octave_idx_type 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; - -#else - - octave_unused_parameter (args); - octave_unused_parameter (nargout); - - err_disabled_feature ("symamd", "COLAMD"); - -#endif -} - -DEFUN_DLD (etree, args, nargout, - doc: /* -*- texinfo -*- -@deftypefn {} {@var{p} =} etree (@var{S}) -@deftypefnx {} {@var{p} =} etree (@var{S}, @var{typ}) -@deftypefnx {} {[@var{p}, @var{q}] =} etree (@var{S}, @var{typ}) - -Return the elimination tree for the matrix @var{S}. - -By default @var{S} is assumed to be symmetric and the symmetric elimination -tree is returned. The argument @var{typ} controls whether a symmetric or -column elimination tree is returned. Valid values of @var{typ} are -@qcode{"sym"} or @qcode{"col"}, for symmetric or column elimination tree -respectively. - -Called with a second argument, @code{etree} also returns the postorder -permutations on the tree. -@end deftypefn */) -{ - int nargin = args.length (); - - if (nargin < 1 || nargin > 2) - print_usage (); - - octave_value_list retval (nargout == 2 ? 2 : 1); - - octave_idx_type n_row = 0; - octave_idx_type n_col = 0; - octave_idx_type *ridx = nullptr; - octave_idx_type *cidx = nullptr; - - if (! args(0).issparse ()) - error ("etree: S must be a sparse matrix"); - - if (args(0).iscomplex ()) - { - SparseComplexMatrix scm = args(0).sparse_complex_matrix_value (); - - n_row = scm.rows (); - n_col = scm.cols (); - ridx = scm.xridx (); - cidx = scm.xcidx (); - } - else - { - SparseMatrix sm = args(0).sparse_matrix_value (); - - n_row = sm.rows (); - n_col = sm.cols (); - ridx = sm.xridx (); - cidx = sm.xcidx (); - } - - bool is_sym = true; - - if (nargin == 2) - { - std::string str = args(1).xstring_value ("etree: TYP must be a string"); - if (str.find ('C') == 0 || str.find ('c') == 0) - is_sym = false; - } - - // column elimination tree post-ordering (reuse variables) - OCTAVE_LOCAL_BUFFER (octave_idx_type, etree, n_col + 1); - - if (is_sym) - { - if (n_row != n_col) - error ("etree: S is marked as symmetric, but is not square"); - - symetree (ridx, cidx, etree, nullptr, n_col); - } - else - { - OCTAVE_LOCAL_BUFFER (octave_idx_type, colbeg, n_col); - OCTAVE_LOCAL_BUFFER (octave_idx_type, colend, n_col); - - for (octave_idx_type 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 (octave_idx_type i = 0; i < n_col; i++) - // We flag a root with n_col while Matlab does it with zero - // Convert for matlab compatible 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 (octave_idx_type, post, n_col + 1); - tree_postorder (n_col, etree, post); - - NDArray postorder (dim_vector (1, n_col)); - for (octave_idx_type i = 0; i < n_col; i++) - postorder(i) = post[i] + 1; - - retval(1) = postorder; - } - - return retval; -} - -/* -%!assert (etree (speye (2)), [0, 0]); -%!assert (etree (gallery ("poisson", 16)), [2:256, 0]); - -%!error etree () -%!error etree (1, 2, 3) -%!error <S must be a sparse matrix> etree ([1, 2; 3, 4]) -%!error <TYP must be a string> etree (speye (2), 3) -%!error <is not square> etree (sprand (2, 4, .25)) -*/
--- a/libinterp/dldfcn/dmperm.cc Sat Jan 25 15:26:07 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,215 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 1998-2020 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or <https://octave.org/copyright/>. -// -// This file is part of Octave. -// -// 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 3 of the License, 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 Octave; see the file COPYING. If not, see -// <https://www.gnu.org/licenses/>. -// -//////////////////////////////////////////////////////////////////////// - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include "CSparse.h" -#include "dRowVector.h" -#include "dSparse.h" -#include "oct-sparse.h" - -#include "defun-dld.h" -#include "errwarn.h" -#include "ov.h" -#include "ovl.h" -#include "utils.h" - -#if defined (OCTAVE_ENABLE_64) -# define CXSPARSE_NAME(name) cs_dl ## name -#else -# define CXSPARSE_NAME(name) cs_di ## name -#endif - -#if defined (HAVE_CXSPARSE) - -static RowVector -put_int (octave::suitesparse_integer *p, octave_idx_type n) -{ - RowVector ret (n); - for (octave_idx_type i = 0; i < n; i++) - ret.xelem (i) = p[i] + 1; - return ret; -} - -static octave_value_list -dmperm_internal (bool rank, const octave_value arg, int nargout) -{ - octave_value_list retval; - octave_idx_type nr = arg.rows (); - octave_idx_type nc = arg.columns (); - SparseMatrix m; - SparseComplexMatrix cm; - CXSPARSE_NAME () csm; - csm.m = nr; - csm.n = nc; - csm.x = nullptr; - csm.nz = -1; - - if (arg.isreal ()) - { - m = arg.sparse_matrix_value (); - csm.nzmax = m.nnz (); - csm.p = octave::to_suitesparse_intptr (m.xcidx ()); - csm.i = octave::to_suitesparse_intptr (m.xridx ()); - } - else - { - cm = arg.sparse_complex_matrix_value (); - csm.nzmax = cm.nnz (); - csm.p = octave::to_suitesparse_intptr (cm.xcidx ()); - csm.i = octave::to_suitesparse_intptr (cm.xridx ()); - } - - if (nargout <= 1 || rank) - { - octave::suitesparse_integer *jmatch = CXSPARSE_NAME (_maxtrans) (&csm, 0); - if (rank) - { - octave_idx_type r = 0; - for (octave_idx_type i = 0; i < nc; i++) - if (jmatch[nr+i] >= 0) - r++; - retval(0) = static_cast<double> (r); - } - else - retval(0) = put_int (jmatch + nr, nc); - CXSPARSE_NAME (_free) (jmatch); - } - else - { - CXSPARSE_NAME (d) *dm = CXSPARSE_NAME(_dmperm) (&csm, 0); - - //retval(5) = put_int (dm->rr, 5); - //retval(4) = put_int (dm->cc, 5); - retval = ovl (put_int (dm->p, nr), put_int (dm->q, nc), - put_int (dm->r, dm->nb+1), put_int (dm->s, dm->nb+1)); - - CXSPARSE_NAME (_dfree) (dm); - } - - return retval; -} - -#endif - -DEFUN_DLD (dmperm, args, nargout, - doc: /* -*- texinfo -*- -@deftypefn {} {@var{p} =} dmperm (@var{S}) -@deftypefnx {} {[@var{p}, @var{q}, @var{r}, @var{S}] =} dmperm (@var{S}) - -@cindex @nospell{Dulmage-Mendelsohn} decomposition -Perform a @nospell{Dulmage-Mendelsohn} permutation of the sparse matrix -@var{S}. - -With a single output argument @code{dmperm} performs the row permutations -@var{p} such that @code{@var{S}(@var{p},:)} has no zero elements on the -diagonal. - -Called with two or more output arguments, returns the row and column -permutations, such that @code{@var{S}(@var{p}, @var{q})} is in block -triangular form. The values of @var{r} and @var{S} define the boundaries -of the blocks. If @var{S} is square then @code{@var{r} == @var{S}}. - -The method used is described in: @nospell{A. Pothen & C.-J. Fan.} -@cite{Computing the Block Triangular Form of a Sparse Matrix}. -@nospell{ACM} Trans.@: Math.@: Software, 16(4):303--324, 1990. -@seealso{colamd, ccolamd} -@end deftypefn */) -{ -#if defined (HAVE_CXSPARSE) - - if (args.length () != 1) - print_usage (); - - return dmperm_internal (false, args(0), nargout); - -#else - - octave_unused_parameter (args); - octave_unused_parameter (nargout); - - err_disabled_feature ("dmperm", "CXSparse"); - -#endif -} - -/* -%!testif HAVE_CXSPARSE -%! n = 20; -%! a = speye (n,n); -%! a = a(randperm (n),:); -%! assert (a(dmperm (a),:), speye (n)); - -%!testif HAVE_CXSPARSE -%! n = 20; -%! d = 0.2; -%! a = tril (sprandn (n,n,d), -1) + speye (n,n); -%! a = a(randperm (n), randperm (n)); -%! [p,q,r,s] = dmperm (a); -%! assert (tril (a(p,q), -1), sparse (n, n)); -*/ - -DEFUN_DLD (sprank, args, nargout, - doc: /* -*- texinfo -*- -@deftypefn {} {@var{p} =} sprank (@var{S}) -@cindex structural rank - -Calculate the structural rank of the sparse matrix @var{S}. - -Note that only the structure of the matrix is used in this calculation based -on a @nospell{Dulmage-Mendelsohn} permutation to block triangular form. As -such the numerical rank of the matrix @var{S} is bounded by -@code{sprank (@var{S}) >= rank (@var{S})}. Ignoring floating point errors -@code{sprank (@var{S}) == rank (@var{S})}. -@seealso{dmperm} -@end deftypefn */) -{ -#if defined (HAVE_CXSPARSE) - - if (args.length () != 1) - print_usage (); - - return dmperm_internal (true, args(0), nargout); - -#else - - octave_unused_parameter (args); - octave_unused_parameter (nargout); - - err_disabled_feature ("sprank", "CXSparse"); - -#endif -} - -/* -%!testif HAVE_CXSPARSE -%! assert (sprank (speye (20)), 20); -%!testif HAVE_CXSPARSE -%! assert (sprank ([1,0,2,0;2,0,4,0]), 2); - -%!error sprank (1,2) -*/
--- a/libinterp/dldfcn/module-files Sat Jan 25 15:26:07 2020 +0100 +++ b/libinterp/dldfcn/module-files Wed Jan 29 06:30:40 2020 -0500 @@ -1,22 +1,13 @@ # FILE|CPPFLAGS|LDFLAGS|LIBRARIES __delaunayn__.cc|$(QHULL_CPPFLAGS)|$(QHULL_LDFLAGS)|$(QHULL_LIBS) -__eigs__.cc|$(ARPACK_CPPFLAGS) $(SPARSE_XCPPFLAGS)|$(ARPACK_LDFLAGS) $(SPARSE_XLDFLAGS)|$(ARPACK_LIBS) $(SPARSE_XLIBS) $(LAPACK_LIBS) $(BLAS_LIBS) __fltk_uigetfile__.cc|$(FLTK_CPPFLAGS) $(FT2_CPPFLAGS)|$(FLTK_LDFLAGS) $(FT2_LDFLAGS)|$(FLTK_LIBS) $(FT2_LIBS) __glpk__.cc|$(GLPK_CPPFLAGS)|$(GLPK_LDFLAGS)|$(GLPK_LIBS) __init_fltk__.cc|$(FLTK_CPPFLAGS) $(FT2_CPPFLAGS) $(FONTCONFIG_CPPFLAGS)|$(FLTK_LDFLAGS) $(FT2_LDFLAGS)|$(FLTK_LIBS) $(FT2_LIBS) $(OPENGL_LIBS) __init_gnuplot__.cc|$(FT2_CPPFLAGS) $(FONTCONFIG_CPPFLAGS)|| __ode15__.cc|$(SUNDIALS_XCPPFLAGS)|$(SUNDIALS_XLDFLAGS)|$(SUNDIALS_XLIBS) __voronoi__.cc|$(QHULL_CPPFLAGS)|$(QHULL_LDFLAGS)|$(QHULL_LIBS) -amd.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS) audiodevinfo.cc|$(PORTAUDIO_CPPFLAGS)|$(PORTAUDIO_LDFLAGS)|$(PORTAUDIO_LIBS) audioread.cc|$(SNDFILE_CPPFLAGS)|$(SNDFILE_LDFLAGS)|$(SNDFILE_LIBS) -ccolamd.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS) -chol.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS) -colamd.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS) convhulln.cc|$(QHULL_CPPFLAGS)|$(QHULL_LDFLAGS)|$(QHULL_LIBS) -dmperm.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS) fftw.cc|$(FFTW_XCPPFLAGS)|$(FFTW_XLDFLAGS)|$(FFTW_XLIBS) gzip.cc|$(Z_CPPFLAGS) $(BZ2_CPPFLAGS)|$(Z_LDFLAGS) $(BZ2_LDFLAGS)|$(Z_LIBS) $(BZ2_LIBS) -qr.cc|$(QRUPDATE_CPPFLAGS) $(SPARSE_XCPPFLAGS)|$(QRUPDATE_LDFLAGS) $(SPARSE_XLDFLAGS)|$(QRUPDATE_LIBS) $(SPARSE_XLIBS) -symbfact.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS) -symrcm.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS)
--- a/libinterp/dldfcn/qr.cc Sat Jan 25 15:26:07 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1751 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 1996-2020 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or <https://octave.org/copyright/>. -// -// This file is part of Octave. -// -// 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 3 of the License, 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 Octave; see the file COPYING. If not, see -// <https://www.gnu.org/licenses/>. -// -//////////////////////////////////////////////////////////////////////// - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include <string> - -#include "MArray.h" -#include "Matrix.h" -#include "qr.h" -#include "qrp.h" -#include "sparse-qr.h" - -#include "defun-dld.h" -#include "error.h" -#include "errwarn.h" -#include "ov.h" -#include "ovl.h" - -/* -## Restore all rand* "state" values -%!function restore_rand_states (state) -%! rand ("state", state.rand); -%! randn ("state", state.randn); -%!endfunction - -%!shared old_state, restore_state -%! ## Save and restore the states of both random number generators that are -%! ## tested by the unit tests in this file. -%! old_state.rand = rand ("state"); -%! old_state.randn = randn ("state"); -%! restore_state = onCleanup (@() restore_rand_states (old_state)); -*/ - -template <typename MT> -static octave_value -get_qr_r (const octave::math::qr<MT>& fact) -{ - MT R = fact.R (); - if (R.issquare () && fact.regular ()) - return octave_value (R, MatrixType (MatrixType::Upper)); - else - return R; -} - -template <typename T> -static typename octave::math::qr<T>::type -qr_type (int nargout, bool economy) -{ - if (nargout == 0 || nargout == 1) - return octave::math::qr<T>::raw; - else if (economy) - return octave::math::qr<T>::economy; - else - return octave::math::qr<T>::std; -} - -// [Q, R] = qr (X): form Q unitary and R upper triangular such -// that Q * R = X -// -// [Q, R] = qr (X, 0): form the economy decomposition such that if X is -// m by n then only the first n columns of Q are -// computed. -// -// [Q, R, P] = qr (X): form QRP factorization of X where -// P is a permutation matrix such that -// A * P = Q * R -// -// [Q, R, P] = qr (X, 0): form the economy decomposition with -// permutation vector P such that Q * R = X(:, P) -// -// qr (X) alone returns the output of the LAPACK routine dgeqrf, such -// that R = triu (qr (X)) - -DEFUN_DLD (qr, args, nargout, - doc: /* -*- texinfo -*- -@deftypefn {} {[@var{Q}, @var{R}] =} qr (@var{A}) -@deftypefnx {} {[@var{Q}, @var{R}, @var{P}] =} qr (@var{A}) # non-sparse A -@deftypefnx {} {@var{X} =} qr (@var{A}) # non-sparse A -@deftypefnx {} {@var{R} =} qr (@var{A}) # sparse A -@deftypefnx {} {[@var{C}, @var{R}] =} qr (@var{A}, @var{B}) -@deftypefnx {} {[@dots{}] =} qr (@dots{}, 0) -@deftypefnx {} {[@dots{}] =} qr (@dots{}, "vector") -@deftypefnx {} {[@dots{}] =} qr (@dots{}, "matrix") -@cindex QR factorization -Compute the QR@tie{}factorization of @var{A}, using standard @sc{lapack} -subroutines. - -The QR@tie{}factorization is -@tex -$QR = A$ where $Q$ is an orthogonal matrix and $R$ is upper triangular. -@end tex -@ifnottex - -@example -@var{Q} * @var{R} = @var{A} -@end example - -@noindent -where @var{Q} is an orthogonal matrix and @var{R} is upper triangular. -@end ifnottex - -For example, given the matrix @code{@var{A} = [1, 2; 3, 4]}, - -@example -[@var{Q}, @var{R}] = qr (@var{A}) -@end example - -@noindent -returns - -@example -@group -@var{Q} = - - -0.31623 -0.94868 - -0.94868 0.31623 - -@var{R} = - - -3.16228 -4.42719 - 0.00000 -0.63246 -@end group -@end example - -@noindent -which multiplied together return the original matrix - -@example -@group -@var{Q} * @var{R} - @result{} - 1.0000 2.0000 - 3.0000 4.0000 -@end group -@end example - -If just a single return value is requested then it is either @var{R}, if -@var{A} is sparse, or @var{X}, such that @code{@var{R} = triu (@var{X})} if -@var{A} is full. (Note: unlike most commands, the single return value is not -the first return value when multiple values are requested.) - -If the matrix @var{A} is full, and a third output @var{P} is requested, then -@code{qr} calculates the permuted QR@tie{}factorization -@tex -$QR = AP$ where $Q$ is an orthogonal matrix, $R$ is upper triangular, and $P$ -is a permutation matrix. -@end tex -@ifnottex - -@example -@var{Q} * @var{R} = @var{A} * @var{P} -@end example - -@noindent -where @var{Q} is an orthogonal matrix, @var{R} is upper triangular, and -@var{P} is a permutation matrix. -@end ifnottex - -The permuted QR@tie{}factorization has the additional property that the -diagonal entries of @var{R} are ordered by decreasing magnitude. In other -words, @code{abs (diag (@var{R}))} will be ordered from largest to smallest. - -For example, given the matrix @code{@var{A} = [1, 2; 3, 4]}, - -@example -[@var{Q}, @var{R}, @var{P}] = qr (@var{A}) -@end example - -@noindent -returns - -@example -@group -@var{Q} = - - -0.44721 -0.89443 - -0.89443 0.44721 - -@var{R} = - - -4.47214 -3.13050 - 0.00000 0.44721 - -@var{P} = - - 0 1 - 1 0 -@end group -@end example - -If the input matrix @var{A} is sparse then the sparse QR@tie{}factorization -is computed using @sc{CSparse}. Because the matrix @var{Q} is, in general, a -full matrix, it is recommended to request only one return value @var{R}. In -that case, the computation avoids the construction of @var{Q} and returns -@var{R} such that @code{@var{R} = chol (@var{A}' * @var{A})}. - -If an additional matrix @var{B} is supplied and two return values are -requested, then @code{qr} returns @var{C}, where -@code{@var{C} = @var{Q}' * @var{B}}. This allows the least squares -approximation of @code{@var{A} \ @var{B}} to be calculated as - -@example -@group -[@var{C}, @var{R}] = qr (@var{A}, @var{B}) -@var{x} = @var{R} \ @var{C} -@end group -@end example - -If the final argument is the string @qcode{"vector"} then @var{P} is a -permutation vector (of the columns of @var{A}) instead of a permutation matrix. -In this case, the defining relationship is - -@example -@var{Q} * @var{R} = @var{A}(:, @var{P}) -@end example - -The default, however, is to return a permutation matrix and this may be -explicitly specified by using a final argument of @qcode{"matrix"}. - -If the final argument is the scalar 0 an @qcode{"economy"} factorization is -returned. When the original matrix @var{A} has size MxN and M > N then the -@qcode{"economy"} factorization will calculate just N rows in @var{R} and N -columns in @var{Q} and omit the zeros in @var{R}. If M @leq{} N there is no -difference between the economy and standard factorizations. When calculating -an @qcode{"economy"} factorization the output @var{P} is always a vector -rather than a matrix. - -Background: The QR factorization has applications in the solution of least -squares problems -@tex -$$ -\min_x \left\Vert A x - b \right\Vert_2 -$$ -@end tex -@ifnottex - -@example -min norm (A*x - b) -@end example - -@end ifnottex -for overdetermined systems of equations (i.e., -@tex -$A$ -@end tex -@ifnottex -@var{A} -@end ifnottex -is a tall, thin matrix). - -The permuted QR@tie{}factorization -@code{[@var{Q}, @var{R}, @var{P}] = qr (@var{A})} allows the construction of an -orthogonal basis of @code{span (A)}. - -@seealso{chol, hess, lu, qz, schur, svd, qrupdate, qrinsert, qrdelete, qrshift} -@end deftypefn */) -{ - int nargin = args.length (); - - if (nargin < 1 || nargin > 3) - print_usage (); - - octave_value_list retval; - - octave_value arg = args(0); - - bool economy = false; - bool is_cmplx = false; - bool have_b = 0; - bool vector_p = 0; - - if (arg.iscomplex ()) - is_cmplx = true; - if (nargin > 1) - { - have_b = true; - if (args(nargin-1).is_scalar_type ()) - { - int val = args(nargin-1).int_value (); - if (val == 0) - { - economy = true; - have_b = (nargin > 2); - } - else if (nargin == 3) // argument 3 should be 0 or a string - print_usage (); - } - else if (args(nargin-1).is_string ()) - { - std::string str = args(nargin-1).string_value (); - if (str == "vector") - vector_p = true; - else if (str != "matrix") - error ("qr: type for P must be 'matrix' or 'vector', not %s", - str.c_str ()); - have_b = (nargin > 2); - } - else if (! args(nargin-1).is_matrix_type ()) - err_wrong_type_arg ("qr", args(nargin-1)); - else if (nargin == 3) // should be caught by is_scalar_type or is_string - print_usage (); - - if (have_b && args(1).iscomplex ()) - is_cmplx = true; - } - - if (arg.issparse ()) - { - if (nargout > 2) - error ("qr: Permutation output is not supported for sparse input"); - - if (is_cmplx) - { - octave::math::sparse_qr<SparseComplexMatrix> q (arg.sparse_complex_matrix_value ()); - - if (have_b) - { - retval = ovl (q.C (args(1).complex_matrix_value ()), - q.R (economy)); - if (arg.rows () < arg.columns ()) - warning ("qr: non minimum norm solution for under-determined " - "problem %" OCTAVE_IDX_TYPE_FORMAT - "x%" OCTAVE_IDX_TYPE_FORMAT, - arg.rows (), arg.columns ()); - } - else if (nargout > 1) - retval = ovl (q.Q (), q.R (economy)); - else - retval = ovl (q.R (economy)); - } - else - { - octave::math::sparse_qr<SparseMatrix> q (arg.sparse_matrix_value ()); - - if (have_b) - { - retval = ovl (q.C (args(1).matrix_value ()), q.R (economy)); - if (arg.rows () < arg.columns ()) - warning ("qr: non minimum norm solution for under-determined " - "problem %" OCTAVE_IDX_TYPE_FORMAT - "x%" OCTAVE_IDX_TYPE_FORMAT, - arg.rows (), arg.columns ()); - } - else if (nargout > 1) - retval = ovl (q.Q (), q.R (economy)); - else - retval = ovl (q.R (economy)); - } - } - else - { - if (arg.is_single_type ()) - { - if (arg.isreal ()) - { - octave::math::qr<FloatMatrix>::type type - = qr_type<FloatMatrix> (nargout, economy); - - FloatMatrix m = arg.float_matrix_value (); - - switch (nargout) - { - case 0: - case 1: - { - octave::math::qr<FloatMatrix> fact (m, type); - retval = ovl (fact.R ()); - } - break; - - case 2: - { - octave::math::qr<FloatMatrix> fact (m, type); - retval = ovl (fact.Q (), get_qr_r (fact)); - if (have_b) - { - if (is_cmplx) - retval(0) = fact.Q ().transpose () - * args(1).float_complex_matrix_value (); - else - retval(0) = fact.Q ().transpose () - * args(1).float_matrix_value (); - } - } - break; - - default: - { - octave::math::qrp<FloatMatrix> fact (m, type); - - if (economy || vector_p) - retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ()); - else - retval = ovl (fact.Q (), get_qr_r (fact), fact.P ()); - } - break; - } - } - else if (arg.iscomplex ()) - { - octave::math::qr<FloatComplexMatrix>::type type - = qr_type<FloatComplexMatrix> (nargout, economy); - - FloatComplexMatrix m = arg.float_complex_matrix_value (); - - switch (nargout) - { - case 0: - case 1: - { - octave::math::qr<FloatComplexMatrix> fact (m, type); - retval = ovl (fact.R ()); - } - break; - - case 2: - { - octave::math::qr<FloatComplexMatrix> fact (m, type); - retval = ovl (fact.Q (), get_qr_r (fact)); - if (have_b) - retval (0) = conj (fact.Q ().transpose ()) - * args(1).float_complex_matrix_value (); - } - break; - - default: - { - octave::math::qrp<FloatComplexMatrix> fact (m, type); - if (economy || vector_p) - retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ()); - else - retval = ovl (fact.Q (), get_qr_r (fact), fact.P ()); - } - break; - } - } - } - else - { - if (arg.isreal ()) - { - octave::math::qr<Matrix>::type type - = qr_type<Matrix> (nargout, economy); - - Matrix m = arg.matrix_value (); - - switch (nargout) - { - case 0: - case 1: - { - octave::math::qr<Matrix> fact (m, type); - retval = ovl (fact.R ()); - } - break; - - case 2: - { - octave::math::qr<Matrix> fact (m, type); - retval = ovl (fact.Q (), get_qr_r (fact)); - if (have_b) - { - if (is_cmplx) - retval(0) = fact.Q ().transpose () - * args(1).complex_matrix_value (); - else - retval(0) = fact.Q ().transpose () - * args(1).matrix_value (); - } - } - break; - - default: - { - octave::math::qrp<Matrix> fact (m, type); - if (economy || vector_p) - retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ()); - else - retval = ovl (fact.Q (), get_qr_r (fact), fact.P ()); - } - break; - } - } - else if (arg.iscomplex ()) - { - octave::math::qr<ComplexMatrix>::type type - = qr_type<ComplexMatrix> (nargout, economy); - - ComplexMatrix m = arg.complex_matrix_value (); - - switch (nargout) - { - case 0: - case 1: - { - octave::math::qr<ComplexMatrix> fact (m, type); - retval = ovl (fact.R ()); - } - break; - - case 2: - { - octave::math::qr<ComplexMatrix> fact (m, type); - retval = ovl (fact.Q (), get_qr_r (fact)); - if (have_b) - retval (0) = conj (fact.Q ().transpose ()) - * args(1).complex_matrix_value (); - } - break; - - default: - { - octave::math::qrp<ComplexMatrix> fact (m, type); - if (economy || vector_p) - retval = ovl (fact.Q (), get_qr_r (fact), fact.Pvec ()); - else - retval = ovl (fact.Q (), get_qr_r (fact), fact.P ()); - } - break; - } - } - else - err_wrong_type_arg ("qr", arg); - } - } - - return retval; -} - -/* -%!test -%! a = [0, 2, 1; 2, 1, 2]; -%! -%! [q, r] = qr (a); -%! [qe, re] = qr (a, 0); -%! -%! assert (q * r, a, sqrt (eps)); -%! assert (qe * re, a, sqrt (eps)); - -%!test -%! a = [0, 2, 1; 2, 1, 2]; -%! -%! [q, r] = qr (a); -%! [qe, re] = qr (a, 0); -%! -%! assert (q * r, a, sqrt (eps)); -%! assert (qe * re, a, sqrt (eps)); - -%!test -%! a = [0, 2, 1; 2, 1, 2]; -%! -%! [q, r, p] = qr (a); # FIXME: not giving right dimensions. -%! [qe, re, pe] = qr (a, 0); -%! -%! assert (q * r, a * p, sqrt (eps)); -%! assert (qe * re, a(:, pe), sqrt (eps)); - -%!test -%! a = [0, 2; 2, 1; 1, 2]; -%! -%! [q, r] = qr (a); -%! [qe, re] = qr (a, 0); -%! -%! assert (q * r, a, sqrt (eps)); -%! assert (qe * re, a, sqrt (eps)); - -%!test -%! a = [0, 2; 2, 1; 1, 2]; -%! -%! [q, r, p] = qr (a); -%! [qe, re, pe] = qr (a, 0); -%! -%! assert (q * r, a * p, sqrt (eps)); -%! assert (qe * re, a(:, pe), sqrt (eps)); - -%!test -%! a = [0, 2, 1; 2, 1, 2; 3, 1, 2]; -%! b = [1, 3, 2; 1, 1, 0; 3, 0, 2]; -%! -%! [q, r] = qr (a); -%! [c, re] = qr (a, b); -%! -%! assert (r, re, sqrt (eps)); -%! assert (q'*b, c, sqrt (eps)); - -%!test -%! a = [0, 2, i; 2, 1, 2; 3, 1, 2]; -%! b = [1, 3, 2; 1, i, 0; 3, 0, 2]; -%! -%! [q, r] = qr (a); -%! [c, re] = qr (a, b); -%! -%! assert (r, re, sqrt (eps)); -%! assert (q'*b, c, sqrt (eps)); - -%!test -%! a = [0, 2, i; 2, 1, 2; 3, 1, 2]; -%! b = [1, 3, 2; 1, 1, 0; 3, 0, 2]; -%! -%! [q, r] = qr (a); -%! [c, re] = qr (a, b); -%! -%! assert (r, re, sqrt (eps)); -%! assert (q'*b, c, sqrt (eps)); - -%!test -%! a = [0, 2, 1; 2, 1, 2; 3, 1, 2]; -%! b = [1, 3, 2; 1, i, 0; 3, 0, 2]; -%! -%! [q, r] = qr (a); -%! [c, re] = qr (a, b); -%! -%! assert (r, re, sqrt (eps)); -%! assert (q'*b, c, sqrt (eps)); - -%!test -%! assert (qr (zeros (0, 0)), zeros (0, 0)) -%! assert (qr (zeros (1, 0)), zeros (1, 0)) -%! assert (qr (zeros (0, 1)), zeros (0, 1)) - -%!error qr () -%!error qr ([1, 2; 3, 4], 0, 2) - -%!function retval = __testqr (q, r, a, p) -%! tol = 100*eps (class (q)); -%! retval = 0; -%! if (nargin == 3) -%! n1 = norm (q*r - a); -%! n2 = norm (q'*q - eye (columns (q))); -%! retval = (n1 < tol && n2 < tol); -%! else -%! n1 = norm (q'*q - eye (columns (q))); -%! retval = (n1 < tol); -%! if (isvector (p)) -%! n2 = norm (q*r - a(:,p)); -%! retval = (retval && n2 < tol); -%! else -%! n2 = norm (q*r - a*p); -%! retval = (retval && n2 < tol); -%! endif -%! endif -%!endfunction - -%!test -%! t = ones (24, 1); -%! j = 1; -%! -%! if (false) # eliminate big matrix tests -%! a = rand (5000, 20); -%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); -%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); -%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); -%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a', p); -%! -%! a = a+1i*eps; -%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); -%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); -%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); -%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a', p); -%! endif -%! -%! a = [ ones(1,15); sqrt(eps)*eye(15) ]; -%! [q,r] = qr (a); t(j++) = __testqr (q, r, a); -%! [q,r] = qr (a'); t(j++) = __testqr (q, r, a'); -%! [q,r,p] = qr (a); t(j++) = __testqr (q, r, a, p); -%! [q,r,p] = qr (a'); t(j++) = __testqr (q, r, a', p); -%! -%! a = a+1i*eps; -%! [q,r] = qr (a); t(j++) = __testqr (q, r, a); -%! [q,r] = qr (a'); t(j++) = __testqr (q, r, a'); -%! [q,r,p] = qr (a); t(j++) = __testqr (q, r, a, p); -%! [q,r,p] = qr (a'); t(j++) = __testqr (q, r, a', p); -%! -%! a = [ ones(1,15); sqrt(eps)*eye(15) ]; -%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); -%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); -%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); -%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a', p); -%! -%! a = a+1i*eps; -%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); -%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); -%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); -%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a', p); -%! -%! a = [ 611 196 -192 407 -8 -52 -49 29 -%! 196 899 113 -192 -71 -43 -8 -44 -%! -192 113 899 196 61 49 8 52 -%! 407 -192 196 611 8 44 59 -23 -%! -8 -71 61 8 411 -599 208 208 -%! -52 -43 49 44 -599 411 208 208 -%! -49 -8 8 59 208 208 99 -911 -%! 29 -44 52 -23 208 208 -911 99 ]; -%! [q,r] = qr (a); -%! -%! assert (all (t) && norm (q*r - a) < 5000*eps); - -%!test -%! a = single ([0, 2, 1; 2, 1, 2]); -%! -%! [q, r] = qr (a); -%! [qe, re] = qr (a, 0); -%! -%! assert (q * r, a, sqrt (eps ("single"))); -%! assert (qe * re, a, sqrt (eps ("single"))); - -%!test -%! a = single ([0, 2, 1; 2, 1, 2]); -%! -%! [q, r, p] = qr (a); # FIXME: not giving right dimensions. -%! [qe, re, pe] = qr (a, 0); -%! -%! assert (q * r, a * p, sqrt (eps ("single"))); -%! assert (qe * re, a(:, pe), sqrt (eps ("single"))); - -%!test -%! a = single ([0, 2; 2, 1; 1, 2]); -%! -%! [q, r] = qr (a); -%! [qe, re] = qr (a, 0); -%! -%! assert (q * r, a, sqrt (eps ("single"))); -%! assert (qe * re, a, sqrt (eps ("single"))); - -%!test -%! a = single ([0, 2; 2, 1; 1, 2]); -%! -%! [q, r, p] = qr (a); -%! [qe, re, pe] = qr (a, 0); -%! -%! assert (q * r, a * p, sqrt (eps ("single"))); -%! assert (qe * re, a(:, pe), sqrt (eps ("single"))); - -%!test -%! a = single([0, 2, 1; 2, 1, 2; 3, 1, 2]); -%! b = single([1, 3, 2; 1, 1, 0; 3, 0, 2]); -%! -%! [q, r] = qr (a); -%! [c, re] = qr (a, b); -%! -%! assert (r, re, sqrt (eps ("single"))); -%! assert (q'*b, c, sqrt (eps ("single"))); - -%!test -%! a = single([0, 2, i; 2, 1, 2; 3, 1, 2]); -%! b = single([1, 3, 2; 1, i, 0; 3, 0, 2]); -%! -%! [q, r] = qr (a); -%! [c, re] = qr (a, b); -%! -%! assert (r, re, sqrt (eps ("single"))); -%! assert (q'*b, c, sqrt (eps ("single"))); - -%!test -%! a = single([0, 2, i; 2, 1, 2; 3, 1, 2]); -%! b = single([1, 3, 2; 1, 1, 0; 3, 0, 2]); -%! -%! [q, r] = qr (a); -%! [c, re] = qr (a, b); -%! -%! assert (r, re, sqrt (eps)); -%! assert (q'*b, c, sqrt (eps)); - -%!test -%! a = single([0, 2, 1; 2, 1, 2; 3, 1, 2]); -%! b = single([1, 3, 2; 1, i, 0; 3, 0, 2]); -%! -%! [q, r] = qr (a); -%! [c, re] = qr (a, b); -%! -%! assert (r, re, sqrt (eps ("single"))); -%! assert (q'*b, c, sqrt (eps ("single"))); - -%!error qr () -%!error qr ([1, 2; 3, 4], 0, 2) - -%!test -%! t = ones (24, 1); -%! j = 1; -%! -%! if (false) # eliminate big matrix tests -%! a = rand (5000,20); -%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); -%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); -%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); -%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a', p); -%! -%! a = a+1i*eps ("single"); -%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); -%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); -%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); -%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a', p); -%! endif -%! -%! a = [ ones(1,15); sqrt(eps("single"))*eye(15) ]; -%! [q,r] = qr (a); t(j++) = __testqr (q, r, a); -%! [q,r] = qr (a'); t(j++) = __testqr (q, r, a'); -%! [q,r,p] = qr (a); t(j++) = __testqr (q, r, a, p); -%! [q,r,p] = qr (a'); t(j++) = __testqr (q, r, a', p); -%! -%! a = a+1i*eps ("single"); -%! [q,r] = qr (a); t(j++) = __testqr (q, r, a); -%! [q,r] = qr (a'); t(j++) = __testqr (q, r, a'); -%! [q,r,p] = qr (a); t(j++) = __testqr (q, r, a, p); -%! [q,r,p] = qr (a'); t(j++) = __testqr (q, r, a', p); -%! -%! a = [ ones(1,15); sqrt(eps("single"))*eye(15) ]; -%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); -%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); -%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); -%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a', p); -%! -%! a = a+1i*eps ("single"); -%! [q,r] = qr (a, 0); t(j++) = __testqr (q, r, a); -%! [q,r] = qr (a',0); t(j++) = __testqr (q, r, a'); -%! [q,r,p] = qr (a, 0); t(j++) = __testqr (q, r, a, p); -%! [q,r,p] = qr (a',0); t(j++) = __testqr (q, r, a',p); -%! -%! a = [ 611 196 -192 407 -8 -52 -49 29 -%! 196 899 113 -192 -71 -43 -8 -44 -%! -192 113 899 196 61 49 8 52 -%! 407 -192 196 611 8 44 59 -23 -%! -8 -71 61 8 411 -599 208 208 -%! -52 -43 49 44 -599 411 208 208 -%! -49 -8 8 59 208 208 99 -911 -%! 29 -44 52 -23 208 208 -911 99 ]; -%! [q,r] = qr (a); -%! -%! assert (all (t) && norm (q*r-a) < 5000*eps ("single")); - -## The deactivated tests below can't be tested till rectangular back-subs is -## implemented for sparse matrices. - -%!testif HAVE_CXSPARSE -%! n = 20; d = 0.2; -%! ## initialize generators to make behavior reproducible -%! rand ("state", 42); -%! randn ("state", 42); -%! a = sprandn (n,n,d) + speye (n,n); -%! r = qr (a); -%! assert (r'*r, a'*a, 1e-10); - -%!testif HAVE_COLAMD -%! n = 20; d = 0.2; -%! ## initialize generators to make behavior reproducible -%! rand ("state", 42); -%! randn ("state", 42); -%! a = sprandn (n,n,d) + speye (n,n); -%! q = symamd (a); -%! a = a(q,q); -%! r = qr (a); -%! assert (r'*r, a'*a, 1e-10); - -%!testif HAVE_CXSPARSE -%! n = 20; d = 0.2; -%! ## initialize generators to make behavior reproducible -%! rand ("state", 42); -%! randn ("state", 42); -%! a = sprandn (n,n,d) + speye (n,n); -%! [c,r] = qr (a, ones (n,1)); -%! assert (r\c, full (a)\ones (n,1), 10e-10); - -%!testif HAVE_CXSPARSE -%! n = 20; d = 0.2; -%! ## initialize generators to make behavior reproducible -%! rand ("state", 42); -%! randn ("state", 42); -%! a = sprandn (n,n,d) + speye (n,n); -%! b = randn (n,2); -%! [c,r] = qr (a, b); -%! assert (r\c, full (a)\b, 10e-10); - -## Test under-determined systems!! -%!#testif HAVE_CXSPARSE -%! n = 20; d = 0.2; -%! ## initialize generators to make behavior reproducible -%! rand ("state", 42); -%! randn ("state", 42); -%! a = sprandn (n,n+1,d) + speye (n,n+1); -%! b = randn (n,2); -%! [c,r] = qr (a, b); -%! assert (r\c, full (a)\b, 10e-10); - -%!testif HAVE_CXSPARSE -%! n = 20; d = 0.2; -%! ## initialize generators to make behavior reproducible -%! rand ("state", 42); -%! randn ("state", 42); -%! a = 1i*sprandn (n,n,d) + speye (n,n); -%! r = qr (a); -%! assert (r'*r,a'*a,1e-10); - -%!testif HAVE_COLAMD -%! n = 20; d = 0.2; -%! ## initialize generators to make behavior reproducible -%! rand ("state", 42); -%! randn ("state", 42); -%! a = 1i*sprandn (n,n,d) + speye (n,n); -%! q = symamd (a); -%! a = a(q,q); -%! r = qr (a); -%! assert (r'*r, a'*a, 1e-10); - -%!testif HAVE_CXSPARSE -%! n = 20; d = 0.2; -%! ## initialize generators to make behavior reproducible -%! rand ("state", 42); -%! randn ("state", 42); -%! a = 1i*sprandn (n,n,d) + speye (n,n); -%! [c,r] = qr (a, ones (n,1)); -%! assert (r\c, full (a)\ones (n,1), 10e-10); - -%!testif HAVE_CXSPARSE -%! n = 20; d = 0.2; -%! ## initialize generators to make behavior reproducible -%! rand ("state", 42); -%! randn ("state", 42); -%! a = 1i*sprandn (n,n,d) + speye (n,n); -%! b = randn (n,2); -%! [c,r] = qr (a, b); -%! assert (r\c, full (a)\b, 10e-10); - -## Test under-determined systems!! -%!#testif HAVE_CXSPARSE -%! n = 20; d = 0.2; -%! ## initialize generators to make behavior reproducible -%! rand ("state", 42); -%! randn ("state", 42); -%! a = 1i*sprandn (n,n+1,d) + speye (n,n+1); -%! b = randn (n,2); -%! [c,r] = qr (a, b); -%! assert (r\c, full (a)\b, 10e-10); - -*/ - -static -bool check_qr_dims (const octave_value& q, const octave_value& r, - bool allow_ecf = false) -{ - octave_idx_type m = q.rows (); - octave_idx_type k = r.rows (); - octave_idx_type n = r.columns (); - return ((q.ndims () == 2 && r.ndims () == 2 && k == q.columns ()) - && (m == k || (allow_ecf && k == n && k < m))); -} - -static -bool check_index (const octave_value& i, bool vector_allowed = false) -{ - return ((i.isreal () || i.isinteger ()) - && (i.is_scalar_type () || vector_allowed)); -} - -DEFUN_DLD (qrupdate, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {[@var{Q1}, @var{R1}] =} qrupdate (@var{Q}, @var{R}, @var{u}, @var{v}) -Update a QR factorization given update vectors or matrices. - -Given a QR@tie{}factorization of a real or complex matrix -@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and -@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization of -@w{@var{A} + @var{u}*@var{v}'}, where @var{u} and @var{v} are column vectors -(rank-1 update) or matrices with equal number of columns -(rank-k update). Notice that the latter case is done as a sequence of -rank-1 updates; thus, for k large enough, it will be both faster and more -accurate to recompute the factorization from scratch. - -The QR@tie{}factorization supplied may be either full (Q is square) or -economized (R is square). - -@seealso{qr, qrinsert, qrdelete, qrshift} -@end deftypefn */) -{ - octave_value_list retval; - - if (args.length () != 4) - print_usage (); - - octave_value argq = args(0); - octave_value argr = args(1); - octave_value argu = args(2); - octave_value argv = args(3); - - if (! argq.isnumeric () || ! argr.isnumeric () - || ! argu.isnumeric () || ! argv.isnumeric ()) - print_usage (); - - if (! check_qr_dims (argq, argr, true)) - error ("qrupdate: Q and R dimensions don't match"); - - if (argq.isreal () && argr.isreal () && argu.isreal () - && argv.isreal ()) - { - // all real case - if (argq.is_single_type () || argr.is_single_type () - || argu.is_single_type () || argv.is_single_type ()) - { - FloatMatrix Q = argq.float_matrix_value (); - FloatMatrix R = argr.float_matrix_value (); - FloatMatrix u = argu.float_matrix_value (); - FloatMatrix v = argv.float_matrix_value (); - - octave::math::qr<FloatMatrix> fact (Q, R); - fact.update (u, v); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - else - { - Matrix Q = argq.matrix_value (); - Matrix R = argr.matrix_value (); - Matrix u = argu.matrix_value (); - Matrix v = argv.matrix_value (); - - octave::math::qr<Matrix> fact (Q, R); - fact.update (u, v); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - } - else - { - // complex case - if (argq.is_single_type () || argr.is_single_type () - || argu.is_single_type () || argv.is_single_type ()) - { - FloatComplexMatrix Q = argq.float_complex_matrix_value (); - FloatComplexMatrix R = argr.float_complex_matrix_value (); - FloatComplexMatrix u = argu.float_complex_matrix_value (); - FloatComplexMatrix v = argv.float_complex_matrix_value (); - - octave::math::qr<FloatComplexMatrix> fact (Q, R); - fact.update (u, v); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - else - { - ComplexMatrix Q = argq.complex_matrix_value (); - ComplexMatrix R = argr.complex_matrix_value (); - ComplexMatrix u = argu.complex_matrix_value (); - ComplexMatrix v = argv.complex_matrix_value (); - - octave::math::qr<ComplexMatrix> fact (Q, R); - fact.update (u, v); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - } - - return retval; -} - -/* -%!shared A, u, v, Ac, uc, vc -%! A = [0.091364 0.613038 0.999083; -%! 0.594638 0.425302 0.603537; -%! 0.383594 0.291238 0.085574; -%! 0.265712 0.268003 0.238409; -%! 0.669966 0.743851 0.445057 ]; -%! -%! u = [0.85082; -%! 0.76426; -%! 0.42883; -%! 0.53010; -%! 0.80683 ]; -%! -%! v = [0.98810; -%! 0.24295; -%! 0.43167 ]; -%! -%! Ac = [0.620405 + 0.956953i 0.480013 + 0.048806i 0.402627 + 0.338171i; -%! 0.589077 + 0.658457i 0.013205 + 0.279323i 0.229284 + 0.721929i; -%! 0.092758 + 0.345687i 0.928679 + 0.241052i 0.764536 + 0.832406i; -%! 0.912098 + 0.721024i 0.049018 + 0.269452i 0.730029 + 0.796517i; -%! 0.112849 + 0.603871i 0.486352 + 0.142337i 0.355646 + 0.151496i ]; -%! -%! uc = [0.20351 + 0.05401i; -%! 0.13141 + 0.43708i; -%! 0.29808 + 0.08789i; -%! 0.69821 + 0.38844i; -%! 0.74871 + 0.25821i ]; -%! -%! vc = [0.85839 + 0.29468i; -%! 0.20820 + 0.93090i; -%! 0.86184 + 0.34689i ]; -%! - -%!test -%! [Q,R] = qr (A); -%! [Q,R] = qrupdate (Q, R, u, v); -%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps); -%! assert (norm (vec (triu (R)-R), Inf) == 0); -%! assert (norm (vec (Q*R - A - u*v'), Inf) < norm (A)*1e1*eps); -%! -%!test -%! [Q,R] = qr (Ac); -%! [Q,R] = qrupdate (Q, R, uc, vc); -%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps); -%! assert (norm (vec (triu (R)-R), Inf) == 0); -%! assert (norm (vec (Q*R - Ac - uc*vc'), Inf) < norm (Ac)*1e1*eps); - -%!test -%! [Q,R] = qr (single (A)); -%! [Q,R] = qrupdate (Q, R, single (u), single (v)); -%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single")); -%! assert (norm (vec (triu (R)-R), Inf) == 0); -%! assert (norm (vec (Q*R - single (A) - single (u)*single (v)'), Inf) < norm (single (A))*1e1*eps ("single")); -%! -%!test -%! [Q,R] = qr (single (Ac)); -%! [Q,R] = qrupdate (Q, R, single (uc), single (vc)); -%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single")); -%! assert (norm (vec (triu (R)-R), Inf) == 0); -%! assert (norm (vec (Q*R - single (Ac) - single (uc)*single (vc)'), Inf) < norm (single (Ac))*1e1*eps ("single")); -*/ - -DEFUN_DLD (qrinsert, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {[@var{Q1}, @var{R1}] =} qrinsert (@var{Q}, @var{R}, @var{j}, @var{x}, @var{orient}) -Update a QR factorization given a row or column to insert in the original -factored matrix. - - -Given a QR@tie{}factorization of a real or complex matrix -@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and -@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization of -@w{[A(:,1:j-1) x A(:,j:n)]}, where @var{u} is a column vector to be inserted -into @var{A} (if @var{orient} is @qcode{"col"}), or the -QR@tie{}factorization of @w{[A(1:j-1,:);x;A(:,j:n)]}, where @var{x} is a row -vector to be inserted into @var{A} (if @var{orient} is @qcode{"row"}). - -The default value of @var{orient} is @qcode{"col"}. If @var{orient} is -@qcode{"col"}, @var{u} may be a matrix and @var{j} an index vector -resulting in the QR@tie{}factorization of a matrix @var{B} such that -@w{B(:,@var{j})} gives @var{u} and @w{B(:,@var{j}) = []} gives @var{A}. -Notice that the latter case is done as a sequence of k insertions; -thus, for k large enough, it will be both faster and more accurate to -recompute the factorization from scratch. - -If @var{orient} is @qcode{"col"}, the QR@tie{}factorization supplied may -be either full (Q is square) or economized (R is square). - -If @var{orient} is @qcode{"row"}, full factorization is needed. -@seealso{qr, qrupdate, qrdelete, qrshift} -@end deftypefn */) -{ - int nargin = args.length (); - - if (nargin < 4 || nargin > 5) - print_usage (); - - octave_value argq = args(0); - octave_value argr = args(1); - octave_value argj = args(2); - octave_value argx = args(3); - - if (! argq.isnumeric () || ! argr.isnumeric () - || ! argx.isnumeric () - || (nargin > 4 && ! args(4).is_string ())) - print_usage (); - - std::string orient = (nargin < 5) ? "col" : args(4).string_value (); - bool col = (orient == "col"); - - if (! col && orient != "row") - error (R"(qrinsert: ORIENT must be "col" or "row")"); - - if (! check_qr_dims (argq, argr, col) || (! col && argx.rows () != 1)) - error ("qrinsert: dimension mismatch"); - - if (! check_index (argj, col)) - error ("qrinsert: invalid index J"); - - octave_value_list retval; - - MArray<octave_idx_type> j = argj.octave_idx_type_vector_value (); - - octave_idx_type one = 1; - - if (argq.isreal () && argr.isreal () && argx.isreal ()) - { - // real case - if (argq.is_single_type () || argr.is_single_type () - || argx.is_single_type ()) - { - FloatMatrix Q = argq.float_matrix_value (); - FloatMatrix R = argr.float_matrix_value (); - FloatMatrix x = argx.float_matrix_value (); - - octave::math::qr<FloatMatrix> fact (Q, R); - - if (col) - fact.insert_col (x, j-one); - else - fact.insert_row (x.row (0), j(0)-one); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - else - { - Matrix Q = argq.matrix_value (); - Matrix R = argr.matrix_value (); - Matrix x = argx.matrix_value (); - - octave::math::qr<Matrix> fact (Q, R); - - if (col) - fact.insert_col (x, j-one); - else - fact.insert_row (x.row (0), j(0)-one); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - } - else - { - // complex case - if (argq.is_single_type () || argr.is_single_type () - || argx.is_single_type ()) - { - FloatComplexMatrix Q = argq.float_complex_matrix_value (); - FloatComplexMatrix R = argr.float_complex_matrix_value (); - FloatComplexMatrix x = argx.float_complex_matrix_value (); - - octave::math::qr<FloatComplexMatrix> fact (Q, R); - - if (col) - fact.insert_col (x, j-one); - else - fact.insert_row (x.row (0), j(0)-one); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - else - { - ComplexMatrix Q = argq.complex_matrix_value (); - ComplexMatrix R = argr.complex_matrix_value (); - ComplexMatrix x = argx.complex_matrix_value (); - - octave::math::qr<ComplexMatrix> fact (Q, R); - - if (col) - fact.insert_col (x, j-one); - else - fact.insert_row (x.row (0), j(0)-one); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - } - - return retval; -} - -/* -%!test -%! [Q,R] = qr (A); -%! [Q,R] = qrinsert (Q, R, 3, u); -%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - [A(:,1:2) u A(:,3)]), Inf) < norm (A)*1e1*eps); -%!test -%! [Q,R] = qr (Ac); -%! [Q,R] = qrinsert (Q, R, 3, uc); -%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 1e1*eps); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - [Ac(:,1:2) uc Ac(:,3)]), Inf) < norm (Ac)*1e1*eps); -%!test -%! x = [0.85082 0.76426 0.42883 ]; -%! -%! [Q,R] = qr (A); -%! [Q,R] = qrinsert (Q, R, 3, x, "row"); -%! assert (norm (vec (Q'*Q - eye (6)), Inf) < 1e1*eps); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - [A(1:2,:);x;A(3:5,:)]), Inf) < norm (A)*1e1*eps); -%!test -%! x = [0.20351 + 0.05401i 0.13141 + 0.43708i 0.29808 + 0.08789i ]; -%! -%! [Q,R] = qr (Ac); -%! [Q,R] = qrinsert (Q, R, 3, x, "row"); -%! assert (norm (vec (Q'*Q - eye (6)), Inf) < 1e1*eps); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - [Ac(1:2,:);x;Ac(3:5,:)]), Inf) < norm (Ac)*1e1*eps); - -%!test -%! [Q,R] = qr (single (A)); -%! [Q,R] = qrinsert (Q, R, 3, single (u)); -%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single")); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - single ([A(:,1:2) u A(:,3)])), Inf) < norm (single (A))*1e1*eps ("single")); -%!test -%! [Q,R] = qr (single (Ac)); -%! [Q,R] = qrinsert (Q, R, 3, single (uc)); -%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single")); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - single ([Ac(:,1:2) uc Ac(:,3)])), Inf) < norm (single (Ac))*1e1*eps ("single")); -%!test -%! x = single ([0.85082 0.76426 0.42883 ]); -%! -%! [Q,R] = qr (single (A)); -%! [Q,R] = qrinsert (Q, R, 3, x, "row"); -%! assert (norm (vec (Q'*Q - eye (6,"single")), Inf) < 1e1*eps ("single")); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - single ([A(1:2,:);x;A(3:5,:)])), Inf) < norm (single (A))*1e1*eps ("single")); -%!test -%! x = single ([0.20351 + 0.05401i 0.13141 + 0.43708i 0.29808 + 0.08789i ]); -%! -%! [Q,R] = qr (single (Ac)); -%! [Q,R] = qrinsert (Q, R, 3, x, "row"); -%! assert (norm (vec (Q'*Q - eye (6,"single")), Inf) < 1e1*eps ("single")); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - single ([Ac(1:2,:);x;Ac(3:5,:)])), Inf) < norm (single (Ac))*1e1*eps ("single")); -*/ - -DEFUN_DLD (qrdelete, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {[@var{Q1}, @var{R1}] =} qrdelete (@var{Q}, @var{R}, @var{j}, @var{orient}) -Update a QR factorization given a row or column to delete from the original -factored matrix. - -Given a QR@tie{}factorization of a real or complex matrix -@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and -@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization of -@w{[A(:,1:j-1), U, A(:,j:n)]}, -where @var{u} is a column vector to be inserted into @var{A} -(if @var{orient} is @qcode{"col"}), -or the QR@tie{}factorization of @w{[A(1:j-1,:);X;A(:,j:n)]}, -where @var{x} is a row @var{orient} is @qcode{"row"}). -The default value of @var{orient} is @qcode{"col"}. - -If @var{orient} is @qcode{"col"}, @var{j} may be an index vector -resulting in the QR@tie{}factorization of a matrix @var{B} such that -@w{A(:,@var{j}) = []} gives @var{B}. Notice that the latter case is done as -a sequence of k deletions; thus, for k large enough, it will be both faster -and more accurate to recompute the factorization from scratch. - -If @var{orient} is @qcode{"col"}, the QR@tie{}factorization supplied may -be either full (Q is square) or economized (R is square). - -If @var{orient} is @qcode{"row"}, full factorization is needed. -@seealso{qr, qrupdate, qrinsert, qrshift} -@end deftypefn */) -{ - int nargin = args.length (); - - if (nargin < 3 || nargin > 4) - print_usage (); - - octave_value argq = args(0); - octave_value argr = args(1); - octave_value argj = args(2); - - if (! argq.isnumeric () || ! argr.isnumeric () - || (nargin > 3 && ! args(3).is_string ())) - print_usage (); - - std::string orient = (nargin < 4) ? "col" : args(3).string_value (); - bool col = orient == "col"; - - if (! col && orient != "row") - error (R"(qrdelete: ORIENT must be "col" or "row")"); - - if (! check_qr_dims (argq, argr, col)) - error ("qrdelete: dimension mismatch"); - - MArray<octave_idx_type> j = argj.octave_idx_type_vector_value (); - if (! check_index (argj, col)) - error ("qrdelete: invalid index J"); - - octave_value_list retval; - - octave_idx_type one = 1; - - if (argq.isreal () && argr.isreal ()) - { - // real case - if (argq.is_single_type () || argr.is_single_type ()) - { - FloatMatrix Q = argq.float_matrix_value (); - FloatMatrix R = argr.float_matrix_value (); - - octave::math::qr<FloatMatrix> fact (Q, R); - - if (col) - fact.delete_col (j-one); - else - fact.delete_row (j(0)-one); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - else - { - Matrix Q = argq.matrix_value (); - Matrix R = argr.matrix_value (); - - octave::math::qr<Matrix> fact (Q, R); - - if (col) - fact.delete_col (j-one); - else - fact.delete_row (j(0)-one); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - } - else - { - // complex case - if (argq.is_single_type () || argr.is_single_type ()) - { - FloatComplexMatrix Q = argq.float_complex_matrix_value (); - FloatComplexMatrix R = argr.float_complex_matrix_value (); - - octave::math::qr<FloatComplexMatrix> fact (Q, R); - - if (col) - fact.delete_col (j-one); - else - fact.delete_row (j(0)-one); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - else - { - ComplexMatrix Q = argq.complex_matrix_value (); - ComplexMatrix R = argr.complex_matrix_value (); - - octave::math::qr<ComplexMatrix> fact (Q, R); - - if (col) - fact.delete_col (j-one); - else - fact.delete_row (j(0)-one); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - } - - return retval; -} - -/* -%!test -%! AA = [0.091364 0.613038 0.027504 0.999083; -%! 0.594638 0.425302 0.562834 0.603537; -%! 0.383594 0.291238 0.742073 0.085574; -%! 0.265712 0.268003 0.783553 0.238409; -%! 0.669966 0.743851 0.457255 0.445057 ]; -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrdelete (Q, R, 3); -%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 16*eps); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps); -%! -%!test -%! AA = [0.364554 + 0.993117i 0.669818 + 0.510234i 0.426568 + 0.041337i 0.847051 + 0.233291i; -%! 0.049600 + 0.242783i 0.448946 + 0.484022i 0.141155 + 0.074420i 0.446746 + 0.392706i; -%! 0.581922 + 0.657416i 0.581460 + 0.030016i 0.219909 + 0.447288i 0.201144 + 0.069132i; -%! 0.694986 + 0.000571i 0.682327 + 0.841712i 0.807537 + 0.166086i 0.192767 + 0.358098i; -%! 0.945002 + 0.066788i 0.350492 + 0.642638i 0.579629 + 0.048102i 0.600170 + 0.636938i ] * I; -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrdelete (Q, R, 3); -%! assert (norm (vec (Q'*Q - eye (5)), Inf) < 16*eps); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps); -%! -%!test -%! AA = [0.091364 0.613038 0.027504 0.999083; -%! 0.594638 0.425302 0.562834 0.603537; -%! 0.383594 0.291238 0.742073 0.085574; -%! 0.265712 0.268003 0.783553 0.238409; -%! 0.669966 0.743851 0.457255 0.445057 ]; -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrdelete (Q, R, 3, "row"); -%! assert (norm (vec (Q'*Q - eye (4)), Inf) < 1e1*eps); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps); -%! -%!test -%! AA = [0.364554 + 0.993117i 0.669818 + 0.510234i 0.426568 + 0.041337i 0.847051 + 0.233291i; -%! 0.049600 + 0.242783i 0.448946 + 0.484022i 0.141155 + 0.074420i 0.446746 + 0.392706i; -%! 0.581922 + 0.657416i 0.581460 + 0.030016i 0.219909 + 0.447288i 0.201144 + 0.069132i; -%! 0.694986 + 0.000571i 0.682327 + 0.841712i 0.807537 + 0.166086i 0.192767 + 0.358098i; -%! 0.945002 + 0.066788i 0.350492 + 0.642638i 0.579629 + 0.048102i 0.600170 + 0.636938i ] * I; -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrdelete (Q, R, 3, "row"); -%! assert (norm (vec (Q'*Q - eye (4)), Inf) < 1e1*eps); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps); - -%!test -%! AA = single ([0.091364 0.613038 0.027504 0.999083; -%! 0.594638 0.425302 0.562834 0.603537; -%! 0.383594 0.291238 0.742073 0.085574; -%! 0.265712 0.268003 0.783553 0.238409; -%! 0.669966 0.743851 0.457255 0.445057 ]); -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrdelete (Q, R, 3); -%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single")); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps ("single")); -%! -%!test -%! AA = single ([0.364554 + 0.993117i 0.669818 + 0.510234i 0.426568 + 0.041337i 0.847051 + 0.233291i; -%! 0.049600 + 0.242783i 0.448946 + 0.484022i 0.141155 + 0.074420i 0.446746 + 0.392706i; -%! 0.581922 + 0.657416i 0.581460 + 0.030016i 0.219909 + 0.447288i 0.201144 + 0.069132i; -%! 0.694986 + 0.000571i 0.682327 + 0.841712i 0.807537 + 0.166086i 0.192767 + 0.358098i; -%! 0.945002 + 0.066788i 0.350492 + 0.642638i 0.579629 + 0.048102i 0.600170 + 0.636938i ]) * I; -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrdelete (Q, R, 3); -%! assert (norm (vec (Q'*Q - eye (5,"single")), Inf) < 1e1*eps ("single")); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - [AA(:,1:2) AA(:,4)]), Inf) < norm (AA)*1e1*eps ("single")); -%! -%!test -%! AA = single ([0.091364 0.613038 0.027504 0.999083; -%! 0.594638 0.425302 0.562834 0.603537; -%! 0.383594 0.291238 0.742073 0.085574; -%! 0.265712 0.268003 0.783553 0.238409; -%! 0.669966 0.743851 0.457255 0.445057 ]); -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrdelete (Q, R, 3, "row"); -%! assert (norm (vec (Q'*Q - eye (4,"single")), Inf) < 1.5e1*eps ("single")); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps ("single")); -%!testif HAVE_QRUPDATE -%! ## Same test as above but with more precicision -%! AA = single ([0.091364 0.613038 0.027504 0.999083; -%! 0.594638 0.425302 0.562834 0.603537; -%! 0.383594 0.291238 0.742073 0.085574; -%! 0.265712 0.268003 0.783553 0.238409; -%! 0.669966 0.743851 0.457255 0.445057 ]); -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrdelete (Q, R, 3, "row"); -%! assert (norm (vec (Q'*Q - eye (4,"single")), Inf) < 1e1*eps ("single")); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps ("single")); -%! -%!test -%! AA = single ([0.364554 + 0.993117i 0.669818 + 0.510234i 0.426568 + 0.041337i 0.847051 + 0.233291i; -%! 0.049600 + 0.242783i 0.448946 + 0.484022i 0.141155 + 0.074420i 0.446746 + 0.392706i; -%! 0.581922 + 0.657416i 0.581460 + 0.030016i 0.219909 + 0.447288i 0.201144 + 0.069132i; -%! 0.694986 + 0.000571i 0.682327 + 0.841712i 0.807537 + 0.166086i 0.192767 + 0.358098i; -%! 0.945002 + 0.066788i 0.350492 + 0.642638i 0.579629 + 0.048102i 0.600170 + 0.636938i ]) * I; -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrdelete (Q, R, 3, "row"); -%! assert (norm (vec (Q'*Q - eye (4,"single")), Inf) < 1e1*eps ("single")); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - [AA(1:2,:);AA(4:5,:)]), Inf) < norm (AA)*1e1*eps ("single")); -*/ - -DEFUN_DLD (qrshift, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {[@var{Q1}, @var{R1}] =} qrshift (@var{Q}, @var{R}, @var{i}, @var{j}) -Update a QR factorization given a range of columns to shift in the original -factored matrix. - -Given a QR@tie{}factorization of a real or complex matrix -@w{@var{A} = @var{Q}*@var{R}}, @var{Q}@tie{}unitary and -@var{R}@tie{}upper trapezoidal, return the QR@tie{}factorization -of @w{@var{A}(:,p)}, where @w{p} is the permutation @* -@code{p = [1:i-1, shift(i:j, 1), j+1:n]} if @w{@var{i} < @var{j}} @* - or @* -@code{p = [1:j-1, shift(j:i,-1), i+1:n]} if @w{@var{j} < @var{i}}. @* - -@seealso{qr, qrupdate, qrinsert, qrdelete} -@end deftypefn */) -{ - if (args.length () != 4) - print_usage (); - - octave_value argq = args(0); - octave_value argr = args(1); - octave_value argi = args(2); - octave_value argj = args(3); - - if (! argq.isnumeric () || ! argr.isnumeric ()) - print_usage (); - - if (! check_qr_dims (argq, argr, true)) - error ("qrshift: dimensions mismatch"); - - octave_idx_type i = argi.idx_type_value (); - octave_idx_type j = argj.idx_type_value (); - - if (! check_index (argi) || ! check_index (argj)) - error ("qrshift: invalid index I or J"); - - octave_value_list retval; - - if (argq.isreal () && argr.isreal ()) - { - // all real case - if (argq.is_single_type () - && argr.is_single_type ()) - { - FloatMatrix Q = argq.float_matrix_value (); - FloatMatrix R = argr.float_matrix_value (); - - octave::math::qr<FloatMatrix> fact (Q, R); - fact.shift_cols (i-1, j-1); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - else - { - Matrix Q = argq.matrix_value (); - Matrix R = argr.matrix_value (); - - octave::math::qr<Matrix> fact (Q, R); - fact.shift_cols (i-1, j-1); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - } - else - { - // complex case - if (argq.is_single_type () - && argr.is_single_type ()) - { - FloatComplexMatrix Q = argq.float_complex_matrix_value (); - FloatComplexMatrix R = argr.float_complex_matrix_value (); - - octave::math::qr<FloatComplexMatrix> fact (Q, R); - fact.shift_cols (i-1, j-1); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - else - { - ComplexMatrix Q = argq.complex_matrix_value (); - ComplexMatrix R = argr.complex_matrix_value (); - - octave::math::qr<ComplexMatrix> fact (Q, R); - fact.shift_cols (i-1, j-1); - - retval = ovl (fact.Q (), get_qr_r (fact)); - } - } - - return retval; -} - -/* -%!test -%! AA = A.'; -%! i = 2; j = 4; p = [1:i-1, shift(i:j,-1), j+1:5]; -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrshift (Q, R, i, j); -%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps); -%! -%! j = 2; i = 4; p = [1:j-1, shift(j:i,+1), i+1:5]; -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrshift (Q, R, i, j); -%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps); -%! -%!test -%! AA = Ac.'; -%! i = 2; j = 4; p = [1:i-1, shift(i:j,-1), j+1:5]; -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrshift (Q, R, i, j); -%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps); -%! -%! j = 2; i = 4; p = [1:j-1, shift(j:i,+1), i+1:5]; -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrshift (Q, R, i, j); -%! assert (norm (vec (Q'*Q - eye (3)), Inf) < 1e1*eps); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps); - -%!test -%! AA = single (A).'; -%! i = 2; j = 4; p = [1:i-1, shift(i:j,-1), j+1:5]; -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrshift (Q, R, i, j); -%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single")); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single")); -%! -%! j = 2; i = 4; p = [1:j-1, shift(j:i,+1), i+1:5]; -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrshift (Q, R, i, j); -%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single")); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single")); -%! -%!test -%! AA = single (Ac).'; -%! i = 2; j = 4; p = [1:i-1, shift(i:j,-1), j+1:5]; -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrshift (Q, R, i, j); -%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single")); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single")); -%! -%! j = 2; i = 4; p = [1:j-1, shift(j:i,+1), i+1:5]; -%! -%! [Q,R] = qr (AA); -%! [Q,R] = qrshift (Q, R, i, j); -%! assert (norm (vec (Q'*Q - eye (3,"single")), Inf) < 1e1*eps ("single")); -%! assert (norm (vec (triu (R) - R), Inf) == 0); -%! assert (norm (vec (Q*R - AA(:,p)), Inf) < norm (AA)*1e1*eps ("single")); -*/
--- a/libinterp/dldfcn/symbfact.cc Sat Jan 25 15:26:07 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,431 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 1998-2020 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or <https://octave.org/copyright/>. -// -// This file is part of Octave. -// -// 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 3 of the License, 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 Octave; see the file COPYING. If not, see -// <https://www.gnu.org/licenses/>. -// -//////////////////////////////////////////////////////////////////////// - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include <cmath> - -#include <algorithm> -#include <string> - -#include "CSparse.h" -#include "boolSparse.h" -#include "dColVector.h" -#include "dSparse.h" -#include "oct-locbuf.h" -#include "oct-sparse.h" -#include "oct-spparms.h" -#include "sparse-util.h" - -#include "defun-dld.h" -#include "error.h" -#include "errwarn.h" -#include "ovl.h" -#include "parse.h" -#include "utils.h" - -DEFMETHOD_DLD (symbfact, interp, args, nargout, - doc: /* -*- texinfo -*- -@deftypefn {} {[@var{count}, @var{h}, @var{parent}, @var{post}, @var{R}] =} symbfact (@var{S}) -@deftypefnx {} {[@dots{}] =} symbfact (@var{S}, @var{typ}) -@deftypefnx {} {[@dots{}] =} symbfact (@var{S}, @var{typ}, @var{mode}) - -Perform a symbolic factorization analysis of the sparse matrix @var{S}. - -The input variables are - -@table @var -@item S -@var{S} is a real or complex sparse matrix. - -@item typ -Is the type of the factorization and can be one of - -@table @asis -@item @qcode{"sym"} (default) -Factorize @var{S}. Assumes @var{S} is symmetric and uses the upper -triangular portion of the matrix. - -@item @qcode{"col"} -Factorize @tcode{@var{S}' * @var{S}}. - -@item @qcode{"row"} -Factorize @tcode{@var{S} * @var{S}'}. - -@item @qcode{"lo"} -Factorize @tcode{@var{S}'}. Assumes @var{S} is symmetric and uses the lower -triangular portion of the matrix. -@end table - -@item mode -When @var{mode} is unspecified return the Cholesky@tie{}factorization for -@var{R}. If @var{mode} is @qcode{"lower"} or @qcode{"L"} then return -the conjugate transpose @tcode{@var{R}'} which is a lower triangular factor. -The conjugate transpose version is faster and uses less memory, but still -returns the same values for all other outputs: @var{count}, @var{h}, -@var{parent}, and @var{post}. -@end table - -The output variables are: - -@table @var -@item count -The row counts of the Cholesky@tie{}factorization as determined by -@var{typ}. The computational difficulty of performing the true -factorization using @code{chol} is @code{sum (@var{count} .^ 2)}. - -@item h -The height of the elimination tree. - -@item parent -The elimination tree itself. - -@item post -A sparse boolean matrix whose structure is that of the -Cholesky@tie{}factorization as determined by @var{typ}. -@end table -@seealso{chol, etree, treelayout} -@end deftypefn */) -{ -#if defined (HAVE_CHOLMOD) - - int nargin = args.length (); - - if (nargin < 1 || nargin > 3) - print_usage (); - - octave_value_list retval; - - double dummy; - cholmod_sparse Astore; - cholmod_sparse *A = &Astore; - A->packed = true; - A->sorted = true; - A->nz = nullptr; -#if defined (OCTAVE_ENABLE_64) - A->itype = CHOLMOD_LONG; -#else - A->itype = CHOLMOD_INT; -#endif - A->dtype = CHOLMOD_DOUBLE; - A->stype = 1; - A->x = &dummy; - - if (args(0).isreal ()) - { - const SparseMatrix a = args(0).sparse_matrix_value (); - A->nrow = a.rows (); - A->ncol = a.cols (); - A->p = a.cidx (); - A->i = a.ridx (); - A->nzmax = a.nnz (); - A->xtype = CHOLMOD_REAL; - - if (a.rows () > 0 && a.cols () > 0) - A->x = a.data (); - } - else if (args(0).iscomplex ()) - { - const SparseComplexMatrix a = args(0).sparse_complex_matrix_value (); - A->nrow = a.rows (); - A->ncol = a.cols (); - A->p = a.cidx (); - A->i = a.ridx (); - A->nzmax = a.nnz (); - A->xtype = CHOLMOD_COMPLEX; - - if (a.rows () > 0 && a.cols () > 0) - A->x = a.data (); - } - else - err_wrong_type_arg ("symbfact", args(0)); - - bool coletree = false; - octave_idx_type n = A->nrow; - - if (nargin > 1) - { - std::string str = args(1).xstring_value ("TYP must be a string"); - // FIXME: The input validation could be improved to use strncmp - char ch; - ch = tolower (str[0]); - if (ch == 'r') // 'row' - A->stype = 0; - else if (ch == 'c') // 'col' - { - n = A->ncol; - coletree = true; - A->stype = 0; - } - else if (ch == 's') // 'sym' (default) - A->stype = 1; - else if (ch == 'l') // 'lo' - A->stype = -1; - else - error (R"(symbfact: unrecognized TYP "%s")", str.c_str ()); - } - - if (nargin == 3) - { - std::string str = args(2).xstring_value ("MODE must be a string"); - // FIXME: The input validation could be improved to use strncmp - char ch; - ch = toupper (str[0]); - if (ch != 'L') - error (R"(symbfact: unrecognized MODE "%s")", str.c_str ()); - } - - if (A->stype && A->nrow != A->ncol) - err_square_matrix_required ("symbfact", "S"); - - OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, Parent, n); - OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, Post, n); - OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, ColCount, n); - OCTAVE_LOCAL_BUFFER (octave::suitesparse_integer, First, n); - OCTAVE_LOCAL_BUFFER (octave_idx_type, Level, n); - - cholmod_common Common; - cholmod_common *cm = &Common; - CHOLMOD_NAME(start) (cm); - - // Lock the function to not loose the SuiteSparse_config structure - interp.mlock (); - - double spu = octave_sparse_params::get_key ("spumoni"); - if (spu == 0.0) - { - cm->print = -1; - SUITESPARSE_ASSIGN_FPTR (printf_func, cm->print_function, nullptr); - } - else - { - cm->print = static_cast<int> (spu) + 2; - SUITESPARSE_ASSIGN_FPTR (printf_func, cm->print_function, &SparseCholPrint); - } - - cm->error_handler = &SparseCholError; - SUITESPARSE_ASSIGN_FPTR2 (divcomplex_func, cm->complex_divide, divcomplex); - SUITESPARSE_ASSIGN_FPTR2 (hypot_func, cm->hypotenuse, hypot); - - cholmod_sparse *F = CHOLMOD_NAME(transpose) (A, 0, cm); - cholmod_sparse *Aup, *Alo; - - if (A->stype == 1 || coletree) - { - Aup = A; - Alo = F; - } - else - { - Aup = F; - Alo = A; - } - - CHOLMOD_NAME(etree) (Aup, Parent, cm); - - ColumnVector tmp (n); // Declaration must precede any goto cleanup. - std::string err_msg; - - if (cm->status < CHOLMOD_OK) - { - err_msg = "symbfact: matrix corrupted"; - goto cleanup; - } - - if (CHOLMOD_NAME(postorder) (Parent, n, nullptr, Post, cm) != n) - { - err_msg = "symbfact: postorder failed"; - goto cleanup; - } - - CHOLMOD_NAME(rowcolcounts) (Alo, nullptr, 0, Parent, Post, nullptr, ColCount, - First, octave::to_suitesparse_intptr (Level), cm); - - if (cm->status < CHOLMOD_OK) - { - err_msg = "symbfact: matrix corrupted"; - goto cleanup; - } - - if (nargout > 4) - { - cholmod_sparse *A1, *A2; - - if (A->stype == 1) - { - A1 = A; - A2 = nullptr; - } - else if (A->stype == -1) - { - A1 = F; - A2 = nullptr; - } - else if (coletree) - { - A1 = F; - A2 = A; - } - else - { - A1 = A; - A2 = F; - } - - // count the total number of entries in L - octave_idx_type lnz = 0; - for (octave_idx_type j = 0 ; j < n ; j++) - lnz += ColCount[j]; - - // allocate the output matrix L (pattern-only) - SparseBoolMatrix L (dim_vector (n, n), lnz); - - // initialize column pointers - lnz = 0; - for (octave_idx_type j = 0 ; j < n ; j++) - { - L.xcidx(j) = lnz; - lnz += ColCount[j]; - } - L.xcidx(n) = lnz; - - // create a copy of the column pointers - octave::suitesparse_integer *W = First; - for (octave_idx_type j = 0 ; j < n ; j++) - W[j] = L.xcidx (j); - - // get workspace for computing one row of L - cholmod_sparse *R - = CHOLMOD_NAME(allocate_sparse) (n, 1, n, false, true, - 0, CHOLMOD_PATTERN, cm); - octave_idx_type *Rp = static_cast<octave_idx_type *> (R->p); - octave_idx_type *Ri = static_cast<octave_idx_type *> (R->i); - - // compute L one row at a time - for (octave_idx_type k = 0 ; k < n ; k++) - { - // get the kth row of L and store in the columns of L - CHOLMOD_NAME(row_subtree) (A1, A2, k, Parent, R, cm); - for (octave_idx_type p = 0 ; p < Rp[1] ; p++) - L.xridx (W[Ri[p]]++) = k; - - // add the diagonal entry - L.xridx (W[k]++) = k; - } - - // free workspace - CHOLMOD_NAME(free_sparse) (&R, cm); - - // fill L with one's - std::fill_n (L.xdata (), lnz, true); - - // transpose L to get R, or leave as is - if (nargin < 3) - L = L.transpose (); - - retval(4) = L; - } - - if (nargout > 3) - { - for (octave_idx_type i = 0; i < n; i++) - tmp(i) = Post[i] + 1; - retval(3) = tmp; - } - - if (nargout > 2) - { - for (octave_idx_type i = 0; i < n; i++) - tmp(i) = Parent[i] + 1; - retval(2) = tmp; - } - - if (nargout > 1) - { - // compute the elimination tree height - octave_idx_type height = 0; - for (int i = 0 ; i < n ; i++) - height = std::max (height, Level[i]); - height++; - retval(1) = static_cast<double> (height); - } - - for (octave_idx_type i = 0; i < n; i++) - tmp(i) = ColCount[i]; - retval(0) = tmp; - -cleanup: - CHOLMOD_NAME(free_sparse) (&F, cm); - CHOLMOD_NAME(finish) (cm); - - if (! err_msg.empty ()) - error ("%s", err_msg.c_str ()); - - return retval; - -#else - - octave_unused_parameter (args); - octave_unused_parameter (nargout); - - err_disabled_feature ("symbfact", "CHOLMOD"); - -#endif -} - -/* -%!testif HAVE_CHOLMOD -%! A = sparse (magic (3)); -%! [count, h, parent, post, r] = symbfact (A); -%! assert (count, [3; 2; 1]); -%! assert (h, 3); -%! assert (parent, [2; 3; 0]); -%! assert (r, sparse (triu (true (3)))); - -%!testif HAVE_CHOLMOD -%! ## Test MODE "lower" -%! A = sparse (magic (3)); -%! [~, ~, ~, ~, l] = symbfact (A, "sym", "lower"); -%! assert (l, sparse (tril (true (3)))); - -%!testif HAVE_CHOLMOD <*42587> -%! ## singular matrix -%! A = sparse ([1 0 8;0 1 8;8 8 1]); -%! [count, h, parent, post, r] = symbfact (A); - -## Test input validation -%!testif HAVE_CHOLMOD -%! fail ("symbfact ()"); -%! fail ("symbfact (1,2,3,4)"); -%! fail ("symbfact ({1})", "wrong type argument 'cell'"); -%! fail ("symbfact (sparse (1), {1})", "TYP must be a string"); -%! fail ("symbfact (sparse (1), 'foobar')", 'unrecognized TYP "foobar"'); -%! fail ("symbfact (sparse (1), 'sym', {'L'})", "MODE must be a string"); -%! fail ('symbfact (sparse (1), "sym", "foobar")', 'unrecognized MODE "foobar"'); -%! fail ("symbfact (sparse ([1, 2; 3, 4; 5, 6]))", "S must be a square matrix"); - -*/
--- a/libinterp/dldfcn/symrcm.cc Sat Jan 25 15:26:07 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,705 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2007-2020 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or <https://octave.org/copyright/>. -// -// This file is part of Octave. -// -// 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 3 of the License, 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 Octave; see the file COPYING. If not, see -// <https://www.gnu.org/licenses/>. -// -//////////////////////////////////////////////////////////////////////// - -/* -An implementation of the Reverse Cuthill-McKee algorithm (symrcm) - -The implementation of this algorithm is based in the descriptions found in - -@INPROCEEDINGS{, - author = {E. Cuthill and J. McKee}, - title = {Reducing the Bandwidth of Sparse Symmetric Matrices}, - booktitle = {Proceedings of the 24th ACM National Conference}, - publisher = {Brandon Press}, - pages = {157 -- 172}, - location = {New Jersey}, - year = {1969} -} - -@BOOK{, - author = {Alan George and Joseph W. H. Liu}, - title = {Computer Solution of Large Sparse Positive Definite Systems}, - publisher = {Prentice Hall Series in Computational Mathematics}, - ISBN = {0-13-165274-5}, - year = {1981} -} - -The algorithm represents a heuristic approach to the NP-complete minimum -bandwidth problem. - -Written by Michael Weitzel <michael.weitzel@@uni-siegen.de> - <weitzel@@ldknet.org> -*/ - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include <algorithm> - -#include "CSparse.h" -#include "boolNDArray.h" -#include "dNDArray.h" -#include "dSparse.h" -#include "oct-locbuf.h" -#include "oct-sparse.h" -#include "quit.h" - -#include "defun-dld.h" -#include "errwarn.h" -#include "ov.h" -#include "ovl.h" - -// A node struct for the Cuthill-McKee algorithm -struct CMK_Node -{ - // the node's id (matrix row index) - octave_idx_type id; - // the node's degree - octave_idx_type deg; - // minimal distance to the root of the spanning tree - octave_idx_type dist; -}; - -// A simple queue. -// Queues Q have a fixed maximum size N (rows,cols of the matrix) and are -// stored in an array. qh and qt point to queue head and tail. - -// Enqueue operation (adds a node "o" at the tail) - -inline static void -Q_enq (CMK_Node *Q, octave_idx_type N, octave_idx_type& qt, const CMK_Node& o) -{ - Q[qt] = o; - qt = (qt + 1) % (N + 1); -} - -// Dequeue operation (removes a node from the head) - -inline static CMK_Node -Q_deq (CMK_Node * Q, octave_idx_type N, octave_idx_type& qh) -{ - CMK_Node r = Q[qh]; - qh = (qh + 1) % (N + 1); - return r; -} - -// Predicate (queue empty) -#define Q_empty(Q, N, qh, qt) ((qh) == (qt)) - -// A simple, array-based binary heap (used as a priority queue for nodes) - -// the left descendant of entry i -#define LEFT(i) (((i) << 1) + 1) // = (2*(i)+1) -// the right descendant of entry i -#define RIGHT(i) (((i) << 1) + 2) // = (2*(i)+2) -// the parent of entry i -#define PARENT(i) (((i) - 1) >> 1) // = floor(((i)-1)/2) - -// Builds a min-heap (the root contains the smallest element). A is an array -// with the graph's nodes, i is a starting position, size is the length of A. - -static void -H_heapify_min (CMK_Node *A, octave_idx_type i, octave_idx_type size) -{ - octave_idx_type j = i; - for (;;) - { - octave_idx_type l = LEFT(j); - octave_idx_type r = RIGHT(j); - - octave_idx_type smallest; - if (l < size && A[l].deg < A[j].deg) - smallest = l; - else - smallest = j; - - if (r < size && A[r].deg < A[smallest].deg) - smallest = r; - - if (smallest != j) - { - std::swap (A[j], A[smallest]); - j = smallest; - } - else - break; - } -} - -// Heap operation insert. Running time is O(log(n)) - -static void -H_insert (CMK_Node *H, octave_idx_type& h, const CMK_Node& o) -{ - octave_idx_type i = h++; - - H[i] = o; - - if (i == 0) - return; - do - { - octave_idx_type p = PARENT(i); - if (H[i].deg < H[p].deg) - { - std::swap (H[i], H[p]); - - i = p; - } - else - break; - } - while (i > 0); -} - -// Heap operation remove-min. Removes the smallest element in O(1) and -// reorganizes the heap optionally in O(log(n)) - -inline static CMK_Node -H_remove_min (CMK_Node *H, octave_idx_type& h, int reorg/*=1*/) -{ - CMK_Node r = H[0]; - H[0] = H[--h]; - if (reorg) - H_heapify_min (H, 0, h); - return r; -} - -// Predicate (heap empty) -#define H_empty(H, h) ((h) == 0) - -// Helper function for the Cuthill-McKee algorithm. Tries to determine a -// pseudo-peripheral node of the graph as starting node. - -static octave_idx_type -find_starting_node (octave_idx_type N, const octave_idx_type *ridx, - const octave_idx_type *cidx, const octave_idx_type *ridx2, - const octave_idx_type *cidx2, octave_idx_type *D, - octave_idx_type start) -{ - CMK_Node w; - - OCTAVE_LOCAL_BUFFER (CMK_Node, Q, N+1); - boolNDArray btmp (dim_vector (1, N), false); - bool *visit = btmp.fortran_vec (); - - octave_idx_type qh = 0; - octave_idx_type qt = 0; - CMK_Node x; - x.id = start; - x.deg = D[start]; - x.dist = 0; - Q_enq (Q, N, qt, x); - visit[start] = true; - - // distance level - octave_idx_type level = 0; - // current largest "eccentricity" - octave_idx_type max_dist = 0; - - for (;;) - { - while (! Q_empty (Q, N, qh, qt)) - { - CMK_Node v = Q_deq (Q, N, qh); - - if (v.dist > x.dist || (v.id != x.id && v.deg > x.deg)) - x = v; - - octave_idx_type i = v.id; - - // add all unvisited neighbors to the queue - octave_idx_type j1 = cidx[i]; - octave_idx_type j2 = cidx2[i]; - while (j1 < cidx[i+1] || j2 < cidx2[i+1]) - { - octave_quit (); - - if (j1 == cidx[i+1]) - { - octave_idx_type r2 = ridx2[j2++]; - if (! visit[r2]) - { - // the distance of node j is dist(i)+1 - w.id = r2; - w.deg = D[r2]; - w.dist = v.dist+1; - Q_enq (Q, N, qt, w); - visit[r2] = true; - - if (w.dist > level) - level = w.dist; - } - } - else if (j2 == cidx2[i+1]) - { - octave_idx_type r1 = ridx[j1++]; - if (! visit[r1]) - { - // the distance of node j is dist(i)+1 - w.id = r1; - w.deg = D[r1]; - w.dist = v.dist+1; - Q_enq (Q, N, qt, w); - visit[r1] = true; - - if (w.dist > level) - level = w.dist; - } - } - else - { - octave_idx_type r1 = ridx[j1]; - octave_idx_type r2 = ridx2[j2]; - if (r1 <= r2) - { - if (! visit[r1]) - { - w.id = r1; - w.deg = D[r1]; - w.dist = v.dist+1; - Q_enq (Q, N, qt, w); - visit[r1] = true; - - if (w.dist > level) - level = w.dist; - } - j1++; - if (r1 == r2) - j2++; - } - else - { - if (! visit[r2]) - { - w.id = r2; - w.deg = D[r2]; - w.dist = v.dist+1; - Q_enq (Q, N, qt, w); - visit[r2] = true; - - if (w.dist > level) - level = w.dist; - } - j2++; - } - } - } - } // finish of BFS - - if (max_dist < x.dist) - { - max_dist = x.dist; - - for (octave_idx_type i = 0; i < N; i++) - visit[i] = false; - - visit[x.id] = true; - x.dist = 0; - qt = qh = 0; - Q_enq (Q, N, qt, x); - } - else - break; - } - return x.id; -} - -// Calculates the node's degrees. This means counting the nonzero elements -// in the symmetric matrix' rows. This works for non-symmetric matrices -// as well. - -static octave_idx_type -calc_degrees (octave_idx_type N, const octave_idx_type *ridx, - const octave_idx_type *cidx, octave_idx_type *D) -{ - octave_idx_type max_deg = 0; - - for (octave_idx_type i = 0; i < N; i++) - D[i] = 0; - - for (octave_idx_type j = 0; j < N; j++) - { - for (octave_idx_type i = cidx[j]; i < cidx[j+1]; i++) - { - octave_quit (); - - octave_idx_type k = ridx[i]; - // there is a nonzero element (k,j) - D[k]++; - if (D[k] > max_deg) - max_deg = D[k]; - // if there is no element (j,k) there is one in - // the symmetric matrix: - if (k != j) - { - bool found = false; - for (octave_idx_type l = cidx[k]; l < cidx[k + 1]; l++) - { - octave_quit (); - - if (ridx[l] == j) - { - found = true; - break; - } - else if (ridx[l] > j) - break; - } - - if (! found) - { - // A(j,k) == 0 - D[j]++; - if (D[j] > max_deg) - max_deg = D[j]; - } - } - } - } - return max_deg; -} - -// Transpose of the structure of a square sparse matrix - -static void -transpose (octave_idx_type N, const octave_idx_type *ridx, - const octave_idx_type *cidx, octave_idx_type *ridx2, - octave_idx_type *cidx2) -{ - octave_idx_type nz = cidx[N]; - - OCTAVE_LOCAL_BUFFER (octave_idx_type, w, N + 1); - for (octave_idx_type i = 0; i < N; i++) - w[i] = 0; - for (octave_idx_type i = 0; i < nz; i++) - w[ridx[i]]++; - nz = 0; - for (octave_idx_type i = 0; i < N; i++) - { - octave_quit (); - - cidx2[i] = nz; - nz += w[i]; - w[i] = cidx2[i]; - } - cidx2[N] = nz; - w[N] = nz; - - for (octave_idx_type j = 0; j < N; j++) - for (octave_idx_type k = cidx[j]; k < cidx[j + 1]; k++) - { - octave_quit (); - - octave_idx_type q = w[ridx[k]]++; - ridx2[q] = j; - } -} - -// An implementation of the Cuthill-McKee algorithm. -DEFUN_DLD (symrcm, args, , - doc: /* -*- texinfo -*- -@deftypefn {} {@var{p} =} symrcm (@var{S}) -Return the symmetric reverse @nospell{Cuthill-McKee} permutation of @var{S}. - -@var{p} is a permutation vector such that -@code{@var{S}(@var{p}, @var{p})} tends to have its diagonal elements closer -to the diagonal than @var{S}. This is a good preordering for LU or -Cholesky@tie{}factorization of matrices that come from ``long, skinny'' -problems. It works for both symmetric and asymmetric @var{S}. - -The algorithm represents a heuristic approach to the NP-complete bandwidth -minimization problem. The implementation is based in the descriptions found -in - -@nospell{E. Cuthill, J. McKee}. -@cite{Reducing the Bandwidth of Sparse Symmetric Matrices}. -Proceedings of the 24th @nospell{ACM} National Conference, -157--172 1969, Brandon Press, New Jersey. - -@nospell{A. George, J.W.H. Liu}. @cite{Computer Solution of Large Sparse -Positive Definite Systems}, Prentice Hall Series in Computational -Mathematics, ISBN 0-13-165274-5, 1981. - -@seealso{colperm, colamd, symamd} -@end deftypefn */) -{ - if (args.length () != 1) - print_usage (); - - octave_value arg = args(0); - - // the parameter of the matrix is converted into a sparse matrix - //(if necessary) - octave_idx_type *cidx; - octave_idx_type *ridx; - SparseMatrix Ar; - SparseComplexMatrix Ac; - - if (arg.isreal ()) - { - Ar = arg.sparse_matrix_value (); - // Note cidx/ridx are const, so use xridx and xcidx... - cidx = Ar.xcidx (); - ridx = Ar.xridx (); - } - else - { - Ac = arg.sparse_complex_matrix_value (); - cidx = Ac.xcidx (); - ridx = Ac.xridx (); - } - - octave_idx_type nr = arg.rows (); - octave_idx_type nc = arg.columns (); - - if (nr != nc) - err_square_matrix_required ("symrcm", "S"); - - if (nr == 0 && nc == 0) - return ovl (NDArray (dim_vector (1, 0))); - - // sizes of the heaps - octave_idx_type s = 0; - - // head- and tail-indices for the queue - octave_idx_type qt = 0; - octave_idx_type qh = 0; - CMK_Node v, w; - // dimension of the matrix - octave_idx_type N = nr; - - OCTAVE_LOCAL_BUFFER (octave_idx_type, cidx2, N + 1); - OCTAVE_LOCAL_BUFFER (octave_idx_type, ridx2, cidx[N]); - transpose (N, ridx, cidx, ridx2, cidx2); - - // the permutation vector - NDArray P (dim_vector (1, N)); - - // compute the node degrees - OCTAVE_LOCAL_BUFFER (octave_idx_type, D, N); - octave_idx_type max_deg = calc_degrees (N, ridx, cidx, D); - - // if none of the nodes has a degree > 0 (a matrix of zeros) - // the return value corresponds to the identity permutation - if (max_deg == 0) - { - for (octave_idx_type i = 0; i < N; i++) - P(i) = i; - - return ovl (P); - } - - // a heap for the a node's neighbors. The number of neighbors is - // limited by the maximum degree max_deg: - OCTAVE_LOCAL_BUFFER (CMK_Node, S, max_deg); - - // a queue for the BFS. The array is always one element larger than - // the number of entries that are stored. - OCTAVE_LOCAL_BUFFER (CMK_Node, Q, N+1); - - // a counter (for building the permutation) - octave_idx_type c = -1; - - // upper bound for the bandwidth (=quality of solution) - // initialize the bandwidth of the graph with 0. B contains the - // the maximum of the theoretical lower limits of the subgraphs - // bandwidths. - octave_idx_type B = 0; - - // mark all nodes as unvisited; with the exception of the nodes - // that have degree==0 and build a CC of the graph. - - boolNDArray btmp (dim_vector (1, N), false); - bool *visit = btmp.fortran_vec (); - - do - { - // locate an unvisited starting node of the graph - octave_idx_type i; - for (i = 0; i < N; i++) - if (! visit[i]) - break; - - // locate a probably better starting node - v.id = find_starting_node (N, ridx, cidx, ridx2, cidx2, D, i); - - // mark the node as visited and enqueue it (a starting node - // for the BFS). Since the node will be a root of a spanning - // tree, its dist is 0. - v.deg = D[v.id]; - v.dist = 0; - visit[v.id] = true; - Q_enq (Q, N, qt, v); - - // lower bound for the bandwidth of a subgraph - // keep a "level" in the spanning tree (= min. distance to the - // root) for determining the bandwidth of the computed - // permutation P - octave_idx_type Bsub = 0; - // min. dist. to the root is 0 - octave_idx_type level = 0; - // the root is the first/only node on level 0 - octave_idx_type level_N = 1; - - while (! Q_empty (Q, N, qh, qt)) - { - v = Q_deq (Q, N, qh); - i = v.id; - - c++; - - // for computing the inverse permutation P where - // A(inv(P),inv(P)) or P'*A*P is banded - // P(i) = c; - - // for computing permutation P where - // A(P(i),P(j)) or P*A*P' is banded - P(c) = i; - - // put all unvisited neighbors j of node i on the heap - s = 0; - octave_idx_type j1 = cidx[i]; - octave_idx_type j2 = cidx2[i]; - - octave_quit (); - - while (j1 < cidx[i+1] || j2 < cidx2[i+1]) - { - octave_quit (); - - if (j1 == cidx[i+1]) - { - octave_idx_type r2 = ridx2[j2++]; - if (! visit[r2]) - { - // the distance of node j is dist(i)+1 - w.id = r2; - w.deg = D[r2]; - w.dist = v.dist+1; - H_insert (S, s, w); - visit[r2] = true; - } - } - else if (j2 == cidx2[i+1]) - { - octave_idx_type r1 = ridx[j1++]; - if (! visit[r1]) - { - w.id = r1; - w.deg = D[r1]; - w.dist = v.dist+1; - H_insert (S, s, w); - visit[r1] = true; - } - } - else - { - octave_idx_type r1 = ridx[j1]; - octave_idx_type r2 = ridx2[j2]; - if (r1 <= r2) - { - if (! visit[r1]) - { - w.id = r1; - w.deg = D[r1]; - w.dist = v.dist+1; - H_insert (S, s, w); - visit[r1] = true; - } - j1++; - if (r1 == r2) - j2++; - } - else - { - if (! visit[r2]) - { - w.id = r2; - w.deg = D[r2]; - w.dist = v.dist+1; - H_insert (S, s, w); - visit[r2] = true; - } - j2++; - } - } - } - - // add the neighbors to the queue (sorted by node degree) - while (! H_empty (S, s)) - { - octave_quit (); - - // locate a neighbor of i with minimal degree in O(log(N)) - v = H_remove_min (S, s, 1); - - // entered the BFS a new level? - if (v.dist > level) - { - // adjustment of bandwidth: - // "[...] the minimum bandwidth that - // can be obtained [...] is the - // maximum number of nodes per level" - if (Bsub < level_N) - Bsub = level_N; - - level = v.dist; - // v is the first node on the new level - level_N = 1; - } - else - { - // there is no new level but another node on - // this level: - level_N++; - } - - // enqueue v in O(1) - Q_enq (Q, N, qt, v); - } - - // synchronize the bandwidth with level_N once again: - if (Bsub < level_N) - Bsub = level_N; - } - // finish of BFS. If there are still unvisited nodes in the graph - // then it is split into CCs. The computed bandwidth is the maximum - // of all subgraphs. Update: - if (Bsub > B) - B = Bsub; - } - // are there any nodes left? - while (c+1 < N); - - // compute the reverse-ordering - s = N / 2 - 1; - for (octave_idx_type i = 0, j = N - 1; i <= s; i++, j--) - std::swap (P.elem (i), P.elem (j)); - - // increment all indices, since Octave is not C - return ovl (P+1); -}
--- a/scripts/help/type.m Sat Jan 25 15:26:07 2020 +0100 +++ b/scripts/help/type.m Wed Jan 29 06:30:40 2020 -0500 @@ -150,7 +150,7 @@ %! txt = type ("ls", "-q"); %! assert (regexp (txt{1}, '[#\s]*Copyright \(C\) 2006')); -%!assert (type ("amd"){1}, "amd is a dynamically-linked function") +%!assert (type ("fftw"){1}, "fftw is a dynamically-linked function") %!assert (type ("cat"){1}, "cat is a built-in function") %!assert (type ("+"){1}, "+ is an operator") %!assert (type ("end"){1}, "end is a keyword")
--- a/scripts/help/which.m Sat Jan 25 15:26:07 2020 +0100 +++ b/scripts/help/which.m Wed Jan 29 06:30:40 2020 -0500 @@ -91,8 +91,8 @@ %! str = which ("ls"); %! assert (str(end-17:end), fullfile ("miscellaneous", "ls.m")); %!test -%! str = which ("amd"); -%! assert (str(end-6:end), "amd.oct"); +%! str = which ("fftw"); +%! assert (str(end-7:end), "fftw.oct"); %!test %! str = which ("inputParser"); %! assert (str, "built-in function"); @@ -104,14 +104,14 @@ %!assert (which ("__NO_SUCH_NAME__"), "") %!test -%! str = which ("amd"); -%! assert (str(end-6:end), "amd.oct"); -%! amd = 12; -%! str = which ("amd"); +%! str = which ("fftw"); +%! assert (str(end-7:end), "fftw.oct"); +%! fftw = 12; +%! str = which ("fftw"); %! assert (str, "variable"); -%! clear amd; -%! str = which ("amd"); -%! assert (str(end-6:end), "amd.oct"); +%! clear fftw; +%! str = which ("fftw"); +%! assert (str(end-7:end), "fftw.oct"); %!error which () %!error which (1)