2261 lines
70 KiB
C
2261 lines
70 KiB
C
/*
|
||
* MATRIX UTILITY MODULE
|
||
*
|
||
* Author: Advising professor:
|
||
* Kenneth S. Kundert Alberto Sangiovanni-Vincentelli
|
||
* UC Berkeley
|
||
*
|
||
* This file contains various optional utility routines.
|
||
*
|
||
* >>> User accessible functions contained in this file:
|
||
* spMNA_Preorder
|
||
* spScale
|
||
* spMultiply
|
||
* spMultTransposed
|
||
* spDeterminant
|
||
* spStripFills
|
||
* spDeleteRowAndCol
|
||
* spPseudoCondition
|
||
* spCondition
|
||
* spNorm
|
||
* spLargestElement
|
||
* spRoundoff
|
||
* spErrorMessage
|
||
*
|
||
* >>> Other functions contained in this file:
|
||
* CountTwins
|
||
* SwapCols
|
||
* ScaleComplexMatrix
|
||
* ComplexMatrixMultiply
|
||
* ComplexCondition
|
||
*/
|
||
|
||
|
||
/*
|
||
* Revision and copyright information.
|
||
*
|
||
* Copyright (c) 1985,86,87,88,89,90
|
||
* by Kenneth S. Kundert and the University of California.
|
||
*
|
||
* Permission to use, copy, modify, and distribute this software and
|
||
* its documentation for any purpose and without fee is hereby granted,
|
||
* provided that the copyright notices appear in all copies and
|
||
* supporting documentation and that the authors and the University of
|
||
* California are properly credited. The authors and the University of
|
||
* California make no representations as to the suitability of this
|
||
* software for any purpose. It is provided `as is', without express
|
||
* or implied warranty.
|
||
*/
|
||
|
||
/*
|
||
* IMPORTS
|
||
*
|
||
* >>> Import descriptions:
|
||
* spConfig.h
|
||
* Macros that customize the sparse matrix routines.
|
||
* spMatrix.h
|
||
* Macros and declarations to be imported by the user.
|
||
* spDefs.h
|
||
* Matrix type and macro definitions for the sparse matrix routines.
|
||
*/
|
||
|
||
#define spINSIDE_SPARSE
|
||
#include "spconfig.h"
|
||
#include "spmatrix.h"
|
||
#include "spdefs.h"
|
||
|
||
|
||
|
||
|
||
|
||
/*
|
||
* Function declarations
|
||
*/
|
||
|
||
static int CountTwins( MatrixPtr, int, ElementPtr*, ElementPtr* );
|
||
static void SwapCols( MatrixPtr, ElementPtr, ElementPtr );
|
||
#if spSEPARATED_COMPLEX_VECTORS
|
||
static void ComplexMatrixMultiply( MatrixPtr, RealVector, RealVector,
|
||
RealVector, RealVector );
|
||
static void ComplexTransposedMatrixMultiply( MatrixPtr, RealVector, RealVector,
|
||
RealVector, RealVector);
|
||
#else
|
||
static void ComplexMatrixMultiply( MatrixPtr, RealVector, RealVector );
|
||
static void ComplexTransposedMatrixMultiply(MatrixPtr, RealVector, RealVector);
|
||
#endif
|
||
|
||
|
||
|
||
|
||
|
||
#if MODIFIED_NODAL
|
||
/*
|
||
* PREORDER MODIFIED NODE ADMITTANCE MATRIX TO REMOVE ZEROS FROM DIAGONAL
|
||
*
|
||
* This routine massages modified node admittance matrices to remove
|
||
* zeros from the diagonal. It takes advantage of the fact that the
|
||
* row and column associated with a zero diagonal usually have
|
||
* structural ones placed symmetricly. This routine should be used
|
||
* only on modified node admittance matrices and should be executed
|
||
* after the matrix has been built but before the factorization
|
||
* begins. It should be executed for the initial factorization only
|
||
* and should be executed before the rows have been linked. Thus it
|
||
* should be run before using spScale(), spMultiply(),
|
||
* spDeleteRowAndCol(), or spNorm().
|
||
*
|
||
* This routine exploits the fact that the structural ones are placed
|
||
* in the matrix in symmetric twins. For example, the stamps for
|
||
* grounded and a floating voltage sources are
|
||
* grounded: floating:
|
||
* [ x x 1 ] [ x x 1 ]
|
||
* [ x x ] [ x x -1 ]
|
||
* [ 1 ] [ 1 -1 ]
|
||
* Notice for the grounded source, there is one set of twins, and for
|
||
* the floating, there are two sets. We remove the zero from the diagonal
|
||
* by swapping the rows associated with a set of twins. For example:
|
||
* grounded: floating 1: floating 2:
|
||
* [ 1 ] [ 1 -1 ] [ x x 1 ]
|
||
* [ x x ] [ x x -1 ] [ 1 -1 ]
|
||
* [ x x 1 ] [ x x 1 ] [ x x -1 ]
|
||
*
|
||
* It is important to deal with any zero diagonals that only have one
|
||
* set of twins before dealing with those that have more than one because
|
||
* swapping row destroys the symmetry of any twins in the rows being
|
||
* swapped, which may limit future moves. Consider
|
||
* [ x x 1 ]
|
||
* [ x x -1 1 ]
|
||
* [ 1 -1 ]
|
||
* [ 1 ]
|
||
* There is one set of twins for diagonal 4 and two for diagonal 3.
|
||
* Dealing with diagonal 4 first requires swapping rows 2 and 4.
|
||
* [ x x 1 ]
|
||
* [ 1 ]
|
||
* [ 1 -1 ]
|
||
* [ x x -1 1 ]
|
||
* We can now deal with diagonal 3 by swapping rows 1 and 3.
|
||
* [ 1 -1 ]
|
||
* [ 1 ]
|
||
* [ x x 1 ]
|
||
* [ x x -1 1 ]
|
||
* And we are done, there are no zeros left on the diagonal. However, if
|
||
* we originally dealt with diagonal 3 first, we could swap rows 2 and 3
|
||
* [ x x 1 ]
|
||
* [ 1 -1 ]
|
||
* [ x x -1 1 ]
|
||
* [ 1 ]
|
||
* Diagonal 4 no longer has a symmetric twin and we cannot continue.
|
||
*
|
||
* So we always take care of lone twins first. When none remain, we
|
||
* choose arbitrarily a set of twins for a diagonal with more than one set
|
||
* and swap the rows corresponding to that twin. We then deal with any
|
||
* lone twins that were created and repeat the procedure until no
|
||
* zero diagonals with symmetric twins remain.
|
||
*
|
||
* In this particular implementation, columns are swapped rather than rows.
|
||
* The algorithm used in this function was developed by Ken Kundert and
|
||
* Tom Quarles.
|
||
*
|
||
* >>> Arguments:
|
||
* eMatrix <input> (char *)
|
||
* Pointer to the matrix to be preordered.
|
||
*
|
||
* >>> Local variables;
|
||
* J (int)
|
||
* Column with zero diagonal being currently considered.
|
||
* pTwin1 (ElementPtr)
|
||
* Pointer to the twin found in the column belonging to the zero diagonal.
|
||
* pTwin2 (ElementPtr)
|
||
* Pointer to the twin found in the row belonging to the zero diagonal.
|
||
* belonging to the zero diagonal.
|
||
* AnotherPassNeeded (BOOLEAN)
|
||
* Flag indicating that at least one zero diagonal with symmetric twins
|
||
* remain.
|
||
* StartAt (int)
|
||
* Column number of first zero diagonal with symmetric twins.
|
||
* Swapped (BOOLEAN)
|
||
* Flag indicating that columns were swapped on this pass.
|
||
* Twins (int)
|
||
* Number of symmetric twins corresponding to current zero diagonal.
|
||
*/
|
||
|
||
void
|
||
spMNA_Preorder( eMatrix )
|
||
|
||
char *eMatrix;
|
||
{
|
||
MatrixPtr Matrix = (MatrixPtr)eMatrix;
|
||
register int J, Size;
|
||
ElementPtr pTwin1, pTwin2;
|
||
int Twins, StartAt = 1;
|
||
BOOLEAN Swapped, AnotherPassNeeded;
|
||
|
||
/* Begin `spMNA_Preorder'. */
|
||
ASSERT( IS_VALID(Matrix) AND NOT Matrix->Factored );
|
||
|
||
if (Matrix->RowsLinked) return;
|
||
Size = Matrix->Size;
|
||
Matrix->Reordered = YES;
|
||
|
||
do
|
||
{ AnotherPassNeeded = Swapped = NO;
|
||
|
||
/* Search for zero diagonals with lone twins. */
|
||
for (J = StartAt; J <= Size; J++)
|
||
{ if (Matrix->Diag[J] == NULL)
|
||
{ Twins = CountTwins( Matrix, J, &pTwin1, &pTwin2 );
|
||
if (Twins == 1)
|
||
{ /* Lone twins found, swap rows. */
|
||
SwapCols( Matrix, pTwin1, pTwin2 );
|
||
Swapped = YES;
|
||
}
|
||
else if ((Twins > 1) AND NOT AnotherPassNeeded)
|
||
{ AnotherPassNeeded = YES;
|
||
StartAt = J;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* All lone twins are gone, look for zero diagonals with multiple twins. */
|
||
if (AnotherPassNeeded)
|
||
{ for (J = StartAt; NOT Swapped AND (J <= Size); J++)
|
||
{ if (Matrix->Diag[J] == NULL)
|
||
{ Twins = CountTwins( Matrix, J, &pTwin1, &pTwin2 );
|
||
SwapCols( Matrix, pTwin1, pTwin2 );
|
||
Swapped = YES;
|
||
}
|
||
}
|
||
}
|
||
} while (AnotherPassNeeded);
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
|
||
/*
|
||
* COUNT TWINS
|
||
*
|
||
* This function counts the number of symmetric twins associated with
|
||
* a zero diagonal and returns one set of twins if any exist. The
|
||
* count is terminated early at two.
|
||
*/
|
||
|
||
static int
|
||
CountTwins( Matrix, Col, ppTwin1, ppTwin2 )
|
||
|
||
MatrixPtr Matrix;
|
||
int Col;
|
||
ElementPtr *ppTwin1, *ppTwin2;
|
||
{
|
||
int Row, Twins = 0;
|
||
ElementPtr pTwin1, pTwin2;
|
||
|
||
/* Begin `CountTwins'. */
|
||
|
||
pTwin1 = Matrix->FirstInCol[Col];
|
||
while (pTwin1 != NULL)
|
||
{ if (ABS(pTwin1->Real) == 1.0)
|
||
{ Row = pTwin1->Row;
|
||
pTwin2 = Matrix->FirstInCol[Row];
|
||
while ((pTwin2 != NULL) AND (pTwin2->Row != Col))
|
||
pTwin2 = pTwin2->NextInCol;
|
||
if ((pTwin2 != NULL) AND (ABS(pTwin2->Real) == 1.0))
|
||
{ /* Found symmetric twins. */
|
||
if (++Twins >= 2) return Twins;
|
||
(*ppTwin1 = pTwin1)->Col = Col;
|
||
(*ppTwin2 = pTwin2)->Col = Row;
|
||
}
|
||
}
|
||
pTwin1 = pTwin1->NextInCol;
|
||
}
|
||
return Twins;
|
||
}
|
||
|
||
|
||
|
||
|
||
/*
|
||
* SWAP COLUMNS
|
||
*
|
||
* This function swaps two columns and is applicable before the rows are
|
||
* linked.
|
||
*/
|
||
|
||
static void
|
||
SwapCols( Matrix, pTwin1, pTwin2 )
|
||
|
||
MatrixPtr Matrix;
|
||
ElementPtr pTwin1, pTwin2;
|
||
{
|
||
int Col1 = pTwin1->Col, Col2 = pTwin2->Col;
|
||
|
||
/* Begin `SwapCols'. */
|
||
|
||
SWAP (ElementPtr, Matrix->FirstInCol[Col1], Matrix->FirstInCol[Col2]);
|
||
SWAP (int, Matrix->IntToExtColMap[Col1], Matrix->IntToExtColMap[Col2]);
|
||
#if TRANSLATE
|
||
Matrix->ExtToIntColMap[Matrix->IntToExtColMap[Col2]]=Col2;
|
||
Matrix->ExtToIntColMap[Matrix->IntToExtColMap[Col1]]=Col1;
|
||
#endif
|
||
|
||
Matrix->Diag[Col1] = pTwin2;
|
||
Matrix->Diag[Col2] = pTwin1;
|
||
Matrix->NumberOfInterchangesIsOdd = NOT Matrix->NumberOfInterchangesIsOdd;
|
||
return;
|
||
}
|
||
#endif /* MODIFIED_NODAL */
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#if SCALING
|
||
/*
|
||
* SCALE MATRIX
|
||
*
|
||
* This function scales the matrix to enhance the possibility of
|
||
* finding a good pivoting order. Note that scaling enhances accuracy
|
||
* of the solution only if it affects the pivoting order, so it makes
|
||
* no sense to scale the matrix before spFactor(). If scaling is
|
||
* desired it should be done before spOrderAndFactor(). There
|
||
* are several things to take into account when choosing the scale
|
||
* factors. First, the scale factors are directly multiplied against
|
||
* the elements in the matrix. To prevent roundoff, each scale factor
|
||
* should be equal to an integer power of the number base of the
|
||
* machine. Since most machines operate in base two, scale factors
|
||
* should be a power of two. Second, the matrix should be scaled such
|
||
* that the matrix of element uncertainties is equilibrated. Third,
|
||
* this function multiplies the scale factors by the elements, so if
|
||
* one row tends to have uncertainties 1000 times smaller than the
|
||
* other rows, then its scale factor should be 1024, not 1/1024.
|
||
* Fourth, to save time, this function does not scale rows or columns
|
||
* if their scale factors are equal to one. Thus, the scale factors
|
||
* should be normalized to the most common scale factor. Rows and
|
||
* columns should be normalized separately. For example, if the size
|
||
* of the matrix is 100 and 10 rows tend to have uncertainties near
|
||
* 1e-6 and the remaining 90 have uncertainties near 1e-12, then the
|
||
* scale factor for the 10 should be 1/1,048,576 and the scale factors
|
||
* for the remaining 90 should be 1. Fifth, since this routine
|
||
* directly operates on the matrix, it is necessary to apply the scale
|
||
* factors to the RHS and Solution vectors. It may be easier to
|
||
* simply use spOrderAndFactor() on a scaled matrix to choose the
|
||
* pivoting order, and then throw away the matrix. Subsequent
|
||
* factorizations, performed with spFactor(), will not need to have
|
||
* the RHS and Solution vectors descaled. Lastly, this function
|
||
* should not be executed before the function spMNA_Preorder.
|
||
*
|
||
* >>> Arguments:
|
||
* eMatrix <input> (char *)
|
||
* Pointer to the matrix to be scaled.
|
||
* SolutionScaleFactors <input> (RealVector)
|
||
* The array of Solution scale factors. These factors scale the columns.
|
||
* All scale factors are real valued.
|
||
* RHS_ScaleFactors <input> (RealVector)
|
||
* The array of RHS scale factors. These factors scale the rows.
|
||
* All scale factors are real valued.
|
||
*
|
||
* >>> Local variables:
|
||
* lSize (int)
|
||
* Local version of the size of the matrix.
|
||
* pElement (ElementPtr)
|
||
* Pointer to an element in the matrix.
|
||
* pExtOrder (int *)
|
||
* Pointer into either IntToExtRowMap or IntToExtColMap vector. Used to
|
||
* compensate for any row or column swaps that have been performed.
|
||
* ScaleFactor (RealNumber)
|
||
* The scale factor being used on the current row or column.
|
||
*/
|
||
|
||
void
|
||
spScale( eMatrix, RHS_ScaleFactors, SolutionScaleFactors )
|
||
|
||
char *eMatrix;
|
||
register RealVector RHS_ScaleFactors, SolutionScaleFactors;
|
||
{
|
||
MatrixPtr Matrix = (MatrixPtr)eMatrix;
|
||
register ElementPtr pElement;
|
||
register int I, lSize, *pExtOrder;
|
||
RealNumber ScaleFactor;
|
||
void ScaleComplexMatrix();
|
||
|
||
/* Begin `spScale'. */
|
||
ASSERT( IS_VALID(Matrix) AND NOT Matrix->Factored );
|
||
if (NOT Matrix->RowsLinked) spcLinkRows( Matrix );
|
||
|
||
#if spCOMPLEX
|
||
if (Matrix->Complex)
|
||
{ ScaleComplexMatrix( Matrix, RHS_ScaleFactors, SolutionScaleFactors );
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
#if REAL
|
||
lSize = Matrix->Size;
|
||
|
||
/* Correct pointers to arrays for ARRAY_OFFSET */
|
||
#if NOT ARRAY_OFFSET
|
||
--RHS_ScaleFactors;
|
||
--SolutionScaleFactors;
|
||
#endif
|
||
|
||
/* Scale Rows */
|
||
pExtOrder = &Matrix->IntToExtRowMap[1];
|
||
for (I = 1; I <= lSize; I++)
|
||
{ if ((ScaleFactor = RHS_ScaleFactors[*(pExtOrder++)]) != 1.0)
|
||
{ pElement = Matrix->FirstInRow[I];
|
||
|
||
while (pElement != NULL)
|
||
{ pElement->Real *= ScaleFactor;
|
||
pElement = pElement->NextInRow;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Scale Columns */
|
||
pExtOrder = &Matrix->IntToExtColMap[1];
|
||
for (I = 1; I <= lSize; I++)
|
||
{ if ((ScaleFactor = SolutionScaleFactors[*(pExtOrder++)]) != 1.0)
|
||
{ pElement = Matrix->FirstInCol[I];
|
||
|
||
while (pElement != NULL)
|
||
{ pElement->Real *= ScaleFactor;
|
||
pElement = pElement->NextInCol;
|
||
}
|
||
}
|
||
}
|
||
return;
|
||
|
||
#endif /* REAL */
|
||
}
|
||
#endif /* SCALING */
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#if spCOMPLEX AND SCALING
|
||
/*
|
||
* SCALE COMPLEX MATRIX
|
||
*
|
||
* This function scales the matrix to enhance the possibility of
|
||
* finding a good pivoting order. Note that scaling enhances accuracy
|
||
* of the solution only if it affects the pivoting order, so it makes
|
||
* no sense to scale the matrix before spFactor(). If scaling is
|
||
* desired it should be done before spOrderAndFactor(). There
|
||
* are several things to take into account when choosing the scale
|
||
* factors. First, the scale factors are directly multiplied against
|
||
* the elements in the matrix. To prevent roundoff, each scale factor
|
||
* should be equal to an integer power of the number base of the
|
||
* machine. Since most machines operate in base two, scale factors
|
||
* should be a power of two. Second, the matrix should be scaled such
|
||
* that the matrix of element uncertainties is equilibrated. Third,
|
||
* this function multiplies the scale factors by the elements, so if
|
||
* one row tends to have uncertainties 1000 times smaller than the
|
||
* other rows, then its scale factor should be 1024, not 1/1024.
|
||
* Fourth, to save time, this function does not scale rows or columns
|
||
* if their scale factors are equal to one. Thus, the scale factors
|
||
* should be normalized to the most common scale factor. Rows and
|
||
* columns should be normalized separately. For example, if the size
|
||
* of the matrix is 100 and 10 rows tend to have uncertainties near
|
||
* 1e-6 and the remaining 90 have uncertainties near 1e-12, then the
|
||
* scale factor for the 10 should be 1/1,048,576 and the scale factors
|
||
* for the remaining 90 should be 1. Fifth, since this routine
|
||
* directly operates on the matrix, it is necessary to apply the scale
|
||
* factors to the RHS and Solution vectors. It may be easier to
|
||
* simply use spOrderAndFactor() on a scaled matrix to choose the
|
||
* pivoting order, and then throw away the matrix. Subsequent
|
||
* factorizations, performed with spFactor(), will not need to have
|
||
* the RHS and Solution vectors descaled. Lastly, this function
|
||
* should not be executed before the function spMNA_Preorder.
|
||
*
|
||
* >>> Arguments:
|
||
* Matrix <input> (char *)
|
||
* Pointer to the matrix to be scaled.
|
||
* SolutionScaleFactors <input> (RealVector)
|
||
* The array of Solution scale factors. These factors scale the columns.
|
||
* All scale factors are real valued.
|
||
* RHS_ScaleFactors <input> (RealVector)
|
||
* The array of RHS scale factors. These factors scale the rows.
|
||
* All scale factors are real valued.
|
||
*
|
||
* >>> Local variables:
|
||
* lSize (int)
|
||
* Local version of the size of the matrix.
|
||
* pElement (ElementPtr)
|
||
* Pointer to an element in the matrix.
|
||
* pExtOrder (int *)
|
||
* Pointer into either IntToExtRowMap or IntToExtColMap vector. Used to
|
||
* compensate for any row or column swaps that have been performed.
|
||
* ScaleFactor (RealNumber)
|
||
* The scale factor being used on the current row or column.
|
||
*/
|
||
|
||
static void
|
||
ScaleComplexMatrix( Matrix, RHS_ScaleFactors, SolutionScaleFactors )
|
||
|
||
MatrixPtr Matrix;
|
||
register RealVector RHS_ScaleFactors, SolutionScaleFactors;
|
||
{
|
||
register ElementPtr pElement;
|
||
register int I, lSize, *pExtOrder;
|
||
RealNumber ScaleFactor;
|
||
|
||
/* Begin `ScaleComplexMatrix'. */
|
||
lSize = Matrix->Size;
|
||
|
||
/* Correct pointers to arrays for ARRAY_OFFSET */
|
||
#if NOT ARRAY_OFFSET
|
||
--RHS_ScaleFactors;
|
||
--SolutionScaleFactors;
|
||
#endif
|
||
|
||
/* Scale Rows */
|
||
pExtOrder = &Matrix->IntToExtRowMap[1];
|
||
for (I = 1; I <= lSize; I++)
|
||
{ if ((ScaleFactor = RHS_ScaleFactors[*(pExtOrder++)]) != 1.0)
|
||
{ pElement = Matrix->FirstInRow[I];
|
||
|
||
while (pElement != NULL)
|
||
{ pElement->Real *= ScaleFactor;
|
||
pElement->Imag *= ScaleFactor;
|
||
pElement = pElement->NextInRow;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Scale Columns */
|
||
pExtOrder = &Matrix->IntToExtColMap[1];
|
||
for (I = 1; I <= lSize; I++)
|
||
{ if ((ScaleFactor = SolutionScaleFactors[*(pExtOrder++)]) != 1.0)
|
||
{ pElement = Matrix->FirstInCol[I];
|
||
|
||
while (pElement != NULL)
|
||
{ pElement->Real *= ScaleFactor;
|
||
pElement->Imag *= ScaleFactor;
|
||
pElement = pElement->NextInCol;
|
||
}
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
#endif /* SCALING AND spCOMPLEX */
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#if MULTIPLICATION
|
||
/*
|
||
* MATRIX MULTIPLICATION
|
||
*
|
||
* Multiplies matrix by solution vector to find source vector.
|
||
* Assumes matrix has not been factored. This routine can be used
|
||
* as a test to see if solutions are correct. It should not be used
|
||
* before spMNA_Preorder().
|
||
*
|
||
* >>> Arguments:
|
||
* eMatrix <input> (char *)
|
||
* Pointer to the matrix.
|
||
* RHS <output> (RealVector)
|
||
* RHS is the right hand side. This is what is being solved for.
|
||
* Solution <input> (RealVector)
|
||
* Solution is the vector being multiplied by the matrix.
|
||
* iRHS <output> (RealVector)
|
||
* iRHS is the imaginary portion of the right hand side. This is
|
||
* what is being solved for. This is only necessary if the matrix is
|
||
* complex and spSEPARATED_COMPLEX_VECTORS is TRUE.
|
||
* iSolution <input> (RealVector)
|
||
* iSolution is the imaginary portion of the vector being multiplied
|
||
* by the matrix. This is only necessary if the matrix is
|
||
* complex and spSEPARATED_COMPLEX_VECTORS is TRUE.
|
||
*
|
||
* >>> Obscure Macros
|
||
* IMAG_VECTORS
|
||
* Replaces itself with `, iRHS, iSolution' if the options spCOMPLEX and
|
||
* spSEPARATED_COMPLEX_VECTORS are set, otherwise it disappears
|
||
* without a trace.
|
||
*/
|
||
|
||
void
|
||
spMultiply( eMatrix, RHS, Solution IMAG_VECTORS )
|
||
|
||
char *eMatrix;
|
||
RealVector RHS, Solution IMAG_VECTORS;
|
||
{
|
||
register ElementPtr pElement;
|
||
register RealVector Vector;
|
||
register RealNumber Sum;
|
||
register int I, *pExtOrder;
|
||
MatrixPtr Matrix = (MatrixPtr)eMatrix;
|
||
extern void ComplexMatrixMultiply();
|
||
|
||
/* Begin `spMultiply'. */
|
||
ASSERT( IS_SPARSE( Matrix ) AND NOT Matrix->Factored );
|
||
if (NOT Matrix->RowsLinked)
|
||
spcLinkRows(Matrix);
|
||
if (NOT Matrix->InternalVectorsAllocated)
|
||
spcCreateInternalVectors( Matrix );
|
||
|
||
#if spCOMPLEX
|
||
if (Matrix->Complex)
|
||
{ ComplexMatrixMultiply( Matrix, RHS, Solution IMAG_VECTORS );
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
#if REAL
|
||
#if NOT ARRAY_OFFSET
|
||
/* Correct array pointers for ARRAY_OFFSET. */
|
||
--RHS;
|
||
--Solution;
|
||
#endif
|
||
|
||
/* Initialize Intermediate vector with reordered Solution vector. */
|
||
Vector = Matrix->Intermediate;
|
||
pExtOrder = &Matrix->IntToExtColMap[Matrix->Size];
|
||
for (I = Matrix->Size; I > 0; I--)
|
||
Vector[I] = Solution[*(pExtOrder--)];
|
||
|
||
pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size];
|
||
for (I = Matrix->Size; I > 0; I--)
|
||
{ pElement = Matrix->FirstInRow[I];
|
||
Sum = 0.0;
|
||
|
||
while (pElement != NULL)
|
||
{ Sum += pElement->Real * Vector[pElement->Col];
|
||
pElement = pElement->NextInRow;
|
||
}
|
||
RHS[*pExtOrder--] = Sum;
|
||
}
|
||
return;
|
||
#endif /* REAL */
|
||
}
|
||
#endif /* MULTIPLICATION */
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#if spCOMPLEX AND MULTIPLICATION
|
||
/*
|
||
* COMPLEX MATRIX MULTIPLICATION
|
||
*
|
||
* Multiplies matrix by solution vector to find source vector.
|
||
* Assumes matrix has not been factored. This routine can be used
|
||
* as a test to see if solutions are correct.
|
||
*
|
||
* >>> Arguments:
|
||
* Matrix <input> (char *)
|
||
* Pointer to the matrix.
|
||
* RHS <output> (RealVector)
|
||
* RHS is the right hand side. This is what is being solved for.
|
||
* This is only the real portion of the right-hand side if the matrix
|
||
* is complex and spSEPARATED_COMPLEX_VECTORS is set TRUE.
|
||
* Solution <input> (RealVector)
|
||
* Solution is the vector being multiplied by the matrix. This is only
|
||
* the real portion if the matrix is complex and
|
||
* spSEPARATED_COMPLEX_VECTORS is set TRUE.
|
||
* iRHS <output> (RealVector)
|
||
* iRHS is the imaginary portion of the right hand side. This is
|
||
* what is being solved for. This is only necessary if the matrix is
|
||
* complex and spSEPARATED_COMPLEX_VECTORS is TRUE.
|
||
* iSolution <input> (RealVector)
|
||
* iSolution is the imaginary portion of the vector being multiplied
|
||
* by the matrix. This is only necessary if the matrix is
|
||
* complex and spSEPARATED_COMPLEX_VECTORS is TRUE.
|
||
*
|
||
* >>> Obscure Macros
|
||
* IMAG_VECTORS
|
||
* Replaces itself with `, iRHS, iSolution' if the options spCOMPLEX and
|
||
* spSEPARATED_COMPLEX_VECTORS are set, otherwise it disappears
|
||
* without a trace.
|
||
*/
|
||
|
||
static void
|
||
ComplexMatrixMultiply( Matrix, RHS, Solution IMAG_VECTORS )
|
||
|
||
MatrixPtr Matrix;
|
||
RealVector RHS, Solution IMAG_VECTORS;
|
||
{
|
||
register ElementPtr pElement;
|
||
register ComplexVector Vector;
|
||
ComplexNumber Sum;
|
||
register int I, *pExtOrder;
|
||
|
||
/* Begin `ComplexMatrixMultiply'. */
|
||
|
||
/* Correct array pointers for ARRAY_OFFSET. */
|
||
#if NOT ARRAY_OFFSET
|
||
#if spSEPARATED_COMPLEX_VECTORS
|
||
--RHS; --iRHS;
|
||
--Solution; --iSolution;
|
||
#else
|
||
RHS -= 2; Solution -= 2;
|
||
#endif
|
||
#endif
|
||
|
||
/* Initialize Intermediate vector with reordered Solution vector. */
|
||
Vector = (ComplexVector)Matrix->Intermediate;
|
||
pExtOrder = &Matrix->IntToExtColMap[Matrix->Size];
|
||
|
||
#if spSEPARATED_COMPLEX_VECTORS
|
||
for (I = Matrix->Size; I > 0; I--)
|
||
{ Vector[I].Real = Solution[*pExtOrder];
|
||
Vector[I].Imag = iSolution[*(pExtOrder--)];
|
||
}
|
||
#else
|
||
for (I = Matrix->Size; I > 0; I--)
|
||
Vector[I] = ((ComplexVector)Solution)[*(pExtOrder--)];
|
||
#endif
|
||
|
||
pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size];
|
||
for (I = Matrix->Size; I > 0; I--)
|
||
{ pElement = Matrix->FirstInRow[I];
|
||
Sum.Real = Sum.Imag = 0.0;
|
||
|
||
while (pElement != NULL)
|
||
{ /* Cmplx expression : Sum += Element * Vector[Col] */
|
||
CMPLX_MULT_ADD_ASSIGN( Sum, *pElement, Vector[pElement->Col] );
|
||
pElement = pElement->NextInRow;
|
||
}
|
||
|
||
#if spSEPARATED_COMPLEX_VECTORS
|
||
RHS[*pExtOrder] = Sum.Real;
|
||
iRHS[*pExtOrder--] = Sum.Imag;
|
||
#else
|
||
((ComplexVector)RHS)[*pExtOrder--] = Sum;
|
||
#endif
|
||
}
|
||
return;
|
||
}
|
||
#endif /* spCOMPLEX AND MULTIPLICATION */
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#if MULTIPLICATION AND TRANSPOSE
|
||
/*
|
||
* TRANSPOSED MATRIX MULTIPLICATION
|
||
*
|
||
* Multiplies transposed matrix by solution vector to find source vector.
|
||
* Assumes matrix has not been factored. This routine can be used
|
||
* as a test to see if solutions are correct. It should not be used
|
||
* before spMNA_Preorder().
|
||
*
|
||
* >>> Arguments:
|
||
* eMatrix <input> (char *)
|
||
* Pointer to the matrix.
|
||
* RHS <output> (RealVector)
|
||
* RHS is the right hand side. This is what is being solved for.
|
||
* Solution <input> (RealVector)
|
||
* Solution is the vector being multiplied by the matrix.
|
||
* iRHS <output> (RealVector)
|
||
* iRHS is the imaginary portion of the right hand side. This is
|
||
* what is being solved for. This is only necessary if the matrix is
|
||
* complex and spSEPARATED_COMPLEX_VECTORS is TRUE.
|
||
* iSolution <input> (RealVector)
|
||
* iSolution is the imaginary portion of the vector being multiplied
|
||
* by the matrix. This is only necessary if the matrix is
|
||
* complex and spSEPARATED_COMPLEX_VECTORS is TRUE.
|
||
*
|
||
* >>> Obscure Macros
|
||
* IMAG_VECTORS
|
||
* Replaces itself with `, iRHS, iSolution' if the options spCOMPLEX and
|
||
* spSEPARATED_COMPLEX_VECTORS are set, otherwise it disappears
|
||
* without a trace.
|
||
*/
|
||
|
||
void
|
||
spMultTransposed( eMatrix, RHS, Solution IMAG_VECTORS )
|
||
|
||
char *eMatrix;
|
||
RealVector RHS, Solution IMAG_VECTORS;
|
||
{
|
||
register ElementPtr pElement;
|
||
register RealVector Vector;
|
||
register RealNumber Sum;
|
||
register int I, *pExtOrder;
|
||
MatrixPtr Matrix = (MatrixPtr)eMatrix;
|
||
extern void ComplexTransposedMatrixMultiply();
|
||
|
||
/* Begin `spMultTransposed'. */
|
||
ASSERT( IS_SPARSE( Matrix ) AND NOT Matrix->Factored );
|
||
if (NOT Matrix->InternalVectorsAllocated)
|
||
spcCreateInternalVectors( Matrix );
|
||
|
||
#if spCOMPLEX
|
||
if (Matrix->Complex)
|
||
{ ComplexTransposedMatrixMultiply( Matrix, RHS, Solution IMAG_VECTORS );
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
#if REAL
|
||
#if NOT ARRAY_OFFSET
|
||
/* Correct array pointers for ARRAY_OFFSET. */
|
||
--RHS;
|
||
--Solution;
|
||
#endif
|
||
|
||
/* Initialize Intermediate vector with reordered Solution vector. */
|
||
Vector = Matrix->Intermediate;
|
||
pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size];
|
||
for (I = Matrix->Size; I > 0; I--)
|
||
Vector[I] = Solution[*(pExtOrder--)];
|
||
|
||
pExtOrder = &Matrix->IntToExtColMap[Matrix->Size];
|
||
for (I = Matrix->Size; I > 0; I--)
|
||
{ pElement = Matrix->FirstInCol[I];
|
||
Sum = 0.0;
|
||
|
||
while (pElement != NULL)
|
||
{ Sum += pElement->Real * Vector[pElement->Row];
|
||
pElement = pElement->NextInCol;
|
||
}
|
||
RHS[*pExtOrder--] = Sum;
|
||
}
|
||
return;
|
||
#endif /* REAL */
|
||
}
|
||
#endif /* MULTIPLICATION AND TRANSPOSE */
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#if spCOMPLEX AND MULTIPLICATION AND TRANSPOSE
|
||
/*
|
||
* COMPLEX TRANSPOSED MATRIX MULTIPLICATION
|
||
*
|
||
* Multiplies transposed matrix by solution vector to find source vector.
|
||
* Assumes matrix has not been factored. This routine can be used
|
||
* as a test to see if solutions are correct.
|
||
*
|
||
* >>> Arguments:
|
||
* Matrix <input> (char *)
|
||
* Pointer to the matrix.
|
||
* RHS <output> (RealVector)
|
||
* RHS is the right hand side. This is what is being solved for.
|
||
* This is only the real portion of the right-hand side if the matrix
|
||
* is complex and spSEPARATED_COMPLEX_VECTORS is set TRUE.
|
||
* Solution <input> (RealVector)
|
||
* Solution is the vector being multiplied by the matrix. This is only
|
||
* the real portion if the matrix is complex and
|
||
* spSEPARATED_COMPLEX_VECTORS is set TRUE.
|
||
* iRHS <output> (RealVector)
|
||
* iRHS is the imaginary portion of the right hand side. This is
|
||
* what is being solved for. This is only necessary if the matrix is
|
||
* complex and spSEPARATED_COMPLEX_VECTORS is TRUE.
|
||
* iSolution <input> (RealVector)
|
||
* iSolution is the imaginary portion of the vector being multiplied
|
||
* by the matrix. This is only necessary if the matrix is
|
||
* complex and spSEPARATED_COMPLEX_VECTORS is TRUE.
|
||
*
|
||
* >>> Obscure Macros
|
||
* IMAG_VECTORS
|
||
* Replaces itself with `, iRHS, iSolution' if the options spCOMPLEX and
|
||
* spSEPARATED_COMPLEX_VECTORS are set, otherwise it disappears
|
||
* without a trace.
|
||
*/
|
||
|
||
static void
|
||
ComplexTransposedMatrixMultiply( Matrix, RHS, Solution IMAG_VECTORS )
|
||
|
||
MatrixPtr Matrix;
|
||
RealVector RHS, Solution IMAG_VECTORS;
|
||
{
|
||
register ElementPtr pElement;
|
||
register ComplexVector Vector;
|
||
ComplexNumber Sum;
|
||
register int I, *pExtOrder;
|
||
|
||
/* Begin `ComplexMatrixMultiply'. */
|
||
|
||
/* Correct array pointers for ARRAY_OFFSET. */
|
||
#if NOT ARRAY_OFFSET
|
||
#if spSEPARATED_COMPLEX_VECTORS
|
||
--RHS; --iRHS;
|
||
--Solution; --iSolution;
|
||
#else
|
||
RHS -= 2; Solution -= 2;
|
||
#endif
|
||
#endif
|
||
|
||
/* Initialize Intermediate vector with reordered Solution vector. */
|
||
Vector = (ComplexVector)Matrix->Intermediate;
|
||
pExtOrder = &Matrix->IntToExtRowMap[Matrix->Size];
|
||
|
||
#if spSEPARATED_COMPLEX_VECTORS
|
||
for (I = Matrix->Size; I > 0; I--)
|
||
{ Vector[I].Real = Solution[*pExtOrder];
|
||
Vector[I].Imag = iSolution[*(pExtOrder--)];
|
||
}
|
||
#else
|
||
for (I = Matrix->Size; I > 0; I--)
|
||
Vector[I] = ((ComplexVector)Solution)[*(pExtOrder--)];
|
||
#endif
|
||
|
||
pExtOrder = &Matrix->IntToExtColMap[Matrix->Size];
|
||
for (I = Matrix->Size; I > 0; I--)
|
||
{ pElement = Matrix->FirstInCol[I];
|
||
Sum.Real = Sum.Imag = 0.0;
|
||
|
||
while (pElement != NULL)
|
||
{ /* Cmplx expression : Sum += Element * Vector[Row] */
|
||
CMPLX_MULT_ADD_ASSIGN( Sum, *pElement, Vector[pElement->Row] );
|
||
pElement = pElement->NextInCol;
|
||
}
|
||
|
||
#if spSEPARATED_COMPLEX_VECTORS
|
||
RHS[*pExtOrder] = Sum.Real;
|
||
iRHS[*pExtOrder--] = Sum.Imag;
|
||
#else
|
||
((ComplexVector)RHS)[*pExtOrder--] = Sum;
|
||
#endif
|
||
}
|
||
return;
|
||
}
|
||
#endif /* spCOMPLEX AND MULTIPLICATION AND TRANSPOSE */
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#if DETERMINANT
|
||
/*
|
||
* CALCULATE DETERMINANT
|
||
*
|
||
* This routine in capable of calculating the determinant of the
|
||
* matrix once the LU factorization has been performed. Hence, only
|
||
* use this routine after spFactor() and before spClear().
|
||
* The determinant equals the product of all the diagonal elements of
|
||
* the lower triangular matrix L, except that this product may need
|
||
* negating. Whether the product or the negative product equals the
|
||
* determinant is determined by the number of row and column
|
||
* interchanges performed. Note that the determinants of matrices can
|
||
* be very large or very small. On large matrices, the determinant
|
||
* can be far larger or smaller than can be represented by a floating
|
||
* point number. For this reason the determinant is scaled to a
|
||
* reasonable value and the logarithm of the scale factor is returned.
|
||
*
|
||
* >>> Arguments:
|
||
* eMatrix <input> (char *)
|
||
* A pointer to the matrix for which the determinant is desired.
|
||
* pExponent <output> (int *)
|
||
* The logarithm base 10 of the scale factor for the determinant. To find
|
||
* the actual determinant, Exponent should be added to the exponent of
|
||
* Determinant.
|
||
* pDeterminant <output> (RealNumber *)
|
||
* The real portion of the determinant. This number is scaled to be
|
||
* greater than or equal to 1.0 and less than 10.0.
|
||
* piDeterminant <output> (RealNumber *)
|
||
* The imaginary portion of the determinant. When the matrix is real
|
||
* this pointer need not be supplied, nothing will be returned. This
|
||
* number is scaled to be greater than or equal to 1.0 and less than 10.0.
|
||
*
|
||
* >>> Local variables:
|
||
* Norm (RealNumber)
|
||
* L-infinity norm of a complex number.
|
||
* Size (int)
|
||
* Local storage for Matrix->Size. Placed in a register for speed.
|
||
* Temp (RealNumber)
|
||
* Temporary storage for real portion of determinant.
|
||
*/
|
||
|
||
#if spCOMPLEX
|
||
void
|
||
spDeterminant( eMatrix, pExponent, pDeterminant, piDeterminant )
|
||
RealNumber *piDeterminant;
|
||
#else
|
||
void
|
||
spDeterminant( eMatrix, pExponent, pDeterminant )
|
||
#endif
|
||
|
||
char *eMatrix;
|
||
register RealNumber *pDeterminant;
|
||
int *pExponent;
|
||
{
|
||
register MatrixPtr Matrix = (MatrixPtr)eMatrix;
|
||
register int I, Size;
|
||
RealNumber Norm, nr, ni;
|
||
ComplexNumber Pivot, cDeterminant;
|
||
|
||
#define NORM(a) (nr = ABS((a).Real), ni = ABS((a).Imag), MAX (nr,ni))
|
||
|
||
/* Begin `spDeterminant'. */
|
||
ASSERT( IS_SPARSE( Matrix ) AND IS_FACTORED(Matrix) );
|
||
*pExponent = 0;
|
||
|
||
if (Matrix->Error == spSINGULAR)
|
||
{ *pDeterminant = 0.0;
|
||
#if spCOMPLEX
|
||
if (Matrix->Complex) *piDeterminant = 0.0;
|
||
#endif
|
||
return;
|
||
}
|
||
|
||
Size = Matrix->Size;
|
||
I = 0;
|
||
|
||
#if spCOMPLEX
|
||
if (Matrix->Complex) /* Complex Case. */
|
||
{ cDeterminant.Real = 1.0;
|
||
cDeterminant.Imag = 0.0;
|
||
|
||
while (++I <= Size)
|
||
{ CMPLX_RECIPROCAL( Pivot, *Matrix->Diag[I] );
|
||
CMPLX_MULT_ASSIGN( cDeterminant, Pivot );
|
||
|
||
/* Scale Determinant. */
|
||
Norm = NORM( cDeterminant );
|
||
if (Norm != 0.0)
|
||
{ while (Norm >= 1.0e12)
|
||
{ cDeterminant.Real *= 1.0e-12;
|
||
cDeterminant.Imag *= 1.0e-12;
|
||
*pExponent += 12;
|
||
Norm = NORM( cDeterminant );
|
||
}
|
||
while (Norm < 1.0e-12)
|
||
{ cDeterminant.Real *= 1.0e12;
|
||
cDeterminant.Imag *= 1.0e12;
|
||
*pExponent -= 12;
|
||
Norm = NORM( cDeterminant );
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Scale Determinant again, this time to be between 1.0 <= x < 10.0. */
|
||
Norm = NORM( cDeterminant );
|
||
if (Norm != 0.0)
|
||
{ while (Norm >= 10.0)
|
||
{ cDeterminant.Real *= 0.1;
|
||
cDeterminant.Imag *= 0.1;
|
||
(*pExponent)++;
|
||
Norm = NORM( cDeterminant );
|
||
}
|
||
while (Norm < 1.0)
|
||
{ cDeterminant.Real *= 10.0;
|
||
cDeterminant.Imag *= 10.0;
|
||
(*pExponent)--;
|
||
Norm = NORM( cDeterminant );
|
||
}
|
||
}
|
||
if (Matrix->NumberOfInterchangesIsOdd)
|
||
CMPLX_NEGATE( cDeterminant );
|
||
|
||
*pDeterminant = cDeterminant.Real;
|
||
*piDeterminant = cDeterminant.Imag;
|
||
}
|
||
#endif /* spCOMPLEX */
|
||
#if REAL AND spCOMPLEX
|
||
else
|
||
#endif
|
||
#if REAL
|
||
{ /* Real Case. */
|
||
*pDeterminant = 1.0;
|
||
|
||
while (++I <= Size)
|
||
{ *pDeterminant /= Matrix->Diag[I]->Real;
|
||
|
||
/* Scale Determinant. */
|
||
if (*pDeterminant != 0.0)
|
||
{ while (ABS(*pDeterminant) >= 1.0e12)
|
||
{ *pDeterminant *= 1.0e-12;
|
||
*pExponent += 12;
|
||
}
|
||
while (ABS(*pDeterminant) < 1.0e-12)
|
||
{ *pDeterminant *= 1.0e12;
|
||
*pExponent -= 12;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Scale Determinant again, this time to be between 1.0 <= x < 10.0. */
|
||
if (*pDeterminant != 0.0)
|
||
{ while (ABS(*pDeterminant) >= 10.0)
|
||
{ *pDeterminant *= 0.1;
|
||
(*pExponent)++;
|
||
}
|
||
while (ABS(*pDeterminant) < 1.0)
|
||
{ *pDeterminant *= 10.0;
|
||
(*pExponent)--;
|
||
}
|
||
}
|
||
if (Matrix->NumberOfInterchangesIsOdd)
|
||
*pDeterminant = -*pDeterminant;
|
||
}
|
||
#endif /* REAL */
|
||
}
|
||
#endif /* DETERMINANT */
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#if STRIP
|
||
/*
|
||
* STRIP FILL-INS FROM MATRIX
|
||
*
|
||
* Strips the matrix of all fill-ins.
|
||
*
|
||
* >>> Arguments:
|
||
* Matrix <input> (char *)
|
||
* Pointer to the matrix to be stripped.
|
||
*
|
||
* >>> Local variables:
|
||
* pElement (ElementPtr)
|
||
* Pointer that is used to step through the matrix.
|
||
* ppElement (ElementPtr *)
|
||
* Pointer to the location of an ElementPtr. This location will be
|
||
* updated if a fill-in is stripped from the matrix.
|
||
* pFillin (ElementPtr)
|
||
* Pointer used to step through the lists of fill-ins while marking them.
|
||
* pLastFillin (ElementPtr)
|
||
* A pointer to the last fill-in in the list. Used to terminate a loop.
|
||
* pListNode (struct FillinListNodeStruct *)
|
||
* A pointer to a node in the FillinList linked-list.
|
||
*/
|
||
|
||
void
|
||
spStripFills( eMatrix )
|
||
|
||
char *eMatrix;
|
||
{
|
||
MatrixPtr Matrix = (MatrixPtr)eMatrix;
|
||
struct FillinListNodeStruct *pListNode;
|
||
|
||
/* Begin `spStripFills'. */
|
||
ASSERT( IS_SPARSE( Matrix ) );
|
||
if (Matrix->Fillins == 0) return;
|
||
Matrix->NeedsOrdering = YES;
|
||
Matrix->Elements -= Matrix->Fillins;
|
||
Matrix->Fillins = 0;
|
||
|
||
/* Mark the fill-ins. */
|
||
{ register ElementPtr pFillin, pLastFillin;
|
||
|
||
pListNode = Matrix->LastFillinListNode = Matrix->FirstFillinListNode;
|
||
Matrix->FillinsRemaining = pListNode->NumberOfFillinsInList;
|
||
Matrix->NextAvailFillin = pListNode->pFillinList;
|
||
|
||
while (pListNode != NULL)
|
||
{ pFillin = pListNode->pFillinList;
|
||
pLastFillin = &(pFillin[ pListNode->NumberOfFillinsInList - 1 ]);
|
||
while (pFillin <= pLastFillin)
|
||
(pFillin++)->Row = 0;
|
||
pListNode = pListNode->Next;
|
||
}
|
||
}
|
||
|
||
/* Unlink fill-ins by searching for elements marked with Row = 0. */
|
||
{ register ElementPtr pElement, *ppElement;
|
||
register int I, Size = Matrix->Size;
|
||
|
||
/* Unlink fill-ins in all columns. */
|
||
for (I = 1; I <= Size; I++)
|
||
{ ppElement = &(Matrix->FirstInCol[I]);
|
||
while ((pElement = *ppElement) != NULL)
|
||
{ if (pElement->Row == 0)
|
||
{ *ppElement = pElement->NextInCol; /* Unlink fill-in. */
|
||
if (Matrix->Diag[pElement->Col] == pElement)
|
||
Matrix->Diag[pElement->Col] = NULL;
|
||
}
|
||
else
|
||
ppElement = &pElement->NextInCol; /* Skip element. */
|
||
}
|
||
}
|
||
|
||
/* Unlink fill-ins in all rows. */
|
||
for (I = 1; I <= Size; I++)
|
||
{ ppElement = &(Matrix->FirstInRow[I]);
|
||
while ((pElement = *ppElement) != NULL)
|
||
{ if (pElement->Row == 0)
|
||
*ppElement = pElement->NextInRow; /* Unlink fill-in. */
|
||
else
|
||
ppElement = &pElement->NextInRow; /* Skip element. */
|
||
}
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
/* Same as above, but strips entire matrix without destroying the frame.
|
||
* This assumes that the matrix will be replaced with one of the same size.
|
||
*/
|
||
void
|
||
spStripMatrix( eMatrix )
|
||
|
||
char *eMatrix;
|
||
{
|
||
MatrixPtr Matrix = (MatrixPtr)eMatrix;
|
||
|
||
/* Begin `spStripMatrix'. */
|
||
ASSERT( IS_SPARSE( Matrix ) );
|
||
if (Matrix->Elements == 0) return;
|
||
Matrix->RowsLinked = NO;
|
||
Matrix->NeedsOrdering = YES;
|
||
Matrix->Elements = 0;
|
||
Matrix->Originals = 0;
|
||
Matrix->Fillins = 0;
|
||
|
||
/* Reset the element lists. */
|
||
{ register ElementPtr pElement;
|
||
struct ElementListNodeStruct *pListNode;
|
||
|
||
pListNode = Matrix->LastElementListNode = Matrix->FirstElementListNode;
|
||
Matrix->ElementsRemaining = pListNode->NumberOfElementsInList;
|
||
Matrix->NextAvailElement = pListNode->pElementList;
|
||
}
|
||
|
||
/* Reset the fill-in lists. */
|
||
{ register ElementPtr pFillin;
|
||
struct FillinListNodeStruct *pListNode;
|
||
|
||
pListNode = Matrix->LastFillinListNode = Matrix->FirstFillinListNode;
|
||
Matrix->FillinsRemaining = pListNode->NumberOfFillinsInList;
|
||
Matrix->NextAvailFillin = pListNode->pFillinList;
|
||
}
|
||
|
||
/* Reset the Row, Column and Diag pointers */
|
||
{ register int I, Size = Matrix->Size;
|
||
for (I = 1; I <= Size; I++)
|
||
{ Matrix->FirstInRow[I] = NULL;
|
||
Matrix->FirstInCol[I] = NULL;
|
||
Matrix->Diag[I] = NULL;
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#if TRANSLATE AND DELETE
|
||
/*
|
||
* DELETE A ROW AND COLUMN FROM THE MATRIX
|
||
*
|
||
* Deletes a row and a column from a matrix.
|
||
*
|
||
* Sparse will abort if an attempt is made to delete a row or column that
|
||
* doesn't exist.
|
||
*
|
||
* >>> Arguments:
|
||
* eMatrix <input> (char *)
|
||
* Pointer to the matrix in which the row and column are to be deleted.
|
||
* Row <input> (int)
|
||
* Row to be deleted.
|
||
* Col <input> (int)
|
||
* Column to be deleted.
|
||
*
|
||
* >>> Local variables:
|
||
* ExtCol (int)
|
||
* The external column that is being deleted.
|
||
* ExtRow (int)
|
||
* The external row that is being deleted.
|
||
* pElement (ElementPtr)
|
||
* Pointer to an element in the matrix. Used when scanning rows and
|
||
* columns in order to eliminate elements from the last row or column.
|
||
* ppElement (ElementPtr *)
|
||
* Pointer to the location of an ElementPtr. This location will be
|
||
* filled with a NULL pointer if it is the new last element in its row
|
||
* or column.
|
||
* pElement (ElementPtr)
|
||
* Pointer to an element in the last row or column of the matrix.
|
||
* Size (int)
|
||
* The local version Matrix->Size, the size of the matrix.
|
||
*/
|
||
|
||
void
|
||
spDeleteRowAndCol( eMatrix, Row, Col )
|
||
|
||
char *eMatrix;
|
||
int Row, Col;
|
||
{
|
||
MatrixPtr Matrix = (MatrixPtr)eMatrix;
|
||
register ElementPtr pElement, *ppElement, pLastElement;
|
||
int Size, ExtRow, ExtCol;
|
||
ElementPtr spcFindElementInCol();
|
||
|
||
/* Begin `spDeleteRowAndCol'. */
|
||
|
||
ASSERT( IS_SPARSE(Matrix) AND Row > 0 AND Col > 0 );
|
||
ASSERT( Row <= Matrix->ExtSize AND Col <= Matrix->ExtSize );
|
||
|
||
Size = Matrix->Size;
|
||
ExtRow = Row;
|
||
ExtCol = Col;
|
||
if (NOT Matrix->RowsLinked) spcLinkRows( Matrix );
|
||
|
||
Row = Matrix->ExtToIntRowMap[Row];
|
||
Col = Matrix->ExtToIntColMap[Col];
|
||
ASSERT( Row > 0 AND Col > 0 );
|
||
|
||
/* Move Row so that it is the last row in the matrix. */
|
||
if (Row != Size) spcRowExchange( Matrix, Row, Size );
|
||
|
||
/* Move Col so that it is the last column in the matrix. */
|
||
if (Col != Size) spcColExchange( Matrix, Col, Size );
|
||
|
||
/* Correct Diag pointers. */
|
||
if (Row == Col)
|
||
SWAP( ElementPtr, Matrix->Diag[Row], Matrix->Diag[Size] )
|
||
else
|
||
{ Matrix->Diag[Row] = spcFindElementInCol( Matrix, Matrix->FirstInCol+Row,
|
||
Row, Row, NO );
|
||
Matrix->Diag[Col] = spcFindElementInCol( Matrix, Matrix->FirstInCol+Col,
|
||
Col, Col, NO );
|
||
}
|
||
|
||
/*
|
||
* Delete last row and column of the matrix.
|
||
*/
|
||
/* Break the column links to every element in the last row. */
|
||
pLastElement = Matrix->FirstInRow[ Size ];
|
||
while (pLastElement != NULL)
|
||
{ ppElement = &(Matrix->FirstInCol[ pLastElement->Col ]);
|
||
while ((pElement = *ppElement) != NULL)
|
||
{ if (pElement == pLastElement)
|
||
*ppElement = NULL; /* Unlink last element in column. */
|
||
else
|
||
ppElement = &pElement->NextInCol; /* Skip element. */
|
||
}
|
||
pLastElement = pLastElement->NextInRow;
|
||
}
|
||
|
||
/* Break the row links to every element in the last column. */
|
||
pLastElement = Matrix->FirstInCol[ Size ];
|
||
while (pLastElement != NULL)
|
||
{ ppElement = &(Matrix->FirstInRow[ pLastElement->Row ]);
|
||
while ((pElement = *ppElement) != NULL)
|
||
{ if (pElement == pLastElement)
|
||
*ppElement = NULL; /* Unlink last element in row. */
|
||
else
|
||
ppElement = &pElement->NextInRow; /* Skip element. */
|
||
}
|
||
pLastElement = pLastElement->NextInCol;
|
||
}
|
||
|
||
/* Clean up some details. */
|
||
Matrix->Size = Size - 1;
|
||
Matrix->Diag[Size] = NULL;
|
||
Matrix->FirstInRow[Size] = NULL;
|
||
Matrix->FirstInCol[Size] = NULL;
|
||
Matrix->CurrentSize--;
|
||
Matrix->ExtToIntRowMap[ExtRow] = -1;
|
||
Matrix->ExtToIntColMap[ExtCol] = -1;
|
||
Matrix->NeedsOrdering = YES;
|
||
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#if PSEUDOCONDITION
|
||
/*
|
||
* CALCULATE PSEUDOCONDITION
|
||
*
|
||
* Computes the magnitude of the ratio of the largest to the smallest
|
||
* pivots. This quantity is an indicator of ill-conditioning in the
|
||
* matrix. If this ratio is large, and if the matrix is scaled such
|
||
* that uncertainties in the RHS and the matrix entries are
|
||
* equilibrated, then the matrix is ill-conditioned. However, a small
|
||
* ratio does not necessarily imply that the matrix is
|
||
* well-conditioned. This routine must only be used after a matrix has
|
||
* been factored by spOrderAndFactor() or spFactor() and before it is
|
||
* cleared by spClear() or spInitialize(). The pseudocondition is
|
||
* faster to compute than the condition number calculated by
|
||
* spCondition(), but is not as informative.
|
||
*
|
||
* >>> Returns:
|
||
* The magnitude of the ratio of the largest to smallest pivot used during
|
||
* previous factorization. If the matrix was singular, zero is returned.
|
||
*
|
||
* >>> Arguments:
|
||
* eMatrix <input> (char *)
|
||
* Pointer to the matrix.
|
||
*/
|
||
|
||
RealNumber
|
||
spPseudoCondition( eMatrix )
|
||
|
||
char *eMatrix;
|
||
{
|
||
MatrixPtr Matrix = (MatrixPtr)eMatrix;
|
||
register int I;
|
||
register ArrayOfElementPtrs Diag;
|
||
RealNumber MaxPivot, MinPivot, Mag;
|
||
|
||
/* Begin `spPseudoCondition'. */
|
||
|
||
ASSERT( IS_SPARSE(Matrix) AND IS_FACTORED(Matrix) );
|
||
if (Matrix->Error == spSINGULAR OR Matrix->Error == spZERO_DIAG)
|
||
return 0.0;
|
||
|
||
Diag = Matrix->Diag;
|
||
MaxPivot = MinPivot = ELEMENT_MAG( Diag[1] );
|
||
for (I = 2; I <= Matrix->Size; I++)
|
||
{ Mag = ELEMENT_MAG( Diag[I] );
|
||
if (Mag > MaxPivot)
|
||
MaxPivot = Mag;
|
||
else if (Mag < MinPivot)
|
||
MinPivot = Mag;
|
||
}
|
||
ASSERT( MaxPivot > 0.0);
|
||
return MaxPivot / MinPivot;
|
||
}
|
||
#endif
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#if CONDITION
|
||
/*
|
||
* ESTIMATE CONDITION NUMBER
|
||
*
|
||
* Computes an estimate of the condition number using a variation on
|
||
* the LINPACK condition number estimation algorithm. This quantity is
|
||
* an indicator of ill-conditioning in the matrix. To avoid problems
|
||
* with overflow, the reciprocal of the condition number is returned.
|
||
* If this number is small, and if the matrix is scaled such that
|
||
* uncertainties in the RHS and the matrix entries are equilibrated,
|
||
* then the matrix is ill-conditioned. If the this number is near
|
||
* one, the matrix is well conditioned. This routine must only be
|
||
* used after a matrix has been factored by spOrderAndFactor() or
|
||
* spFactor() and before it is cleared by spClear() or spInitialize().
|
||
*
|
||
* Unlike the LINPACK condition number estimator, this routines
|
||
* returns the L infinity condition number. This is an artifact of
|
||
* Sparse placing ones on the diagonal of the upper triangular matrix
|
||
* rather than the lower. This difference should be of no importance.
|
||
*
|
||
* References:
|
||
* A.K. Cline, C.B. Moler, G.W. Stewart, J.H. Wilkinson. An estimate
|
||
* for the condition number of a matrix. SIAM Journal on Numerical
|
||
* Analysis. Vol. 16, No. 2, pages 368-375, April 1979.
|
||
*
|
||
* J.J. Dongarra, C.B. Moler, J.R. Bunch, G.W. Stewart. LINPACK
|
||
* User's Guide. SIAM, 1979.
|
||
*
|
||
* Roger G. Grimes, John G. Lewis. Condition number estimation for
|
||
* sparse matrices. SIAM Journal on Scientific and Statistical
|
||
* Computing. Vol. 2, No. 4, pages 384-388, December 1981.
|
||
*
|
||
* Dianne Prost O'Leary. Estimating matrix condition numbers. SIAM
|
||
* Journal on Scientific and Statistical Computing. Vol. 1, No. 2,
|
||
* pages 205-209, June 1980.
|
||
*
|
||
* >>> Returns:
|
||
* The reciprocal of the condition number. If the matrix was singular,
|
||
* zero is returned.
|
||
*
|
||
* >>> Arguments:
|
||
* eMatrix <input> (char *)
|
||
* Pointer to the matrix.
|
||
* NormOfMatrix <input> (RealNumber)
|
||
* The L-infinity norm of the unfactored matrix as computed by
|
||
* spNorm().
|
||
* pError <output> (int *)
|
||
* Used to return error code.
|
||
*
|
||
* >>> Possible errors:
|
||
* spSINGULAR
|
||
* spNO_MEMORY
|
||
*/
|
||
|
||
RealNumber
|
||
spCondition( eMatrix, NormOfMatrix, pError )
|
||
|
||
char *eMatrix;
|
||
RealNumber NormOfMatrix;
|
||
int *pError;
|
||
{
|
||
MatrixPtr Matrix = (MatrixPtr)eMatrix;
|
||
register ElementPtr pElement;
|
||
register RealVector T, Tm;
|
||
register int I, K, Row;
|
||
ElementPtr pPivot;
|
||
int Size;
|
||
RealNumber E, Em, Wp, Wm, ASp, ASm, ASw, ASy, ASv, ASz, MaxY, ScaleFactor;
|
||
RealNumber Linpack, OLeary, InvNormOfInverse, ComplexCondition();
|
||
#define SLACK 1e4
|
||
|
||
/* Begin `spCondition'. */
|
||
|
||
ASSERT( IS_SPARSE(Matrix) AND IS_FACTORED(Matrix) );
|
||
*pError = Matrix->Error;
|
||
if (Matrix->Error >= spFATAL) return 0.0;
|
||
if (NormOfMatrix == 0.0)
|
||
{ *pError = spSINGULAR;
|
||
return 0.0;
|
||
}
|
||
|
||
#if spCOMPLEX
|
||
if (Matrix->Complex)
|
||
return ComplexCondition( Matrix, NormOfMatrix, pError );
|
||
#endif
|
||
|
||
#if REAL
|
||
Size = Matrix->Size;
|
||
T = Matrix->Intermediate;
|
||
#if spCOMPLEX
|
||
Tm = Matrix->Intermediate + Size;
|
||
#else
|
||
Tm = ALLOC( RealNumber, Size+1 );
|
||
if (Tm == NULL)
|
||
{ *pError = spNO_MEMORY;
|
||
return 0.0;
|
||
}
|
||
#endif
|
||
for (I = Size; I > 0; I--) T[I] = 0.0;
|
||
|
||
/*
|
||
* Part 1. Ay = e.
|
||
* Solve Ay = LUy = e where e consists of +1 and -1 terms with the sign
|
||
* chosen to maximize the size of w in Lw = e. Since the terms in w can
|
||
* get very large, scaling is used to avoid overflow.
|
||
*/
|
||
|
||
/* Forward elimination. Solves Lw = e while choosing e. */
|
||
E = 1.0;
|
||
for (I = 1; I <= Size; I++)
|
||
{ pPivot = Matrix->Diag[I];
|
||
if (T[I] < 0.0) Em = -E; else Em = E;
|
||
Wm = (Em + T[I]) * pPivot->Real;
|
||
if (ABS(Wm) > SLACK)
|
||
{ ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(Wm) );
|
||
for (K = Size; K > 0; K--) T[K] *= ScaleFactor;
|
||
E *= ScaleFactor;
|
||
Em *= ScaleFactor;
|
||
Wm = (Em + T[I]) * pPivot->Real;
|
||
}
|
||
Wp = (T[I] - Em) * pPivot->Real;
|
||
ASp = ABS(T[I] - Em);
|
||
ASm = ABS(Em + T[I]);
|
||
|
||
/* Update T for both values of W, minus value is placed in Tm. */
|
||
pElement = pPivot->NextInCol;
|
||
while (pElement != NULL)
|
||
{ Row = pElement->Row;
|
||
Tm[Row] = T[Row] - (Wm * pElement->Real);
|
||
T[Row] -= (Wp * pElement->Real);
|
||
ASp += ABS(T[Row]);
|
||
ASm += ABS(Tm[Row]);
|
||
pElement = pElement->NextInCol;
|
||
}
|
||
|
||
/* If minus value causes more growth, overwrite T with its values. */
|
||
if (ASm > ASp)
|
||
{ T[I] = Wm;
|
||
pElement = pPivot->NextInCol;
|
||
while (pElement != NULL)
|
||
{ T[pElement->Row] = Tm[pElement->Row];
|
||
pElement = pElement->NextInCol;
|
||
}
|
||
}
|
||
else T[I] = Wp;
|
||
}
|
||
|
||
/* Compute 1-norm of T, which now contains w, and scale ||T|| to 1/SLACK. */
|
||
for (ASw = 0.0, I = Size; I > 0; I--) ASw += ABS(T[I]);
|
||
ScaleFactor = 1.0 / (SLACK * ASw);
|
||
if (ScaleFactor < 0.5)
|
||
{ for (I = Size; I > 0; I--) T[I] *= ScaleFactor;
|
||
E *= ScaleFactor;
|
||
}
|
||
|
||
/* Backward Substitution. Solves Uy = w.*/
|
||
for (I = Size; I >= 1; I--)
|
||
{ pElement = Matrix->Diag[I]->NextInRow;
|
||
while (pElement != NULL)
|
||
{ T[I] -= pElement->Real * T[pElement->Col];
|
||
pElement = pElement->NextInRow;
|
||
}
|
||
if (ABS(T[I]) > SLACK)
|
||
{ ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(T[I]) );
|
||
for (K = Size; K > 0; K--) T[K] *= ScaleFactor;
|
||
E *= ScaleFactor;
|
||
}
|
||
}
|
||
|
||
/* Compute 1-norm of T, which now contains y, and scale ||T|| to 1/SLACK. */
|
||
for (ASy = 0.0, I = Size; I > 0; I--) ASy += ABS(T[I]);
|
||
ScaleFactor = 1.0 / (SLACK * ASy);
|
||
if (ScaleFactor < 0.5)
|
||
{ for (I = Size; I > 0; I--) T[I] *= ScaleFactor;
|
||
ASy = 1.0 / SLACK;
|
||
E *= ScaleFactor;
|
||
}
|
||
|
||
/* Compute infinity-norm of T for O'Leary's estimate. */
|
||
for (MaxY = 0.0, I = Size; I > 0; I--)
|
||
if (MaxY < ABS(T[I])) MaxY = ABS(T[I]);
|
||
|
||
/*
|
||
* Part 2. A* z = y where the * represents the transpose.
|
||
* Recall that A = LU implies A* = U* L*.
|
||
*/
|
||
|
||
/* Forward elimination, U* v = y. */
|
||
for (I = 1; I <= Size; I++)
|
||
{ pElement = Matrix->Diag[I]->NextInRow;
|
||
while (pElement != NULL)
|
||
{ T[pElement->Col] -= T[I] * pElement->Real;
|
||
pElement = pElement->NextInRow;
|
||
}
|
||
if (ABS(T[I]) > SLACK)
|
||
{ ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(T[I]) );
|
||
for (K = Size; K > 0; K--) T[K] *= ScaleFactor;
|
||
ASy *= ScaleFactor;
|
||
}
|
||
}
|
||
|
||
/* Compute 1-norm of T, which now contains v, and scale ||T|| to 1/SLACK. */
|
||
for (ASv = 0.0, I = Size; I > 0; I--) ASv += ABS(T[I]);
|
||
ScaleFactor = 1.0 / (SLACK * ASv);
|
||
if (ScaleFactor < 0.5)
|
||
{ for (I = Size; I > 0; I--) T[I] *= ScaleFactor;
|
||
ASy *= ScaleFactor;
|
||
}
|
||
|
||
/* Backward Substitution, L* z = v. */
|
||
for (I = Size; I >= 1; I--)
|
||
{ pPivot = Matrix->Diag[I];
|
||
pElement = pPivot->NextInCol;
|
||
while (pElement != NULL)
|
||
{ T[I] -= pElement->Real * T[pElement->Row];
|
||
pElement = pElement->NextInCol;
|
||
}
|
||
T[I] *= pPivot->Real;
|
||
if (ABS(T[I]) > SLACK)
|
||
{ ScaleFactor = 1.0 / MAX( SQR( SLACK ), ABS(T[I]) );
|
||
for (K = Size; K > 0; K--) T[K] *= ScaleFactor;
|
||
ASy *= ScaleFactor;
|
||
}
|
||
}
|
||
|
||
/* Compute 1-norm of T, which now contains z. */
|
||
for (ASz = 0.0, I = Size; I > 0; I--) ASz += ABS(T[I]);
|
||
|
||
#if NOT spCOMPLEX
|
||
FREE( Tm );
|
||
#endif
|
||
|
||
Linpack = ASy / ASz;
|
||
OLeary = E / MaxY;
|
||
InvNormOfInverse = MIN( Linpack, OLeary );
|
||
return InvNormOfInverse / NormOfMatrix;
|
||
#endif /* REAL */
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
#if spCOMPLEX
|
||
/*
|
||
* ESTIMATE CONDITION NUMBER
|
||
*
|
||
* Complex version of spCondition().
|
||
*
|
||
* >>> Returns:
|
||
* The reciprocal of the condition number.
|
||
*
|
||
* >>> Arguments:
|
||
* Matrix <input> (MatrixPtr)
|
||
* Pointer to the matrix.
|
||
* NormOfMatrix <input> (RealNumber)
|
||
* The L-infinity norm of the unfactored matrix as computed by
|
||
* spNorm().
|
||
* pError <output> (int *)
|
||
* Used to return error code.
|
||
*
|
||
* >>> Possible errors:
|
||
* spNO_MEMORY
|
||
*/
|
||
|
||
static RealNumber
|
||
ComplexCondition( Matrix, NormOfMatrix, pError )
|
||
|
||
MatrixPtr Matrix;
|
||
RealNumber NormOfMatrix;
|
||
int *pError;
|
||
{
|
||
register ElementPtr pElement;
|
||
register ComplexVector T, Tm;
|
||
register int I, K, Row;
|
||
ElementPtr pPivot;
|
||
int Size;
|
||
RealNumber E, Em, ASp, ASm, ASw, ASy, ASv, ASz, MaxY, ScaleFactor;
|
||
RealNumber Linpack, OLeary, InvNormOfInverse;
|
||
ComplexNumber Wp, Wm;
|
||
|
||
/* Begin `ComplexCondition'. */
|
||
|
||
Size = Matrix->Size;
|
||
T = (ComplexVector)Matrix->Intermediate;
|
||
Tm = ALLOC( ComplexNumber, Size+1 );
|
||
if (Tm == NULL)
|
||
{ *pError = spNO_MEMORY;
|
||
return 0.0;
|
||
}
|
||
for (I = Size; I > 0; I--) T[I].Real = T[I].Imag = 0.0;
|
||
|
||
/*
|
||
* Part 1. Ay = e.
|
||
* Solve Ay = LUy = e where e consists of +1 and -1 terms with the sign
|
||
* chosen to maximize the size of w in Lw = e. Since the terms in w can
|
||
* get very large, scaling is used to avoid overflow.
|
||
*/
|
||
|
||
/* Forward elimination. Solves Lw = e while choosing e. */
|
||
E = 1.0;
|
||
for (I = 1; I <= Size; I++)
|
||
{ pPivot = Matrix->Diag[I];
|
||
if (T[I].Real < 0.0) Em = -E; else Em = E;
|
||
Wm = T[I];
|
||
Wm.Real += Em;
|
||
ASm = CMPLX_1_NORM( Wm );
|
||
CMPLX_MULT_ASSIGN( Wm, *pPivot );
|
||
if (CMPLX_1_NORM(Wm) > SLACK)
|
||
{ ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(Wm) );
|
||
for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor );
|
||
E *= ScaleFactor;
|
||
Em *= ScaleFactor;
|
||
ASm *= ScaleFactor;
|
||
SCLR_MULT_ASSIGN( Wm, ScaleFactor );
|
||
}
|
||
Wp = T[I];
|
||
Wp.Real -= Em;
|
||
ASp = CMPLX_1_NORM( Wp );
|
||
CMPLX_MULT_ASSIGN( Wp, *pPivot );
|
||
|
||
/* Update T for both values of W, minus value is placed in Tm. */
|
||
pElement = pPivot->NextInCol;
|
||
while (pElement != NULL)
|
||
{ Row = pElement->Row;
|
||
/* Cmplx expr: Tm[Row] = T[Row] - (Wp * *pElement). */
|
||
CMPLX_MULT_SUBT( Tm[Row], Wm, *pElement, T[Row] );
|
||
/* Cmplx expr: T[Row] -= Wp * *pElement. */
|
||
CMPLX_MULT_SUBT_ASSIGN( T[Row], Wm, *pElement );
|
||
ASp += CMPLX_1_NORM(T[Row]);
|
||
ASm += CMPLX_1_NORM(Tm[Row]);
|
||
pElement = pElement->NextInCol;
|
||
}
|
||
|
||
/* If minus value causes more growth, overwrite T with its values. */
|
||
if (ASm > ASp)
|
||
{ T[I] = Wm;
|
||
pElement = pPivot->NextInCol;
|
||
while (pElement != NULL)
|
||
{ T[pElement->Row] = Tm[pElement->Row];
|
||
pElement = pElement->NextInCol;
|
||
}
|
||
}
|
||
else T[I] = Wp;
|
||
}
|
||
|
||
/* Compute 1-norm of T, which now contains w, and scale ||T|| to 1/SLACK. */
|
||
for (ASw = 0.0, I = Size; I > 0; I--) ASw += CMPLX_1_NORM(T[I]);
|
||
ScaleFactor = 1.0 / (SLACK * ASw);
|
||
if (ScaleFactor < 0.5)
|
||
{ for (I = Size; I > 0; I--) SCLR_MULT_ASSIGN( T[I], ScaleFactor );
|
||
E *= ScaleFactor;
|
||
}
|
||
|
||
/* Backward Substitution. Solves Uy = w.*/
|
||
for (I = Size; I >= 1; I--)
|
||
{ pElement = Matrix->Diag[I]->NextInRow;
|
||
while (pElement != NULL)
|
||
{ /* Cmplx expr: T[I] -= T[pElement->Col] * *pElement. */
|
||
CMPLX_MULT_SUBT_ASSIGN( T[I], T[pElement->Col], *pElement );
|
||
pElement = pElement->NextInRow;
|
||
}
|
||
if (CMPLX_1_NORM(T[I]) > SLACK)
|
||
{ ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(T[I]) );
|
||
for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor );
|
||
E *= ScaleFactor;
|
||
}
|
||
}
|
||
|
||
/* Compute 1-norm of T, which now contains y, and scale ||T|| to 1/SLACK. */
|
||
for (ASy = 0.0, I = Size; I > 0; I--) ASy += CMPLX_1_NORM(T[I]);
|
||
ScaleFactor = 1.0 / (SLACK * ASy);
|
||
if (ScaleFactor < 0.5)
|
||
{ for (I = Size; I > 0; I--) SCLR_MULT_ASSIGN( T[I], ScaleFactor );
|
||
ASy = 1.0 / SLACK;
|
||
E *= ScaleFactor;
|
||
}
|
||
|
||
/* Compute infinity-norm of T for O'Leary's estimate. */
|
||
for (MaxY = 0.0, I = Size; I > 0; I--)
|
||
if (MaxY < CMPLX_1_NORM(T[I])) MaxY = CMPLX_1_NORM(T[I]);
|
||
|
||
/*
|
||
* Part 2. A* z = y where the * represents the transpose.
|
||
* Recall that A = LU implies A* = U* L*.
|
||
*/
|
||
|
||
/* Forward elimination, U* v = y. */
|
||
for (I = 1; I <= Size; I++)
|
||
{ pElement = Matrix->Diag[I]->NextInRow;
|
||
while (pElement != NULL)
|
||
{ /* Cmplx expr: T[pElement->Col] -= T[I] * *pElement. */
|
||
CMPLX_MULT_SUBT_ASSIGN( T[pElement->Col], T[I], *pElement );
|
||
pElement = pElement->NextInRow;
|
||
}
|
||
if (CMPLX_1_NORM(T[I]) > SLACK)
|
||
{ ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(T[I]) );
|
||
for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor );
|
||
ASy *= ScaleFactor;
|
||
}
|
||
}
|
||
|
||
/* Compute 1-norm of T, which now contains v, and scale ||T|| to 1/SLACK. */
|
||
for (ASv = 0.0, I = Size; I > 0; I--) ASv += CMPLX_1_NORM(T[I]);
|
||
ScaleFactor = 1.0 / (SLACK * ASv);
|
||
if (ScaleFactor < 0.5)
|
||
{ for (I = Size; I > 0; I--) SCLR_MULT_ASSIGN( T[I], ScaleFactor );
|
||
ASy *= ScaleFactor;
|
||
}
|
||
|
||
/* Backward Substitution, L* z = v. */
|
||
for (I = Size; I >= 1; I--)
|
||
{ pPivot = Matrix->Diag[I];
|
||
pElement = pPivot->NextInCol;
|
||
while (pElement != NULL)
|
||
{ /* Cmplx expr: T[I] -= T[pElement->Row] * *pElement. */
|
||
CMPLX_MULT_SUBT_ASSIGN( T[I], T[pElement->Row], *pElement );
|
||
pElement = pElement->NextInCol;
|
||
}
|
||
CMPLX_MULT_ASSIGN( T[I], *pPivot );
|
||
if (CMPLX_1_NORM(T[I]) > SLACK)
|
||
{ ScaleFactor = 1.0 / MAX( SQR( SLACK ), CMPLX_1_NORM(T[I]) );
|
||
for (K = Size; K > 0; K--) SCLR_MULT_ASSIGN( T[K], ScaleFactor );
|
||
ASy *= ScaleFactor;
|
||
}
|
||
}
|
||
|
||
/* Compute 1-norm of T, which now contains z. */
|
||
for (ASz = 0.0, I = Size; I > 0; I--) ASz += CMPLX_1_NORM(T[I]);
|
||
|
||
FREE( Tm );
|
||
|
||
Linpack = ASy / ASz;
|
||
OLeary = E / MaxY;
|
||
InvNormOfInverse = MIN( Linpack, OLeary );
|
||
return InvNormOfInverse / NormOfMatrix;
|
||
}
|
||
#endif /* spCOMPLEX */
|
||
|
||
|
||
|
||
|
||
|
||
/*
|
||
* L-INFINITY MATRIX NORM
|
||
*
|
||
* Computes the L-infinity norm of an unfactored matrix. It is a fatal
|
||
* error to pass this routine a factored matrix.
|
||
*
|
||
* One difficulty is that the rows may not be linked.
|
||
*
|
||
* >>> Returns:
|
||
* The largest absolute row sum of matrix.
|
||
*
|
||
* >>> Arguments:
|
||
* eMatrix <input> (char *)
|
||
* Pointer to the matrix.
|
||
*/
|
||
|
||
RealNumber
|
||
spNorm( eMatrix )
|
||
|
||
char *eMatrix;
|
||
{
|
||
MatrixPtr Matrix = (MatrixPtr)eMatrix;
|
||
register ElementPtr pElement;
|
||
register int I;
|
||
RealNumber Max = 0.0, AbsRowSum;
|
||
|
||
/* Begin `spNorm'. */
|
||
ASSERT( IS_SPARSE(Matrix) AND NOT IS_FACTORED(Matrix) );
|
||
if (NOT Matrix->RowsLinked) spcLinkRows( Matrix );
|
||
|
||
/* Compute row sums. */
|
||
#if REAL
|
||
if (NOT Matrix->Complex)
|
||
{ for (I = Matrix->Size; I > 0; I--)
|
||
{ pElement = Matrix->FirstInRow[I];
|
||
AbsRowSum = 0.0;
|
||
while (pElement != NULL)
|
||
{ AbsRowSum += ABS( pElement->Real );
|
||
pElement = pElement->NextInRow;
|
||
}
|
||
if (Max < AbsRowSum) Max = AbsRowSum;
|
||
}
|
||
}
|
||
#endif
|
||
#if spCOMPLEX
|
||
if (Matrix->Complex)
|
||
{ for (I = Matrix->Size; I > 0; I--)
|
||
{ pElement = Matrix->FirstInRow[I];
|
||
AbsRowSum = 0.0;
|
||
while (pElement != NULL)
|
||
{ AbsRowSum += CMPLX_1_NORM( *pElement );
|
||
pElement = pElement->NextInRow;
|
||
}
|
||
if (Max < AbsRowSum) Max = AbsRowSum;
|
||
}
|
||
}
|
||
#endif
|
||
return Max;
|
||
}
|
||
#endif /* CONDITION */
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#if STABILITY
|
||
/*
|
||
* STABILITY OF FACTORIZATION
|
||
*
|
||
* The following routines are used to gauge the stability of a
|
||
* factorization. If the factorization is determined to be too unstable,
|
||
* then the matrix should be reordered. The routines compute quantities
|
||
* that are needed in the computation of a bound on the error attributed
|
||
* to any one element in the matrix during the factorization. In other
|
||
* words, there is a matrix E = [e_ij] of error terms such that A+E = LU.
|
||
* This routine finds a bound on |e_ij|. Erisman & Reid [1] showed that
|
||
* |e_ij| < 3.01 u rho m_ij, where u is the machine rounding unit,
|
||
* rho = max a_ij where the max is taken over every row i, column j, and
|
||
* step k, and m_ij is the number of multiplications required in the
|
||
* computation of l_ij if i > j or u_ij otherwise. Barlow [2] showed that
|
||
* rho < max_i || l_i ||_p max_j || u_j ||_q where 1/p + 1/q = 1.
|
||
*
|
||
* The first routine finds the magnitude on the largest element in the
|
||
* matrix. If the matrix has not yet been factored, the largest
|
||
* element is found by direct search. If the matrix is factored, a
|
||
* bound on the largest element in any of the reduced submatrices is
|
||
* computed using Barlow with p = oo and q = 1. The ratio of these
|
||
* two numbers is the growth, which can be used to determine if the
|
||
* pivoting order is adequate. A large growth implies that
|
||
* considerable error has been made in the factorization and that it
|
||
* is probably a good idea to reorder the matrix. If a large growth
|
||
* in encountered after using spFactor(), reconstruct the matrix and
|
||
* refactor using spOrderAndFactor(). If a large growth is
|
||
* encountered after using spOrderAndFactor(), refactor using
|
||
* spOrderAndFactor() with the pivot threshold increased, say to 0.1.
|
||
*
|
||
* Using only the size of the matrix as an upper bound on m_ij and
|
||
* Barlow's bound, the user can estimate the size of the matrix error
|
||
* terms e_ij using the bound of Erisman and Reid. The second routine
|
||
* computes a tighter bound (with more work) based on work by Gear
|
||
* [3], |e_ij| < 1.01 u rho (t c^3 + (1 + t)c^2) where t is the
|
||
* threshold and c is the maximum number of off-diagonal elements in
|
||
* any row of L. The expensive part of computing this bound is
|
||
* determining the maximum number of off-diagonals in L, which changes
|
||
* only when the order of the matrix changes. This number is computed
|
||
* and saved, and only recomputed if the matrix is reordered.
|
||
*
|
||
* [1] A. M. Erisman, J. K. Reid. Monitoring the stability of the
|
||
* triangular factorization of a sparse matrix. Numerische
|
||
* Mathematik. Vol. 22, No. 3, 1974, pp 183-186.
|
||
*
|
||
* [2] J. L. Barlow. A note on monitoring the stability of triangular
|
||
* decomposition of sparse matrices. "SIAM Journal of Scientific
|
||
* and Statistical Computing." Vol. 7, No. 1, January 1986, pp 166-168.
|
||
*
|
||
* [3] I. S. Duff, A. M. Erisman, J. K. Reid. "Direct Methods for Sparse
|
||
* Matrices." Oxford 1986. pp 99.
|
||
*/
|
||
|
||
/*
|
||
* LARGEST ELEMENT IN MATRIX
|
||
*
|
||
* >>> Returns:
|
||
* If matrix is not factored, returns the magnitude of the largest element in
|
||
* the matrix. If the matrix is factored, a bound on the magnitude of the
|
||
* largest element in any of the reduced submatrices is returned.
|
||
*
|
||
* >>> Arguments:
|
||
* eMatrix <input> (char *)
|
||
* Pointer to the matrix.
|
||
*/
|
||
|
||
RealNumber
|
||
spLargestElement( eMatrix )
|
||
|
||
char *eMatrix;
|
||
{
|
||
MatrixPtr Matrix = (MatrixPtr)eMatrix;
|
||
register int I;
|
||
RealNumber Mag, AbsColSum, Max = 0.0, MaxRow = 0.0, MaxCol = 0.0;
|
||
RealNumber Pivot;
|
||
ComplexNumber cPivot;
|
||
register ElementPtr pElement, pDiag;
|
||
|
||
/* Begin `spLargestElement'. */
|
||
ASSERT( IS_SPARSE(Matrix) );
|
||
|
||
#if REAL
|
||
if (Matrix->Factored AND NOT Matrix->Complex)
|
||
{ if (Matrix->Error == spSINGULAR) return 0.0;
|
||
|
||
/* Find the bound on the size of the largest element over all factorization. */
|
||
for (I = 1; I <= Matrix->Size; I++)
|
||
{ pDiag = Matrix->Diag[I];
|
||
|
||
/* Lower triangular matrix. */
|
||
Pivot = 1.0 / pDiag->Real;
|
||
Mag = ABS( Pivot );
|
||
if (Mag > MaxRow) MaxRow = Mag;
|
||
pElement = Matrix->FirstInRow[I];
|
||
while (pElement != pDiag)
|
||
{ Mag = ABS( pElement->Real );
|
||
if (Mag > MaxRow) MaxRow = Mag;
|
||
pElement = pElement->NextInRow;
|
||
}
|
||
|
||
/* Upper triangular matrix. */
|
||
pElement = Matrix->FirstInCol[I];
|
||
AbsColSum = 1.0; /* Diagonal of U is unity. */
|
||
while (pElement != pDiag)
|
||
{ AbsColSum += ABS( pElement->Real );
|
||
pElement = pElement->NextInCol;
|
||
}
|
||
if (AbsColSum > MaxCol) MaxCol = AbsColSum;
|
||
}
|
||
}
|
||
else if (NOT Matrix->Complex)
|
||
{ for (I = 1; I <= Matrix->Size; I++)
|
||
{ pElement = Matrix->FirstInCol[I];
|
||
while (pElement != NULL)
|
||
{ Mag = ABS( pElement->Real );
|
||
if (Mag > Max) Max = Mag;
|
||
pElement = pElement->NextInCol;
|
||
}
|
||
}
|
||
return Max;
|
||
}
|
||
#endif
|
||
#if spCOMPLEX
|
||
if (Matrix->Factored AND Matrix->Complex)
|
||
{ if (Matrix->Error == spSINGULAR) return 0.0;
|
||
|
||
/* Find the bound on the size of the largest element over all factorization. */
|
||
for (I = 1; I <= Matrix->Size; I++)
|
||
{ pDiag = Matrix->Diag[I];
|
||
|
||
/* Lower triangular matrix. */
|
||
CMPLX_RECIPROCAL( cPivot, *pDiag );
|
||
Mag = CMPLX_1_NORM( cPivot );
|
||
if (Mag > MaxRow) MaxRow = Mag;
|
||
pElement = Matrix->FirstInRow[I];
|
||
while (pElement != pDiag)
|
||
{ Mag = CMPLX_1_NORM( *pElement );
|
||
if (Mag > MaxRow) MaxRow = Mag;
|
||
pElement = pElement->NextInRow;
|
||
}
|
||
|
||
/* Upper triangular matrix. */
|
||
pElement = Matrix->FirstInCol[I];
|
||
AbsColSum = 1.0; /* Diagonal of U is unity. */
|
||
while (pElement != pDiag)
|
||
{ AbsColSum += CMPLX_1_NORM( *pElement );
|
||
pElement = pElement->NextInCol;
|
||
}
|
||
if (AbsColSum > MaxCol) MaxCol = AbsColSum;
|
||
}
|
||
}
|
||
else if (Matrix->Complex)
|
||
{ for (I = 1; I <= Matrix->Size; I++)
|
||
{ pElement = Matrix->FirstInCol[I];
|
||
while (pElement != NULL)
|
||
{ Mag = CMPLX_1_NORM( *pElement );
|
||
if (Mag > Max) Max = Mag;
|
||
pElement = pElement->NextInCol;
|
||
}
|
||
}
|
||
return Max;
|
||
}
|
||
#endif
|
||
return MaxRow * MaxCol;
|
||
}
|
||
|
||
|
||
|
||
|
||
/*
|
||
* MATRIX ROUNDOFF ERROR
|
||
*
|
||
* >>> Returns:
|
||
* Returns a bound on the magnitude of the largest element in E = A - LU.
|
||
*
|
||
* >>> Arguments:
|
||
* eMatrix <input> (char *)
|
||
* Pointer to the matrix.
|
||
* Rho <input> (RealNumber)
|
||
* The bound on the magnitude of the largest element in any of the
|
||
* reduced submatrices. This is the number computed by the function
|
||
* spLargestElement() when given a factored matrix. If this number is
|
||
* negative, the bound will be computed automatically.
|
||
*/
|
||
|
||
RealNumber
|
||
spRoundoff( eMatrix, Rho )
|
||
|
||
char *eMatrix;
|
||
RealNumber Rho;
|
||
{
|
||
MatrixPtr Matrix = (MatrixPtr)eMatrix;
|
||
register ElementPtr pElement;
|
||
register int Count, I, MaxCount = 0;
|
||
RealNumber Reid, Gear;
|
||
|
||
/* Begin `spRoundoff'. */
|
||
ASSERT( IS_SPARSE(Matrix) AND IS_FACTORED(Matrix) );
|
||
|
||
/* Compute Barlow's bound if it is not given. */
|
||
if (Rho < 0.0) Rho = spLargestElement( eMatrix );
|
||
|
||
/* Find the maximum number of off-diagonals in L if not previously computed. */
|
||
if (Matrix->MaxRowCountInLowerTri < 0)
|
||
{ for (I = Matrix->Size; I > 0; I--)
|
||
{ pElement = Matrix->FirstInRow[I];
|
||
Count = 0;
|
||
while (pElement->Col < I)
|
||
{ Count++;
|
||
pElement = pElement->NextInRow;
|
||
}
|
||
if (Count > MaxCount) MaxCount = Count;
|
||
}
|
||
Matrix->MaxRowCountInLowerTri = MaxCount;
|
||
}
|
||
else MaxCount = Matrix->MaxRowCountInLowerTri;
|
||
|
||
/* Compute error bound. */
|
||
Gear = 1.01*((MaxCount + 1) * Matrix->RelThreshold + 1.0) * SQR(MaxCount);
|
||
Reid = 3.01 * Matrix->Size;
|
||
|
||
if (Gear < Reid)
|
||
return (MACHINE_RESOLUTION * Rho * Gear);
|
||
else
|
||
return (MACHINE_RESOLUTION * Rho * Reid);
|
||
}
|
||
#endif
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#if DOCUMENTATION
|
||
/*
|
||
* SPARSE ERROR MESSAGE
|
||
*
|
||
* This routine prints a short message to a stream describing the error
|
||
* error state of sparse. No message is produced if there is no error.
|
||
*
|
||
* >>> Arguments:
|
||
* eMatrix <input> (char *)
|
||
* Matrix for which the error message is to be printed.
|
||
* Stream <input> (FILE *)
|
||
* Stream to which the error message is to be printed.
|
||
* Originator <input> (char *)
|
||
* Name of originator of error message. If NULL, `sparse' is used.
|
||
* If zero-length string, no originator is printed.
|
||
*/
|
||
|
||
void
|
||
spErrorMessage( eMatrix, Stream, Originator )
|
||
|
||
char *eMatrix, *Originator;
|
||
FILE *Stream;
|
||
{
|
||
int Row, Col, Error;
|
||
|
||
/* Begin `spErrorMessage'. */
|
||
if (eMatrix == NULL)
|
||
Error = spNO_MEMORY;
|
||
else
|
||
{ ASSERT(((MatrixPtr)eMatrix)->ID == SPARSE_ID);
|
||
Error = ((MatrixPtr)eMatrix)->Error;
|
||
}
|
||
|
||
if (Error == spOKAY) return;
|
||
if (Originator == NULL) Originator = "sparse";
|
||
if (Originator[0] != '\0') fprintf( Stream, "%s: ", Originator);
|
||
if (Error >= spFATAL)
|
||
fprintf( Stream, "fatal error, ");
|
||
else
|
||
fprintf( Stream, "warning, ");
|
||
/*
|
||
* Print particular error message.
|
||
* Do not use switch statement because error codes may not be unique.
|
||
*/
|
||
if (Error == spPANIC)
|
||
fprintf( Stream, "Sparse called improperly.\n");
|
||
else if (Error == spNO_MEMORY)
|
||
fprintf( Stream, "insufficient memory available.\n");
|
||
else if (Error == spSINGULAR)
|
||
{ spWhereSingular( eMatrix, &Row, &Col );
|
||
fprintf( Stream, "singular matrix detected at row %d and column %d.\n",
|
||
Row, Col);
|
||
}
|
||
else if (Error == spZERO_DIAG)
|
||
{ spWhereSingular( eMatrix, &Row, &Col );
|
||
fprintf( Stream, "zero diagonal detected at row %d and column %d.\n",
|
||
Row, Col);
|
||
}
|
||
else if (Error == spSMALL_PIVOT)
|
||
{ fprintf( Stream,
|
||
"unable to find a pivot that is larger than absolute threshold.\n");
|
||
}
|
||
else ABORT();
|
||
return;
|
||
}
|
||
#endif /* DOCUMENTATION */
|