home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / numana01.zip / SRC / MAT.MOD < prev    next >
Text File  |  1996-08-16  |  36KB  |  966 lines

  1. IMPLEMENTATION MODULE Mat;
  2.  
  3.         (********************************************************)
  4.         (*                                                      *)
  5.         (*                 Matrix arithmetic                    *)
  6.         (*                                                      *)
  7.         (*  Programmer:         P. Moylan                       *)
  8.         (*  Last edited:        16 August 1996                  *)
  9.         (*  Status:             Seems to be working             *)
  10.         (*                                                      *)
  11.         (*      Portability note: this module contains some     *)
  12.         (*      open array operations which are a language      *)
  13.         (*      extension in XDS Modula-2 but not part of       *)
  14.         (*      ISO standard Modula-2.  So far I haven't worked *)
  15.         (*      out how to do this in the standard language.    *)
  16.         (*                                                      *)
  17.         (********************************************************)
  18.  
  19. FROM Storage IMPORT
  20.     (* proc *)  ALLOCATE, DEALLOCATE;
  21.  
  22. FROM Vec IMPORT
  23.     (* type *)  VectorPtr,
  24.     (* proc *)  NewVector, DisposeVector;
  25.  
  26. FROM LongComplexMath IMPORT
  27.     (* proc *)  conj;
  28.  
  29. FROM MiscM2 IMPORT
  30.     (* proc *)  Error, WriteLn, WriteString, WriteLongReal, Sqrt;
  31.  
  32. FROM Rand IMPORT
  33.     (* proc *)  RANDOM;
  34.  
  35. (************************************************************************)
  36.  
  37. CONST small = 1.0E-15;
  38.  
  39. <* m2extensions+ *>
  40. <* storage+ *>
  41.  
  42. TYPE
  43.     subscript = [0..8191];
  44.     Permutation = POINTER TO ARRAY subscript OF subscript;
  45.  
  46. (************************************************************************)
  47. (*                  CREATING AND DESTROYING MATRICES                    *)
  48. (************************************************************************)
  49.  
  50. PROCEDURE NewArray (N, M: CARDINAL): ArrayPtr;
  51.  
  52.     (* Creates an NxM matrix. *)
  53.  
  54.     VAR result: ArrayPtr;
  55.  
  56.     BEGIN
  57.         NEW (result, N, M);
  58.         RETURN result;
  59.     END NewArray;
  60.  
  61. (************************************************************************)
  62.  
  63. PROCEDURE DisposeArray (VAR (*INOUT*) V: ArrayPtr;  N, M: CARDINAL);
  64.  
  65.     (* Deallocates an NxM matrix. *)
  66.  
  67.     BEGIN
  68.         DISPOSE (V);
  69.     END DisposeArray;
  70.  
  71. (************************************************************************)
  72. (*                          COPYING A MATRIX                            *)
  73. (************************************************************************)
  74.  
  75. PROCEDURE Copy (A: ARRAY OF ARRAY OF EltType;  r, c: CARDINAL;
  76.                          VAR (*OUT*) B: ARRAY OF ARRAY OF EltType);
  77.  
  78.     (* Copies an rxc matrix A to B. *)
  79.  
  80.     VAR i, j: CARDINAL;
  81.  
  82.     BEGIN
  83.         FOR i := 0 TO r-1 DO
  84.             FOR j := 0 TO c-1 DO
  85.                 B[i,j] := A[i,j];
  86.             END (*FOR*);
  87.         END (*FOR*);
  88.     END Copy;
  89.  
  90. (************************************************************************)
  91. (*                        SPECIAL MATRICES                              *)
  92. (************************************************************************)
  93.  
  94. PROCEDURE Zero (VAR (*INOUT*) M: ARRAY OF ARRAY OF EltType;  r, c: CARDINAL);
  95.  
  96.     (* Creates an r by c matrix with all zero entries. *)
  97.  
  98.     VAR i, j: CARDINAL;
  99.  
  100.     BEGIN
  101.         FOR i := 0 TO r-1 DO
  102.             FOR j := 0 TO c-1 DO
  103.                 M[i,j] := 0.0;
  104.             END (*FOR*);
  105.         END (*FOR*);
  106.     END Zero;
  107.  
  108. (************************************************************************)
  109.  
  110. PROCEDURE Unit (VAR (*INOUT*) M: ARRAY OF ARRAY OF EltType;  N: CARDINAL);
  111.  
  112.     (* Creates an N by N unit matrix. *)
  113.  
  114.     VAR i: CARDINAL;
  115.  
  116.     BEGIN
  117.         Zero (M, N, N);
  118.         FOR i := 0 TO N-1 DO
  119.             M[i,i] := 1.0;
  120.         END (*FOR*);
  121.     END Unit;
  122.  
  123. (************************************************************************)
  124.  
  125. PROCEDURE Random (VAR (*INOUT*) M: ARRAY OF ARRAY OF EltType;  r, c: CARDINAL);
  126.  
  127.     (* Creates an r by c matrix with random entries. *)
  128.  
  129.     VAR i, j: CARDINAL;
  130.  
  131.     BEGIN
  132.         FOR i := 0 TO r-1 DO
  133.             FOR j := 0 TO c-1 DO
  134.                 M[i,j] := VAL(EltType, RANDOM());
  135.             END (*FOR*);
  136.         END (*FOR*);
  137.     END Random;
  138.  
  139. (************************************************************************)
  140. (*                  THE STANDARD ARITHMETIC OPERATIONS                  *)
  141. (************************************************************************)
  142.  
  143. PROCEDURE Add (A, B: ARRAY OF ARRAY OF EltType;  r, c: CARDINAL;
  144.                       VAR (*OUT*) C: ARRAY OF ARRAY OF EltType);
  145.  
  146.     (* Computes C := A + B.  All matrices are rxc. *)
  147.  
  148.     VAR i, j: CARDINAL;
  149.  
  150.     BEGIN
  151.         FOR i := 0 TO r-1 DO
  152.             FOR j := 0 TO c-1 DO
  153.                 C[i,j] := A[i,j] + B[i,j];
  154.             END (*FOR*);
  155.         END (*FOR*);
  156.     END Add;
  157.  
  158. (************************************************************************)
  159.  
  160. PROCEDURE Sub (A, B: ARRAY OF ARRAY OF EltType;  r, c: CARDINAL;
  161.                       VAR (*OUT*) C: ARRAY OF ARRAY OF EltType);
  162.  
  163.     (* Computes C := A - B.  All matrices are rxc.  *)
  164.  
  165.     VAR i, j: CARDINAL;
  166.  
  167.     BEGIN
  168.         FOR i := 0 TO r-1 DO
  169.             FOR j := 0 TO c-1 DO
  170.                 C[i,j] := A[i,j] - B[i,j];
  171.             END (*FOR*);
  172.         END (*FOR*);
  173.     END Sub;
  174.  
  175. (************************************************************************)
  176.  
  177. PROCEDURE Mul (A, B: ARRAY OF ARRAY OF EltType;  r, c1, c2: CARDINAL;
  178.                       VAR (*OUT*) C: ARRAY OF ARRAY OF EltType);
  179.  
  180.     (* Computes C := A*B, where A is rxc1 and B is c1xc2. *)
  181.  
  182.     VAR i, j, k: CARDINAL;  temp: EltType;
  183.  
  184.     BEGIN
  185.         FOR i := 0 TO r-1 DO
  186.             FOR j := 0 TO c2-1 DO
  187.                 temp := 0.0;
  188.                 FOR k := 0 TO c1-1 DO
  189.                     temp := temp + A[i,k]*B[k,j];
  190.                 END (*FOR*);
  191.                 C[i,j] := temp;
  192.             END (*FOR*);
  193.         END (*FOR*);
  194.     END Mul;
  195.  
  196. (************************************************************************)
  197.  
  198. PROCEDURE ScalarMul (A: EltType;  B: ARRAY OF ARRAY OF EltType;  r, c: CARDINAL;
  199.                                   VAR (*OUT*) C: ARRAY OF ARRAY OF EltType);
  200.  
  201.     (* Computes C := A*B, where B is rxc. *)
  202.  
  203.     VAR i, j: CARDINAL;
  204.  
  205.     BEGIN
  206.         FOR i := 0 TO r-1 DO
  207.             FOR j := 0 TO c-1 DO
  208.                 C[i,j] := A * B[i,j];
  209.             END (*FOR*);
  210.         END (*FOR*);
  211.     END ScalarMul;
  212.  
  213. (************************************************************************)
  214. (*                      SOLVING LINEAR EQUATIONS                        *)
  215. (************************************************************************)
  216.  
  217. PROCEDURE LUFactor (VAR (*INOUT*) A: ARRAY OF ARRAY OF EltType;  N: CARDINAL;
  218.                           perm: Permutation;  VAR (*OUT*) oddswaps: BOOLEAN);
  219.  
  220.     (* LU decomposition of a square matrix.  We express A in the form   *)
  221.     (* P*L*U, where P is a permutation matrix, L is lower triangular    *)
  222.     (* with unit diagonal elements, and U is upper triangular.  This is *)
  223.     (* an in-place computation, where on exit U occupies the upper      *)
  224.     (* triangle of A, and L (not including its diagonal entries) is in  *)
  225.     (* the lower triangle.  The permutation information is returned in  *)
  226.     (* perm.  Output parameter oddswaps is TRUE iff an odd number of    *)
  227.     (* row interchanges were done by the permutation.  (We need to know *)
  228.     (* this only if we are going to go on to calculate a determinant.)  *)
  229.  
  230.     (* The precise relationship between the implied permutation matrix  *)
  231.     (* P and the output parameter perm is somewhat obscure.  The vector *)
  232.     (* perm^ is not simply a permutation of the subscripts [0..N-1]; it *)
  233.     (* does, however, have the property that we can recreate P by       *)
  234.     (* walking through perm^ from start to finish, in the order used    *)
  235.     (* by procedure LUSolve.                                            *)
  236.  
  237.     VAR row, col, k, pivotrow: CARDINAL;
  238.         sum, temp, maxval: LONGREAL;  VV: VectorPtr;
  239.  
  240.     BEGIN
  241.         VV := NewVector (N);
  242.         oddswaps := FALSE;
  243.  
  244.         (* Start by collecting (in VV), the maximum absolute value in   *)
  245.         (* each row; we'll use this for pivoting decisions.             *)
  246.  
  247.         FOR row := 0 TO N-1 DO
  248.             maxval := 0.0;
  249.             FOR col := 0 TO N-1 DO
  250.                 IF ABS(A[row,col]) > maxval THEN
  251.                     maxval := ABS(A[row,col]);
  252.                 END (*IF*);
  253.             END (*FOR*);
  254.             IF maxval = 0.0 THEN
  255.                 (* An all-zero row can never contribute pivot elements. *)
  256.                 VV^[row] := 0.0;
  257.             ELSE
  258.                 VV^[row] := 1.0/maxval;
  259.             END (*IF*);
  260.         END (*FOR*);
  261.  
  262.         (* Crout's method: we work through one column at a time. *)
  263.  
  264.         FOR col := 0 TO N-1 DO
  265.  
  266.             (* Upper triangular component of this column - except for   *)
  267.             (* the diagonal element, which we leave until after we've   *)
  268.             (* selected a pivot from on or below the diagonal.          *)
  269.  
  270.             IF col > 0 THEN
  271.                 FOR row := 0 TO col-1 DO
  272.                     sum := A[row,col];
  273.                     IF row > 0 THEN
  274.                         FOR k := 0 TO row-1 DO
  275.                            sum := sum - A[row,k] * A[k,col];
  276.                         END (*FOR*);
  277.                     END (*IF*);
  278.                     A[row,col] := sum;
  279.                 END (*FOR*);
  280.             END (*IF*);
  281.  
  282.             (* Lower triangular component in this column.  The results  *)
  283.             (* we get in this loop will not be correct until we've      *)
  284.             (* divided by the pivot; but we work out the pivot location *)
  285.             (* as we go, and come back later for this division.         *)
  286.  
  287.             maxval := 0.0;  pivotrow := col;
  288.             FOR row := col TO N-1 DO
  289.                 sum := A[row,col];
  290.                 IF col > 0 THEN
  291.                     FOR k := 0 TO col-1 DO
  292.                        sum := sum - A[row,k] * A[k,col];
  293.                     END (*FOR*);
  294.                 END (*IF*);
  295.                 A[row,col] := sum;
  296.                 temp := VV^[row] * ABS(sum);
  297.                 IF temp >= maxval THEN
  298.                     maxval := temp;  pivotrow := row;
  299.                 END (*IF*);
  300.             END (*FOR*);
  301.  
  302.             (* If pivot element was not already on the diagonal, do a   *)
  303.             (* row swap.                                                *)
  304.  
  305.             IF pivotrow <> col THEN
  306.                 FOR k := 0 TO N-1 DO
  307.                     temp := A[pivotrow,k];
  308.                     A[pivotrow,k] := A[col,k];
  309.                     A[col,k] := temp;
  310.                 END (*FOR*);
  311.                 oddswaps := NOT oddswaps;
  312.                 VV^[pivotrow] := VV^[col];
  313.  
  314.                 (* We don't bother updating VV^[col] here, because      *)
  315.                 (* its value will never be used again.                  *)
  316.  
  317.             END (*IF*);
  318.             perm^[col] := pivotrow;
  319.  
  320.             (* Finish off the calculation of the lower triangular part  *)
  321.             (* for this column by scaling by the pivot A[col,col].      *)
  322.  
  323.             (* Remark: if the pivot is still zero at this stage, then   *)
  324.             (* all the elements below it are also zero.  The LU         *)
  325.             (* decomposition in this case is not unique - the original  *)
  326.             (* matrix is singular, therefore U will also be singular -  *)
  327.             (* but one solution is to leave all those elements zero.    *)
  328.  
  329.             temp := A[col,col];
  330.             IF (col <> N-1) AND (temp <> 0.0) THEN
  331.                 temp := 1.0/temp;
  332.                 FOR row := col+1 TO N-1 DO
  333.                     A[row,col] := temp*A[row,col];
  334.                 END (*FOR*);
  335.             END (*IF*);
  336.  
  337.         END (*FOR*);
  338.  
  339.     END LUFactor;
  340.  
  341. (************************************************************************)
  342.  
  343. PROCEDURE LUSolve (LU: ARRAY OF ARRAY OF EltType;
  344.                     VAR (*INOUT*) B: ARRAY OF ARRAY OF EltType;
  345.                     N, M: CARDINAL;  perm: Permutation);
  346.  
  347.     (* Solves the equation P*L*U*X = B, where P is a permutation        *)
  348.     (* matrix specified indirectly by perm; L is lower triangular; and  *)
  349.     (* U is upper triangular.  The "Matrix" LU is not a genuine matrix, *)
  350.     (* but rather a packed form of L and U as produced by procedure     *)
  351.     (* LUfactor above.  On return B is replaced by the solution X.      *)
  352.     (* Dimensions: left side is NxN, B is NxM.                          *)
  353.  
  354.     VAR i, j, k, ip: CARDINAL;  sum, scale: LONGREAL;
  355.  
  356.     BEGIN
  357.         (* Pass 1: Solve the equation L*Y = B (at the same time sorting *)
  358.         (* B in accordance with the specified permutation).  The        *)
  359.         (* solution Y overwrites the original value of B.               *)
  360.  
  361.         (* Understanding how the permutations work is something of a    *)
  362.         (* black art.  It helps to know that (a) ip>=i for all i, and   *)
  363.         (* (b) in the summation over k below, we are accessing only     *)
  364.         (* those elements of B that have already been sorted into the   *)
  365.         (* correct order.                                               *)
  366.  
  367.         FOR i := 0 TO N-1 DO
  368.             ip := perm^[i];
  369.             FOR j := 0 TO M-1 DO
  370.                 sum := B[ip,j];
  371.                 B[ip,j] := B[i,j];
  372.                 IF i > 0 THEN
  373.                     FOR k := 0 TO i-1 DO
  374.                         sum := sum - LU[i,k] * B[k,j];
  375.                     END (*FOR*);
  376.                 END (*IF*);
  377.                 B[i,j] := sum;
  378.             END (*FOR*);
  379.         END (*FOR*);
  380.  
  381.         (* Pass 2: solve the equation U*X = Y. *)
  382.  
  383.         FOR i := N-1 TO 0 BY -1 DO
  384.             scale := LU[i,i];
  385.             IF scale = 0.0 THEN
  386.                 Error ("Matrix is singular");
  387.                 RETURN;
  388.             END (*IF*);
  389.             FOR j := 0 TO M-1 DO
  390.                 sum := B[i,j];
  391.                 FOR k := i+1 TO N-1 DO
  392.                     sum := sum - LU[i,k] * B[k,j];
  393.                 END (*FOR*);
  394.                 B[i,j] := sum/scale;
  395.             END (*FOR*);
  396.         END (*FOR*);
  397.  
  398.     END LUSolve;
  399.  
  400. (************************************************************************)
  401.  
  402. PROCEDURE Solve (A, B: ARRAY OF ARRAY OF EltType;
  403.                     VAR (*OUT*) X: ARRAY OF ARRAY OF EltType;
  404.                     N, M: CARDINAL);
  405.  
  406.     (* Solves the equation AX = B.  In the present version A must be    *)
  407.     (* square and nonsingular.                                          *)
  408.     (* Dimensions: A is NxN, B is NxM.                                  *)
  409.  
  410.     VAR LU, error, product: ArrayPtr;
  411.         perm: Permutation;  s: BOOLEAN;
  412.  
  413.     BEGIN
  414.         LU := NewArray (N, N);
  415.         Copy (A, N, N, LU^);  Copy (B, N, M, X);
  416.         ALLOCATE (perm, N*SIZE(subscript));
  417.         LUFactor (LU^, N, perm, s);
  418.         LUSolve (LU^, X, N, M, perm);
  419.  
  420.         (* For better accuracy, apply one step of iterative     *)
  421.         (* improvement.  (Two or three steps might be better;   *)
  422.         (* but they might even make things worse, because we're *)
  423.         (* still stuck with the rounding errors in LUFactor.)   *)
  424.  
  425.         error := NewArray (N, M);
  426.         product := NewArray (N, M);
  427.         Mul (A, X, N, N, M, product^);
  428.         Sub (B, product^, N, M, error^);
  429.         LUSolve (LU^, error^, N, M, perm);
  430.         Add (X, error^, N, M, X);
  431.         DisposeArray (product, N, M);  DisposeArray (error, N, M);
  432.  
  433.         DEALLOCATE (perm, N*SIZE(subscript));
  434.         DisposeArray (LU, N, N);
  435.     END Solve;
  436.  
  437. (************************************************************************)
  438.  
  439. PROCEDURE GaussJ (A, B: ARRAY OF ARRAY OF EltType;
  440.                      VAR (*OUT*) X: ARRAY OF ARRAY OF EltType;
  441.                      N, M: CARDINAL);
  442.  
  443.     (* Solves the equation AX = B by Gauss-Jordan elimination.  In the  *)
  444.     (* present version A must be square and nonsingular.                *)
  445.     (* Dimensions: A is NxN, B is NxM.                                  *)
  446.  
  447.     VAR W: ArrayPtr;  i, j, k, prow: CARDINAL;
  448.         pivot, scale, temp: EltType;
  449.  
  450.     BEGIN
  451.         W := NewArray (N, N);  Copy (A, N, N, W^);  Copy (B, N, M, X);
  452.  
  453.         (* Remark: we are going to use elementary row operations to     *)
  454.         (* turn W into a unit matrix.  However we don't bother to store *)
  455.         (* the new 1.0 and 0.0 entries, because those entries will      *)
  456.         (* never be fetched again.  We simply base our calculations on  *)
  457.         (* the assumption that those values have been stored.           *)
  458.  
  459.         (* Pass 1: by elementary row operations, make W into an upper   *)
  460.         (* triangular matrix.                                           *)
  461.  
  462.         prow := 0;
  463.         FOR i := 0 TO N-1 DO
  464.             pivot := 0.0;
  465.             FOR j := i TO N-1 DO
  466.                 temp := W^[j,i];
  467.                 IF ABS(temp) > ABS(pivot) THEN
  468.                     pivot := temp;  prow := j;
  469.                 END (*IF*);
  470.             END (*FOR*);
  471.             IF ABS(pivot) < small THEN
  472.                 Error ("Coefficient matrix is singular");
  473.             END (*IF*);
  474.  
  475.             (* Swap rows i and prow. *)
  476.  
  477.             FOR j := i TO N-1 DO
  478.                 temp := W^[i,j];
  479.                 W^[i,j] := W^[prow,j];
  480.                 W^[prow,j] := temp;
  481.             END (*FOR*);
  482.             FOR j := 0 TO M-1 DO
  483.                 temp := X[i,j];
  484.                 X[i,j] := X[prow,j];
  485.                 X[prow,j] := temp;
  486.             END (*FOR*);
  487.  
  488.             (* Scale the i'th row of both W and X. *)
  489.  
  490.             FOR j := i+1 TO N-1 DO
  491.                 W^[i,j] := W^[i,j]/pivot;
  492.             END (*FOR*);
  493.             FOR j := 0 TO M-1 DO
  494.                 X[i,j] := X[i,j]/pivot;
  495.             END (*FOR*);
  496.  
  497.             (* Implicitly reduce the sub-column below W[i,i] to zero.   *)
  498.  
  499.             FOR k := i+1 TO N-1 DO
  500.                 scale := W^[k,i];
  501.                 FOR j := i+1 TO N-1 DO
  502.                     W^[k,j] := W^[k,j] - scale*W^[i,j];
  503.                 END (*FOR*);
  504.                 FOR j := 0 TO M-1 DO
  505.                     X[k,j] := X[k,j] - scale*X[i,j];
  506.                 END (*FOR*);
  507.             END (*FOR*);
  508.  
  509.         END (*FOR*);
  510.  
  511.         (* Pass 2: reduce the above-diagonal elements of W to zero.     *)
  512.  
  513.         FOR i := N-1 TO 1 BY -1 DO
  514.  
  515.             (* Implicitly reduce the sub-column above W[i,i] to zero.   *)
  516.  
  517.             FOR k := 0 TO i-1 DO
  518.                 scale := W^[k,i];
  519.                 FOR j := 0 TO M-1 DO
  520.                     X[k,j] := X[k,j] - scale*X[i,j];
  521.                 END (*FOR*);
  522.             END (*FOR*);
  523.  
  524.         END (*FOR*);
  525.  
  526.         DisposeArray (W, N, N);
  527.  
  528.     END GaussJ;
  529.  
  530. (************************************************************************)
  531.  
  532. PROCEDURE Invert (A: ARRAY OF ARRAY OF EltType;
  533.                            VAR (*INOUT*) X: ARRAY OF ARRAY OF EltType;
  534.                            N: CARDINAL);
  535.  
  536.     (* Inverts an NxN nonsingular matrix. *)
  537.  
  538.     VAR I: ArrayPtr;
  539.  
  540.     BEGIN
  541.         I := NewArray (N, N);  Unit (I^, N);
  542.         Solve (A, I^, X, N, N);
  543.         DisposeArray (I, N, N);
  544.     END Invert;
  545.  
  546. (************************************************************************)
  547. (*                         EIGENPROBLEMS                                *)
  548. (************************************************************************)
  549.  
  550. PROCEDURE Balance (VAR (*INOUT*) A: ARRAY OF ARRAY OF EltType;  N: CARDINAL);
  551.  
  552.     (* Replaces A by a better-balanced matrix with the same eigenvalues.*)
  553.     (* There is no effect on symmetrical matrices.  To minimize the     *)
  554.     (* effect of rounding, we scale only by a restricted set of scaling *)
  555.     (* factors derived from the machine's radix.                        *)
  556.  
  557.     CONST radix = 2.0;  radixsq = radix*radix;
  558.  
  559.     VAR row, j: CARDINAL;  c, r, f, g, s: LONGREAL;
  560.         done: BOOLEAN;
  561.  
  562.     BEGIN
  563.         REPEAT
  564.             done := TRUE;
  565.             FOR row := 0 TO N-1 DO
  566.                 c := 0.0;  r := 0.0;
  567.                 FOR j := 0 TO N-1 DO
  568.                     IF j <> row THEN
  569.                         c := c + ABS(A[j,row]);
  570.                         r := r + ABS(A[row,j]);
  571.                     END (*IF*);
  572.                 END (*FOR*);
  573.                 IF (c <> 0.0) AND (r <> 0.0) THEN
  574.                     g := r/radix;  f := 1.0;  s := c + r;
  575.                     WHILE c < g DO
  576.                         f := f*radix;  c := c*radixsq;
  577.                     END (*WHILE*);
  578.                     g := r*radix;
  579.                     WHILE c > g DO
  580.                         f := f/radix;  c := c/radixsq;
  581.                     END (*WHILE*);
  582.                     IF (c+r)/f < 0.95*s THEN
  583.                         done := FALSE;  g := 1.0/f;
  584.  
  585.                         (* Here is the actual transformation: a scaling *)
  586.                         (* of this row and the corresponding column.    *)
  587.  
  588.                         FOR j := 0 TO N-1 DO
  589.                             A[row,j] := g*A[row,j];
  590.                         END (*FOR*);
  591.                         FOR j := 0 TO N-1 DO
  592.                             A[j,row] := f*A[j,row];
  593.                         END (*FOR*);
  594.                     END (*IF*);
  595.  
  596.                 END (*IF c and r nonzero *);
  597.  
  598.             END (*FOR row := 0 TO N-1*);
  599.  
  600.         UNTIL done;
  601.  
  602.     END Balance;
  603.  
  604. (************************************************************************)
  605.  
  606. PROCEDURE Hessenberg (VAR (*INOUT*) A: ARRAY OF ARRAY OF EltType;  N: CARDINAL);
  607.  
  608.     (* Transforms an NxN matrix into upper Hessenberg form, i.e. all    *)
  609.     (* entries below the diagonal zero except for the first subdiagonal.*)
  610.     (* This is an "in-place" calculation, i.e. the answer replaces the  *)
  611.     (* original matrix.                                                 *)
  612.  
  613.     CONST small = 1.0E-15;
  614.  
  615.     VAR pos, i, j, pivotrow: CARDINAL;
  616.         pivot, temp: LONGREAL;  V: VectorPtr;
  617.  
  618.     BEGIN
  619.         IF N <= 2 THEN RETURN END(*IF*);
  620.  
  621.         V := NewVector (N);
  622.         FOR pos := 1 TO N-2 DO
  623.  
  624.             (* At this point in the calculation, A has the form         *)
  625.             (*          A11     A12                                     *)
  626.             (*          A21     A22                                     *)
  627.             (* where A11 has (pos+1) rows and columns and is already in *)
  628.             (* upper Hessenberg form; and A21 is zero except for its    *)
  629.             (* last two columns.  This time around the loop, we are     *)
  630.             (* going to transform A such that column (pos-1) of A21 is  *)
  631.             (* reduced to zero.  The transformation will affect only    *)
  632.             (* the last column of A11, therefore will not alter its     *)
  633.             (* Hessenberg property.                                     *)
  634.  
  635.             (* Step 1: we need A[pos,pos-1] to be nonzero.  To keep     *)
  636.             (* the calculations as well-conditioned as possible, we     *)
  637.             (* allow for a preliminary row and column swap.             *)
  638.  
  639.             pivot := A[pos,pos-1];  pivotrow := pos;
  640.             FOR i := pos+1 TO N-1 DO
  641.                 temp := A[i,pos-1];
  642.                 IF ABS(temp) > ABS(pivot) THEN
  643.                     pivot := temp;  pivotrow := i;
  644.                 END (*IF*);
  645.             END (*FOR*);
  646.  
  647.             IF ABS(pivot) < small THEN
  648.  
  649.                 (* The pivot is essentially zero, so we already have    *)
  650.                 (* the desired property and no transformation is        *)
  651.                 (* necessary this time.  We simply replace all of the   *)
  652.                 (* "approximately zero" entries by 0.0.                 *)
  653.  
  654.                 FOR i := pos TO N-1 DO
  655.                     A[i,pos-1] := 0.0;
  656.                 END (*FOR*);
  657.  
  658.             ELSE
  659.  
  660.                 IF pivotrow <> pos THEN
  661.  
  662.                     (* Swap rows pos and pivotrow, and then swap the    *)
  663.                     (* corresponding columns.                           *)
  664.  
  665.                     FOR j := pos-1 TO N-1 DO
  666.                         temp := A[pivotrow,j];
  667.                         A[pivotrow,j] := A[pos,j];
  668.                         A[pos,j] := temp;
  669.                     END (*FOR*);
  670.                     FOR i := 0 TO N-1 DO
  671.                         temp := A[i,pivotrow];
  672.                         A[i,pivotrow] := A[i,pos];
  673.                         A[i,pos] := temp;
  674.                     END (*FOR*);
  675.  
  676.                 END (*IF*);
  677.  
  678.                 (* Now we are going to replace A by T*A*Inverse(T),     *)
  679.                 (* where T is a unit matrix except for column pos.      *)
  680.                 (* That column is equal to a vector V, where V[i] = 0.0 *)
  681.                 (* for i < pos, and V[pos] = 1.0.  We don't bother      *)
  682.                 (* storing those fixed elements explicitly.             *)
  683.  
  684.                 FOR i := pos+1 TO N-1 DO
  685.                     V^[i] := -A[i,pos-1] / pivot;
  686.                 END (*FOR*);
  687.  
  688.                 (* Premultiplication of A by T.  Because of the special *)
  689.                 (* structure of T, this affects only rows [pos+1..N].   *)
  690.                 (* We also know that some of the results will be zero.  *)
  691.  
  692.                 FOR i := pos+1 TO N-1 DO
  693.                     A[i,pos-1] := 0.0;
  694.                     FOR j := pos TO N-1 DO
  695.                         A[i,j] := A[i,j] + V^[i] * A[pos,j];
  696.                     END (*FOR*);
  697.                 END (*FOR*);
  698.  
  699.                 (* Postmultiplication by the inverse of T.  This affects*)
  700.                 (* only column pos.                                     *)
  701.  
  702.                 FOR i := 0 TO N-1 DO
  703.                     temp := 0.0;
  704.                     FOR j := pos+1 TO N-1 DO
  705.                         temp := temp + A[i,j] * V^[j];
  706.                     END (*FOR*);
  707.                     A[i,pos] := A[i,pos] - temp;
  708.                 END (*FOR*);
  709.  
  710.             END (*IF*);
  711.  
  712.         END (*FOR*);
  713.         DisposeVector (V, N);
  714.  
  715.     END Hessenberg;
  716.  
  717. (************************************************************************)
  718.  
  719. PROCEDURE QR (A: ARRAY OF ARRAY OF EltType;  VAR (*OUT*) W: ARRAY OF LONGCOMPLEX;
  720.                                              N: CARDINAL);
  721.  
  722.     (* Finds all the eigenvalues of an upper Hessenberg matrix.         *)
  723.     (* On return W contains the eigenvalues.                            *)
  724.  
  725.     (* Source: this is an adaption of code from "Numerical Recipes"     *)
  726.     (* by Press, Flannery, Teutolsky, and Vetterling.                   *)
  727.  
  728.     VAR last, m, j, k, L, its, i, imax: CARDINAL;
  729.         z, y, x, w, v, u, shift, s, r, q, p, anorm: LONGREAL;
  730.  
  731.     (********************************************************************)
  732.  
  733.     BEGIN
  734.         (* Compute matrix norm.  This looks wrong to me, but it seems   *)
  735.         (* to be giving satisfactory results.                           *)
  736.  
  737.         anorm := ABS(A[0,0]);
  738.         FOR i := 1 TO N-1 DO
  739.             FOR j := i-1 TO N-1 DO
  740.                 anorm := anorm + ABS(A[i,j]);
  741.             END (*FOR*);
  742.         END (*FOR*);
  743.  
  744.         last  := N-1;  shift := 0.0;
  745.         its := 0;
  746.  
  747.         LOOP
  748.  
  749.             (* Find, if possible, an L such that A[L,L-1] is zero to    *)
  750.             (* machine accuracy.  If we succeed then A is now block     *)
  751.             (* diagonal, and we can work independently on the final     *)
  752.             (* block (rows and columns L to last).                      *)
  753.  
  754.             L := last;
  755.             LOOP
  756.                 IF L = 0 THEN EXIT(*LOOP*) END(*IF*);
  757.                 s := ABS(A[L-1,L-1]) + ABS(A[L,L]);
  758.                 IF s = 0.0 THEN s := anorm END(*IF*);
  759.                 IF ABS(A[L,L-1]) + s = s THEN
  760.                     EXIT (*LOOP*);
  761.                 END (*IF*);
  762.                 DEC (L);
  763.             END (*LOOP*);
  764.  
  765.             x := A[last,last];
  766.             IF L = last THEN
  767.  
  768.                 (* One eigenvalue found. *)
  769.  
  770.                 W[last] := CMPLX (x+shift, 0.0);
  771.                 IF last > 0 THEN DEC (last);  its := 0;
  772.                 ELSE EXIT (*LOOP*);
  773.                 END (*IF*);
  774.  
  775.             ELSE
  776.                 y := A[last-1,last-1];
  777.                 w := A[last,last-1]*A[last-1,last];
  778.                 IF L = last-1 THEN
  779.  
  780.                     (* We're down to a 2x2 submatrix, so can work out   *)
  781.                     (* the eigenvalues directly.                        *)
  782.  
  783.                     p := 0.5*(y-x);  q := p*p + w;
  784.                     z := Sqrt(ABS(q));  x := x + shift;
  785.                     IF q >= 0.0 THEN
  786.                         IF p < 0.0 THEN z := p - z;
  787.                         ELSE z := p + z;
  788.                         END (*IF*);
  789.                         W[last-1] := CMPLX (x+z, 0.0);
  790.                         IF z = 0.0 THEN
  791.                             W[last] := CMPLX (x, 0.0);
  792.                         ELSE
  793.                             W[last] := CMPLX (x - w/z, 0.0);
  794.                         END (*IF*);
  795.                     ELSE
  796.                         W[last-1] := CMPLX (x+p, z);
  797.                         W[last] := conj (W[last-1]);
  798.                     END (*IF*);
  799.                     IF last >= 2 THEN DEC (last, 2);  its := 0;
  800.                     ELSE EXIT (*LOOP*)
  801.                     END (*IF*);
  802.  
  803.                 ELSE
  804.  
  805.                     IF its < 10 THEN
  806.                         INC (its);
  807.                     ELSE
  808.                         (* If we're converging too slowly,      *)
  809.                         (* modify the shift.                    *)
  810.  
  811.                         shift := shift + x;
  812.                         FOR i := 0 TO last DO
  813.                             A[i,i] := A[i,i] - x;
  814.                         END (*FOR*);
  815.                         s := ABS(A[last,last-1]) + ABS(A[last-1,last-2]);
  816.                         x := 0.75*s;
  817.                         y := x;  w := -0.4375*s*s;
  818.                         its := 0;
  819.                     END (*IF*);
  820.  
  821.                     (* We're now working on a sub-array [L..last] of    *)
  822.                     (* size 3x3 or greater.  Our goal is to transform   *)
  823.                     (* the matrix so as to reduce the magnitudes of     *)
  824.                     (* the elements on the first sub-diagonal, so that  *)
  825.                     (* after one or more iterations one of them will be *)
  826.                     (* zero to within machine accuracy.                 *)
  827.  
  828.                     (* Shortcut: if we can find two consecutive         *)
  829.                     (* subdiagonal elements whose product is small,     *)
  830.                     (* we're even better off.                           *)
  831.  
  832.                     m := last-2;
  833.                     LOOP
  834.                         z := A[m,m];  r := x-z;  s := y-z;
  835.                         p := (r*s-w)/A[m+1,m] + A[m,m+1];
  836.                         q := A[m+1,m+1] - z - r - s;
  837.                         r := A[m+2,m+1];
  838.                         s := ABS(p) + ABS(q) + ABS(r);
  839.                         p := p/s;  q := q/s;  r := r/s;
  840.                         IF m = L THEN EXIT(*LOOP*) END(*IF*);
  841.                         u := ABS(A[m,m-1]) * (ABS(q) + ABS(r));
  842.                         v := ABS(p) * (ABS(A[m-1,m-1]) + ABS(z)
  843.                                         + ABS(A[m+1,m+1]));
  844.                         IF u+v = v THEN EXIT(*LOOP*) END(*IF*);
  845.                         DEC (m);
  846.                     END (*LOOP*);
  847.  
  848.                     A[m+2,m] := 0.0;
  849.                     FOR i := m+3 TO last DO
  850.                         A[i,i-2] := 0.0;
  851.                         A[i,i-3] := 0.0;
  852.                     END (*FOR*);
  853.  
  854.                     (* Apply row and column transformations that should *)
  855.                     (* reduce the magnitudes of subdiagonal elements.   *)
  856.  
  857.                     FOR k := m TO last-1 DO
  858.                         IF k <> m THEN
  859.                             p := A[k,k-1];  q := A[k+1,k-1];
  860.                             r := 0.0;
  861.                             IF k <> last-1 THEN
  862.                                 r := A[k+2,k-1];
  863.                             END (*IF*);
  864.                             x := ABS(p) + ABS(q) + ABS(r);
  865.                             IF x <> 0.0 THEN
  866.                                 p := p/x;  q := q/x;  r := r/x;
  867.                             END (*IF*);
  868.                         END (*IF*);
  869.                         s := Sqrt(p*p + q*q + r*r);
  870.                         IF p < 0.0 THEN s := -s END(*IF*);
  871.                         IF s <> 0.0 THEN
  872.                             IF k = m THEN
  873.                                 IF L <> m THEN
  874.                                     A[k,k-1] := -A[k,k-1];
  875.                                 END(*IF*);
  876.                             ELSE
  877.                                 A[k,k-1] := -s*x;
  878.                             END (*IF*);
  879.                             p := p + s;  x := p/s;  y := q/s;  z := r/s;
  880.                             q := q/p;  r := r/p;
  881.  
  882.                             (* Row transformation. *)
  883.  
  884.                             FOR j := k TO last DO
  885.                                 p := A[k,j] + q * A[k+1,j];
  886.                                 IF k <> last-1 THEN
  887.                                     p := p + r * A[k+2,j];
  888.                                     A[k+2,j] := A[k+2,j] - p*z;
  889.                                 END (*IF*);
  890.                                 A[k+1,j] := A[k+1,j] - p*y;
  891.                                 A[k,j] := A[k,j] - p*x;
  892.                             END (*FOR*);
  893.  
  894.                             (* Column transformation. *)
  895.  
  896.                             imax := k+3;
  897.                             IF last < imax THEN imax := last END(*IF*);
  898.                             FOR i := L TO imax DO
  899.                                 p := x * A[i,k] + y * A[i,k+1];
  900.                                 IF k <> last-1 THEN
  901.                                     p := p + z * A[i,k+2];
  902.                                     A[i,k+2] := A[i,k+2] - p*r;
  903.                                 END (*IF*);
  904.                                 A[i,k+1] := A[i,k+1] - p*q;
  905.                                 A[i,k] := A[i,k] - p;
  906.                             END (*FOR*);
  907.  
  908.                         END (*IF s <> 0.0 *);
  909.  
  910.                     END (*FOR k := m TO last-1 *);
  911.  
  912.                 END (*IF test for 2x2 or bigger *);
  913.  
  914.             END (*IF test for 1x1 *);
  915.  
  916.         END (* main loop *);
  917.  
  918.     END QR;
  919.  
  920. (************************************************************************)
  921.  
  922. PROCEDURE Eigenvalues (A: ARRAY OF ARRAY OF EltType;
  923.                           VAR (*OUT*) W: ARRAY OF LONGCOMPLEX;
  924.                           N: CARDINAL);
  925.  
  926.     (* Finds all the eigenvalues of an NxN matrix.    *)
  927.     (* This procedure does not modify A.              *)
  928.  
  929.     VAR Acopy: ArrayPtr;
  930.  
  931.     BEGIN
  932.         IF N > 0 THEN
  933.             Acopy := NewArray (N, N);  Copy (A, N, N, Acopy^);
  934.             Balance (Acopy^, N);
  935.             Hessenberg (Acopy^, N);
  936.             QR (Acopy^, W, N);
  937.             DisposeArray (Acopy, N, N);
  938.         END (*IF*);
  939.     END Eigenvalues;
  940.  
  941. (************************************************************************)
  942. (*                              OUTPUT                                  *)
  943. (************************************************************************)
  944.  
  945. PROCEDURE Write (M: ARRAY OF ARRAY OF EltType;  r, c: CARDINAL;  places: CARDINAL);
  946.  
  947.     (* Writes the rxc matrix M to the screen, where each column *)
  948.     (* occupies a field "places" characters wide.               *)
  949.  
  950.     VAR i, j: CARDINAL;
  951.  
  952.     BEGIN
  953.         FOR i := 0 TO r-1 DO
  954.             FOR j := 0 TO c-1 DO
  955.                 WriteString ("  ");
  956.                 WriteLongReal (M[i,j], places-2);
  957.             END (*FOR*);
  958.             WriteLn;
  959.         END (*FOR*);
  960.     END Write;
  961.  
  962. (************************************************************************)
  963.  
  964. END Mat.
  965.  
  966.