/*
* 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
* spStrip
* spDeleteRowAndCol
* spPseudoCondition
* spCondition
* spNorm
* spLargestElement
* spRoundoff
*
* >>> Other functions contained in this file:
* CountTwins
* SwapCols
* ScaleComplexMatrix
* ComplexMatrixMultiply
* ComplexCondition
*/
/*
* Revision and copyright information.
*
* Copyright (c) 1985,86,87,88
* 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 "spUtils.h"
#include "spmatrix.h"
#include "spDefs.h"
#include "spmalloc.h"
#include "spBuild.h"
static int CountTwins(MatrixPtr Matrix, int Col, ElementPtr *ppTwin1, ElementPtr *ppTwin2 );
static int SwapCols( MatrixPtr Matrix, ElementPtr pTwin1, ElementPtr pTwin2 );
#if spCOMPLEX AND SCALING
static void ScaleComplexMatrix( MatrixPtr Matrix, register RealVector RHS_ScaleFactors, register RealVector SolutionScaleFactors );
#endif
#if spCOMPLEX AND MULTIPLICATION
static void ComplexMatrixMultiply( MatrixPtr Matrix, RealVector RHS , RealVector Solution IMAG_VECTORS );
#endif
#if spCOMPLEX AND MULTIPLICATION AND TRANSPOSE
static void
ComplexTransposedMatrixMultiply( MatrixPtr Matrix, RealVector RHS, RealVector Solution IMAG_VECTORS );
#endif
#if spCOMPLEX
static RealNumber ComplexCondition( MatrixPtr Matrix, RealNumber NormOfMatrix, int *pError );
#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 (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 (SPBOOLEAN)
* Flag indicating that at least one zero diagonal with symmetric twins
* remain.
* StartAt (int)
* Column number of first zero diagonal with symmetric twins.
* Swapped (SPBOOLEAN)
* Flag indicating that columns were swapped on this pass.
* Twins (int)
* Number of symmetric twins corresponding to current zero diagonal.
*/
#undef SPBOOLEAN
#define SPBOOLEAN int
void spMNA_Preorder( char * eMatrix )
{
MatrixPtr Matrix = (MatrixPtr)eMatrix;
register int J, Size;
ElementPtr pTwin1, pTwin2;
int Twins, StartAt = 1;
SPBOOLEAN 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( MatrixPtr Matrix, int Col, ElementPtr *ppTwin1, ElementPtr *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 int
SwapCols( MatrixPtr Matrix, ElementPtr pTwin1, ElementPtr 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 0;
}
#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 int 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 (char *)
* Pointer to the matrix to be scaled.
* SolutionScaleFactors (RealVector)
* The array of Solution scale factors. These factors scale the columns.
* All scale factors are real valued.
* RHS_ScaleFactors (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(char *eMatrix, register RealVector RHS_ScaleFactors, register RealVector SolutionScaleFactors )
{
MatrixPtr Matrix = (MatrixPtr)eMatrix;
register ElementPtr pElement;
register int I, lSize, *pExtOrder;
RealNumber ScaleFactor;
/* 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 int 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 (char *)
* Pointer to the matrix to be scaled.
* SolutionScaleFactors (RealVector)
* The array of Solution scale factors. These factors scale the columns.
* All scale factors are real valued.
* RHS_ScaleFactors (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( MatrixPtr Matrix, register RealVector RHS_ScaleFactors, register RealVector 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 (char *)
* Pointer to the matrix.
* RHS