diff scripts/polynomial/residue.m @ 6964:33f20a41aeea

[project @ 2007-10-06 04:31:18 by jwe]
author jwe
date Sat, 06 Oct 2007 04:31:18 +0000
parents 34f96dd5441b
children c8fc3487ed2c
line wrap: on
line diff
--- a/scripts/polynomial/residue.m
+++ b/scripts/polynomial/residue.m
@@ -1,4 +1,5 @@
 ## Copyright (C) 1996, 1997 John W. Eaton
+## Copyright (C) 2007 Ben Abbott
 ##
 ## This file is part of Octave.
 ##
@@ -18,17 +19,22 @@
 ## 02110-1301, USA.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} {} residue (@var{b}, @var{a}, @var{tol})
-## If @var{b} and @var{a} are vectors of polynomial coefficients, then
-## residue calculates the partial fraction expansion corresponding to the
-## ratio of the two polynomials.
+## @deftypefn {Function File} {[@var{r}, @var{p}, @var{k}] =} residue (@var{b}, @var{a})
+## @deftypefnx {Function File} {[@var{b}, @var{a}] =} residue (@var{r}, @var{p}, @var{k})
+## In the instance of two inputs, they are assumed to correspond to vectors
+## of polynomial coefficients, @var{b} and @var{a}. From these polynomial
+## coefficients, the function residue calculates the partial fraction 
+## expansion corresponding to the ratio of the two polynomials.
 ## @cindex partial fraction expansion
 ##
-## The function @code{residue} returns @var{r}, @var{p}, @var{k}, and
-## @var{e}, where the vector @var{r} contains the residue terms, @var{p}
-## contains the pole values, @var{k} contains the coefficients of a direct
-## polynomial term (if it exists) and @var{e} is a vector containing the
-## powers of the denominators in the partial fraction terms.
+## In this instance, the function @code{residue} returns @var{r}, @var{p},
+## and @var{k}, where the vector @var{r} contains the residue terms,
+## @var{p} contains the pole values, @var{k} contains the coefficients of a 
+## direct polynomial term (if it exists).
+## 
+## In the instance of three inputs, the function 'residue' performs the
+## reciprocal task. Meaning the partial fraction expansion is reconstituted
+## into the corresponding pair of polynomial coefficients.
 ##
 ## Assuming @var{b} and @var{a} represent polynomials
 ## @iftex
@@ -60,25 +66,19 @@
 ## @noindent
 ## where @math{M} is the number of poles (the length of the @var{r},
 ## @var{p}, and @var{e} vectors) and @math{N} is the length of the
-## @var{k} vector.
+## @var{k} vector. The @var{e} vector specifies the multiplicity of the
+## mth residue's pole.
 ##
-## The argument @var{tol} is optional, and if not specified, a default
-## value of 0.001 is assumed.  The tolerance value is used to determine
-## whether poles with small imaginary components are declared real.  It is
-## also used to determine if two poles are distinct.  If the ratio of the
-## imaginary part of a pole to the real part is less than @var{tol}, the
-## imaginary part is discarded.  If two poles are farther apart than
-## @var{tol} they are distinct.  For example,
+## For example,
 ##
 ## @example
 ## @group
-##  b = [1, 1, 1];
-##  a = [1, -5, 8, -4];
-##  [r, p, k, e] = residue (b, a);
+## b = [1, 1, 1];
+## a = [1, -5, 8, -4];
+## [r, p, k] = residue (b, a);
 ##      @result{} r = [-2, 7, 3]
 ##      @result{} p = [2, 2, 1]
 ##      @result{} k = [](0x0)
-##      @result{} e = [1, 2, 1]
 ## @end group
 ## @end example
 ##
@@ -98,84 +98,61 @@
 ##    ------------------- = ----- + ------- + -----
 ##    s^3 - 5s^2 + 8s - 4   (s-2)   (s-2)^2   (s-1)
 ## @end example
+##
 ## @end ifinfo
-## @seealso{poly, roots, conv, deconv, polyval, polyderiv, and polyinteg}
+## A similar, but reciprocal example, where the fraction's polynomials are 
+## reconstituted from the residues, poles, and direct term is
+##
+## @example
+## @group
+## r = [-2, 7, 3];
+## p = [2, 2, 1];
+## k = [1 0];
+## [b, a] = residue (r, p, k);
+##      @result{} b = [1, -5, 9, -3, 1]
+##      @result{} a = [1, -5, 8, 4]
+## @end group
+## @end example
+##
+## @noindent
+## which implies the following partial fraction expansion
+## @iftex
+## @tex
+## $$
+## {s^4-5s^3+9s^2-3s+1\over s^3-5s^2+8s-4} = {-2\over s-2} + {7\over (s-2)^2} + {3\over s-1} + s
+## $$
+## @end tex
+## @end iftex
+## @ifinfo
+##
+## @example
+##    s^4 - 5s^3 + 9s^2 - 3s + 1    -2        7        3
+##    -------------------------- = ----- + ------- + ----- + s
+##        s^3 - 5s^2 + 8s - 4      (s-2)   (s-2)^2   (s-1)
+## @end example
+## @end ifinfo
+## @seealso{poly, roots, conv, deconv, mpoles, polyval, polyderiv, polyinteg}
 ## @end deftypefn
 
 ## Author: Tony Richardson <arichard@stark.cc.oh.us>
+## Author: Ben Abbott <bpabbott@mac.com>
 ## Created: June 1994
 ## Adapted-By: jwe
 
-function [r, p, k, e] = residue (b, a, toler)
-
-  ## Here's the method used to find the residues.
-  ## The partial fraction expansion can be written as:
-  ##
-  ##
-  ##   P(s)    D   M(k)      A(k,m)
-  ##   ---- =  #    #    -------------
-  ##   Q(s)   k=1  m=1   (s - pr(k))^m
-  ##
-  ## (# is used to represent a summation) where D is the number of
-  ## distinct roots, pr(k) is the kth distinct root, M(k) is the
-  ## multiplicity of the root, and A(k,m) is the residue cooresponding
-  ## to the kth distinct root with multiplicity m.  For example,
-  ##
-  ##        s^2            A(1,1)   A(2,1)    A(2,2)
-  ## ------------------- = ------ + ------ + -------
-  ## s^3 + 4s^2 + 5s + 2    (s+2)    (s+1)   (s+1)^2
-  ##
-  ## In this case there are two distinct roots (D=2 and pr = [-2 -1]),
-  ## the first root has multiplicity one and the second multiplicity
-  ## two (M = [1 2]) The residues are actually stored in vector format as
-  ## r = [ A(1,1) A(2,1) A(2,2) ].
-  ##
-  ## We then multiply both sides by Q(s).  Continuing the example:
-  ##
-  ## s^2 = r(1)*(s+1)^2 + r(2)*(s+1)*(s+2) + r(3)*(s+2)
-  ##
-  ## or
-  ##
-  ## s^2 = r(1)*(s^2+2s+1) + r(2)*(s^2+3s+2) +r(3)*(s+2)
-  ##
-  ## The coefficients of the polynomials on the right are stored in a row
-  ## vector called rhs, while the coefficients of the polynomial on the
-  ## left is stored in a row vector called lhs.  If the multiplicity of
-  ## any root is greater than one we'll also need derivatives of this
-  ## equation of order up to the maximum multiplicity minus one.  The
-  ## derivative coefficients are stored in successive rows of lhs and
-  ## rhs.
-  ##
-  ## For our example lhs and rhs would be:
-  ##
-  ##       | 1 0 0 |
-  ## lhs = |       |
-  ##       | 0 2 0 |
-  ##
-  ##       | 1 2 1 1 3 2 0 1 2 |
-  ## rhs = |                   |
-  ##       | 0 2 2 0 2 3 0 0 1 |
-  ##
-  ## We then form a vector B and a matrix A obtained by evaluating the
-  ## polynomials in lhs and rhs at the pole values. If a pole has a
-  ## multiplicity greater than one we also evaluate the derivative
-  ## polynomials (successive rows) at the pole value.
-  ##
-  ## For our example we would have
-  ##
-  ## | 4|   | 1 0 0 |   | r(1) |
-  ## | 1| = | 0 0 1 | * | r(2) |
-  ## |-2|   | 0 1 1 |   | r(3) |
-  ##
-  ## We then solve for the residues using matrix division.
+function [r, p, k] = residue (b, a, varargin)
 
   if (nargin < 2 || nargin > 3)
     print_usage ();
   endif
 
-  if (nargin == 2)
-    toler = .001;
-  endif
+  toler = .001;
+
+  if (nargin == 3)
+    ## The inputs are the residue, pole, and direct part. Solve for the
+    ## corresponding numerator and denominator polynomials
+    [r, p] = rresidue (b, a, varargin{1}, toler);
+    return
+  end
 
   ## Make sure both polynomials are in reduced form.
 
@@ -206,17 +183,26 @@
 
   ## Determine if the poles are (effectively) zero.
 
-  p(abs (p) < toler) = 0;
+  small = max (abs (p));
+  small = max ([small, 1] ) * 1e-8 * (1 + numel (p))^2;
+  p(abs (p) < small) = 0;
+
+  ## Determine if the poles are (effectively) real, or imaginary.
 
-  ## Determine if the poles are (effectively) real.
+  index = (abs (imag (p)) < small);
+  p(index) = real (p(index));
+  index = (abs (real (p)) < small);
+  p(index) = 1i * imag (p(index));
 
-  index = (abs (p) >= toler & (abs (imag (p)) ./ abs (p) < toler));
-  p(index) = real (p(index));
+  ## Sort poles so that multiplicity loop will work.
+
+  [e, indx] = mpoles (p, toler, 1);
+  p = p (indx);
 
   ## Find the direct term if there is one.
 
   if (lb >= la)
-    ## Also returns the reduced numerator.
+    ## Also return the reduced numerator.
     [k, b] = deconv (b, a);
     lb = length (b);
   else
@@ -225,123 +211,106 @@
 
   if (lp == 1)
     r = polyval (b, p);
-    e = 1;
     return;
   endif
 
-  ## We need to determine the number and multiplicity of the roots.
-  ##
-  ## D  is the number of distinct roots.
-  ## M  is a vector of length D containing the multiplicity of each root.
-  ## pr is a vector of length D containing only the distinct roots.
-  ## e  is a vector of length lp which indicates the power in the partial
-  ##    fraction expansion of each term in p.
-
-  ## Set initial values.  We'll remove elements from pr as we find
-  ## multiplicities.  We'll shorten M afterwards.
-
-  e = ones (lp, 1);
-  M = zeros (lp, 1);
-  pr = p;
-  D = 1;
-  M(1) = 1;
-
-  old_p_index = 1;
-  new_p_index = 2;
-  M_index = 1;
-  pr_index = 2;
+  ## Determine the order of the denominator and remaining numerator.
+  ## With the direct term removed the potential order of the numerator
+  ## is one less than the order of the denominator.
 
-  while (new_p_index <= lp)
-    if (abs (p (new_p_index) - p (old_p_index)) < toler)
-      ## We've found a multiple pole.
-      M (M_index) = M (M_index) + 1;
-      e (new_p_index) = e (new_p_index-1) + 1;
-      ## Remove the pole from pr.
-      pr (pr_index) = [];
-    else
-      ## It's a different pole.
-      D++;
-      M_index++;
-      M (M_index) = 1;
-      old_p_index = new_p_index;
-      pr_index++;
-    endif
-    new_p_index++;
-  endwhile
-
-  ## Shorten M to it's proper length
-
-  M = M (1:D);
-
-  ## Now set up the polynomial matrices.
-
-  MM = max(M);
-
-  ## Left hand side polynomial
+  aorder = numel (a) - 1;
+  border = aorder - 1;
 
-  lhs = zeros (MM, lb);
-  rhs = zeros (MM, lp*lp);
-  lhs (1, :) = b;
-  rhi = 1;
-  dpi = 1;
-  mpi = 1;
-  while (dpi <= D)
-    for ind = 1:M(dpi)
-      if (mpi > 1 && (mpi+ind) <= lp)
-        cp = [p(1:mpi-1); p(mpi+ind:lp)];
-      elseif (mpi == 1)
-        cp = p (mpi+ind:lp);
-      else
-        cp = p (1:mpi-1);
-      endif
-      rhs (1, rhi:rhi+lp-1) = prepad (poly (cp), lp, 0, 2);
-      rhi = rhi + lp;
-    endfor
-    mpi = mpi + M (dpi);
-    dpi++;
-  endwhile
-  if (MM > 1)
-    for index = 2:MM
-      lhs (index, :) = prepad (polyderiv (lhs (index-1, :)), lb, 0, 2);
-      ind = 1;
-      for rhi = 1:lp
-        cp = rhs (index-1, ind:ind+lp-1);
-        rhs (index, ind:ind+lp-1) = prepad (polyderiv (cp), lp, 0, 2);
-        ind = ind + lp;
-      endfor
-    endfor
-  endif
+  ## Construct a system of equations relating the individual
+  ## contributions from each residue to the complete numerator.
 
-  ## Now lhs contains the numerator polynomial and as many derivatives as
-  ## are required.  rhs is a matrix of polynomials, the first row
-  ## contains the corresponding polynomial for each residue and
-  ## successive rows are derivatives.
-
-  ## Now we need to evaluate the first row of lhs and rhs at each
-  ## distinct pole value.  If there are multiple poles we will also need
-  ## to evaluate the derivatives at the pole value also.
-
-  B = zeros (lp, 1);
-  A = zeros (lp, lp);
-
-  dpi = 1;
-  row = 1;
-  while (dpi <= D)
-    for mi = 1:M(dpi)
-      B (row) = polyval (lhs (mi, :), pr (dpi));
-      ci = 1;
-      for col = 1:lp
-        cp = rhs (mi, ci:ci+lp-1);
-        A (row, col) = polyval (cp, pr(dpi));
-        ci = ci + lp;
-      endfor
-      row++;
-    endfor
-    dpi++;
-  endwhile
+  A = zeros (border+1, border+1);
+  B = prepad (reshape (b, [numel(b), 1]), border+1, 0);
+  for ip = 1:numel(p)
+    ri = zeros (size (p));
+    ri(ip) = 1;
+    A(:,ip) = prepad (rresidue (ri, p, [], toler), border+1, 0).';
+  endfor
 
   ## Solve for the residues.
 
   r = A \ B;
 
 endfunction
+
+function [pnum, pden] = rresidue (r, p, k, toler)
+
+  ## Reconstitute the numerator and denominator polynomials from the
+  ## residues, poles, and direct term.
+
+  if (nargin < 2 || nargin > 4)
+    print_usage ();
+  endif
+
+  if (nargin < 4)
+    toler = [];
+  endif
+
+  if (nargin < 3)
+    k = [];
+  endif
+  
+  [multp, indx] = mpoles (p, toler, 0);
+
+  p = p (indx);
+  r = r (indx);
+
+  indx = 1:numel(p);
+
+  for n = indx
+    pn = [1, -p(n)];
+    if n == 1
+      pden = pn;
+    else
+      pden = conv (pden, pn);
+    endif
+  endfor
+
+  ## D is the order of the denominator
+  ## K is the order of the direct polynomial
+  ## N is the order of the resulting numerator
+  ## pnum(1:(N+1)) is the numerator's polynomial
+  ## pden(1:(D+1)) is the denominator's polynomial
+  ## pm is the multible pole for the nth residue
+  ## pn is the numerator contribution for the nth residue
+
+  D = numel (pden) - 1;
+  K = numel (k) - 1;
+  N = K + D;
+  pnum = zeros (1, N+1);
+  for n = indx(abs(r)>0)
+    p1 = [1, -p(n)];
+    for m = 1:multp(n)
+      if m == 1
+        pm = p1;
+      else
+        pm = conv (pm, p1);
+      endif
+    endfor
+    pn = deconv (pden, pm);
+    pn = r(n) * pn;
+    pnum = pnum + prepad ( pn, N+1, 0);
+  endfor
+
+  ## Add the direct term.
+
+  if (numel (k))
+    pnum = pnum + conv (pden, k);
+  endif
+
+  ## Check for leading zeros and trim the polynomial coefficients.
+
+  small = max ([max(abs(pden)), max(abs(pnum)), 1]) * eps;
+
+  pnum (abs (pnum) < small) = 0;
+  pden (abs (pden) < small) = 0;
+
+  pnum = polyreduce (pnum);
+  pden = polyreduce (pden);
+
+endfunction