spUtils.c

Go to the documentation of this file.
00001 /*
00002  *  MATRIX UTILITY MODULE
00003  *
00004  *  Author:                     Advising professor:
00005  *      Kenneth S. Kundert          Alberto Sangiovanni-Vincentelli
00006  *      UC Berkeley
00007  *
00008  *  This file contains various optional utility routines.
00009  *
00010  *  >>> User accessible functions contained in this file:
00011  *  spMNA_Preorder
00012  *  spScale
00013  *  spMultiply
00014  *  spMultTransposed
00015  *  spDeterminant
00016  *  spStrip
00017  *  spDeleteRowAndCol
00018  *  spPseudoCondition
00019  *  spCondition
00020  *  spNorm
00021  *  spLargestElement
00022  *  spRoundoff
00023  *
00024  *  >>> Other functions contained in this file:
00025  *  CountTwins
00026  *  SwapCols
00027  *  ScaleComplexMatrix
00028  *  ComplexMatrixMultiply
00029  *  ComplexCondition
00030  */
00031 
00032 
00033 /*
00034  *  Revision and copyright information.
00035  *
00036  *  Copyright (c) 1985,86,87,88
00037  *  by Kenneth S. Kundert and the University of California.
00038  *
00039  *  Permission to use, copy, modify, and distribute this software and
00040  *  its documentation for any purpose and without fee is hereby granted,
00041  *  provided that the copyright notices appear in all copies and
00042  *  supporting documentation and that the authors and the University of
00043  *  California are properly credited.  The authors and the University of
00044  *  California make no representations as to the suitability of this
00045  *  software for any purpose.  It is provided `as is', without express
00046  *  or implied warranty.
00047  */
00048 
00049 
00050 /*
00051  *  IMPORTS
00052  *
00053  *  >>> Import descriptions:
00054  *  spConfig.h
00055  *      Macros that customize the sparse matrix routines.
00056  *  spmatrix.h
00057  *      Macros and declarations to be imported by the user.
00058  *  spDefs.h
00059  *      Matrix type and macro definitions for the sparse matrix routines.
00060  */
00061 
00062 #define spINSIDE_SPARSE
00063 
00064 
00065 #include "spConfig.h"
00066 #include "spUtils.h"
00067 #include "spmatrix.h"
00068 #include "spDefs.h"
00069 #include "spmalloc.h"
00070 
00071 #if MODIFIED_NODAL
00072 /*
00073  *  PREORDER MODIFIED NODE ADMITTANCE MATRIX TO REMOVE ZEROS FROM DIAGONAL
00074  *
00075  *  This routine massages modified node admittance matrices to remove
00076  *  zeros from the diagonal.  It takes advantage of the fact that the
00077  *  row and column associated with a zero diagonal usually have
00078  *  structural ones placed symmetricly.  This routine should be used
00079  *  only on modified node admittance matrices and should be executed
00080  *  after the matrix has been built but before the factorization
00081  *  begins.  It should be executed for the initial factorization only
00082  *  and should be executed before the rows have been linked.  Thus it
00083  *  should be run before using spScale(), spMultiply(),
00084  *  spDeleteRowAndCol(), or spNorm().
00085  *
00086  *  This routine exploits the fact that the structural ones are placed
00087  *  in the matrix in symmetric twins.  For example, the stamps for
00088  *  grounded and a floating voltage sources are
00089  *  grounded:              floating:
00090  *  [  x   x   1 ]         [  x   x   1 ]
00091  *  [  x   x     ]         [  x   x  -1 ]
00092  *  [  1         ]         [  1  -1     ]
00093  *  Notice for the grounded source, there is one set of twins, and for
00094  *  the floating, there are two sets.  We remove the zero from the diagonal
00095  *  by swapping the rows associated with a set of twins.  For example:
00096  *  grounded:              floating 1:            floating 2:
00097  *  [  1         ]         [  1  -1     ]         [  x   x   1 ]
00098  *  [  x   x     ]         [  x   x  -1 ]         [  1  -1     ]
00099  *  [  x   x   1 ]         [  x   x   1 ]         [  x   x  -1 ]
00100  *
00101  *  It is important to deal with any zero diagonals that only have one
00102  *  set of twins before dealing with those that have more than one because
00103  *  swapping row destroys the symmetry of any twins in the rows being
00104  *  swapped, which may limit future moves.  Consider
00105  *  [  x   x   1     ]
00106  *  [  x   x  -1   1 ]
00107  *  [  1  -1         ]
00108  *  [      1         ]
00109  *  There is one set of twins for diagonal 4 and two for diagonal 3.
00110  *  Dealing with diagonal 4 first requires swapping rows 2 and 4.
00111  *  [  x   x   1     ]
00112  *  [      1         ]
00113  *  [  1  -1         ]
00114  *  [  x   x  -1   1 ]
00115  *  We can now deal with diagonal 3 by swapping rows 1 and 3.
00116  *  [  1  -1         ]
00117  *  [      1         ]
00118  *  [  x   x   1     ]
00119  *  [  x   x  -1   1 ]
00120  *  And we are done, there are no zeros left on the diagonal.  However, if
00121  *  we originally dealt with diagonal 3 first, we could swap rows 2 and 3
00122  *  [  x   x   1     ]
00123  *  [  1  -1         ]
00124  *  [  x   x  -1   1 ]
00125  *  [      1         ]
00126  *  Diagonal 4 no longer has a symmetric twin and we cannot continue.
00127  *
00128  *  So we always take care of lone twins first.  When none remain, we
00129  *  choose arbitrarily a set of twins for a diagonal with more than one set
00130  *  and swap the rows corresponding to that twin.  We then deal with any
00131  *  lone twins that were created and repeat the procedure until no
00132  *  zero diagonals with symmetric twins remain.
00133  *
00134  *  In this particular implementation, columns are swapped rather than rows.
00135  *  The algorithm used in this function was developed by Ken Kundert and
00136  *  Tom Quarles.
00137  *
00138  *  >>> Arguments:
00139  *  eMatrix  <input>  (char *)
00140  *      Pointer to the matrix to be preordered.
00141  *
00142  *  >>> Local variables;
00143  *  J  (int)
00144  *      Column with zero diagonal being currently considered.
00145  *  pTwin1  (ElementPtr)
00146  *      Pointer to the twin found in the column belonging to the zero diagonal.
00147  *  pTwin2  (ElementPtr)
00148  *      Pointer to the twin found in the row belonging to the zero diagonal.
00149  *      belonging to the zero diagonal.
00150  *  AnotherPassNeeded  (SPBOOLEAN)
00151  *      Flag indicating that at least one zero diagonal with symmetric twins
00152  *      remain.
00153  *  StartAt  (int)
00154  *      Column number of first zero diagonal with symmetric twins.
00155  *  Swapped  (SPBOOLEAN)
00156  *      Flag indicating that columns were swapped on this pass.
00157  *  Twins  (int)
00158  *      Number of symmetric twins corresponding to current zero diagonal.
00159  */
00160 #undef SPBOOLEAN
00161 #define  SPBOOLEAN        int
00162 
00163 void spMNA_Preorder( char * eMatrix )
00164 {
00165 MatrixPtr  Matrix = (MatrixPtr)eMatrix;
00166 register  int  J, Size;
00167 ElementPtr  pTwin1, pTwin2;
00168 int  Twins, StartAt = 1;
00169 SPBOOLEAN  Swapped, AnotherPassNeeded;
00170 
00171 /* Begin `spMNA_Preorder'. */
00172     ASSERT( IS_VALID(Matrix) AND NOT Matrix->Factored );
00173 
00174     if (Matrix->RowsLinked) return;
00175     Size = Matrix->Size;
00176     Matrix->Reordered = YES;
00177 
00178     do
00179     {   AnotherPassNeeded = Swapped = NO;
00180 
00181 /* Search for zero diagonals with lone twins. */
00182         for (J = StartAt; J <= Size; J++)
00183         {   if (Matrix->Diag[J] == NULL)
00184             {   Twins = CountTwins( Matrix, J, &pTwin1, &pTwin2 );
00185                 if (Twins == 1)
00186                 {   /* Lone twins found, swap rows. */
00187                     SwapCols( Matrix, pTwin1, pTwin2 );
00188                     Swapped = YES;
00189                 }
00190                 else if ((Twins > 1) AND NOT AnotherPassNeeded)
00191                 {   AnotherPassNeeded = YES;
00192                     StartAt = J;
00193                 }
00194             }
00195         }
00196 
00197 /* All lone twins are gone, look for zero diagonals with multiple twins. */
00198         if (AnotherPassNeeded)
00199         {   for (J = StartAt; NOT Swapped AND (J <= Size); J++)
00200             {   if (Matrix->Diag[J] == NULL)
00201                 {   Twins = CountTwins( Matrix, J, &pTwin1, &pTwin2 );
00202                     SwapCols( Matrix, pTwin1, pTwin2 );
00203                     Swapped = YES;
00204                 }
00205             }
00206         }
00207     } while (AnotherPassNeeded);
00208     return;
00209 }
00210 
00211 
00212 
00213 
00214 /*
00215  *  COUNT TWINS
00216  *
00217  *  This function counts the number of symmetric twins associated with
00218  *  a zero diagonal and returns one set of twins if any exist.  The
00219  *  count is terminated early at two.
00220  */
00221 
00222 static int
00223 CountTwins( MatrixPtr Matrix, int Col, ElementPtr *ppTwin1, ElementPtr *ppTwin2 )
00224 {
00225 int Row, Twins = 0;
00226 ElementPtr pTwin1, pTwin2;
00227 
00228 /* Begin `CountTwins'. */
00229 
00230     pTwin1 = Matrix->FirstInCol[Col];
00231     while (pTwin1 != NULL)
00232     {   if (ABS(pTwin1->Real) == 1.0)
00233         {   Row = pTwin1->Row;
00234             pTwin2 = Matrix->FirstInCol[Row];
00235             while ((pTwin2 != NULL) AND (pTwin2->Row != Col))
00236                 pTwin2 = pTwin2->NextInCol;
00237             if ((pTwin2 != NULL) AND (ABS(pTwin2->Real) == 1.0))
00238             {   /* Found symmetric twins. */
00239                 if (++Twins >= 2) return Twins;
00240                 (*ppTwin1 = pTwin1)->Col = Col;
00241                 (*ppTwin2 = pTwin2)->Col = Row;
00242             }
00243         }
00244         pTwin1 = pTwin1->NextInCol;
00245     }
00246     return Twins;
00247 }
00248 
00249 
00250 
00251 
00252 /*
00253  *  SWAP COLUMNS
00254  *
00255  *  This function swaps two columns and is applicable before the rows are
00256  *  linked.
00257  */
00258 
00259 static int
00260 SwapCols( MatrixPtr Matrix, ElementPtr pTwin1, ElementPtr pTwin2 )
00261 {
00262 int Col1 = pTwin1->Col, Col2 = pTwin2->Col;
00263 
00264 /* Begin `SwapCols'. */
00265 
00266     SWAP (ElementPtr, Matrix->FirstInCol[Col1], Matrix->FirstInCol[Col2]);
00267     SWAP (int, Matrix->IntToExtColMap[Col1], Matrix->IntToExtColMap[Col2]);
00268 #if TRANSLATE
00269     Matrix->ExtToIntColMap[Matrix->IntToExtColMap[Col2]]=Col2;
00270     Matrix->ExtToIntColMap[Matrix->IntToExtColMap[Col1]]=Col1;
00271 #endif
00272 
00273     Matrix->Diag[Col1] = pTwin2;
00274     Matrix->Diag[Col2] = pTwin1;
00275     Matrix->NumberOfInterchangesIsOdd = NOT Matrix->NumberOfInterchangesIsOdd;
00276     return 0;
00277 }
00278 #endif /* MODIFIED_NODAL */
00279 
00280 #if SCALING
00281 /*
00282  *  SCALE MATRIX
00283  *
00284  *  This function scales the matrix to enhance the possibility of
00285  *  finding a good pivoting order.  Note that scaling enhances accuracy
00286  *  of the solution only if it affects the pivoting order, so it makes
00287  *  no sense to scale the matrix before spFactor().  If scaling is
00288  *  desired it should be done before spOrderAndFactor().  There
00289  *  are several things to take into account when choosing the scale
00290  *  factors.  First, the scale factors are directly multiplied against
00291  *  the elements in the matrix.  To prevent roundoff, each scale factor
00292  *  should be equal to an integer power of the number base of the
00293  *  machine.  Since most machines operate in base two, scale factors
00294  *  should be a power of two.  Second, the matrix should be scaled such
00295  *  that the matrix of element uncertainties is equilibrated.  Third,
00296  *  this function multiplies the scale factors by the elements, so if
00297  *  one row tends to have uncertainties 1000 times smaller than the
00298  *  other rows, then its scale factor should be 1024, not 1/1024.
00299  *  Fourth, to save time, this function does not scale rows or columns
00300  *  if their scale factors are equal to one.  Thus, the scale factors
00301  *  should be normalized to the most common scale factor.  Rows and
00302  *  columns should be normalized separately.  For example, if the size
00303  *  of the matrix is 100 and 10 rows tend to have uncertainties near
00304  *  1e-6 and the remaining 90 have uncertainties near 1e-12, then the
00305  *  scale factor for the 10 should be 1/1,048,576 and the scale factors
00306  *  for the remaining 90 should be 1.  Fifth, since this routine
00307  *  directly operates on the matrix, it is necessary to apply the scale
00308  *  factors to the RHS and Solution vectors.  It may be easier to
00309  *  simply use spOrderAndFactor() on a scaled matrix to choose the
00310  *  pivoting order, and then throw away the matrix.  Subsequent
00311  *  factorizations, performed with spFactor(), will not need to have
00312  *  the RHS and Solution vectors descaled.  Lastly, this function
00313  *  should not be executed before the function spMNA_Preorder.
00314  *
00315  *  >>> Arguments:
00316  *  eMatrix  <input> (char *)
00317  *      Pointer to the matrix to be scaled.
00318  *  SolutionScaleFactors  <input>  (RealVector)
00319  *      The array of Solution scale factors.  These factors scale the columns.
00320  *      All scale factors are real valued.
00321  *  RHS_ScaleFactors  <input>  (RealVector)
00322  *      The array of RHS scale factors.  These factors scale the rows.
00323  *      All scale factors are real valued.
00324  *
00325  *  >>> Local variables:
00326  *  lSize  (int)
00327  *      Local version of the size of the matrix.
00328  *  pElement  (ElementPtr)
00329  *      Pointer to an element in the matrix.
00330  *  pExtOrder  (int *)
00331  *      Pointer into either IntToExtRowMap or IntToExtColMap vector. Used to
00332  *      compensate for any row or column swaps that have been performed.
00333  *  ScaleFactor  (RealNumber)
00334  *      The scale factor being used on the current row or column.
00335  */
00336 
00337 extern void spcLinkRows(MatrixPtr Matrix);
00338 
00339 
00340 void
00341 spScale( eMatrix, RHS_ScaleFactors, SolutionScaleFactors )
00342 
00343 char *eMatrix;
00344 register  RealVector  RHS_ScaleFactors, SolutionScaleFactors;
00345 {
00346 MatrixPtr  Matrix = (MatrixPtr)eMatrix;
00347 register ElementPtr  pElement;
00348 register int  I, lSize, *pExtOrder;
00349 RealNumber  ScaleFactor;
00350 
00351 
00352 /* Begin `spScale'. */
00353     ASSERT( IS_VALID(Matrix) AND NOT Matrix->Factored );
00354     if (NOT Matrix->RowsLinked) spcLinkRows( Matrix );
00355 
00356 #if spCOMPLEX
00357     if (Matrix->Complex)
00358     {   ScaleComplexMatrix( Matrix, RHS_ScaleFactors, SolutionScaleFactors );
00359         return;
00360     }
00361 #endif
00362 
00363 #if REAL
00364     lSize = Matrix->Size;
00365 
00366 /* Correct pointers to arrays for ARRAY_OFFSET */
00367 #if NOT ARRAY_OFFSET
00368     --RHS_ScaleFactors;
00369     --SolutionScaleFactors;
00370 #endif
00371 
00372 /* Scale Rows */
00373     pExtOrder = &Matrix->IntToExtRowMap[1];
00374     for (I = 1; I <= lSize; I++)
00375     {   if ((ScaleFactor = RHS_ScaleFactors[*(pExtOrder++)]) != 1.0)
00376         {   pElement = Matrix->FirstInRow[I];
00377 
00378             while (pElement != NULL)
00379             {   pElement->Real *= ScaleFactor;
00380                 pElement = pElement->NextInRow;
00381             }
00382         }
00383     }
00384 
00385 /* Scale Columns */
00386     pExtOrder = &Matrix->IntToExtColMap[1];
00387     for (I = 1; I <= lSize; I++)
00388     {   if ((ScaleFactor = SolutionScaleFactors[*(pExtOrder++)]) != 1.0)
00389         {   pElement = Matrix->FirstInCol[I];
00390 
00391             while (pElement != NULL)
00392             {   pElement->Real *= ScaleFactor;
00393                 pElement = pElement->NextInCol;
00394             }
00395         }
00396     }
00397     return;
00398 
00399 #endif /* REAL */
00400 }
00401 #endif /* SCALING */
00402 
00403 #if spCOMPLEX AND SCALING
00404 /*
00405  *  SCALE COMPLEX MATRIX
00406  *
00407  *  This function scales the matrix to enhance the possibility of
00408  *  finding a good pivoting order.  Note that scaling enhances accuracy
00409  *  of the solution only if it affects the pivoting order, so it makes
00410  *  no sense to scale the matrix before spFactor().  If scaling is
00411  *  desired it should be done before spOrderAndFactor().  There
00412  *  are several things to take into account when choosing the scale
00413  *  factors.  First, the scale factors are directly multiplied against
00414  *  the elements in the matrix.  To prevent roundoff, each scale factor
00415  *  should be equal to an integer power of the number base of the
00416  *  machine.  Since most machines operate in base two, scale factors
00417  *  should be a power of two.  Second, the matrix should be scaled such
00418  *  that the matrix of element uncertainties is equilibrated.  Third,
00419  *  this function multiplies the scale factors by the elements, so if
00420  *  one row tends to have uncertainties 1000 times smaller than the
00421  *  other rows, then its scale factor should be 1024, not 1/1024.
00422  *  Fourth, to save time, this function does not scale rows or columns
00423  *  if their scale factors are equal to one.  Thus, the scale factors
00424  *  should be normalized to the most common scale factor.  Rows and
00425  *  columns should be normalized separately.  For example, if the size
00426  *  of the matrix is 100 and 10 rows tend to have uncertainties near
00427  *  1e-6 and the remaining 90 have uncertainties near 1e-12, then the
00428  *  scale factor for the 10 should be 1/1,048,576 and the scale factors
00429  *  for the remaining 90 should be 1. Fifth, since this routine
00430  *  directly operates on the matrix, it is necessary to apply the scale
00431  *  factors to the RHS and Solution vectors.  It may be easier to
00432  *  simply use spOrderAndFactor() on a scaled matrix to choose the
00433  *  pivoting order, and then throw away the matrix.  Subsequent
00434  *  factorizations, performed with spFactor(), will not need to have
00435  *  the RHS and Solution vectors descaled.  Lastly, this function
00436  *  should not be executed before the function spMNA_Preorder.
00437  *
00438  *  >>> Arguments:
00439  *  Matrix  <input> (char *)
00440  *      Pointer to the matrix to be scaled.
00441  *  SolutionScaleFactors  <input>  (RealVector)
00442  *      The array of Solution scale factors.  These factors scale the columns.
00443  *      All scale factors are real valued.
00444  *  RHS_ScaleFactors  <input>  (RealVector)
00445  *      The array of RHS scale factors.  These factors scale the rows.
00446  *      All scale factors are real valued.
00447  *
00448  *  >>> Local variables:
00449  *  lSize  (int)
00450  *      Local version of the size of the matrix.
00451  *  pElement  (ElementPtr)
00452  *      Pointer to an element in the matrix.
00453  *  pExtOrder  (int *)
00454  *      Pointer into either IntToExtRowMap or IntToExtColMap vector. Used to
00455  *      compensate for any row or column swaps that have been performed.
00456  *  ScaleFactor  (RealNumber)
00457  *      The scale factor being used on the current row or column.
00458  */
00459 
00460 static void
00461 ScaleComplexMatrix( MatrixPtr Matrix, register  RealVector  RHS_ScaleFactors, register  RealVector  SolutionScaleFactors )
00462 {
00463 register ElementPtr  pElement;
00464 register int  I, lSize, *pExtOrder;
00465 RealNumber  ScaleFactor;
00466 
00467 /* Begin `ScaleComplexMatrix'. */
00468     lSize = Matrix->Size;
00469 
00470 /* Correct pointers to arrays for ARRAY_OFFSET */
00471 #if NOT ARRAY_OFFSET
00472     --RHS_ScaleFactors;
00473     --SolutionScaleFactors;
00474 #endif
00475 
00476 /* Scale Rows */
00477     pExtOrder = &Matrix->IntToExtRowMap[1];
00478     for (I = 1; I <= lSize; I++)
00479     {   if ((ScaleFactor = RHS_ScaleFactors[*(pExtOrder++)]) != 1.0)
00480         {   pElement = Matrix->FirstInRow[I];
00481 
00482             while (pElement != NULL)
00483             {   pElement->Real *= ScaleFactor;
00484                 pElement->Imag *= ScaleFactor;
00485                 pElement = pElement->NextInRow;
00486             }
00487         }
00488     }
00489 
00490 /* Scale Columns */
00491     pExtOrder = &Matrix->IntToExtColMap[1];
00492     for (I = 1; I <= lSize; I++)
00493     {   if ((ScaleFactor = SolutionScaleFactors[*(pExtOrder++)]) != 1.0)
00494         {   pElement = Matrix->FirstInCol[I];
00495 
00496             while (pElement != NULL)
00497             {   pElement->Real *= ScaleFactor;
00498                 pElement->Imag *= ScaleFactor;
00499                 pElement = pElement->NextInCol;
00500             }
00501         }
00502     }
00503     return;
00504 }
00505 #endif /* SCALING AND spCOMPLEX */
00506 
00507 #if MULTIPLICATION
00508 /*
00509  *  MATRIX MULTIPLICATION
00510  *
00511  *  Multiplies matrix by solution vector to find source vector.
00512  *  Assumes matrix has not been factored.  This routine can be used
00513  *  as a test to see if solutions are correct.  It should not be used
00514  *  before spMNA_Preorder().
00515  *
00516  *  >>> Arguments:
00517  *  eMatrix  <input>  (char *)
00518  *      Pointer to the matrix.
00519  *  RHS  <output>  (RealVector)
00520  *      RHS is the right hand side. This is what is being solved for.
00521  *  Solution  <input>  (RealVector)
00522  *      Solution is the vector being multiplied by the matrix.
00523  *  iRHS  <output>  (RealVector)
00524  *      iRHS is the imaginary portion of the right hand side. This is
00525  *      what is being solved for.  This is only necessary if the matrix is
00526  *      complex and spSEPARATED_COMPLEX_VECTORS is true.
00527  *  iSolution  <input>  (RealVector)
00528  *      iSolution is the imaginary portion of the vector being multiplied
00529  *      by the matrix. This is only necessary if the matrix is
00530  *      complex and spSEPARATED_COMPLEX_VECTORS is true.
00531  *
00532  *  >>> Obscure Macros
00533  *  IMAG_VECTORS
00534  *      Replaces itself with `, iRHS, iSolution' if the options spCOMPLEX and
00535  *      spSEPARATED_COMPLEX_VECTORS are set, otherwise it disappears
00536  *      without a trace.
00537  */
00538 
00539 void
00540 spMultiply( eMatrix, RHS, Solution IMAG_VECTORS )
00541 
00542 char *eMatrix;
00543 RealVector RHS, Solution IMAG_VECTORS;
00544 {
00545 register  ElementPtr  pElement;
00546 register  RealVector  Vector;
00547 register  RealNumber  Sum;
00548 register  int  I, *pExtOrder;
00549 MatrixPtr  Matrix = (MatrixPtr)eMatrix;
00550 
00551 /* Begin `spMultiply'. */
00552     ASSERT( IS_SPARSE( Matrix ) AND NOT Matrix->Factored );
00553     if (NOT Matrix->RowsLinked) spcLinkRows(Matrix);
00554 
00555 #if spCOMPLEX
00556     if (Matrix->Complex)
00557     {   ComplexMatrixMultiply( Matrix, RHS, Solution IMAG_VECTORS );
00558         return;
00559     }
00560 #endif
00561 
00562 #if REAL
00563 #if NOT ARRAY_OFFSET
00564 /* Correct array pointers for ARRAY_OFFSET. */
00565     --RHS;
00566     --Solution;
00567 #endif
00568 
00569 /* Initialize Intermediate vector with reordered Solution vector. */
00570     Vector = Matrix->Intermediate;
00571     pExtOrder = &Matrix->IntToExtColMap[Matrix->Size];
00572     for (I = Matrix->Size; I > 0; I--)
00573         Vector[I] = Solution[*(pExtOrder--)];
00574 
00575     pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size];
00576     for (I = Matrix->Size; I > 0; I--)
00577     {   pElement = Matrix->FirstInRow[I];
00578         Sum = 0.0;
00579 
00580         while (pElement != NULL)
00581         {   Sum += pElement->Real * Vector[pElement->Col];
00582             pElement = pElement->NextInRow;
00583         }
00584         RHS[*pExtOrder--] = Sum;
00585     }
00586     return;
00587 #endif /* REAL */
00588 }
00589 #endif /* MULTIPLICATION */
00590 
00591 #if spCOMPLEX AND MULTIPLICATION
00592 /*
00593  *  COMPLEX MATRIX MULTIPLICATION
00594  *
00595  *  Multiplies matrix by solution vector to find source vector.
00596  *  Assumes matrix has not been factored.  This routine can be  used
00597  *  as a test to see if solutions are correct.
00598  *
00599  *  >>> Arguments:
00600  *  Matrix  <input>  (char *)
00601  *      Pointer to the matrix.
00602  *  RHS  <output>  (RealVector)
00603  *      RHS is the right hand side. This is what is being solved for.
00604  *      This is only the real portion of the right-hand side if the matrix
00605  *      is complex and spSEPARATED_COMPLEX_VECTORS is set true.
00606  *  Solution  <input>  (RealVector)
00607  *      Solution is the vector being multiplied by the matrix. This is only
00608  *      the real portion if the matrix is complex and
00609  *      spSEPARATED_COMPLEX_VECTORS is set true.
00610  *  iRHS  <output>  (RealVector)
00611  *      iRHS is the imaginary portion of the right hand side. This is
00612  *      what is being solved for.  This is only necessary if the matrix is
00613  *      complex and spSEPARATED_COMPLEX_VECTORS is true.
00614  *  iSolution  <input>  (RealVector)
00615  *      iSolution is the imaginary portion of the vector being multiplied
00616  *      by the matrix. This is only necessary if the matrix is
00617  *      complex and spSEPARATED_COMPLEX_VECTORS is true.
00618  *
00619  *  >>> Obscure Macros
00620  *  IMAG_VECTORS
00621  *      Replaces itself with `, iRHS, iSolution' if the options spCOMPLEX and
00622  *      spSEPARATED_COMPLEX_VECTORS are set, otherwise it disappears
00623  *      without a trace.
00624  */
00625 
00626 static void
00627 ComplexMatrixMultiply( MatrixPtr Matrix, RealVector RHS , RealVector Solution IMAG_VECTORS )
00628 {
00629 register  ElementPtr  pElement;
00630 register  ComplexVector  Vector;
00631 register  ComplexNumber  Sum;
00632 register  int  I, *pExtOrder;
00633 
00634 /* Begin `ComplexMatrixMultiply'. */
00635 
00636 /* Correct array pointers for ARRAY_OFFSET. */
00637 #if NOT ARRAY_OFFSET
00638 #if spSEPARATED_COMPLEX_VECTORS
00639     --RHS;              --iRHS;
00640     --Solution;         --iSolution;
00641 #else
00642     RHS -= 2;           Solution -= 2;
00643 #endif
00644 #endif
00645 
00646 /* Initialize Intermediate vector with reordered Solution vector. */
00647     Vector = (ComplexVector)Matrix->Intermediate;
00648     pExtOrder = &Matrix->IntToExtColMap[Matrix->Size];
00649 
00650 #if spSEPARATED_COMPLEX_VECTORS
00651     for (I = Matrix->Size; I > 0; I--)
00652     {   Vector[I].Real = Solution[*pExtOrder];
00653         Vector[I].Imag = iSolution[*(pExtOrder--)];
00654     }
00655 #else
00656     for (I = Matrix->Size; I > 0; I--)
00657         Vector[I] = ((ComplexVector)Solution)[*(pExtOrder--)];
00658 #endif
00659 
00660     pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size];
00661     for (I = Matrix->Size; I > 0; I--)
00662     {   pElement = Matrix->FirstInRow[I];
00663         Sum.Real = Sum.Imag = 0.0;
00664 
00665         while (pElement != NULL)
00666         {   /* Cmplx expression : Sum += Element * Vector[Col] */
00667             CMPLX_MULT_ADD_ASSIGN( Sum, *pElement, Vector[pElement->Col] );
00668             pElement = pElement->NextInRow;
00669         }
00670 
00671 #if spSEPARATED_COMPLEX_VECTORS
00672         RHS[*pExtOrder] = Sum.Real;
00673         iRHS[*pExtOrder--] = Sum.Imag;
00674 #else
00675         ((ComplexVector)RHS)[*pExtOrder--] = Sum;
00676 #endif
00677     }
00678     return;
00679 }
00680 #endif /* spCOMPLEX AND MULTIPLICATION */
00681 
00682 #if MULTIPLICATION AND TRANSPOSE
00683 /*
00684  *  TRANSPOSED MATRIX MULTIPLICATION
00685  *
00686  *  Multiplies transposed matrix by solution vector to find source vector.
00687  *  Assumes matrix has not been factored.  This routine can be used
00688  *  as a test to see if solutions are correct.  It should not be used
00689  *  before spMNA_Preorder().
00690  *
00691  *  >>> Arguments:
00692  *  eMatrix  <input>  (char *)
00693  *      Pointer to the matrix.
00694  *  RHS  <output>  (RealVector)
00695  *      RHS is the right hand side. This is what is being solved for.
00696  *  Solution  <input>  (RealVector)
00697  *      Solution is the vector being multiplied by the matrix.
00698  *  iRHS  <output>  (RealVector)
00699  *      iRHS is the imaginary portion of the right hand side. This is
00700  *      what is being solved for.  This is only necessary if the matrix is
00701  *      complex and spSEPARATED_COMPLEX_VECTORS is true.
00702  *  iSolution  <input>  (RealVector)
00703  *      iSolution is the imaginary portion of the vector being multiplied
00704  *      by the matrix. This is only necessary if the matrix is
00705  *      complex and spSEPARATED_COMPLEX_VECTORS is true.
00706  *
00707  *  >>> Obscure Macros
00708  *  IMAG_VECTORS
00709  *      Replaces itself with `, iRHS, iSolution' if the options spCOMPLEX and
00710  *      spSEPARATED_COMPLEX_VECTORS are set, otherwise it disappears
00711  *      without a trace.
00712  */
00713 
00714 void
00715 spMultTransposed( eMatrix, RHS, Solution IMAG_VECTORS )
00716 
00717 char *eMatrix;
00718 RealVector RHS, Solution IMAG_VECTORS;
00719 {
00720 register  ElementPtr  pElement;
00721 register  RealVector  Vector;
00722 register  RealNumber  Sum;
00723 register  int  I, *pExtOrder;
00724 MatrixPtr  Matrix = (MatrixPtr)eMatrix;
00725 
00726 
00727 /* Begin `spMultTransposed'. */
00728     ASSERT( IS_SPARSE( Matrix ) AND NOT Matrix->Factored );
00729 
00730 #if spCOMPLEX
00731     if (Matrix->Complex)
00732     {   ComplexTransposedMatrixMultiply( Matrix, RHS, Solution IMAG_VECTORS );
00733         return;
00734     }
00735 #endif
00736 
00737 #if REAL
00738 #if NOT ARRAY_OFFSET
00739 /* Correct array pointers for ARRAY_OFFSET. */
00740     --RHS;
00741     --Solution;
00742 #endif
00743 
00744 /* Initialize Intermediate vector with reordered Solution vector. */
00745     Vector = Matrix->Intermediate;
00746     pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size];
00747     for (I = Matrix->Size; I > 0; I--)
00748         Vector[I] = Solution[*(pExtOrder--)];
00749 
00750     pExtOrder = &Matrix->IntToExtColMap[Matrix->Size];
00751     for (I = Matrix->Size; I > 0; I--)
00752     {   pElement = Matrix->FirstInCol[I];
00753         Sum = 0.0;
00754 
00755         while (pElement != NULL)
00756         {   Sum += pElement->Real * Vector[pElement->Row];
00757             pElement = pElement->NextInCol;
00758         }
00759         RHS[*pExtOrder--] = Sum;
00760     }
00761     return;
00762 #endif /* REAL */
00763 }
00764 #endif /* MULTIPLICATION AND TRANSPOSE */
00765 
00766 #if spCOMPLEX AND MULTIPLICATION AND TRANSPOSE
00767 /*
00768  *  COMPLEX TRANSPOSED MATRIX MULTIPLICATION
00769  *
00770  *  Multiplies transposed matrix by solution vector to find source vector.
00771  *  Assumes matrix has not been factored.  This routine can be  used
00772  *  as a test to see if solutions are correct.
00773  *
00774  *  >>> Arguments:
00775  *  Matrix  <input>  (char *)
00776  *      Pointer to the matrix.
00777  *  RHS  <output>  (RealVector)
00778  *      RHS is the right hand side. This is what is being solved for.
00779  *      This is only the real portion of the right-hand side if the matrix
00780  *      is complex and spSEPARATED_COMPLEX_VECTORS is set true.
00781  *  Solution  <input>  (RealVector)
00782  *      Solution is the vector being multiplied by the matrix. This is only
00783  *      the real portion if the matrix is complex and
00784  *      spSEPARATED_COMPLEX_VECTORS is set true.
00785  *  iRHS  <output>  (RealVector)
00786  *      iRHS is the imaginary portion of the right hand side. This is
00787  *      what is being solved for.  This is only necessary if the matrix is
00788  *      complex and spSEPARATED_COMPLEX_VECTORS is true.
00789  *  iSolution  <input>  (RealVector)
00790  *      iSolution is the imaginary portion of the vector being multiplied
00791  *      by the matrix. This is only necessary if the matrix is
00792  *      complex and spSEPARATED_COMPLEX_VECTORS is true.
00793  *
00794  *  >>> Obscure Macros
00795  *  IMAG_VECTORS
00796  *      Replaces itself with `, iRHS, iSolution' if the options spCOMPLEX and
00797  *      spSEPARATED_COMPLEX_VECTORS are set, otherwise it disappears
00798  *      without a trace.
00799  */
00800 
00801 static void
00802 ComplexTransposedMatrixMultiply( MatrixPtr Matrix, RealVector RHS, RealVector Solution IMAG_VECTORS )
00803 {
00804 register  ElementPtr  pElement;
00805 register  ComplexVector  Vector;
00806 register  ComplexNumber  Sum;
00807 register  int  I, *pExtOrder;
00808 
00809 /* Begin `ComplexMatrixMultiply'. */
00810 
00811 /* Correct array pointers for ARRAY_OFFSET. */
00812 #if NOT ARRAY_OFFSET
00813 #if spSEPARATED_COMPLEX_VECTORS
00814     --RHS;              --iRHS;
00815     --Solution;         --iSolution;
00816 #else
00817     RHS -= 2;           Solution -= 2;
00818 #endif
00819 #endif
00820 
00821 /* Initialize Intermediate vector with reordered Solution vector. */
00822     Vector = (ComplexVector)Matrix->Intermediate;
00823     pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size];
00824 
00825 #if spSEPARATED_COMPLEX_VECTORS
00826     for (I = Matrix->Size; I > 0; I--)
00827     {   Vector[I].Real = Solution[*pExtOrder];
00828         Vector[I].Imag = iSolution[*(pExtOrder--)];
00829     }
00830 #else
00831     for (I = Matrix->Size; I > 0; I--)
00832         Vector[I] = ((ComplexVector)Solution)[*(pExtOrder--)];
00833 #endif
00834 
00835     pExtOrder = &Matrix->IntToExtColMap[Matrix->Size];
00836     for (I = Matrix->Size; I > 0; I--)
00837     {   pElement = Matrix->FirstInCol[I];
00838         Sum.Real = Sum.Imag = 0.0;
00839 
00840         while (pElement != NULL)
00841         {   /* Cmplx expression : Sum += Element * Vector[Row] */
00842             CMPLX_MULT_ADD_ASSIGN( Sum, *pElement, Vector[pElement->Row] );
00843             pElement = pElement->NextInCol;
00844         }
00845 
00846 #if spSEPARATED_COMPLEX_VECTORS
00847         RHS[*pExtOrder] = Sum.Real;
00848         iRHS[*pExtOrder--] = Sum.Imag;
00849 #else
00850         ((ComplexVector)RHS)[*pExtOrder--] = Sum;
00851 #endif
00852     }
00853     return;
00854 }
00855 #endif /* spCOMPLEX AND MULTIPLICATION AND TRANSPOSE */
00856 
00857 #if DETERMINANT
00858 /*
00859  *  CALCULATE DETERMINANT
00860  *
00861  *  This routine in capable of calculating the determinant of the
00862  *  matrix once the LU factorization has been performed.  Hence, only
00863  *  use this routine after spFactor() and before spClear().
00864  *  The determinant equals the product of all the diagonal elements of
00865  *  the lower triangular matrix L, except that this product may need
00866  *  negating.  Whether the product or the negative product equals the
00867  *  determinant is determined by the number of row and column
00868  *  interchanges performed.  Note that the determinants of matrices can
00869  *  be very large or very small.  On large matrices, the determinant
00870  *  can be far larger or smaller than can be represented by a floating
00871  *  point number.  For this reason the determinant is scaled to a
00872  *  reasonable value and the logarithm of the scale factor is returned.
00873  *
00874  *  >>> Arguments:
00875  *  eMatrix  <input>  (char *)
00876  *      A pointer to the matrix for which the determinant is desired.
00877  *  pExponent  <output>  (int *)
00878  *      The logarithm base 10 of the scale factor for the determinant.  To find
00879  *      the actual determinant, Exponent should be added to the exponent of
00880  *      Determinant.
00881  *  pDeterminant  <output>  (RealNumber *)
00882  *      The real portion of the determinant.   This number is scaled to be
00883  *      greater than or equal to 1.0 and less than 10.0.
00884  *  piDeterminant  <output>  (RealNumber *)
00885  *      The imaginary portion of the determinant.  When the matrix is real
00886  *      this pointer need not be supplied, nothing will be returned.   This
00887  *      number is scaled to be greater than or equal to 1.0 and less than 10.0.
00888  *
00889  *  >>> Local variables:
00890  *  Norm  (RealNumber)
00891  *      L-infinity norm of a complex number.
00892  *  Size  (int)
00893  *      Local storage for Matrix->Size.  Placed in a register for speed.
00894  *  Temp  (RealNumber)
00895  *      Temporary storage for real portion of determinant.
00896  */
00897 
00898 #if spCOMPLEX
00899 void
00900 spDeterminant( char *eMatrix, int *pExponent, register  RealNumber *pDeterminant, RealNumber *piDeterminant )
00901 #else
00902 void
00903 spDeterminant( char *eMatrix, int *pExponent, register  RealNumber *pDeterminant )
00904 #endif
00905 {
00906 register MatrixPtr  Matrix = (MatrixPtr)eMatrix;
00907 register int I, Size;
00908 RealNumber Norm, nr, ni;
00909 ComplexNumber Pivot, cDeterminant;
00910 
00911 #define  NORM(a)     (nr = ABS((a).Real), ni = ABS((a).Imag), MAX (nr,ni))
00912 
00913 /* Begin `spDeterminant'. */
00914     ASSERT( IS_SPARSE( Matrix ) AND IS_FACTORED(Matrix) );
00915     *pExponent = 0;
00916 
00917     if (Matrix->Error == spSINGULAR)
00918     {   *pDeterminant = 0.0;
00919 #if spCOMPLEX
00920         if (Matrix->Complex) *piDeterminant = 0.0;
00921 #endif
00922         return;
00923     }
00924 
00925     Size = Matrix->Size;
00926     I = 0;
00927 
00928 #if spCOMPLEX
00929     if (Matrix->Complex)        /* Complex Case. */
00930     {   cDeterminant.Real = 1.0;
00931         cDeterminant.Imag = 0.0;
00932 
00933         while (++I <= Size)
00934         {   CMPLX_RECIPROCAL( Pivot, *Matrix->Diag[I] );
00935             CMPLX_MULT_ASSIGN( cDeterminant, Pivot );
00936 
00937 /* Scale Determinant. */
00938             Norm = NORM( cDeterminant );
00939             if (Norm != 0.0)
00940             {   while (Norm >= 1.0e12)
00941                 {   cDeterminant.Real *= 1.0e-12;
00942                     cDeterminant.Imag *= 1.0e-12;
00943                     *pExponent += 12;
00944                     Norm = NORM( cDeterminant );
00945                 }
00946                 while (Norm < 1.0e-12)
00947                 {   cDeterminant.Real *= 1.0e12;
00948                     cDeterminant.Imag *= 1.0e12;
00949                     *pExponent -= 12;
00950                     Norm = NORM( cDeterminant );
00951                 }
00952             }
00953         }
00954 
00955 /* Scale Determinant again, this time to be between 1.0 <= x < 10.0. */
00956         Norm = NORM( cDeterminant );
00957         if (Norm != 0.0)
00958         {   while (Norm >= 10.0)
00959             {   cDeterminant.Real *= 0.1;
00960                 cDeterminant.Imag *= 0.1;
00961                 (*pExponent)++;
00962                 Norm = NORM( cDeterminant );
00963             }
00964             while (Norm < 1.0)
00965             {   cDeterminant.Real *= 10.0;
00966                 cDeterminant.Imag *= 10.0;
00967                 (*pExponent)--;
00968                 Norm = NORM( cDeterminant );
00969             }
00970         }
00971         if (Matrix->NumberOfInterchangesIsOdd)
00972             CMPLX_NEGATE( cDeterminant );
00973         
00974         *pDeterminant = cDeterminant.Real;
00975         *piDeterminant = cDeterminant.Imag;
00976     }
00977 #endif /* spCOMPLEX */
00978 #if REAL AND spCOMPLEX
00979     else
00980 #endif
00981 #if REAL
00982     {   /* Real Case. */
00983         *pDeterminant = 1.0;
00984 
00985         while (++I <= Size)
00986         {   *pDeterminant /= Matrix->Diag[I]->Real;
00987 
00988 /* Scale Determinant. */
00989             if (*pDeterminant != 0.0)
00990             {   while (ABS(*pDeterminant) >= 1.0e12)
00991                 {   *pDeterminant *= 1.0e-12;
00992                     *pExponent += 12;
00993                 }
00994                 while (ABS(*pDeterminant) < 1.0e-12)
00995                 {   *pDeterminant *= 1.0e12;
00996                     *pExponent -= 12;
00997                 }
00998             }
00999         }
01000 
01001 /* Scale Determinant again, this time to be between 1.0 <= x < 10.0. */
01002         if (*pDeterminant != 0.0)
01003         {   while (ABS(*pDeterminant) >= 10.0)
01004             {   *pDeterminant *= 0.1;
01005                 (*pExponent)++;
01006             }
01007             while (ABS(*pDeterminant) < 1.0)
01008             {   *pDeterminant *= 10.0;
01009                 (*pExponent)--;
01010             }
01011         }
01012         if (Matrix->NumberOfInterchangesIsOdd)
01013             *pDeterminant = -*pDeterminant;
01014     }
01015 #endif /* REAL */
01016 }
01017 #endif /* DETERMINANT */
01018 
01019 #if STRIP
01020 
01021 /*
01022  *  STRIP FILL-INS FROM MATRIX
01023  *
01024  *  Strips the matrix of all fill-ins.
01025  *
01026  *  >>> Arguments:
01027  *  Matrix  <input>  (char *)
01028  *      Pointer to the matrix to be stripped.
01029  *
01030  *  >>> Local variables:
01031  *  pElement  (ElementPtr)
01032  *      Pointer that is used to step through the matrix.
01033  *  ppElement  (ElementPtr *)
01034  *      Pointer to the location of an ElementPtr.  This location will be
01035  *      updated if a fill-in is stripped from the matrix.
01036  *  pFillin  (ElementPtr)
01037  *      Pointer used to step through the lists of fill-ins while marking them.
01038  *  pLastFillin  (ElementPtr)
01039  *      A pointer to the last fill-in in the list.  Used to terminate a loop.
01040  *  pListNode  (struct  FillinListNodeStruct *)
01041  *      A pointer to a node in the FillinList linked-list.
01042  */
01043 
01044 void
01045 spStripFills( eMatrix )
01046 
01047 char *eMatrix;
01048 {
01049 MatrixPtr  Matrix = (MatrixPtr)eMatrix;
01050 struct FillinListNodeStruct  *pListNode;
01051 
01052 /* Begin `spStripFills'. */
01053     ASSERT( IS_SPARSE( Matrix ) );
01054     if (Matrix->Fillins == 0) return;
01055     Matrix->NeedsOrdering = YES;
01056     Matrix->Elements -= Matrix->Fillins;
01057     Matrix->Fillins = 0;
01058 
01059 /* Mark the fill-ins. */
01060     {   register  ElementPtr  pFillin, pLastFillin;
01061 
01062         pListNode = Matrix->LastFillinListNode = Matrix->FirstFillinListNode;
01063         Matrix->FillinsRemaining = pListNode->NumberOfFillinsInList;
01064         Matrix->NextAvailFillin = pListNode->pFillinList;
01065 
01066         while (pListNode != NULL)
01067         {   pFillin = pListNode->pFillinList;
01068             pLastFillin = &(pFillin[ pListNode->NumberOfFillinsInList - 1 ]);
01069             while (pFillin <= pLastFillin)
01070                 (pFillin++)->Row = 0;
01071             pListNode = pListNode->Next;
01072         }
01073     }
01074 
01075 /* Unlink fill-ins by searching for elements marked with Row = 0. */
01076     {   register  ElementPtr pElement, *ppElement;
01077         register  int  I, Size = Matrix->Size;
01078 
01079 /* Unlink fill-ins in all columns. */
01080         for (I = 1; I <= Size; I++)
01081         {   ppElement = &(Matrix->FirstInCol[I]);
01082             while ((pElement = *ppElement) != NULL)
01083             {   if (pElement->Row == 0)
01084                 {   *ppElement = pElement->NextInCol;  /* Unlink fill-in. */
01085                     if (Matrix->Diag[pElement->Col] == pElement)
01086                         Matrix->Diag[pElement->Col] = NULL;
01087                 }
01088                 else
01089                     ppElement = &pElement->NextInCol;  /* Skip element. */
01090             }
01091         }
01092 
01093 /* Unlink fill-ins in all rows. */
01094         for (I = 1; I <= Size; I++)
01095         {   ppElement = &(Matrix->FirstInRow[I]);
01096             while ((pElement = *ppElement) != NULL)
01097             {   if (pElement->Row == 0)
01098                     *ppElement = pElement->NextInRow;  /* Unlink fill-in. */
01099                 else
01100                     ppElement = &pElement->NextInRow;  /* Skip element. */
01101             }
01102         }
01103     }
01104     return;
01105 }
01106 #endif
01107 
01108 #if TRANSLATE AND DELETE
01109 /*
01110  *  DELETE A ROW AND COLUMN FROM THE MATRIX
01111  *
01112  *  Deletes a row and a column from a matrix.
01113  *
01114  *  Sparse will abort if an attempt is made to delete a row or column that
01115  *  doesn't exist.
01116  *
01117  *  >>> Arguments:
01118  *  eMatrix  <input>  (char *)
01119  *      Pointer to the matrix in which the row and column are to be deleted.
01120  *  Row  <input>  (int)
01121  *      Row to be deleted.
01122  *  Col  <input>  (int)
01123  *      Column to be deleted.
01124  *
01125  *  >>> Local variables:
01126  *  ExtCol  (int)
01127  *      The external column that is being deleted.
01128  *  ExtRow  (int)
01129  *      The external row that is being deleted.
01130  *  pElement  (ElementPtr)
01131  *      Pointer to an element in the matrix.  Used when scanning rows and
01132  *      columns in order to eliminate elements from the last row or column.
01133  *  ppElement  (ElementPtr *)
01134  *      Pointer to the location of an ElementPtr.  This location will be
01135  *      filled with a NULL pointer if it is the new last element in its row
01136  *      or column.
01137  *  pElement  (ElementPtr)
01138  *      Pointer to an element in the last row or column of the matrix.
01139  *  Size  (int)
01140  *      The local version Matrix->Size, the size of the matrix.
01141  */
01142 
01143 extern void spcRowExchange(MatrixPtr Matrix,int  Row1,int Row2);
01144 extern void spcColExchange(MatrixPtr Matrix,int  Col1,int Col2);
01145 
01146 void spDeleteRowAndCol( char *eMatrix, int Row, int Col )
01147 {
01148 MatrixPtr  Matrix = (MatrixPtr)eMatrix;
01149 register  ElementPtr  pElement, *ppElement, pLastElement;
01150 int  Size, ExtRow, ExtCol;
01151 ElementPtr  spcFindElementInCol();
01152 
01153 /* Begin `spDeleteRowAndCol'. */
01154 
01155     ASSERT( IS_SPARSE(Matrix) AND Row > 0 AND Col > 0 );
01156     ASSERT( Row <= Matrix->ExtSize AND Col <= Matrix->ExtSize );
01157 
01158     Size = Matrix->Size;
01159     ExtRow = Row;
01160     ExtCol = Col;
01161     if (NOT Matrix->RowsLinked) spcLinkRows( Matrix );
01162 
01163     Row = Matrix->ExtToIntRowMap[Row];
01164     Col = Matrix->ExtToIntColMap[Col];
01165     ASSERT( Row > 0 AND Col > 0 );
01166 
01167 /* Move Row so that it is the last row in the matrix. */
01168     if (Row != Size) spcRowExchange( Matrix, Row, Size );
01169 
01170 /* Move Col so that it is the last column in the matrix. */
01171     if (Col != Size) spcColExchange( Matrix, Col, Size );
01172 
01173 /* Correct Diag pointers. */
01174     if (Row == Col)
01175         SWAP( ElementPtr, Matrix->Diag[Row], Matrix->Diag[Size] )
01176     else
01177     {   Matrix->Diag[Row] = spcFindElementInCol( Matrix, Matrix->FirstInCol+Row,
01178                                                  Row, Row, NO );
01179         Matrix->Diag[Col] = spcFindElementInCol( Matrix, Matrix->FirstInCol+Col,
01180                                                  Col, Col, NO );
01181     }
01182 
01183 /*
01184  * Delete last row and column of the matrix.
01185  */
01186 /* Break the column links to every element in the last row. */
01187     pLastElement = Matrix->FirstInRow[ Size ];
01188     while (pLastElement != NULL)
01189     {   ppElement = &(Matrix->FirstInCol[ pLastElement->Col ]);
01190         while ((pElement = *ppElement) != NULL)
01191         {   if (pElement == pLastElement)
01192                 *ppElement = NULL;  /* Unlink last element in column. */
01193             else
01194                 ppElement = &pElement->NextInCol;  /* Skip element. */
01195         }
01196         pLastElement = pLastElement->NextInRow;
01197     }
01198 
01199 /* Break the row links to every element in the last column. */
01200     pLastElement = Matrix->FirstInCol[ Size ];
01201     while (pLastElement != NULL)
01202     {   ppElement = &(Matrix->FirstInRow[ pLastElement->Row ]);
01203         while ((pElement = *ppElement) != NULL)
01204         {   if (pElement == pLastElement)
01205                 *ppElement = NULL;  /* Unlink last element in row. */
01206             else
01207                 ppElement = &pElement->NextInRow;  /* Skip element. */
01208         }
01209         pLastElement = pLastElement->NextInCol;
01210     }
01211 
01212 /* Clean up some details. */
01213     Matrix->Size = Size - 1;
01214     Matrix->Diag[Size] = NULL;
01215     Matrix->FirstInRow[Size] = NULL;
01216     Matrix->FirstInCol[Size] = NULL;
01217     Matrix->CurrentSize--;
01218     Matrix->ExtToIntRowMap[ExtRow] = -1;
01219     Matrix->ExtToIntColMap[ExtCol] = -1;
01220     Matrix->NeedsOrdering = YES;
01221 
01222     return;
01223 }
01224 #endif
01225 
01226 #if PSEUDOCONDITION
01227 /*
01228  *  CALCULATE PSEUDOCONDITION
01229  *
01230  *  Computes the magnitude of the ratio of the largest to the smallest
01231  *  pivots.  This quantity is an indicator of ill-conditioning in the
01232  *  matrix.  If this ratio is large, and if the matrix is scaled such
01233  *  that uncertainties in the RHS and the matrix entries are
01234  *  equilibrated, then the matrix is ill-conditioned.  However, a small
01235  *  ratio does not necessarily imply that the matrix is
01236  *  well-conditioned.  This routine must only be used after a matrix has
01237  *  been factored by spOrderAndFactor() or spFactor() and before it is
01238  *  cleared by spClear() or spInitialize().  The pseudocondition is
01239  *  faster to compute than the condition number calculated by
01240  *  spCondition(), but is not as informative.
01241  *
01242  *  >>> Returns:
01243  *  The magnitude of the ratio of the largest to smallest pivot used during
01244  *  previous factorization.  If the matrix was singular, zero is returned.
01245  *
01246  *  >>> Arguments:
01247  *  eMatrix  <input>  (char *)
01248  *      Pointer to the matrix.
01249  */
01250 
01251 RealNumber
01252 spPseudoCondition( eMatrix )
01253 
01254 char *eMatrix;
01255 {
01256 MatrixPtr  Matrix = (MatrixPtr)eMatrix;
01257 register int I;
01258 register ArrayOfElementPtrs Diag;
01259 RealNumber MaxPivot, MinPivot, Mag;
01260 
01261 /* Begin `spPseudoCondition'. */
01262 
01263     ASSERT( IS_SPARSE(Matrix) AND IS_FACTORED(Matrix) );
01264     if (Matrix->Error == spSINGULAR OR Matrix->Error == spZERO_DIAG)
01265         return 0.0;
01266 
01267     Diag = Matrix->Diag;
01268     MaxPivot = MinPivot = ELEMENT_MAG( Diag[1] );
01269     for (I = 2; I <= Matrix->Size; I++)
01270     {   Mag = ELEMENT_MAG( Diag[I] );
01271         if (Mag > MaxPivot)
01272             MaxPivot = Mag;
01273         else if (Mag < MinPivot)
01274             MinPivot = Mag;
01275     }
01276     ASSERT( MaxPivot > 0.0);
01277     return MaxPivot / MinPivot;
01278 }
01279 #endif
01280 
01281 #if CONDITION
01282 /*
01283  *  ESTIMATE CONDITION NUMBER
01284  *
01285  *  Computes an estimate of the condition number using a variation on
01286  *  the LINPACK condition number estimation algorithm.  This quantity is
01287  *  an indicator of ill-conditioning in the matrix.  To avoid problems
01288  *  with overflow, the reciprocal of the condition number is returned.
01289  *  If this number is small, and if the matrix is scaled such that
01290  *  uncertainties in the RHS and the matrix entries are equilibrated,
01291  *  then the matrix is ill-conditioned.  If the this number is near
01292  *  one, the matrix is well conditioned.  This routine must only be
01293  *  used after a matrix has been factored by spOrderAndFactor() or
01294  *  spFactor() and before it is cleared by spClear() or spInitialize().
01295  *
01296  *  Unlike the LINPACK condition number estimator, this routines
01297  *  returns the L infinity condition number.  This is an artifact of
01298  *  Sparse placing ones on the diagonal of the upper triangular matrix
01299  *  rather than the lower.  This difference should be of no importance.
01300  *
01301  *  References:
01302  *  A.K. Cline, C.B. Moler, G.W. Stewart, J.H. Wilkinson.  An estimate
01303  *  for the condition number of a matrix.  SIAM Journal on Numerical
01304  *  Analysis.  Vol. 16, No. 2, pages 368-375, April 1979.
01305  *  
01306  *  J.J. Dongarra, C.B. Moler, J.R. Bunch, G.W. Stewart.  LINPACK
01307  *  User's Guide.  SIAM, 1979.
01308  *  
01309  *  Roger G. Grimes, John G. Lewis.  Condition number estimation for
01310  *  sparse matrices.  SIAM Journal on Scientific and Statistical
01311  *  Computing.  Vol. 2, No. 4, pages 384-388, December 1981.
01312  *  
01313  *  Dianne Prost O'Leary.  Estimating matrix condition numbers.  SIAM
01314  *  Journal on Scientific and Statistical Computing.  Vol. 1, No. 2,
01315  *  pages 205-209, June 1980.
01316  *
01317  *  >>> Returns:
01318  *  The reciprocal of the condition number.  If the matrix was singular,
01319  *  zero is returned.
01320  *
01321  *  >>> Arguments:
01322  *  eMatrix  <input>  (char *)
01323  *      Pointer to the matrix.
01324  *  NormOfMatrix  <input>  (RealNumber)
01325  *      The L-infinity norm of the unfactored matrix as computed by
01326  *      spNorm().
01327  *  pError  <output>  (int *)
01328  *      Used to return error code.
01329  *
01330  *  >>> Possible errors:
01331  *  spSINGULAR
01332  *  spNO_MEMORY
01333  */
01334 
01335 RealNumber
01336 spCondition( eMatrix, NormOfMatrix, pError )
01337 
01338 char *eMatrix;
01339 RealNumber NormOfMatrix;
01340 int *pError;
01341 {
01342 MatrixPtr  Matrix = (MatrixPtr)eMatrix;
01343 register ElementPtr pElement;
01344 register RealVector T, Tm;
01345 register int I, K, Row;
01346 ElementPtr pPivot;
01347 int Size;
01348 RealNumber E, Em, Wp, Wm, ASp, ASm, ASw, ASy, ASv, ASz, MaxY, ScaleFactor;
01349 RealNumber Linpack, OLeary, InvNormOfInverse;
01350 #define SLACK   1e4
01351 
01352 /* Begin `spCondition'. */
01353 
01354     ASSERT( IS_SPARSE(Matrix) AND IS_FACTORED(Matrix) );
01355     *pError = Matrix->Error;
01356     if (Matrix->Error >= spFATAL) return 0.0;
01357     if (NormOfMatrix == 0.0)
01358     {   *pError = spSINGULAR;
01359         return 0.0;
01360     }
01361 
01362 #if spCOMPLEX
01363     if (Matrix->Complex)
01364         return ComplexCondition( Matrix, NormOfMatrix, pError );
01365 #endif
01366 
01367 #if REAL
01368     Size = Matrix->Size;
01369     T = Matrix->Intermediate;
01370 #if spCOMPLEX
01371     Tm = Matrix->Intermediate + Size;
01372 #else
01373     Tm = SPALLOC( RealNumber, Size+1 );
01374     if (Tm == NULL)
01375     {   *pError = spNO_MEMORY;
01376         return 0.0;
01377     }
01378 #endif
01379     for (I = Size; I > 0; I--) T[I] = 0.0;
01380 
01381 /*
01382  * Part 1.  Ay = e.
01383  * Solve Ay = LUy = e where e consists of +1 and -1 terms with the sign
01384  * chosen to maximize the size of w in Lw = e.  Since the terms in w can
01385  * get very large, scaling is used to avoid overflow.
01386  */
01387 
01388 /* Forward elimination. Solves Lw = e while choosing e. */
01389     E = 1.0;
01390     for (I = 1; I <= Size; I++)
01391     {   pPivot = Matrix->Diag[I];
01392         if (T[I] < 0.0) Em = -E; else Em = E;
01393         Wm = (Em + T[I]) * pPivot->Real;
01394         if (ABS(Wm) > SLACK)
01395         {   ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(Wm) );
01396             for (K = Size; K > 0; K--) T[K] *= ScaleFactor;
01397             E *= ScaleFactor;
01398             Em *= ScaleFactor;
01399             Wm = (Em + T[I]) * pPivot->Real;
01400         }
01401         Wp = (T[I] - Em) * pPivot->Real;
01402         ASp = ABS(T[I] - Em);
01403         ASm = ABS(Em + T[I]);
01404 
01405 /* Update T for both values of W, minus value is placed in Tm. */
01406         pElement = pPivot->NextInCol;
01407         while (pElement != NULL)
01408         {   Row = pElement->Row;
01409             Tm[Row] = T[Row] - (Wm * pElement->Real);
01410             T[Row] -= (Wp * pElement->Real);
01411             ASp += ABS(T[Row]);
01412             ASm += ABS(Tm[Row]);
01413             pElement = pElement->NextInCol;
01414         }
01415 
01416 /* If minus value causes more growth, overwrite T with its values. */
01417         if (ASm > ASp)
01418         {   T[I] = Wm;
01419             pElement = pPivot->NextInCol;
01420             while (pElement != NULL)
01421             {   T[pElement->Row] = Tm[pElement->Row];
01422                 pElement = pElement->NextInCol;
01423             }
01424         }
01425         else T[I] = Wp;
01426     }
01427 
01428 /* Compute 1-norm of T, which now contains w, and scale ||T|| to 1/SLACK. */
01429     for (ASw = 0.0, I = Size; I > 0; I--) ASw += ABS(T[I]);
01430     ScaleFactor = 1.0 / (SLACK * ASw);
01431     if (ScaleFactor < 0.5)
01432     {   for (I = Size; I > 0; I--) T[I] *= ScaleFactor;
01433         E *= ScaleFactor;
01434     }
01435 
01436 /* Backward Substitution. Solves Uy = w.*/
01437     for (I = Size; I >= 1; I--)
01438     {   pElement = Matrix->Diag[I]->NextInRow;
01439         while (pElement != NULL)
01440         {   T[I] -= pElement->Real * T[pElement->Col];
01441             pElement = pElement->NextInRow;
01442         }
01443         if (ABS(T[I]) > SLACK)
01444         {   ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(T[I]) );
01445             for (K = Size; K > 0; K--) T[K] *= ScaleFactor;
01446             E *= ScaleFactor;
01447         }
01448     }
01449 
01450 /* Compute 1-norm of T, which now contains y, and scale ||T|| to 1/SLACK. */
01451     for (ASy = 0.0, I = Size; I > 0; I--) ASy += ABS(T[I]);
01452     ScaleFactor = 1.0 / (SLACK * ASy);
01453     if (ScaleFactor < 0.5)
01454     {   for (I = Size; I > 0; I--) T[I] *= ScaleFactor;
01455         ASy = 1.0 / SLACK;
01456         E *= ScaleFactor;
01457     }
01458 
01459 /* Compute infinity-norm of T for O'Leary's estimate. */
01460     for (MaxY = 0.0, I = Size; I > 0; I--)
01461         if (MaxY < ABS(T[I])) MaxY = ABS(T[I]);
01462 
01463 /*
01464  * Part 2.  A* z = y where the * represents the transpose.
01465  * Recall that A = LU implies A* = U* L*.
01466  */
01467 
01468 /* Forward elimination, U* v = y. */
01469     for (I = 1; I <= Size; I++)
01470     {   pElement = Matrix->Diag[I]->NextInRow;
01471         while (pElement != NULL)
01472         {   T[pElement->Col] -= T[I] * pElement->Real;
01473             pElement = pElement->NextInRow;
01474         }
01475         if (ABS(T[I]) > SLACK)
01476         {   ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(T[I]) );
01477             for (K = Size; K > 0; K--) T[K] *= ScaleFactor;
01478             ASy *= ScaleFactor;
01479         }
01480     }
01481 
01482 /* Compute 1-norm of T, which now contains v, and scale ||T|| to 1/SLACK. */
01483     for (ASv = 0.0, I = Size; I > 0; I--) ASv += ABS(T[I]);
01484     ScaleFactor = 1.0 / (SLACK * ASv);
01485     if (ScaleFactor < 0.5)
01486     {   for (I = Size; I > 0; I--) T[I] *= ScaleFactor;
01487         ASy *= ScaleFactor;
01488     }
01489 
01490 /* Backward Substitution, L* z = v. */
01491     for (I = Size; I >= 1; I--)
01492     {   pPivot = Matrix->Diag[I];
01493         pElement = pPivot->NextInCol;
01494         while (pElement != NULL)
01495         {   T[I] -= pElement->Real * T[pElement->Row];
01496             pElement = pElement->NextInCol;
01497         }
01498         T[I] *= pPivot->Real;
01499         if (ABS(T[I]) > SLACK)
01500         {   ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(T[I]) );
01501             for (K = Size; K > 0; K--) T[K] *= ScaleFactor;
01502             ASy *= ScaleFactor;
01503         }
01504     }
01505 
01506 /* Compute 1-norm of T, which now contains z. */
01507     for (ASz = 0.0, I = Size; I > 0; I--) ASz += ABS(T[I]);
01508 
01509 #if NOT spCOMPLEX
01510     SPFREE( Tm );
01511 #endif
01512 
01513     Linpack = ASy / ASz;
01514     OLeary = E / MaxY;
01515     InvNormOfInverse = MIN( Linpack, OLeary );
01516     return InvNormOfInverse / NormOfMatrix;
01517 #endif /* REAL */
01518 }
01519 
01520 #if spCOMPLEX
01521 /*
01522  *  ESTIMATE CONDITION NUMBER
01523  *
01524  *  Complex version of spCondition().
01525  *
01526  *  >>> Returns:
01527  *  The reciprocal of the condition number.
01528  *
01529  *  >>> Arguments:
01530  *  Matrix  <input>  (MatrixPtr)
01531  *      Pointer to the matrix.
01532  *  NormOfMatrix  <input>  (RealNumber)
01533  *      The L-infinity norm of the unfactored matrix as computed by
01534  *      spNorm().
01535  *  pError  <output>  (int *)
01536  *      Used to return error code.
01537  *
01538  *  >>> Possible errors:
01539  *  spNO_MEMORY
01540  */
01541 
01542 static RealNumber
01543 ComplexCondition( MatrixPtr Matrix, RealNumber NormOfMatrix, int *pError )
01544 {
01545 register ElementPtr pElement;
01546 register ComplexVector T, Tm;
01547 register int I, K, Row;
01548 ElementPtr pPivot;
01549 int Size;
01550 RealNumber E, Em, ASp, ASm, ASw, ASy, ASv, ASz, MaxY, ScaleFactor;
01551 RealNumber Linpack, OLeary, InvNormOfInverse;
01552 ComplexNumber Wp, Wm;
01553 
01554 /* Begin `ComplexCondition'. */
01555 
01556     Size = Matrix->Size;
01557     T = (ComplexVector)Matrix->Intermediate;
01558     Tm = SPALLOC( ComplexNumber, Size+1 );
01559     if (Tm == NULL)
01560     {   *pError = spNO_MEMORY;
01561         return 0.0;
01562     }
01563     for (I = Size; I > 0; I--) T[I].Real = T[I].Imag = 0.0;
01564 
01565 /*
01566  * Part 1.  Ay = e.
01567  * Solve Ay = LUy = e where e consists of +1 and -1 terms with the sign
01568  * chosen to maximize the size of w in Lw = e.  Since the terms in w can
01569  * get very large, scaling is used to avoid overflow.
01570  */
01571 
01572 /* Forward elimination. Solves Lw = e while choosing e. */
01573     E = 1.0;
01574     for (I = 1; I <= Size; I++)
01575     {   pPivot = Matrix->Diag[I];
01576         if (T[I].Real < 0.0) Em = -E; else Em = E;
01577         Wm = T[I];
01578         Wm.Real += Em;
01579         ASm = CMPLX_1_NORM( Wm );
01580         CMPLX_MULT_ASSIGN( Wm, *pPivot );
01581         if (CMPLX_1_NORM(Wm) > SLACK)
01582         {   ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(Wm) );
01583             for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor );
01584             E *= ScaleFactor;
01585             Em *= ScaleFactor;
01586             ASm *= ScaleFactor;
01587             SCLR_MULT_ASSIGN( Wm, ScaleFactor );
01588         }
01589         Wp = T[I];
01590         Wp.Real -= Em;
01591         ASp = CMPLX_1_NORM( Wp );
01592         CMPLX_MULT_ASSIGN( Wp, *pPivot );
01593 
01594 /* Update T for both values of W, minus value is placed in Tm. */
01595         pElement = pPivot->NextInCol;
01596         while (pElement != NULL)
01597         {   Row = pElement->Row;
01598             /* Cmplx expr: Tm[Row] = T[Row] - (Wp * *pElement). */
01599             CMPLX_MULT_SUBT( Tm[Row], Wm, *pElement, T[Row] );
01600             /* Cmplx expr: T[Row] -= Wp * *pElement. */
01601             CMPLX_MULT_SUBT_ASSIGN( T[Row], Wm, *pElement );
01602             ASp += CMPLX_1_NORM(T[Row]);
01603             ASm += CMPLX_1_NORM(Tm[Row]);
01604             pElement = pElement->NextInCol;
01605         }
01606 
01607 /* If minus value causes more growth, overwrite T with its values. */
01608         if (ASm > ASp)
01609         {   T[I] = Wm;
01610             pElement = pPivot->NextInCol;
01611             while (pElement != NULL)
01612             {   T[pElement->Row] = Tm[pElement->Row];
01613                 pElement = pElement->NextInCol;
01614             }
01615         }
01616         else T[I] = Wp;
01617     }
01618 
01619 /* Compute 1-norm of T, which now contains w, and scale ||T|| to 1/SLACK. */
01620     for (ASw = 0.0, I = Size; I > 0; I--) ASw += CMPLX_1_NORM(T[I]);
01621     ScaleFactor = 1.0 / (SLACK * ASw);
01622     if (ScaleFactor < 0.5)
01623     {   for (I = Size; I > 0; I--) SCLR_MULT_ASSIGN( T[I], ScaleFactor );
01624         E *= ScaleFactor;
01625     }
01626 
01627 /* Backward Substitution. Solves Uy = w.*/
01628     for (I = Size; I >= 1; I--)
01629     {   pElement = Matrix->Diag[I]->NextInRow;
01630         while (pElement != NULL)
01631         {   /* Cmplx expr: T[I] -= T[pElement->Col] * *pElement. */
01632             CMPLX_MULT_SUBT_ASSIGN( T[I], T[pElement->Col], *pElement );
01633             pElement = pElement->NextInRow;
01634         }
01635         if (CMPLX_1_NORM(T[I]) > SLACK)
01636         {   ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(T[I]) );
01637             for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor );
01638             E *= ScaleFactor;
01639         }
01640     }
01641 
01642 /* Compute 1-norm of T, which now contains y, and scale ||T|| to 1/SLACK. */
01643     for (ASy = 0.0, I = Size; I > 0; I--) ASy += CMPLX_1_NORM(T[I]);
01644     ScaleFactor = 1.0 / (SLACK * ASy);
01645     if (ScaleFactor < 0.5)
01646     {   for (I = Size; I > 0; I--) SCLR_MULT_ASSIGN( T[I], ScaleFactor );
01647         ASy = 1.0 / SLACK;
01648         E *= ScaleFactor;
01649     }
01650 
01651 /* Compute infinity-norm of T for O'Leary's estimate. */
01652     for (MaxY = 0.0, I = Size; I > 0; I--)
01653         if (MaxY < CMPLX_1_NORM(T[I])) MaxY = CMPLX_1_NORM(T[I]);
01654 
01655 /*
01656  * Part 2.  A* z = y where the * represents the transpose.
01657  * Recall that A = LU implies A* = U* L*.
01658  */
01659 
01660 /* Forward elimination, U* v = y. */
01661     for (I = 1; I <= Size; I++)
01662     {   pElement = Matrix->Diag[I]->NextInRow;
01663         while (pElement != NULL)
01664         {   /* Cmplx expr: T[pElement->Col] -= T[I] * *pElement. */
01665             CMPLX_MULT_SUBT_ASSIGN( T[pElement->Col], T[I], *pElement );
01666             pElement = pElement->NextInRow;
01667         }
01668         if (CMPLX_1_NORM(T[I]) > SLACK)
01669         {   ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(T[I]) );
01670             for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor );
01671             ASy *= ScaleFactor;
01672         }
01673     }
01674 
01675 /* Compute 1-norm of T, which now contains v, and scale ||T|| to 1/SLACK. */
01676     for (ASv = 0.0, I = Size; I > 0; I--) ASv += CMPLX_1_NORM(T[I]);
01677     ScaleFactor = 1.0 / (SLACK * ASv);
01678     if (ScaleFactor < 0.5)
01679     {   for (I = Size; I > 0; I--) SCLR_MULT_ASSIGN( T[I], ScaleFactor );
01680         ASy *= ScaleFactor;
01681     }
01682 
01683 /* Backward Substitution, L* z = v. */
01684     for (I = Size; I >= 1; I--)
01685     {   pPivot = Matrix->Diag[I];
01686         pElement = pPivot->NextInCol;
01687         while (pElement != NULL)
01688         {   /* Cmplx expr: T[I] -= T[pElement->Row] * *pElement. */
01689             CMPLX_MULT_SUBT_ASSIGN( T[I], T[pElement->Row], *pElement );
01690             pElement = pElement->NextInCol;
01691         }
01692         CMPLX_MULT_ASSIGN( T[I], *pPivot );
01693         if (CMPLX_1_NORM(T[I]) > SLACK)
01694         {   ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(T[I]) );
01695             for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor );
01696             ASy *= ScaleFactor;
01697         }
01698     }
01699 
01700 /* Compute 1-norm of T, which now contains z. */
01701     for (ASz = 0.0, I = Size; I > 0; I--) ASz += CMPLX_1_NORM(T[I]);
01702 
01703     SPFREE( Tm );
01704 
01705     Linpack = ASy / ASz;
01706     OLeary = E / MaxY;
01707     InvNormOfInverse = MIN( Linpack, OLeary );
01708     return InvNormOfInverse / NormOfMatrix;
01709 }
01710 #endif /* spCOMPLEX */
01711 
01712 /*
01713  *  L-INFINITY MATRIX NORM 
01714  *
01715  *  Computes the L-infinity norm of an unfactored matrix.  It is a fatal
01716  *  error to pass this routine a factored matrix.
01717  *
01718  *  One difficulty is that the rows may not be linked.
01719  *
01720  *  >>> Returns:
01721  *  The largest absolute row sum of matrix.
01722  *
01723  *  >>> Arguments:
01724  *  eMatrix  <input>  (char *)
01725  *      Pointer to the matrix.
01726  */
01727 
01728 RealNumber
01729 spNorm( eMatrix )
01730 
01731 char *eMatrix;
01732 {
01733 MatrixPtr  Matrix = (MatrixPtr)eMatrix;
01734 register ElementPtr pElement;
01735 register int I;
01736 RealNumber Max = 0.0, AbsRowSum;
01737 
01738 /* Begin `spNorm'. */
01739     ASSERT( IS_SPARSE(Matrix) AND NOT IS_FACTORED(Matrix) );
01740     if (NOT Matrix->RowsLinked) spcLinkRows( Matrix );
01741 
01742 /* Compute row sums. */
01743 #if REAL
01744     if (NOT Matrix->Complex)
01745     {   for (I = Matrix->Size; I > 0; I--)
01746         {   pElement = Matrix->FirstInRow[I];
01747             AbsRowSum = 0.0;
01748             while (pElement != NULL)
01749             {   AbsRowSum += ABS( pElement->Real );
01750                 pElement = pElement->NextInRow;
01751             }
01752             if (Max < AbsRowSum) Max = AbsRowSum;
01753         }
01754     }
01755 #endif
01756 #if spCOMPLEX
01757     if (Matrix->Complex)
01758     {   for (I = Matrix->Size; I > 0; I--)
01759         {   pElement = Matrix->FirstInRow[I];
01760             AbsRowSum = 0.0;
01761             while (pElement != NULL)
01762             {   AbsRowSum += CMPLX_1_NORM( *pElement );
01763                 pElement = pElement->NextInRow;
01764             }
01765             if (Max < AbsRowSum) Max = AbsRowSum;
01766         }
01767     }
01768 #endif
01769     return Max;
01770 }
01771 #endif /* CONDITION */
01772 
01773 #if STABILITY
01774 /*
01775  *  STABILITY OF FACTORIZATION
01776  *
01777  *  The following routines are used to gauge the stability of a
01778  *  factorization.  If the factorization is determined to be too unstable,
01779  *  then the matrix should be reordered.  The routines compute quantities
01780  *  that are needed in the computation of a bound on the error attributed
01781  *  to any one element in the matrix during the factorization.  In other
01782  *  words, there is a matrix E = [e_ij] of error terms such that A+E = LU.
01783  *  This routine finds a bound on |e_ij|.  Erisman & Reid [1] showed that
01784  *  |e_ij| < 3.01 u rho m_ij, where u is the machine rounding unit,
01785  *  rho = max a_ij where the max is taken over every row i, column j, and
01786  *  step k, and m_ij is the number of multiplications required in the
01787  *  computation of l_ij if i > j or u_ij otherwise.  Barlow [2] showed that
01788  *  rho < max_i || l_i ||_p max_j || u_j ||_q where 1/p + 1/q = 1.
01789  *
01790  *  The first routine finds the magnitude on the largest element in the
01791  *  matrix.  If the matrix has not yet been factored, the largest
01792  *  element is found by direct search.  If the matrix is factored, a
01793  *  bound on the largest element in any of the reduced submatrices is
01794  *  computed using Barlow with p = oo and q = 1.  The ratio of these
01795  *  two numbers is the growth, which can be used to determine if the
01796  *  pivoting order is adequate.  A large growth implies that
01797  *  considerable error has been made in the factorization and that it
01798  *  is probably a good idea to reorder the matrix.  If a large growth
01799  *  in encountered after using spFactor(), reconstruct the matrix and
01800  *  refactor using spOrderAndFactor().  If a large growth is
01801  *  encountered after using spOrderAndFactor(), refactor using
01802  *  spOrderAndFactor() with the pivot threshold increased, say to 0.1.
01803  *
01804  *  Using only the size of the matrix as an upper bound on m_ij and
01805  *  Barlow's bound, the user can estimate the size of the matrix error
01806  *  terms e_ij using the bound of Erisman and Reid.  The second routine
01807  *  computes a tighter bound (with more work) based on work by Gear
01808  *  [3], |e_ij| < 1.01 u rho (t c^3 + (1 + t)c^2) where t is the
01809  *  threshold and c is the maximum number of off-diagonal elements in
01810  *  any row of L.  The expensive part of computing this bound is
01811  *  determining the maximum number of off-diagonals in L, which changes
01812  *  only when the order of the matrix changes.  This number is computed
01813  *  and saved, and only recomputed if the matrix is reordered.
01814  *
01815  *  [1] A. M. Erisman, J. K. Reid.  Monitoring the stability of the
01816  *      triangular factorization of a sparse matrix.  Numerische
01817  *      Mathematik.  Vol. 22, No. 3, 1974, pp 183-186.
01818  *
01819  *  [2] J. L. Barlow.  A note on monitoring the stability of triangular
01820  *      decomposition of sparse matrices.  "SIAM Journal of Scientific
01821  *      and Statistical Computing."  Vol. 7, No. 1, January 1986, pp 166-168.
01822  *
01823  *  [3] I. S. Duff, A. M. Erisman, J. K. Reid.  "Direct Methods for Sparse
01824  *      Matrices."  Oxford 1986. pp 99.
01825  */
01826 
01827 /*
01828  *  LARGEST ELEMENT IN MATRIX
01829  *
01830  *  >>> Returns:
01831  *  If matrix is not factored, returns the magnitude of the largest element in
01832  *  the matrix.  If the matrix is factored, a bound on the magnitude of the
01833  *  largest element in any of the reduced submatrices is returned.
01834  *
01835  *  >>> Arguments:
01836  *  eMatrix  <input>  (char *)
01837  *      Pointer to the matrix.
01838  */
01839 
01840 RealNumber
01841 spLargestElement( eMatrix )
01842 
01843 char *eMatrix;
01844 {
01845 MatrixPtr  Matrix = (MatrixPtr)eMatrix;
01846 register int I;
01847 RealNumber Mag, AbsColSum, Max = 0.0, MaxRow = 0.0, MaxCol = 0.0;
01848 RealNumber Pivot;
01849 ComplexNumber cPivot;
01850 register ElementPtr pElement, pDiag;
01851 
01852 /* Begin `spLargestElement'. */
01853     ASSERT( IS_SPARSE(Matrix) );
01854 
01855 #if REAL
01856     if (Matrix->Factored AND NOT Matrix->Complex)
01857     {   if (Matrix->Error == spSINGULAR) return 0.0;
01858 
01859 /* Find the bound on the size of the largest element over all factorization. */
01860         for (I = 1; I <= Matrix->Size; I++)
01861         {   pDiag = Matrix->Diag[I];
01862 
01863 /* Lower triangular matrix. */
01864             Pivot = 1.0 / pDiag->Real;
01865             Mag = ABS( Pivot );
01866             if (Mag > MaxRow) MaxRow = Mag;
01867             pElement = Matrix->FirstInRow[I];
01868             while (pElement != pDiag)
01869             {   Mag = ABS( pElement->Real );
01870                 if (Mag > MaxRow) MaxRow = Mag;
01871                 pElement = pElement->NextInRow;
01872             }
01873 
01874 /* Upper triangular matrix. */
01875             pElement = Matrix->FirstInCol[I];
01876             AbsColSum = 1.0;  /* Diagonal of U is unity. */
01877             while (pElement != pDiag)
01878             {   AbsColSum += ABS( pElement->Real );
01879                 pElement = pElement->NextInCol;
01880             }
01881             if (AbsColSum > MaxCol) MaxCol = AbsColSum;
01882         }
01883     }
01884     else if (NOT Matrix->Complex)
01885     {   for (I = 1; I <= Matrix->Size; I++)
01886         {   pElement = Matrix->FirstInCol[I];
01887             while (pElement != NULL)
01888             {   Mag = ABS( pElement->Real );
01889                 if (Mag > Max) Max = Mag;
01890                 pElement = pElement->NextInCol;
01891             }
01892         }
01893         return Max;
01894     }
01895 #endif
01896 #if spCOMPLEX
01897     if (Matrix->Factored AND Matrix->Complex)
01898     {   if (Matrix->Error == spSINGULAR) return 0.0;
01899 
01900 /* Find the bound on the size of the largest element over all factorization. */
01901         for (I = 1; I <= Matrix->Size; I++)
01902         {   pDiag = Matrix->Diag[I];
01903 
01904 /* Lower triangular matrix. */
01905             CMPLX_RECIPROCAL( cPivot, *pDiag );
01906             Mag = CMPLX_1_NORM( cPivot );
01907             if (Mag > MaxRow) MaxRow = Mag;
01908             pElement = Matrix->FirstInRow[I];
01909             while (pElement != pDiag)
01910             {   Mag = CMPLX_1_NORM( *pElement );
01911                 if (Mag > MaxRow) MaxRow = Mag;
01912                 pElement = pElement->NextInRow;
01913             }
01914 
01915 /* Upper triangular matrix. */
01916             pElement = Matrix->FirstInCol[I];
01917             AbsColSum = 1.0;  /* Diagonal of U is unity. */
01918             while (pElement != pDiag)
01919             {   AbsColSum += CMPLX_1_NORM( *pElement );
01920                 pElement = pElement->NextInCol;
01921             }
01922             if (AbsColSum > MaxCol) MaxCol = AbsColSum;
01923         }
01924     }
01925     else if (Matrix->Complex)
01926     {   for (I = 1; I <= Matrix->Size; I++)
01927         {   pElement = Matrix->FirstInCol[I];
01928             while (pElement != NULL)
01929             {   Mag = CMPLX_1_NORM( *pElement );
01930                 if (Mag > Max) Max = Mag;
01931                 pElement = pElement->NextInCol;
01932             }
01933         }
01934         return Max;
01935     }
01936 #endif
01937     return MaxRow * MaxCol;
01938 }
01939 
01940 
01941 
01942 
01943 /*
01944  *  MATRIX ROUNDOFF ERROR
01945  *
01946  *  >>> Returns:
01947  *  Returns a bound on the magnitude of the largest element in E = A - LU.
01948  *
01949  *  >>> Arguments:
01950  *  eMatrix  <input>  (char *)
01951  *      Pointer to the matrix.
01952  *  Rho  <input>  (RealNumber)
01953  *      The bound on the magnitude of the largest element in any of the
01954  *      reduced submatrices.  This is the number computed by the function
01955  *      spLargestElement() when given a factored matrix.  If this number is
01956  *      negative, the bound will be computed automatically.
01957  */
01958 
01959 RealNumber
01960 spRoundoff( eMatrix, Rho )
01961 
01962 char *eMatrix;
01963 RealNumber Rho;
01964 {
01965 MatrixPtr  Matrix = (MatrixPtr)eMatrix;
01966 register ElementPtr pElement;
01967 register int Count, I, MaxCount = 0;
01968 RealNumber Reid, Gear;
01969 
01970 /* Begin `spRoundoff'. */
01971     ASSERT( IS_SPARSE(Matrix) AND IS_FACTORED(Matrix) );
01972 
01973 /* Compute Barlow's bound if it is not given. */
01974     if (Rho < 0.0) Rho = spLargestElement( eMatrix );
01975 
01976 /* Find the maximum number of off-diagonals in L if not previously computed. */
01977     if (Matrix->MaxRowCountInLowerTri < 0)
01978     {   for (I = Matrix->Size; I > 0; I--)
01979         {   pElement = Matrix->FirstInRow[I];
01980             Count = 0;
01981             while (pElement->Col < I)
01982             {   Count++;
01983                 pElement = pElement->NextInRow;
01984             }
01985             if (Count > MaxCount) MaxCount = Count;
01986         }
01987         Matrix->MaxRowCountInLowerTri = MaxCount;
01988     }
01989     else MaxCount = Matrix->MaxRowCountInLowerTri;
01990 
01991 /* Compute error bound. */
01992     Gear = 1.01*((MaxCount + 1) * Matrix->RelThreshold + 1.0) * SQR(MaxCount);
01993     Reid = 3.01 * Matrix->Size;
01994 
01995     if (Gear < Reid)
01996         return (MACHINE_RESOLUTION * Rho * Gear);
01997     else
01998         return (MACHINE_RESOLUTION * Rho * Reid);
01999 }
02000 #endif

Generated on Sun Mar 4 15:04:00 2007 for Scilab [trunk] by  doxygen 1.5.1