diff scripts/linear-algebra/krylov.m @ 3240:2e74d8aa1a20

[project @ 1999-04-07 18:33:23 by jwe]
author jwe
date Wed, 07 Apr 1999 18:34:20 +0000
parents 98d0ee053ba4
children eb27ea9b7ff8
line wrap: on
line diff
--- a/scripts/linear-algebra/krylov.m
+++ b/scripts/linear-algebra/krylov.m
@@ -1,162 +1,188 @@
-# Copyright (C) 1996,1998 A. Scottedward Hodel 
+# Copyright (C) 1993, 1998, 1999 A. Scottedward Hodel
+#
+# This file is part of Octave.
 #
-# 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 2, or (at your option) any
+# later version.
+#
+# Octave is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# for more details.
 #
-# Octave is free software; you can redistribute it and/or modify it 
-# under the terms of the GNU General Public License as published by the 
-# Free Software Foundation; either version 2, or (at your option) any 
-# later version. 
-# 
-# Octave is distributed in the hope that it will be useful, but WITHOUT 
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
-# for more details.
-# 
-# You should have received a copy of the GNU General Public License 
-# along with Octave; see the file COPYING.  If not, write to the Free 
-# Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+# You should have received a copy of the GNU General Public License
+# along with Octave; see the file COPYING.  If not, write to the Free
+# Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+function [Uret,H,nu] = krylov(A,V,k,eps1,pflg);
+  # function [U,H,nu] = krylov(A,V,k{,eps1,pflg});
+  # construct orthogonal basis U of block Krylov subspace;
+  # 	[V AV A^2*V ... A^(k+1)*V];
+  # method used: householder reflections to guard against loss of
+  # orthogonality
+  # eps1: threshhold for 0 (default: 1e-12)
+  # pflg: flag to use row pivoting  (improves numerical behavior)
+  #   0 [default]: no pivoting; prints a warning message if trivial
+  #                null space is corrupted
+  #   1          : pivoting performed
+  #
+  # outputs:
+  #   Uret: orthogonal basis of block krylov subspace
+  #   H: Hessenberg matrix; if V is a vector then A U = U H 
+  #      otherwise H is meaningless
+  # nu: dimension of span of krylov subspace (based on eps1)
+  # if B is a vector and k > m-1, krylov returns H = the Hessenberg 
+  # decompostion of A.
+  #
+  # Reference: Hodel and Misra, "Partial Pivoting in the Computation of 
+  #     Krylov Subspaces", In preparation.
+
+  defeps = 1e-12;
+  if(nargin < 3 | nargin > 5)
+    usage("[U,nu] = krylov(A,V,k{,eps1,pflg})")
+  elseif(nargin < 5)
+    pflg = 0;        # default permutation flag
+  endif
+  if(nargin < 4)
+    eps1 = defeps;    # default tolerance parameter
+  endif
+  if(isempty(eps1)) eps1 = defeps; endif
+  
+  na = is_square(A);
+  if( !na ) error("A(%d x %d) must be square",rows(A),columns(A)); endif
  
-function [U,H,k1] = krylov(A,v,k,eps1,pflg);
-  # function [U,H,k1] = krylov(A,v,k{,eps1});
-  # construct orthogonal basis U of Krylov subspace;
-  #     span([v Av A^2*v ... A^(k+1)*v]);
-  # via householder reflections; reflections are multiplied to obtain U
-  # inputs:
-  #   A: square matrix
-  #   v: column vector
-  #   k: desired krylov subspace dimension (as above)
-  #   eps1: threshhold for 0 relative to norm of current column (default: 1e-12)
-  #   pflg: permutation flag (default 0): avoid using zero rows of
-  #      [A,v] as householder pivots; this avoids spurious non-zero entries
-  #      in returned orthogonal matrix U, but destroys the Householder matrix
-  #      structure of H.
-  # outputs:
-  #   U: (n x k+1) orthogonal basis of Krylov subspace. A*U(:,1:k) = U*H
-  #   H: (pflg=0): Hessenberg matrix satisfying A*U(:,1:k) = U*H
-  #      (pflg=1): Workspace; does not satisfy above equation.
-  # k1: dimension of span of krylov subspace (based on eps1)
-  # if k > m-1, krylov returns the Hessenberg decompostion of A.
+  [m,kb] = size(V); 
+  if(m != na);
+    error("A(%d x %d), V(%d x %d): argument dimensions do not match", ...
+      na,na,m,kb)
+  endif
 
-  # Written by A. S. Hodel 1992
-  # $Revision: 1.2 $
-  # $Log$
-
-  save_empty_list_elements_ok = empty_list_elements_ok;
-  empty_list_elements_ok = 1;
+  if( !is_scalar(k) )
+    error("krylov: third argument must be a scalar integer")
+  endif
 
-  if    (nargin > 5)   usage("[U,H,k1] = krylov(A,v,k{,eps1,pflg})");
-  elseif(nargin < 5)   pflg = 0;
-  elseif(nargin < 4)   eps1 = 1e-12; endif
-  na = is_square(A);
-  if(!na) error("krylov: A(%dx%d) must be square",na,na); endif
-  [m,n] = size(v);
-  if(!is_vector(v)) error("krylov: v(%dx%d) must be a vector",m,n);
-  elseif(length(v) != na)
-    error("krylov: A(%dx%d), v(%dx1); mismatch",na,na,length(v));
-  endif
-  v = vec(v);    # make sure it's a column vector
-  if(!is_scalar(k))
-    error("krylov: k(%dx%d) must be a scalar",rows(k), columns(k));
-  elseif( k > m)
-    warning("krylov: k is too large; reducing to %d",m-1);
-    k = m-1;
+  Vnrm = norm(V,Inf);
+  
+  # check for trivial solution
+  if(Vnrm == 0)
+    Uret = []; nu = 0;  return;
   endif
 
-  # check for zero input vector
-  if(norm(v) == 0) U = []; H = []; k1 = 0; return endif
+  # identify trivial null space
+  abm = max(abs([A,V]'));  nzidx = find(abm != 0);  zidx = find(abm == 0);
+
+  # set up vector of pivot points
+  pivot_vec = 1:na;
 
-  # indices of legal pivot points (identifies trivial null space).
-  abm = max(abs([A,v]')); nzidx = find(abm != 0); zidx = find(abm == 0);
+  iter = 0;
+  alpha = [];  
+  nh = 0;
+  while (length(alpha) < na) & (columns(V) > 0) & (iter < k)
+    iter++;
 
-  # check permutation flag
-  if(pflg)
-    # permute zero rows of [A,v] to trailing rows
-    permvec = [vec(nzidx); vec(zidx)];
-    pmat = eye(na); pmat = pmat(permvec,:);
-    ipermvec = pmat'*vec(1:na);
-    A = A(permvec,permvec);
-    v = v(permvec);
-  endif
+    # get orthogonal basis of V
+    jj = 1;
+    while(jj <= columns(V) & length(alpha) < na)
+      nu = length(alpha)+1;   # index of next Householder reflection
+     
+      short_pv = pivot_vec(nu:na);
+      q = V(:,jj);
+      short_q = q(short_pv);
 
-  k1 = k+1;		# Assume subspace basis has full rank
-  [m,n] = size(A);
-  [hv,alpha(1),z] = housh(v,1,0);
-
-  # initial orthogonal vector
-  q = zeros(n,1);
-  q(1) = 1;
-  q = q - alpha*hv*hv'*q;
-  normq = norm(q);
-  normres = normq;
+      if(norm(short_q) < eps1)
+        # insignificant column; delete
+        nv = columns(V);
+        if(jj != nv) 
+          [V(:,jj),V(:,nv)] = swap(V(:,jj),V(:,nv)); 
+          # FIX ME: H columns should be swapped too.  Not done since
+          # Block Hessenberg structure is lost anyway.
+        endif
+        V = V(:,1:(nv-1));
+        nu = nu - 1;    # one less reflection
 
-  U(:,1) = hv;
-  j = 1;
-  while(j <= k & normres > eps1*normq)
-    # multiply to get new vector;
-    q = A*q;
-    normq = norm(q);
+      else
+        # new householder reflection
+        if(pflg)
+          # locate max magnitude element in short_q
+          asq = abs(short_q);
+	  maxv = max(asq);
+          maxidx = find(asq == maxv);
+          pivot_idx = short_pv(maxidx(1));
+           
+          # see if need to change the pivot list
+          if(pivot_idx != pivot_vec(nu))
+            swapidx = maxidx(1) + (nu-1);
+            [pivot_vec(nu),pivot_vec(swapidx)] = ...
+              swap(pivot_vec(nu),pivot_vec(swapidx));
+          endif
+        endif
+        
+        # isolate portion of vector for reflection
+        idx = pivot_vec(nu:na);
+        jdx = pivot_vec(1:nu);
+
+        [hv,av,z] = housh(q(idx),1,0);
+        alpha(nu) = av;
+        U(idx,nu) = hv;
 
-    # multiply by householder reflections to obtain projected vector and the
-    # next column of H
-    for i=1:j
-     hv = U(i:n,i);
-     av = alpha(i);
-     q(i:n,1) = q(i:n,1)-av*hv*(hv'*q(i:n,1));
-    endfor
+        # reduce V per the reflection
+        V(idx,:) = V(idx,:) - av*hv*(hv' * V(idx,:));
+        if(iter > 1)
+          # FIX ME: not done correctly for block case
+          H(nu,nu-1) = V(pivot_vec(nu),jj);
+        endif
+
+        # advance to next column of V
+        jj=jj+1;
+      endif
+    endwhile
 
-    i = j+1;
-    # compute and apply next householder vector;
-    if(i <= n)
-      [hv,av,z] = housh(q(i:n,1),1,0);
-      alpha(i) = av;
-      q(i:n,1) = q(i:n,1)-av*hv*(hv'*q(i:n,1));
-      U(i:n,i) = hv;
-      H(1:i,j) = q(1:i);
-    else
-      av = 0;
-      H(:,j) = q;	# complete Hessenberg decomposition
+    if(columns(V) > 0)
+      # construct next Q and multiply
+      Q = zeros(size(V));
+      for kk=1:columns(Q)
+        Q(pivot_vec(nu-columns(Q)+kk),kk) = 1;
+      endfor
+      
+      # apply Householder reflections
+      for ii = nu:-1:1
+        idx = pivot_vec(ii:na);
+        hv = U(idx,ii);
+        av = alpha(ii);
+        Q(idx,:) = Q(idx,:) - av*hv*(hv'*Q(idx,:));
+      endfor
     endif
 
-    # see if done yet
-    normres = norm(q(i:n));
-    if(normres > eps1*normq)
-      j = j+1;
-    else   
-      k1 = min(k1,j);	# time to quit; norm of residual is small
-    endif
-  
-    # back out new q vector for next pass;
-    j1 = columns(U);
-    q = zeros(n,1);
-    q(j1) = 1;
-    for i=j1:-1:1;
-      hv = U(i:n,i);
-      av = alpha(i);
-      q(i:n,1) = q(i:n,1)-av*hv*(hv'*q(i:n,1));
-    endfor
+    # multiply to get new vector;
+    V = A*Q;
+    # project off of previous vectors
+    nu = length(alpha);
+    for i=1:nu
+      hv = U(:,i);  av = alpha(i);
+      V = V - av*hv*(hv'*V);
+      H(i,nu-columns(V)+(1:columns(V))) = V(pivot_vec(i),:);
+    end
+
   endwhile
 
+  # Back out complete U matrix
   # back out U matrix ;
   j1 = columns(U);
   for i=j1:-1:1;
-   hv = U(i:n,i);
-   av = alpha(i);
-   U(:,i) = zeros(n,1);
-   U(i,i) = 1;
-   U(i:n,i:j1) = U(i:n,i:j1)-av*hv*(hv'*U(i:n,i:j1));
+    idx = pivot_vec(i:na);
+    hv = U(idx,i);
+    av = alpha(i);
+    U(:,i) = zeros(na,1);
+    U(idx(1),i) = 1;
+    U(idx,i:j1) = U(idx,i:j1)-av*hv*(hv'*U(idx,i:j1));
   endfor
-
-  # check permutation flag
-  if(pflg)
-    # permute rows of U back to original coordinates
-    U = U(ipermvec,:);
-  endif
-
-  # check for spurious nonzero entries
-  if( max(max( abs(U(zidx,:)) )) > eps1 )
+  
+  nu = length(alpha);
+  Uret = U;
+  if( max(max( abs(Uret(zidx,:)) )) > 0)
     warning("krylov: trivial null space corrupted; set pflg=1 or eps1>%e",eps1);
   endif
 
-  empty_list_elements_ok = save_empty_list_elements_ok;
-
 endfunction