📄 spfactor.c
字号:
/*
* MATRIX FACTORIZATION MODULE
*
* Author: Advising Professor:
* Kenneth S. Kundert Alberto Sangiovanni-Vincentelli
* UC Berkeley
*/
/*! \file
* This file contains the routines to factor the matrix into LU form.
*
* Objects that begin with the \a spc prefix are considered private
* and should not be used.
*
* \author
* Kenneth S. Kundert <kundert@users.sourceforge.net>
*/
/* >>> User accessible functions contained in this file:
* spOrderAndFactor
* spFactor
* spPartition
*
* >>> Other functions contained in this file:
* FactorComplexMatrix spcCreateInternalVectors
* CountMarkowitz MarkowitzProducts
* SearchForPivot SearchForSingleton
* QuicklySearchDiagonal SearchDiagonal
* SearchEntireMatrix FindLargestInCol
* FindBiggestInColExclude ExchangeRowsAndCols
* spcRowExchange spcColExchange
* ExchangeColElements ExchangeRowElements
* RealRowColElimination ComplexRowColElimination
* UpdateMarkowitzNumbers MatrixIsSingular
* ZeroPivot WriteStatus
*/
/*
* Revision and copyright information.
*
* Copyright (c) 1985-2004
* by Kenneth S. Kundert
*/
#if 0
static char copyright[] =
"Sparse1.4: Copyright (c) 1985-2003 by Kenneth S. Kundert";
#endif
/*
* 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 <stdio.h>
#include "spConfig.h"
#include "spMatrix.h"
#include "spDefs.h"
/*
* Function declarations
*/
#if spCOMPLEX
static int FactorComplexMatrix( MatrixPtr );
static void CreateInternalVectors( MatrixPtr );
#endif
static void CountMarkowitz( MatrixPtr, RealVector, int );
static void MarkowitzProducts( MatrixPtr, int );
static ElementPtr SearchForPivot( MatrixPtr, int, int );
static ElementPtr SearchForSingleton( MatrixPtr, int );
static ElementPtr SearchDiagonal( MatrixPtr, int );
static ElementPtr QuicklySearchDiagonal( MatrixPtr, int );
static ElementPtr SearchEntireMatrix( MatrixPtr, int );
static RealNumber FindLargestInCol( ElementPtr );
static RealNumber FindBiggestInColExclude( MatrixPtr, ElementPtr, int );
static void ExchangeRowsAndCols( MatrixPtr, ElementPtr, int );
static void ExchangeColElements( MatrixPtr, int, ElementPtr, int,
ElementPtr, int );
static void ExchangeRowElements( MatrixPtr, int, ElementPtr, int,
ElementPtr, int );
static void RealRowColElimination( MatrixPtr, ElementPtr );
#if spCOMPLEX
static void ComplexRowColElimination( MatrixPtr, ElementPtr );
#endif
static void UpdateMarkowitzNumbers( MatrixPtr, ElementPtr );
static int MatrixIsSingular( MatrixPtr, int );
static int ZeroPivot( MatrixPtr, int );
#if (ANNOTATE == FULL)
static void WriteStatus( MatrixPtr, int );
#endif
/*!
* This routine chooses a pivot order for the matrix and factors it
* into \a LU form. It handles both the initial factorization and subsequent
* factorizations when a reordering is desired. This is handled in a manner
* that is transparent to the user. The routine uses a variation of
* Gauss's method where the pivots are associated with \a L and the
* diagonal terms of \a U are one.
*
* \return
* The error code is returned. Possible errors are \a spNO_MEMORY,
* \a spSINGULAR and \a spSMALL_PIVOT.
* Error is cleared upon entering this function.
*
* \param eMatrix
* Pointer to the matrix.
* \param RHS
* Representative right-hand side vector that is used to determine
* pivoting order when the right hand side vector is sparse. If
* RHS is a NULL pointer then the RHS vector is assumed to
* be full and it is not used when determining the pivoting
* order.
* \param RelThreshold
* This number determines what the pivot relative threshold will
* be. It should be between zero and one. If it is one then the
* pivoting method becomes complete pivoting, which is very slow
* and tends to fill up the matrix. If it is set close to zero
* the pivoting method becomes strict Markowitz with no
* threshold. The pivot threshold is used to eliminate pivot
* candidates that would cause excessive element growth if they
* were used. Element growth is the cause of roundoff error.
* Element growth occurs even in well-conditioned matrices.
* Setting the \a RelThreshold large will reduce element growth and
* roundoff error, but setting it too large will cause execution
* time to be excessive and will result in a large number of
* fill-ins. If this occurs, accuracy can actually be degraded
* because of the large number of operations required on the
* matrix due to the large number of fill-ins. A good value seems
* to be 0.001. The default is chosen by giving a value larger
* than one or less than or equal to zero. This value should be
* increased and the matrix resolved if growth is found to be
* excessive. Changing the pivot threshold does not improve
* performance on matrices where growth is low, as is often the
* case with ill-conditioned matrices. Once a valid threshold is
* given, it becomes the new default. The default value of
* \a RelThreshold was choosen for use with nearly diagonally
* dominant matrices such as node- and modified-node admittance
* matrices. For these matrices it is usually best to use
* diagonal pivoting. For matrices without a strong diagonal, it
* is usually best to use a larger threshold, such as 0.01 or
* 0.1.
* \param AbsThreshold
* The absolute magnitude an element must have to be considered
* as a pivot candidate, except as a last resort. This number
* should be set significantly smaller than the smallest diagonal
* element that is is expected to be placed in the matrix. If
* there is no reasonable prediction for the lower bound on these
* elements, then \a AbsThreshold should be set to zero.
* \a AbsThreshold is used to reduce the possibility of choosing as a
* pivot an element that has suffered heavy cancellation and as a
* result mainly consists of roundoff error. Once a valid
* threshold is given, it becomes the new default.
* \param DiagPivoting
* A flag indicating that pivot selection should be confined to the
* diagonal if possible. If \a DiagPivoting is nonzero and if
* \a DIAGONAL_PIVOTING is enabled pivots will be chosen only from
* the diagonal unless there are no diagonal elements that satisfy
* the threshold criteria. Otherwise, the entire reduced
* submatrix is searched when looking for a pivot. The diagonal
* pivoting in Sparse is efficient and well refined, while the
* off-diagonal pivoting is not. For symmetric and near symmetric
* matrices, it is best to use diagonal pivoting because it
* results in the best performance when reordering the matrix and
* when factoring the matrix without ordering. If there is a
* considerable amount of nonsymmetry in the matrix, then
* off-diagonal pivoting may result in a better equation ordering
* simply because there are more pivot candidates to choose from.
* A better ordering results in faster subsequent factorizations.
* However, the initial pivot selection process takes considerably
* longer for off-diagonal pivoting.
*
* \see spFactor()
*/
/* >>> Local variables:
* pPivot (ElementPtr)
* Pointer to the element being used as a pivot.
*
*/
spError
spOrderAndFactor(
spMatrix eMatrix,
spREAL RHS[],
spREAL RelThreshold,
spREAL AbsThreshold,
int DiagPivoting
)
{
MatrixPtr Matrix = (MatrixPtr)eMatrix;
ElementPtr pPivot;
int Step, Size;
RealNumber LargestInCol;
/* Begin `spOrderAndFactor'. */
ASSERT_IS_SPARSE( Matrix );
ASSERT_NO_ERRORS( Matrix );
ASSERT_IS_NOT_FACTORED( Matrix );
Matrix->Error = spOKAY;
Size = Matrix->Size;
if (RelThreshold <= 0.0) RelThreshold = Matrix->RelThreshold;
if (RelThreshold > 1.0) RelThreshold = Matrix->RelThreshold;
Matrix->RelThreshold = RelThreshold;
if (AbsThreshold < 0.0) AbsThreshold = Matrix->AbsThreshold;
Matrix->AbsThreshold = AbsThreshold;
if (NOT Matrix->NeedsOrdering)
{
/* Matrix has been factored before and reordering is not required. */
for (Step = 1; Step <= Size; Step++)
{ pPivot = Matrix->Diag[Step];
LargestInCol = FindLargestInCol(pPivot->NextInCol);
if ((LargestInCol * RelThreshold < ELEMENT_MAG(pPivot)))
{
#if spCOMPLEX
if (Matrix->Complex)
ComplexRowColElimination( Matrix, pPivot );
else
RealRowColElimination( Matrix, pPivot );
#else
RealRowColElimination( Matrix, pPivot );
#endif
}
else
{ Matrix->NeedsOrdering = YES;
break; /* for loop */
}
}
if (NOT Matrix->NeedsOrdering)
goto Done;
else
{
/*
* A pivot was not large enough to maintain accuracy,
* so a partial reordering is required.
*/
#if (ANNOTATE >= ON_STRANGE_BEHAVIOR)
printf("Reordering, Step = %1d\n", Step);
#endif
}
} /* End of if(NOT Matrix->NeedsOrdering) */
else
{
/*
* This is the first time the matrix has been factored. These few statements
* indicate to the rest of the code that a full reodering is required rather
* than a partial reordering, which occurs during a failure of a fast
* factorization.
*/
Step = 1;
if (NOT Matrix->RowsLinked)
spcLinkRows( Matrix );
if (NOT Matrix->InternalVectorsAllocated)
spcCreateInternalVectors( Matrix );
if (Matrix->Error >= spFATAL)
return Matrix->Error;
}
/* Form initial Markowitz products. */
CountMarkowitz( Matrix, RHS, Step );
MarkowitzProducts( Matrix, Step );
Matrix->MaxRowCountInLowerTri = -1;
/* Perform reordering and factorization. */
for (; Step <= Size; Step++)
{ pPivot = SearchForPivot( Matrix, Step, DiagPivoting );
if (pPivot == NULL) return MatrixIsSingular( Matrix, Step );
ExchangeRowsAndCols( Matrix, pPivot, Step );
#if spCOMPLEX
if (Matrix->Complex)
ComplexRowColElimination( Matrix, pPivot );
else
RealRowColElimination( Matrix, pPivot );
#else
RealRowColElimination( Matrix, pPivot );
#endif
if(Matrix->Error>spFATAL)
Size = 0;
if (Matrix->Error >= spFATAL) return Matrix->Error;
UpdateMarkowitzNumbers( Matrix, pPivot );
#if (ANNOTATE == FULL)
WriteStatus( Matrix, Step );
#endif
}
Done:
Matrix->NeedsOrdering = NO;
Matrix->Reordered = YES;
Matrix->Factored = YES;
return Matrix->Error;
}
/*!
* This routine is the companion routine to spOrderAndFactor().
* Unlike spOrderAndFactor(), spFactor() cannot change the ordering.
* It is also faster than spOrderAndFactor(). The standard way of
* using these two routines is to first use spOrderAndFactor() for the
* initial factorization. For subsequent factorizations, spFactor()
* is used if there is some assurance that little growth will occur
* (say for example, that the matrix is diagonally dominant). If
* spFactor() is called for the initial factorization of the matrix,
* then spOrderAndFactor() is automatically called with the default
* threshold. This routine uses "row at a time" \a LU factorization.
* Pivots are associated with the lower triangular matrix and the
* diagonals of the upper triangular matrix are ones.
*
* \return
* The error code is returned. Possible errors are
* \a spNO_MEMORY, \a spSINGULAR, \a spZERO_DIAG and \a spSMALL_PIVOT.
* Error is cleared upon entering this function.
*
* \param eMatrix
* Pointer to matrix.
* \see spOrderAndFactor()
*/
spError
spFactor( spMatrix eMatrix )
{
MatrixPtr Matrix = (MatrixPtr)eMatrix;
register ElementPtr pElement;
register ElementPtr pColumn;
register int Step, Size;
RealNumber Mult;
/* Begin `spFactor'. */
ASSERT_IS_SPARSE( Matrix );
ASSERT_NO_ERRORS( Matrix );
ASSERT_IS_NOT_FACTORED( Matrix );
if (Matrix->NeedsOrdering)
{ return spOrderAndFactor( eMatrix, (RealVector)NULL,
0.0, 0.0, DIAG_PIVOTING_AS_DEFAULT );
}
if (NOT Matrix->Partitioned) spPartition( eMatrix, spDEFAULT_PARTITION );
#if spCOMPLEX
if (Matrix->Complex) return FactorComplexMatrix( Matrix );
#endif
#if REAL
Size = Matrix->Size;
if (Matrix->Diag[1]->Real == 0.0) return ZeroPivot( Matrix, 1 );
Matrix->Diag[1]->Real = 1.0 / Matrix->Diag[1]->Real;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -