📄 spfactor.c
字号:
/* * MATRIX FACTORIZATION MODULE * * Author: Advising Professor: * Kenneth S. Kundert Alberto Sangiovanni-Vincentelli * UC Berkeley * * This file contains the routines to factor the matrix into LU form. * * >>> 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 CreateFillin * MatrixIsSingular ZeroPivot * WriteStatus *//* * 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. */#include <assert.h>#define spINSIDE_SPARSE#include "spconfig.h"#include "spmatrix.h"#include "spdefs.h"/* * Function declarations */static int FactorComplexMatrix( MatrixPtr );static void CountMarkowitz( MatrixPtr, RealVector, int );static void MarkowitzProducts( MatrixPtr, int );static ElementPtr SearchForPivot( MatrixPtr, int, int );static ElementPtr SearchForSingleton( MatrixPtr, int );static ElementPtr QuicklySearchDiagonal( MatrixPtr, int );static ElementPtr SearchDiagonal( 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 );static void ComplexRowColElimination( MatrixPtr, ElementPtr );static void UpdateMarkowitzNumbers( MatrixPtr, ElementPtr );static ElementPtr CreateFillin( MatrixPtr, int, int );static int MatrixIsSingular( MatrixPtr, int );static int ZeroPivot( MatrixPtr, int );/* * ORDER AND FACTOR MATRIX * * This routine chooses a pivot order for the matrix and factors it * into 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 L and the * diagonal terms of U are one. * * >>> Returned: * The error code is returned. Possible errors are listed below. * * >>> Arguments: * Matrix <input> (char *) * Pointer to matrix. * RHS <input> (RealVector) * 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. * RelThreshold <input> (RealNumber) * 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 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 * 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. * AbsThreshold <input> (RealNumber) * 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 AbsThreshold should be set to zero. * 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. * DiagPivoting <input> (int) * A flag indicating that pivot selection should be confined to the * diagonal if possible. If DiagPivoting is nonzero and if * 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. * * >>> Local variables: * pPivot (ElementPtr) * Pointer to the element being used as a pivot. * ReorderingRequired (int) * Flag that indicates whether reordering is required. * * >>> Possible errors: * spNO_MEMORY * spSINGULAR * spSMALL_PIVOT * Error is cleared in this function. */intspOrderAndFactor(void *eMatrix, RealNumber RHS[], RealNumber RelThreshold, RealNumber AbsThreshold, int DiagPivoting){ MatrixPtr Matrix = (MatrixPtr)eMatrix; ElementPtr pPivot; int Step, Size, ReorderingRequired; ElementPtr SearchForPivot(); RealNumber LargestInCol, FindLargestInCol(); /* Begin `spOrderAndFactor'. */ assert( IS_VALID(Matrix) && !Matrix->Factored); 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; ReorderingRequired = NO; if (!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 (Matrix->Complex) ComplexRowColElimination( Matrix, pPivot ); else RealRowColElimination( Matrix, pPivot ); } else { ReorderingRequired = YES; break; /* for loop */ } } if (!ReorderingRequired) 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(!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 (!Matrix->RowsLinked) spcLinkRows( Matrix ); if (!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 (Matrix->Complex) ComplexRowColElimination( Matrix, pPivot ); else RealRowColElimination( Matrix, pPivot ); 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;}/* * FACTOR MATRIX * * 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" LU * factorization. Pivots are associated with the lower triangular * matrix and the diagonals of the upper triangular matrix are ones. * * >>> Returned: * The error code is returned. Possible errors are listed below. * * >>> Arguments: * Matrix <input> (char *) * Pointer to matrix. * * >>> Possible errors: * spNO_MEMORY * spSINGULAR * spZERO_DIAG * spSMALL_PIVOT * Error is cleared in this function. */intspFactor(void *eMatrix){ MatrixPtr Matrix = (MatrixPtr)eMatrix; ElementPtr pElement; ElementPtr pColumn; int Step, Size; RealNumber Mult; /* Begin `spFactor'. */ assert( IS_VALID(Matrix) && !Matrix->Factored); if (Matrix->NeedsOrdering) { return spOrderAndFactor( eMatrix, (RealVector)NULL, 0.0, 0.0, DIAG_PIVOTING_AS_DEFAULT ); } if (!Matrix->Partitioned) spPartition( eMatrix, spDEFAULT_PARTITION ); if (Matrix->Complex) return FactorComplexMatrix( Matrix ); Size = Matrix->Size; if (Matrix->Diag[1]->Real == 0.0) return ZeroPivot( Matrix, 1 ); Matrix->Diag[1]->Real = 1.0 / Matrix->Diag[1]->Real; /* Start factorization. */ for (Step = 2; Step <= Size; Step++) { if (Matrix->DoRealDirect[Step]) { /* Update column using direct addressing scatter-gather. */ RealNumber *Dest = (RealNumber *)Matrix->Intermediate; /* Scatter. */ pElement = Matrix->FirstInCol[Step]; while (pElement != NULL) { Dest[pElement->Row] = pElement->Real; pElement = pElement->NextInCol; } /* Update column. */ pColumn = Matrix->FirstInCol[Step]; while (pColumn->Row < Step) { pElement = Matrix->Diag[pColumn->Row]; pColumn->Real = Dest[pColumn->Row] * pElement->Real; while ((pElement = pElement->NextInCol) != NULL) Dest[pElement->Row] -= pColumn->Real * pElement->Real; pColumn = pColumn->NextInCol; } /* Gather. */ pElement = Matrix->Diag[Step]->NextInCol; while (pElement != NULL) { pElement->Real = Dest[pElement->Row];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -